import { execSync } from 'child_process'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); export const projectRoot = join(__dirname, '../..'); export const composeFile = join(projectRoot, 'compose.yaml'); export function execWithRetry(command: string, maxRetries = 3): string { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return execSync(command, { encoding: 'utf-8' }); } catch (error) { if (attempt === maxRetries) throw error; // Wait before retry: 1s, 2s, 3s execSync(`sleep ${attempt}`); } } throw new Error('Unreachable'); } export function runSql(sql: string) { execWithRetry( `docker compose -f "${composeFile}" exec -T php php bin/console dbal:run-sql "${sql}" 2>&1` ); } export function clearCache() { try { execWithRetry( `docker compose -f "${composeFile}" exec -T php php bin/console cache:pool:clear paginated_queries.cache 2>&1` ); } catch { // Cache pool may not exist } } export function resolveDeterministicIds(tenantId: string): { schoolId: string; academicYearId: string } { const output = execWithRetry( `docker compose -f "${composeFile}" exec -T php php -r '` + `require "/app/vendor/autoload.php"; ` + `$t="${tenantId}"; $ns="6ba7b814-9dad-11d1-80b4-00c04fd430c8"; ` + `echo Ramsey\\Uuid\\Uuid::uuid5($ns,"school-$t")->toString()."\\n"; ` + `$m=(int)date("n"); $s=$m>=9?(int)date("Y"):(int)date("Y")-1; $e=$s+1; ` + `echo Ramsey\\Uuid\\Uuid::uuid5($ns,"$t:$s-$e")->toString();` + `' 2>&1` ).trim(); const [schoolId, academicYearId] = output.split('\n'); return { schoolId: schoolId!, academicYearId: academicYearId! }; } export function createTestUser(tenant: string, email: string, password: string, role: string) { execWithRetry( `docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --tenant=${tenant} --email=${email} --password=${password} --role=${role} 2>&1` ); }