Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { test, expect } from '@playwright/test';

/**
* Regression test for issue #424 — "Artifacts no longer show in UI".
*
* When a remote (proxy) repository pulls a package through to its upstream,
* the cached object must appear in the repository artifact listing so it can
* be browsed and scanned. The v1.2.0 regression (backend #1278 / #1280)
* stopped recording proxy-cached items in the `artifacts` table to fix a
* filesystem storage-path bug; the side effect was that remote-cached items
* vanished from:
*
* GET /api/v1/repositories/{key}/artifacts
*
* and therefore from the repo browser UI, even though the bytes were written
* to storage. The backend now reconstructs the listing for remote repos from
* the proxy cache (backend #1548), so this verifies the user-visible flow:
* pull a package through the seeded NPM remote, then confirm it shows up in
* both the listing API and the Artifacts tab of the repo detail page.
*
* Uses the seeded `e2e-npm-remote` repository (proxies registry.npmjs.org,
* see e2e/setup/seed-data.ts). The NPM proxy routes are mounted at
* `/npm/{repo_key}/...`; fetching package metadata caches an entry under the
* package path, which the listing surfaces with `path === <package>`.
*/
test.describe('Remote repository cached artifacts (#424)', () => {
const REPO_KEY = 'e2e-npm-remote';
// A tiny, stable package with no dependencies — cheap to pull through.
const PACKAGE = 'is-odd';

/**
* Pull a package through the NPM proxy so the backend caches it. Returns
* true once the proxy responds successfully; remote upstream hiccups should
* skip the test rather than fail it (the listing behavior, not upstream
* availability, is under test).
*/
async function pullThroughProxy(
request: import('@playwright/test').APIRequestContext
): Promise<boolean> {
const resp = await request.get(`/npm/${REPO_KEY}/${PACKAGE}`);
return resp.ok();
}

test('proxy-cached package appears in the artifact listing API', async ({ request }) => {
const pulled = await pullThroughProxy(request);
if (!pulled) {
test.skip(true, 'Upstream NPM registry unavailable; cannot exercise pull-through');
return;
}

const listResp = await request.get(
`/api/v1/repositories/${REPO_KEY}/artifacts?per_page=100`
);
expect(listResp.ok(), `Listing failed: ${listResp.status()}`).toBeTruthy();

const body = await listResp.json();
expect(Array.isArray(body.items), 'listing has an items array').toBeTruthy();

// The regression manifested as an empty listing despite storage filling
// up. After the fix the pulled package must be present.
expect(
body.items.length,
'remote repo listing must not be empty after a pull-through'
).toBeGreaterThan(0);

Check failure on line 64 in e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts

View workflow job for this annotation

GitHub Actions / E2E Interactions (shard 3/3)

[interactions] › e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts:44:7 › Remote repository cached artifacts (#424) › proxy-cached package appears in the artifact listing API

3) [interactions] › e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts:44:7 › Remote repository cached artifacts (#424) › proxy-cached package appears in the artifact listing API Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: remote repo listing must not be empty after a pull-through expect(received).toBeGreaterThan(expected) Expected: > 0 Received: 0 62 | body.items.length, 63 | 'remote repo listing must not be empty after a pull-through' > 64 | ).toBeGreaterThan(0); | ^ 65 | 66 | const match = body.items.find( 67 | (item: { path?: string; name?: string }) => at /home/runner/work/artifact-keeper-web/artifact-keeper-web/e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts:64:7

Check failure on line 64 in e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts

View workflow job for this annotation

GitHub Actions / E2E Interactions (shard 3/3)

[interactions] › e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts:44:7 › Remote repository cached artifacts (#424) › proxy-cached package appears in the artifact listing API

3) [interactions] › e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts:44:7 › Remote repository cached artifacts (#424) › proxy-cached package appears in the artifact listing API Error: remote repo listing must not be empty after a pull-through expect(received).toBeGreaterThan(expected) Expected: > 0 Received: 0 62 | body.items.length, 63 | 'remote repo listing must not be empty after a pull-through' > 64 | ).toBeGreaterThan(0); | ^ 65 | 66 | const match = body.items.find( 67 | (item: { path?: string; name?: string }) => at /home/runner/work/artifact-keeper-web/artifact-keeper-web/e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts:64:7

const match = body.items.find(
(item: { path?: string; name?: string }) =>
(item.path ?? '').includes(PACKAGE) || (item.name ?? '').includes(PACKAGE)
);
expect(
match,
`pulled package "${PACKAGE}" should be listed in the remote repo`
).toBeTruthy();
// The cached entry carries real metadata reconstructed from the sidecar.
expect(match.size_bytes, 'cached entry reports a size').toBeGreaterThan(0);
expect(match.checksum_sha256, 'cached entry reports a checksum').toBeTruthy();
});

test('proxy-cached package shows in the repo browser Artifacts tab', async ({
page,
request,
}) => {
const pulled = await pullThroughProxy(request);
if (!pulled) {
test.skip(true, 'Upstream NPM registry unavailable; cannot exercise pull-through');
return;
}

await page.goto(`/repositories/${REPO_KEY}`);
await page.waitForLoadState('domcontentloaded');

// The Artifacts tab is the flat-list browser fed by the listing endpoint.
const artifactsTab = page.getByRole('tab', { name: /artifacts/i }).first();
if (await artifactsTab.isVisible({ timeout: 5000 }).catch(() => false)) {
await artifactsTab.click();
}

const table = page.getByRole('table').first();
await expect(table).toBeVisible({ timeout: 15000 });

// Narrow the table to the package we pulled. The search input filters the
// listing server-side (maps to the `q` parameter).
const searchInput = page.getByPlaceholder(/search/i).first();
if (await searchInput.isVisible({ timeout: 3000 }).catch(() => false)) {
await searchInput.fill(PACKAGE);
await page.waitForTimeout(2000);
}

const artifactRow = table.getByRole('row').filter({ hasText: PACKAGE });
await expect(
artifactRow.first(),
`cached package "${PACKAGE}" should be a row in the Artifacts tab`
).toBeVisible({ timeout: 10000 });

Check failure on line 113 in e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts

View workflow job for this annotation

GitHub Actions / E2E Interactions (shard 3/3)

[interactions] › e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts:79:7 › Remote repository cached artifacts (#424) › proxy-cached package shows in the repo browser Artifacts tab

4) [interactions] › e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts:79:7 › Remote repository cached artifacts (#424) › proxy-cached package shows in the repo browser Artifacts tab Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: cached package "is-odd" should be a row in the Artifacts tab expect(locator).toBeVisible() failed Locator: getByRole('table').first().getByRole('row').filter({ hasText: 'is-odd' }).first() Expected: visible Timeout: 10000ms Error: element(s) not found Call log: - cached package "is-odd" should be a row in the Artifacts tab with timeout 10000ms - waiting for getByRole('table').first().getByRole('row').filter({ hasText: 'is-odd' }).first() 111 | artifactRow.first(), 112 | `cached package "${PACKAGE}" should be a row in the Artifacts tab` > 113 | ).toBeVisible({ timeout: 10000 }); | ^ 114 | }); 115 | }); 116 | at /home/runner/work/artifact-keeper-web/artifact-keeper-web/e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts:113:7

Check failure on line 113 in e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts

View workflow job for this annotation

GitHub Actions / E2E Interactions (shard 3/3)

[interactions] › e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts:79:7 › Remote repository cached artifacts (#424) › proxy-cached package shows in the repo browser Artifacts tab

4) [interactions] › e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts:79:7 › Remote repository cached artifacts (#424) › proxy-cached package shows in the repo browser Artifacts tab Error: cached package "is-odd" should be a row in the Artifacts tab expect(locator).toBeVisible() failed Locator: getByRole('table').first().getByRole('row').filter({ hasText: 'is-odd' }).first() Expected: visible Timeout: 10000ms Error: element(s) not found Call log: - cached package "is-odd" should be a row in the Artifacts tab with timeout 10000ms - waiting for getByRole('table').first().getByRole('row').filter({ hasText: 'is-odd' }).first() 111 | artifactRow.first(), 112 | `cached package "${PACKAGE}" should be a row in the Artifacts tab` > 113 | ).toBeVisible({ timeout: 10000 }); | ^ 114 | }); 115 | }); 116 | at /home/runner/work/artifact-keeper-web/artifact-keeper-web/e2e/suites/interactions/repositories/remote-cached-artifacts.spec.ts:113:7
});
});
Loading