Regalamiunsorriso/faceai/tests/e2e/faceai-test-utils.js

204 lines
5.8 KiB
JavaScript
Raw Normal View History

2026-04-12 19:31:12 +02:00
const fs = require('node:fs/promises');
const path = require('node:path');
const { spawn } = require('node:child_process');
const ROOT_DIR = path.resolve(__dirname, '..', '..');
const WORKSPACE_ROOT = path.resolve(ROOT_DIR, '..');
const LOG_ROOT = path.join(ROOT_DIR, 'logs');
const SEARCH_LOG_ROOT = path.join(LOG_ROOT, 'searches');
const FACEAI_BASE_URL = process.env.FACEAI_E2E_BASE_URL || 'http://127.0.0.1:3001';
2026-04-22 18:41:37 +02:00
const SIMULATOR_URL = process.env.FACEAI_E2E_SIMULATOR_URL || 'http://127.0.0.1:8080/Foto2.abl?id_gara=1018557&pageRow=96&pageNumber=1';
2026-04-12 19:31:12 +02:00
const LEGACY_BASE_URL = process.env.FACEAI_E2E_LEGACY_BASE_URL || 'http://127.0.0.1:8080';
const LEGACY_HOME_URL = process.env.FACEAI_E2E_LEGACY_HOME_URL || `${LEGACY_BASE_URL}/index.jsp`;
const SELFIE_NAME = process.env.FACEAI_E2E_SELFIE || 'DSC_1960.JPG';
const EXPECTED_MATCH_COUNT = Number(process.env.FACEAI_E2E_EXPECTED_MATCH_COUNT || '6');
2026-04-22 18:41:37 +02:00
const LEGACY_RACE_ID = process.env.FACEAI_E2E_RACE_ID || '1018557';
2026-04-12 19:31:12 +02:00
function quoteShellArg(value) {
if (!/[\s"]/u.test(value)) {
return value;
}
return `"${value.replace(/"/g, '\\"')}"`;
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function runCommand(command, args, options = {}) {
const { cwd = ROOT_DIR, allowFailure = false } = options;
const executable = process.platform === 'win32' && command === 'npm' ? 'npm.cmd' : command;
const useShell = process.platform === 'win32';
return new Promise((resolve, reject) => {
const child = useShell
? spawn([executable, ...args].map(quoteShellArg).join(' '), {
cwd,
env: process.env,
shell: true,
stdio: ['ignore', 'pipe', 'pipe']
})
: spawn(executable, args, {
cwd,
env: process.env,
stdio: ['ignore', 'pipe', 'pipe']
});
let stdout = '';
let stderr = '';
child.stdout.on('data', (chunk) => {
stdout += chunk.toString();
});
child.stderr.on('data', (chunk) => {
stderr += chunk.toString();
});
child.on('error', reject);
child.on('close', (code) => {
const result = { code, stdout, stderr };
if (code === 0 || allowFailure) {
resolve(result);
return;
}
const error = new Error(`Command failed: ${executable} ${args.join(' ')}`);
error.result = result;
reject(error);
});
});
}
function dockerCompose(args, options) {
return runCommand('docker', ['compose', ...args], options);
}
async function prepareHostState() {
await fs.rm(LOG_ROOT, { recursive: true, force: true });
await fs.mkdir(LOG_ROOT, { recursive: true });
}
async function waitForHttp(url, validate, timeoutMs = 3 * 60 * 1000) {
const deadline = Date.now() + timeoutMs;
let lastError = null;
while (Date.now() < deadline) {
try {
const response = await fetch(url);
const bodyText = await response.text();
let parsedBody = null;
try {
parsedBody = JSON.parse(bodyText);
} catch {
parsedBody = null;
}
if (validate({ response, bodyText, parsedBody })) {
return;
}
lastError = new Error(`Readiness check did not pass for ${url}.`);
} catch (error) {
lastError = error;
}
await sleep(1000);
}
throw lastError || new Error(`Timed out waiting for ${url}`);
}
function getSelfiePath(fileName = SELFIE_NAME) {
return path.join(WORKSPACE_ROOT, 'test_pkl', 'test_images', fileName);
}
function buildSimulatorUrl({
2026-04-22 18:41:37 +02:00
raceId = LEGACY_RACE_ID,
2026-04-12 19:31:12 +02:00
lang = 'it',
2026-04-22 18:41:37 +02:00
raceSlug = 'livorno',
raceName = 'Livorno',
2026-04-12 19:31:12 +02:00
raceYear = '2026',
raceMonthFolder = '04.APRILE',
2026-04-22 18:41:37 +02:00
raceFolder = 'LIVORNO',
pageRow = '96',
pageNumber = '1'
2026-04-12 19:31:12 +02:00
} = {}) {
2026-04-22 18:41:37 +02:00
const url = new URL('/Foto2.abl', LEGACY_BASE_URL);
url.searchParams.set('id_gara', raceId);
url.searchParams.set('pageRow', pageRow);
url.searchParams.set('pageNumber', pageNumber);
2026-04-12 19:31:12 +02:00
url.searchParams.set('lang', lang);
return url.toString();
}
function buildHandoffUrl({
2026-04-22 18:41:37 +02:00
raceId = LEGACY_RACE_ID,
2026-04-12 19:31:12 +02:00
lang = 'it',
2026-04-22 18:41:37 +02:00
raceSlug = 'livorno',
raceName = 'Livorno',
2026-04-12 19:31:12 +02:00
raceYear = '2026',
raceMonthFolder = '04.APRILE',
2026-04-22 18:41:37 +02:00
raceFolder = 'LIVORNO',
2026-04-12 19:31:12 +02:00
userId = '1',
displayName = `Local Test User ${userId}`,
email = `local-test-${userId}@example.invalid`,
membershipStatus = 'active',
returnUrl = buildSimulatorUrl({ raceId, lang, raceSlug, raceName, raceYear, raceMonthFolder, raceFolder })
} = {}) {
2026-04-22 18:41:37 +02:00
const url = new URL('/dev/legacy/launch', FACEAI_BASE_URL);
2026-04-12 19:31:12 +02:00
url.searchParams.set('raceId', raceId);
url.searchParams.set('raceSlug', raceSlug);
url.searchParams.set('raceName', raceName);
url.searchParams.set('raceYear', raceYear);
url.searchParams.set('raceMonthFolder', raceMonthFolder);
url.searchParams.set('raceFolder', raceFolder);
url.searchParams.set('lang', lang);
url.searchParams.set('returnUrl', returnUrl);
url.searchParams.set('devUserId', userId);
url.searchParams.set('devDisplayName', displayName);
url.searchParams.set('devEmail', email);
url.searchParams.set('devMembershipStatus', membershipStatus);
return url.toString();
}
function getSearchArtifacts(searchId) {
const searchRoot = path.join(SEARCH_LOG_ROOT, searchId);
return {
searchRoot,
backendLogPath: path.join(LOG_ROOT, 'backend.log'),
processorLogPath: path.join(LOG_ROOT, 'processor.log'),
workerLogPath: path.join(searchRoot, 'worker.log'),
matcherLogPath: path.join(searchRoot, 'matcher.log')
};
}
async function readUtf8(filePath) {
return fs.readFile(filePath, 'utf8');
}
module.exports = {
ROOT_DIR,
LOG_ROOT,
SEARCH_LOG_ROOT,
FACEAI_BASE_URL,
LEGACY_BASE_URL,
LEGACY_HOME_URL,
SIMULATOR_URL,
SELFIE_NAME,
EXPECTED_MATCH_COUNT,
2026-04-22 18:41:37 +02:00
LEGACY_RACE_ID,
2026-04-12 19:31:12 +02:00
buildHandoffUrl,
buildSimulatorUrl,
dockerCompose,
getSearchArtifacts,
getSelfiePath,
prepareHostState,
readUtf8,
runCommand,
waitForHttp
};