13type ThrowawayDeployTestOpts = { 14 syncWithAction: (action: string) => Promise<void> 16 podName?: string // optional if using verifyFn 17 pvcNames?: string[] // if provided, waits for PV cleanup after delpvcs (prevents flaky "pvc not found") 20 includeDelpvcs?: boolean 21 includeCleanup?: boolean 22 verifyFn?: () => Promise<{passed: boolean, msg: string}> // custom verification (replaces default podHealthy check) 23 setupFn?: () => Promise<{passed: boolean, msg: string}> // optional setup step (runs after delete/delpvcs, before apply) - e.g., configure SSH 26const throwawayPtenv = 'testprod' // throwaway tests are always deployed-only 28const includesRecreateSuite = () => { 30 if (!suites) return true 31 const list = suites.split(',') 32 return list.includes('recreate') || list.includes('all') 35export const runThrowawayDeployTest = async ({syncWithAction, cluster_name, podName, pvcNames, suiteName, ep, includeDelpvcs, includeCleanup, verifyFn, setupFn}: ThrowawayDeployTestOpts) => { 36 // Handle --history flag 39 return { passed: true, results: [] } 42 // Check memoization (skip if already passed at this SHA) 43 // Simple SHA-match memo (throwaway tests don't have Next.js import tree) 48 r.ep === epRel && r.action === 'runtests' && r.ptenv === throwawayPtenv && r.success 50 if (last?.gitSha === currentSha) { 52 return { passed: true, results: [] } 57 const startTime = Date.now() 61 const doRecreate = includesRecreateSuite() 62 const skipRecreate = {passed: true, skipped: true, msg: 'add --suite=recreate to run'} as const 63 const skipCleanup = {passed: true, skipped: true, msg: 'skipped'} as const 65 test('delete', async () => { 66 if (!doRecreate) return skipRecreate 67 await syncWithAction('delete') 68 return {passed: true, msg: 'delete completed'} 72 test('delpvcs', async () => { 73 if (!doRecreate) return skipRecreate 74 // Get PV names before deleting PVCs (needed to wait for cleanup) 75 const pvNames = pvcNames ? await Promise.all(pvcNames.map(pvcName => getPvNameForPvc({pvcName, cluster_name}))) : [] 76 await syncWithAction('delpvcs') 77 // Wait for PV cleanup to prevent race condition on re-create 78 await Promise.all(pvNames.filter(Boolean).map(pvName => waitForPvCleanup({pvName: pvName!, cluster_name}))) 79 return {passed: true, msg: 'delpvcs completed'} 84 test('setup', setupFn) 87 test('apply', async () => { 88 const doApply = async () => { 89 await syncWithAction('apply') 90 return {passed: true, msg: 'apply completed'} 93 return await doApply() 95 // Auto-recover from disk full in throwaway tests 96 // CrashLoopBackOff on init container usually means disk full for codeserv 97 const isCrashLoop = err.uniqDebugH?.reason === 'CrashLoopBackOff' && err.uniqDebugH?.containerType === 'init container' 98 if (!isCrashLoop) throw err 99 console.log('[throwaway-pvcs-full-delpvcs] Init container CrashLoopBackOff, deleting PVCs and retrying...') 100 await syncWithAction('delete') 101 await syncWithAction('delpvcs') 103 const pvNames = await Promise.all(pvcNames.map(pvcName => getPvNameForPvc({pvcName, cluster_name}))) 104 await Promise.all(pvNames.filter(Boolean).map(pvName => waitForPvCleanup({pvName: pvName!, cluster_name}))) 106 return await doApply() 110 test('verify', async () => { 111 if (verifyFn) return verifyFn() 113 return result.healthy 114 ? {passed: true, msg: `${podName}: pods healthy`} 115 : {passed: false, msg: result.message} 118 if (includeCleanup) { 119 test('cleanupDelete', async () => { 120 if (!doRecreate) return skipCleanup 121 await syncWithAction('delete') 122 return {passed: true, msg: 'cleanup delete completed'} 124 if (includeDelpvcs) { 125 test('cleanupDelpvcs', async () => { 126 if (!doRecreate) return skipCleanup 127 await syncWithAction('delpvcs') 128 return {passed: true, msg: 'cleanup delpvcs completed'} 133 const result = await quietModeCtx.run({quiet: true}, () => runTestsWithProgress({tests, suiteName, failFast: true})) 135 if (ep) recordSuiteResult({ep, ptenv: throwawayPtenv, suiteName, passed: result.passed, message: result.message, startTime}) 137 if (!result.passed) process.exitCode = 1