chore: one-shot script to remove leaked test data from dev DB
Test runs that crash before reaching afterAll leave orphan @test.local users + programs (Test Program / getCandidates- / bulk- / source-flag- / mentor-files- name patterns). Mirrors tests/helpers.ts cleanupTestData cascade order. Idempotent — safe to re-run any time the dev DB picks up test pollution. Run: npx tsx scripts/cleanup-test-pollution.ts
This commit is contained in:
101
scripts/cleanup-test-pollution.ts
Normal file
101
scripts/cleanup-test-pollution.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* One-shot: remove leaked test data from dev DB.
|
||||
*
|
||||
* Test runs that crashed before reaching `afterAll` left orphan test users +
|
||||
* programs. This mirrors `tests/helpers.ts#cleanupTestData` with the same
|
||||
* reverse-dependency order, applied to all programs whose name matches the
|
||||
* test patterns.
|
||||
*
|
||||
* Run: npx tsx scripts/cleanup-test-pollution.ts
|
||||
*/
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
const TEST_PROGRAM_PATTERNS = [
|
||||
'Test Program prog-%',
|
||||
'getCandidates-%',
|
||||
'bulk-%',
|
||||
'source-flag-%',
|
||||
'mentor-files-%',
|
||||
'mentor-config-%',
|
||||
]
|
||||
|
||||
async function main() {
|
||||
const programs = await prisma.program.findMany({
|
||||
where: {
|
||||
OR: TEST_PROGRAM_PATTERNS.map((p) => ({ name: { startsWith: p.replace('%', '') } })),
|
||||
},
|
||||
select: { id: true, name: true },
|
||||
})
|
||||
|
||||
console.log(`Found ${programs.length} test programs:`)
|
||||
programs.forEach((p) => console.log(` - ${p.id} ${p.name}`))
|
||||
|
||||
for (const program of programs) {
|
||||
const programId = program.id
|
||||
console.log(`\nCleaning ${program.name}...`)
|
||||
|
||||
// MentorAssignment isn't in cleanupTestData — kill it first
|
||||
const ma = await prisma.mentorAssignment.deleteMany({
|
||||
where: { project: { programId } },
|
||||
})
|
||||
if (ma.count > 0) console.log(` ${ma.count} MentorAssignment`)
|
||||
|
||||
// Mirror tests/helpers.ts#cleanupTestData order
|
||||
await prisma.cohortProject.deleteMany({ where: { cohort: { round: { competition: { programId } } } } })
|
||||
await prisma.cohort.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.liveProgressCursor.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.filteringResult.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.filteringRule.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.filteringJob.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.assignmentJob.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.conflictOfInterest.deleteMany({ where: { assignment: { round: { competition: { programId } } } } })
|
||||
await prisma.evaluation.deleteMany({ where: { assignment: { round: { competition: { programId } } } } })
|
||||
await prisma.assignment.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.evaluationForm.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.fileRequirement.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.gracePeriod.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.reminderLog.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.evaluationSummary.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.evaluationDiscussion.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.projectRoundState.deleteMany({ where: { round: { competition: { programId } } } })
|
||||
await prisma.awardEligibility.deleteMany({ where: { award: { program: { id: programId } } } })
|
||||
await prisma.awardVote.deleteMany({ where: { award: { program: { id: programId } } } })
|
||||
await prisma.awardJuror.deleteMany({ where: { award: { program: { id: programId } } } })
|
||||
await prisma.specialAward.deleteMany({ where: { programId } })
|
||||
await prisma.round.deleteMany({ where: { competition: { programId } } })
|
||||
await prisma.competition.deleteMany({ where: { programId } })
|
||||
await prisma.projectStatusHistory.deleteMany({ where: { project: { programId } } })
|
||||
await prisma.projectFile.deleteMany({ where: { project: { programId } } })
|
||||
await prisma.projectTag.deleteMany({ where: { project: { programId } } })
|
||||
await prisma.project.deleteMany({ where: { programId } })
|
||||
await prisma.program.deleteMany({ where: { id: programId } })
|
||||
console.log(' cascade complete')
|
||||
}
|
||||
|
||||
// Delete test users (@test.local). Catch any audit-log refs first.
|
||||
const testUsers = await prisma.user.findMany({
|
||||
where: { email: { endsWith: '@test.local' } },
|
||||
select: { id: true },
|
||||
})
|
||||
const testUserIds = testUsers.map((u) => u.id)
|
||||
console.log(`\nDeleting ${testUserIds.length} @test.local users...`)
|
||||
if (testUserIds.length > 0) {
|
||||
await prisma.decisionAuditLog.deleteMany({ where: { actorId: { in: testUserIds } } })
|
||||
await prisma.auditLog.deleteMany({ where: { userId: { in: testUserIds } } })
|
||||
// Any remaining MentorAssignments referencing these users (e.g., from other tests)
|
||||
await prisma.mentorAssignment.deleteMany({ where: { mentorId: { in: testUserIds } } })
|
||||
await prisma.user.deleteMany({ where: { id: { in: testUserIds } } })
|
||||
}
|
||||
|
||||
console.log('\nDone.')
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => prisma.$disconnect())
|
||||
.catch(async (e) => {
|
||||
console.error(e)
|
||||
await prisma.$disconnect()
|
||||
process.exit(1)
|
||||
})
|
||||
Reference in New Issue
Block a user