Enhance Docker and PowerShell scripts for improved functionality and maintainability
All checks were successful
Publish FaceAI Container / publish (push) Successful in 6m52s

- Updated Dockerfile to include default MySQL client for better database interaction.
- Modified entrypoint.sh to support additional workspace for legacy applications and added MySQL readiness check before startup.
- Enhanced PowerShell script for trimming MySQL dumps to include overlay dumps and improved error handling for missing race and user IDs.
- Added new image files and face encoding pickles for various projects, ensuring comprehensive data availability.
- Removed outdated face encoding pickle from PISA directory to maintain data relevance.

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
MaddoScientisto 2026-04-22 22:45:44 +02:00
commit dd7d4c865b
54 changed files with 492 additions and 144 deletions

View file

@ -1,8 +1,10 @@
const { test, expect } = require('@playwright/test');
const {
ensureLocalAuthenticatedRacePage,
EXPECTED_MATCH_COUNT,
FACEAI_BASE_URL,
LEGACY_RACE_ID,
SELFIE_NAME,
buildHandoffUrl,
buildSimulatorUrl,
getSearchArtifacts,
@ -61,6 +63,7 @@ async function readLaunchUrlFromLegacyPage(page) {
}
async function startSearch(page, selfieName) {
const selfieLabel = selfieName.split(/[\\/]+/u).pop();
const createResponsePromise = page.waitForResponse((response) => {
return response.url().includes('/api/searches')
&& response.request().method() === 'POST'
@ -68,7 +71,7 @@ async function startSearch(page, selfieName) {
});
await page.locator('input[type="file"]').setInputFiles(getSelfiePath(selfieName));
await expect(page.getByText(selfieName)).toBeVisible();
await expect(page.getByText(selfieLabel)).toBeVisible();
await page.getByRole('button', { name: 'Avvia ricerca Face ID' }).click();
const createResponse = await createResponsePromise;
@ -165,40 +168,40 @@ test('runs the legacy Tomcat flow through FaceAI and returns to the filtered leg
await launchFromSimulator(page, {
raceId: LEGACY_RACE_ID,
raceSlug: 'livorno',
raceName: 'Livorno',
raceFolder: 'LIVORNO'
raceSlug: 'isolotto',
raceName: 'Festa sociale UP Isolotto',
raceFolder: 'ISOLOTTO'
});
const search = await startSearch(page, 'DSC_1960.JPG');
const search = await startSearch(page, SELFIE_NAME);
await waitForLegacyResult(page, LEGACY_RACE_ID, EXPECTED_MATCH_COUNT);
await verifySearchLogs(search.id, {
expectedMatchCount: EXPECTED_MATCH_COUNT,
expectedSelfieName: 'DSC_1960.JPG'
expectedSelfieName: SELFIE_NAME.split(/[\\/]+/u).pop()
});
});
test('builds the legacy FaceAI handoff URL with the exact local race storage metadata', async ({ page }) => {
await page.goto(buildSimulatorUrl({
raceId: LEGACY_RACE_ID,
raceSlug: 'livorno',
raceName: 'Livorno',
raceSlug: 'isolotto',
raceName: 'Festa sociale UP Isolotto',
raceYear: '2026',
raceMonthFolder: '04.APRILE',
raceFolder: 'LIVORNO'
raceFolder: 'ISOLOTTO'
}), { waitUntil: 'domcontentloaded' });
await expect(page.locator('#faceAiRaceYear')).toHaveValue('2026');
await expect(page.locator('#faceAiRaceMonthFolder')).toHaveValue('04.APRILE');
await expect(page.locator('#faceAiRaceFolder')).toHaveValue('LIVORNO');
await expect(page.locator('#faceAiRaceFolder')).toHaveValue('ISOLOTTO');
const launchUrl = await readLaunchUrlFromLegacyPage(page);
expect(launchUrl.searchParams.get('raceYear')).toBe('2026');
expect(launchUrl.searchParams.get('raceMonthFolder')).toBe('04.APRILE');
expect(launchUrl.searchParams.get('raceFolder')).toBe('LIVORNO');
expect(launchUrl.searchParams.get('raceStorageRelativeDir')).toBe('2026/04.APRILE/LIVORNO');
expect(launchUrl.searchParams.get('raceFolder')).toBe('ISOLOTTO');
expect(launchUrl.searchParams.get('raceStorageRelativeDir')).toBe('2026/04.APRILE/ISOLOTTO');
});
test('shows the unsupported-race message when the current race has no PKL data and lets the user go back', async ({ page }) => {
@ -300,6 +303,19 @@ test('rejects a not-logged-in user after clicking the Face ID button and sends t
await expect(page.locator('#faceAiErrorModalMessage')).toContainText('Il servizio Face ID non e al momento disponibile. Riprova piu tardi.');
});
test('authenticates with the seeded local user and lets that user browse and launch the Livorno race page', async ({ page }) => {
await ensureLocalAuthenticatedRacePage(page, {
raceId: '1018557',
raceName: 'VIVICITTA LIVORNO',
raceYear: '2026',
raceMonthFolder: '04.APRILE',
raceFolder: 'LIVORNO'
});
await page.locator('#faceaiLaunchButton').click();
await waitForFaceAiHome(page);
});
test('shows the no-face message and allows the user to return to the race page', async ({ page }) => {
test.slow();
@ -341,8 +357,8 @@ test('opens the file chooser when the user clicks the upload button', async ({ p
await page.getByRole('button', { name: 'Scegli immagine' }).click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(getSelfiePath('DSC_1960.JPG'));
await expect(page.getByText('DSC_1960.JPG')).toBeVisible();
await fileChooser.setFiles(getSelfiePath(SELFIE_NAME));
await expect(page.getByText(SELFIE_NAME.split(/[\\/]+/u).pop())).toBeVisible();
await expect(page.getByRole('button', { name: 'Avvia ricerca Face ID' })).toBeEnabled();
});
@ -364,7 +380,7 @@ test('lets the user retry with a valid photo after a no-face upload and then ret
await expect(page.locator('.faceai-feedback')).toContainText('Nessun volto rilevato nella foto caricata');
await expect(page.locator('input[type="file"]')).toBeEnabled();
const retrySearch = await startSearch(page, 'DSC_1960.JPG');
const retrySearch = await startSearch(page, SELFIE_NAME);
await waitForLegacyResult(page, '202', EXPECTED_MATCH_COUNT);
await verifySearchLogs(noFaceSearch.id, {
@ -373,7 +389,7 @@ test('lets the user retry with a valid photo after a no-face upload and then ret
});
await verifySearchLogs(retrySearch.id, {
expectedMatchCount: EXPECTED_MATCH_COUNT,
expectedSelfieName: 'DSC_1960.JPG'
expectedSelfieName: SELFIE_NAME.split(/[\\/]+/u).pop()
});
});
@ -403,7 +419,7 @@ test('allows two users to process different photos at the same time', async ({ b
]);
const [searchOne, searchTwo] = await Promise.all([
startSearch(pages[0], 'DSC_1960.JPG'),
startSearch(pages[0], SELFIE_NAME),
startSearch(pages[1], 'DSC_1987.JPG')
]);
@ -413,7 +429,7 @@ test('allows two users to process different photos at the same time', async ({ b
]);
await Promise.all([
verifySearchLogs(searchOne.id, { expectedSelfieName: 'DSC_1960.JPG' }),
verifySearchLogs(searchOne.id, { expectedSelfieName: SELFIE_NAME.split(/[\\/]+/u).pop() }),
verifySearchLogs(searchTwo.id, { expectedSelfieName: 'DSC_1987.JPG' })
]);
} finally {
@ -436,7 +452,7 @@ test('queues the third user until a worker is free and then completes all three
]);
const [searchOne, searchTwo, searchThree] = await Promise.all([
startSearch(pages[0], 'DSC_1960.JPG'),
startSearch(pages[0], SELFIE_NAME),
startSearch(pages[1], 'DSC_1987.JPG'),
startSearch(pages[2], 'DSC_2058.JPG')
]);
@ -477,7 +493,7 @@ test('queues the third user until a worker is free and then completes all three
await Promise.all(pages.map((page) => waitForLegacyResult(page, '202')));
await Promise.all([
verifySearchLogs(searchOne.id, { expectedSelfieName: 'DSC_1960.JPG' }),
verifySearchLogs(searchOne.id, { expectedSelfieName: SELFIE_NAME.split(/[\\/]+/u).pop() }),
verifySearchLogs(searchTwo.id, { expectedSelfieName: 'DSC_1987.JPG' }),
verifySearchLogs(searchThree.id, { expectedSelfieName: 'DSC_2058.JPG' })
]);