From f19b8e20ff7ecaec468ddcc4fa02effec2aa6dcd Mon Sep 17 00:00:00 2001 From: MaddoScientisto Date: Sun, 19 Apr 2026 12:10:18 +0200 Subject: [PATCH] Enhance Git LFS validation in CI workflow and update Docker Compose configuration --- .../workflows/publish-faceai-container.yml | 30 +++++++++++++++++++ faceai/docker-compose.yml | 1 - faceai/tests/e2e/faceai-simulator.spec.js | 8 ++--- faceai/tests/live-site/live-race.spec.js | 25 ++++++++++++++++ test-results/.last-run.json | 4 +++ 5 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 test-results/.last-run.json diff --git a/.forgejo/workflows/publish-faceai-container.yml b/.forgejo/workflows/publish-faceai-container.yml index 356c5444..935101fb 100644 --- a/.forgejo/workflows/publish-faceai-container.yml +++ b/.forgejo/workflows/publish-faceai-container.yml @@ -28,6 +28,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + lfs: true - name: Validate workflow variables run: | @@ -41,6 +43,34 @@ jobs: if [ ! -f "faceai/package.json" ]; then echo "faceai/package.json is missing from the repository checkout"; exit 1; fi if [ ! -f "bin/Face_Recognition_Unix/face_matcher" ]; then echo "bin/Face_Recognition_Unix/face_matcher is missing from the repository checkout"; exit 1; fi + - name: Validate Git LFS checkout for matcher binary + run: | + set -eu + if ! command -v git >/dev/null 2>&1; then + echo "git is required to validate Git LFS checkout" + exit 1 + fi + + if ! git lfs version >/dev/null 2>&1; then + echo "git-lfs is required on the runner so the processor image can include the real matcher binary" + exit 1 + fi + + git lfs pull --include="bin/Face_Recognition_Unix/face_matcher" + + if grep -q "https://git-lfs.github.com/spec/v1" bin/Face_Recognition_Unix/face_matcher; then + echo "bin/Face_Recognition_Unix/face_matcher is still an LFS pointer, not the real binary" + exit 1 + fi + + MATCHER_SIZE="$(wc -c < bin/Face_Recognition_Unix/face_matcher | tr -d '[:space:]')" + if [ "${MATCHER_SIZE}" -lt 1000000 ]; then + echo "bin/Face_Recognition_Unix/face_matcher is unexpectedly small (${MATCHER_SIZE} bytes) and does not look like the real binary" + exit 1 + fi + + echo "Validated Git LFS checkout for matcher binary (${MATCHER_SIZE} bytes)" + - name: Validate registry secrets run: | set -eu diff --git a/faceai/docker-compose.yml b/faceai/docker-compose.yml index e6659a58..616a7c37 100644 --- a/faceai/docker-compose.yml +++ b/faceai/docker-compose.yml @@ -75,7 +75,6 @@ services: FACEAI_PROCESSOR_HEARTBEAT_INTERVAL_MS: 5000 FACEAI_PROCESSOR_HEARTBEAT_TTL_SECONDS: 20 volumes: - - .:/app - ./logs:/data/logs - ../test_pkl:/data/pkl:ro - faceai-runtime:/data/runtime diff --git a/faceai/tests/e2e/faceai-simulator.spec.js b/faceai/tests/e2e/faceai-simulator.spec.js index 65dd4096..9f8c9bbd 100644 --- a/faceai/tests/e2e/faceai-simulator.spec.js +++ b/faceai/tests/e2e/faceai-simulator.spec.js @@ -166,7 +166,7 @@ test('shows the unsupported-race message when the current race has no PKL data a await page.waitForTimeout(2000); await expect(page).toHaveURL(FACEAI_HOME_URL_RE); - await page.getByRole('link', { name: 'Torna alla pagina gara' }).click(); + await page.getByRole('button', { name: 'Torna alla pagina gara' }).click(); await expect(page).toHaveURL(buildLegacySimulatorReturnMatcher('404')); }); @@ -198,7 +198,7 @@ test('shows a localized invalid-race error when session race data points to a mi await expect(page.locator('input[type="file"]')).toBeDisabled(); await expect(page.getByRole('button', { name: 'Choose image' })).toBeDisabled(); await expect(page.getByRole('button', { name: 'Start Face ID search' })).toHaveCount(0); - await expect(page.getByRole('link', { name: 'Back to the race page' })).toBeVisible(); + await expect(page.getByRole('button', { name: 'Back to the race page' })).toBeVisible(); await expect.poll(() => { return consoleErrors.find((entry) => entry.includes('[FaceAI] Invalid race data:')) || null; }).toContain('RACE_DIRECTORY_NOT_FOUND'); @@ -206,7 +206,7 @@ test('shows a localized invalid-race error when session race data points to a mi return consoleErrors.find((entry) => entry.includes('[FaceAI] Invalid race data:')) || null; }).toContain('THIS RACE DOES NOT EXIST'); - await page.getByRole('link', { name: 'Back to the race page' }).click(); + await page.getByRole('button', { name: 'Back to the race page' }).click(); await expect(page).toHaveURL(buildLegacySimulatorReturnMatcher('405')); }); @@ -262,7 +262,7 @@ test('shows the no-face message and allows the user to return to the race page', expectedSelfieName: 'DSC_1994.JPG' }); - await page.getByRole('link', { name: 'Torna alla pagina gara' }).click(); + await page.getByRole('button', { name: 'Torna alla pagina gara' }).click(); await expect(page).toHaveURL(buildLegacySimulatorReturnMatcher('202')); }); diff --git a/faceai/tests/live-site/live-race.spec.js b/faceai/tests/live-site/live-race.spec.js index 82d4402f..1ef27293 100644 --- a/faceai/tests/live-site/live-race.spec.js +++ b/faceai/tests/live-site/live-race.spec.js @@ -121,6 +121,31 @@ test('launches the live FaceAI app with race storage metadata and a styled heade expect(consoleErrors.find((entry) => entry.includes('[FaceAI] Invalid race data:')) || '').toBe(''); }); +test('returns to the live race page from FaceAI without leaving the legacy spinner stuck', async ({ page }) => { + await openLiveFaceAi(page); + + await page.getByRole('button', { name: /Torna alla pagina gara|Back to the race page/i }).click(); + + await page.waitForURL((url) => { + return url.toString().startsWith(LIVE_SITE_BASE_URL) && !url.toString().startsWith(LIVE_FACEAI_BASE_URL); + }, { + timeout: 60 * 1000 + }); + + await expect(page.locator('form[onsubmit="return searching()"]')).toBeVisible(); + await expect(page.locator('#faceaiLaunchButton')).toBeVisible(); + + const bodyState = await page.locator('body').evaluate((element) => { + return { + className: element.className, + ariaBusy: element.getAttribute('aria-busy') + }; + }); + + expect(bodyState.className).not.toContain('loading'); + expect(bodyState.ariaBusy).not.toBe('true'); +}); + test.skip(!LIVE_SITE_RUN_UPLOAD_FLOW, 'Set LIVE_SITE_RUN_UPLOAD_FLOW=1 to exercise the live upload flow.'); test('accepts the supplied portrait image for the live upload flow', async ({ page }) => { diff --git a/test-results/.last-run.json b/test-results/.last-run.json new file mode 100644 index 00000000..5fca3f84 --- /dev/null +++ b/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "failed", + "failedTests": [] +} \ No newline at end of file