158 lines
5.4 KiB
TypeScript
158 lines
5.4 KiB
TypeScript
/**
|
|
* Global teardown for Playwright tests
|
|
* Handles cleanup based on environment:
|
|
* - CI: Full schema reset (database can be destroyed and recreated)
|
|
* - Dev: Selective cleanup of test records (preserve real data)
|
|
* - Prod: Selective cleanup of test records (preserve real data)
|
|
*/
|
|
|
|
import { type FullConfig } from '@playwright/test';
|
|
import { PrismaClient } from '@prisma/client';
|
|
import { PrismaPg } from '@prisma/adapter-pg';
|
|
import { execSync } from 'child_process';
|
|
|
|
const TEST_PATTERNS = {
|
|
players: [
|
|
'%Test%',
|
|
'%Setup%',
|
|
'%Home Test%',
|
|
'%Home Match Player%',
|
|
'%Admin User%',
|
|
'%NinePart%',
|
|
'%Nine Part%',
|
|
'%Test Player%',
|
|
'%TestUser%',
|
|
'%Cucumber%',
|
|
'%Config Admin%',
|
|
'%Elo Test%',
|
|
'%Dedupe%',
|
|
'%Whitespace%',
|
|
'%Aggregate%',
|
|
'%Tournament Player%',
|
|
'%Schedule Player%',
|
|
'%Test Activity Player%',
|
|
'%HP%',
|
|
],
|
|
events: [
|
|
'%Test%',
|
|
'%Setup%',
|
|
'%Recent%',
|
|
'%Test Tournament%',
|
|
'%Cucumber%',
|
|
'%Elo Test%',
|
|
'%Schedule%',
|
|
],
|
|
users: [
|
|
'%test%',
|
|
'%setup%',
|
|
'%cucumber%',
|
|
'%TestUser%',
|
|
'%logout-test%',
|
|
'%admin-%',
|
|
'%config-admin%',
|
|
'%schedule-admin%',
|
|
'%nine-part-test%',
|
|
'%tour-admin-%',
|
|
'%president-%',
|
|
]
|
|
};
|
|
|
|
function isDatabase(url: string, name: string): boolean {
|
|
return url.includes(name);
|
|
}
|
|
|
|
function isProductionDatabase(): boolean {
|
|
const dbUrl = process.env.DATABASE_URL || '';
|
|
return isDatabase(dbUrl, 'euchre_camp') && !isDatabase(dbUrl, '_dev') && !isDatabase(dbUrl, '_ci');
|
|
}
|
|
|
|
function isCIDatabase(): boolean {
|
|
const dbUrl = process.env.DATABASE_URL || '';
|
|
return isDatabase(dbUrl, '_ci');
|
|
}
|
|
|
|
function buildLikeClause(patterns: string[]): string {
|
|
return patterns.map(p => `name LIKE '${p}'`).join(' OR ');
|
|
}
|
|
|
|
function buildEmailLikeClause(patterns: string[]): string {
|
|
return patterns.map(p => `email LIKE '${p}'`).join(' OR ');
|
|
}
|
|
|
|
function createPrismaClient() {
|
|
const databaseUrl = process.env.DATABASE_URL;
|
|
if (!databaseUrl) throw new Error('DATABASE_URL is required');
|
|
const adapter = new PrismaPg({ connectionString: databaseUrl });
|
|
return new PrismaClient({ adapter });
|
|
}
|
|
|
|
async function resetDatabaseSchema(prisma: PrismaClient) {
|
|
console.log('Resetting database schema...');
|
|
|
|
const tables = await prisma.$queryRaw<{ tablename: string }[]>`
|
|
SELECT tablename FROM pg_tables
|
|
WHERE schemaname = 'public' AND tablename NOT LIKE '_prisma_migrations'
|
|
`;
|
|
|
|
if (tables.length > 0) {
|
|
await prisma.$executeRawUnsafe(`
|
|
DROP SCHEMA public CASCADE;
|
|
CREATE SCHEMA public;
|
|
`);
|
|
console.log(`Dropped ${tables.length} tables`);
|
|
}
|
|
|
|
console.log('Running migrations...');
|
|
execSync('bunx prisma migrate deploy', { stdio: 'inherit' });
|
|
}
|
|
|
|
async function cleanupTestRecords(prisma: PrismaClient) {
|
|
console.log('Cleaning up test records...');
|
|
|
|
const playerWhere = buildLikeClause(TEST_PATTERNS.players);
|
|
const eventWhere = buildLikeClause(TEST_PATTERNS.events);
|
|
const userWhere = buildEmailLikeClause(TEST_PATTERNS.users);
|
|
|
|
await prisma.$executeRawUnsafe(`DELETE FROM elo_snapshots WHERE "playerId" IN (SELECT id FROM players WHERE (${playerWhere}));`);
|
|
await prisma.$executeRawUnsafe(`DELETE FROM partnership_games WHERE "player1Id" IN (SELECT id FROM players WHERE (${playerWhere}));`);
|
|
await prisma.$executeRawUnsafe(`DELETE FROM partnership_stats WHERE "player1Id" IN (SELECT id FROM players WHERE (${playerWhere}));`);
|
|
await prisma.$executeRawUnsafe(`DELETE FROM event_participants WHERE "eventId" IN (SELECT id FROM events WHERE (${eventWhere}));`);
|
|
await prisma.$executeRawUnsafe(`DELETE FROM tournament_rounds WHERE "eventId" IN (SELECT id FROM events WHERE (${eventWhere}));`);
|
|
await prisma.$executeRawUnsafe(`DELETE FROM bracket_matchups WHERE "eventId" IN (SELECT id FROM events WHERE (${eventWhere}));`);
|
|
await prisma.$executeRawUnsafe(`DELETE FROM events WHERE (${eventWhere});`);
|
|
console.log('Deleted test events');
|
|
|
|
await prisma.$executeRawUnsafe(`DELETE FROM elo_ratings WHERE "playerId" IN (SELECT id FROM players WHERE (${playerWhere}));`);
|
|
await prisma.$executeRawUnsafe(`DELETE FROM glicko2_ratings WHERE "playerId" IN (SELECT id FROM players WHERE (${playerWhere}));`);
|
|
await prisma.$executeRawUnsafe(`DELETE FROM open_skill_ratings WHERE "playerId" IN (SELECT id FROM players WHERE (${playerWhere}));`);
|
|
await prisma.$executeRawUnsafe(`DELETE FROM players WHERE (${playerWhere});`);
|
|
console.log('Deleted test players');
|
|
|
|
await prisma.$executeRawUnsafe(`DELETE FROM users WHERE (${userWhere}) AND "playerId" IS NULL;`);
|
|
console.log('Deleted test users (without player associations)');
|
|
|
|
await prisma.$disconnect();
|
|
}
|
|
|
|
export default async function globalTeardown(config: FullConfig) {
|
|
console.log('\n=== Global Teardown ===');
|
|
|
|
const dbUrl = process.env.DATABASE_URL || '';
|
|
|
|
if (isCIDatabase()) {
|
|
console.log('CI environment - resetting database schema');
|
|
const prisma = createPrismaClient();
|
|
await resetDatabaseSchema(prisma);
|
|
await prisma.$disconnect();
|
|
} else if (isProductionDatabase()) {
|
|
console.log('Production environment - selective cleanup of test records');
|
|
const prisma = createPrismaClient();
|
|
await cleanupTestRecords(prisma);
|
|
} else {
|
|
console.log('Development environment - selective cleanup of test records');
|
|
const prisma = createPrismaClient();
|
|
await cleanupTestRecords(prisma);
|
|
}
|
|
|
|
console.log('=== Global Teardown Complete ===\n');
|
|
} |