11import { execSync } from 'child_process'
22import fs from 'fs'
3+ import { createRequire } from 'module'
34import path from 'path'
45import ts from 'typescript'
56import { fileURLToPath } from 'url'
@@ -79,6 +80,42 @@ const isContentAPIMode = process.env.PAYLOAD_DATABASE === 'content-api'
7980const contentAPISuiteTimeout = 120000
8081const vitestBinary = './node_modules/.bin/vitest'
8182
83+ // Debug dump config (temporary diagnostic for content-api CI).
84+ // - SUMMARY_DEBUG=1: dump every suite that produced 0 passes or errored.
85+ // - SUMMARY_DEBUG_SUITES="a,b": dump only those suites (when they fail).
86+ const debugDumpEnabled = process . env . SUMMARY_DEBUG === '1' || process . env . SUMMARY_DEBUG === 'true'
87+ const debugDumpSuites = new Set (
88+ ( process . env . SUMMARY_DEBUG_SUITES || '' )
89+ . split ( ',' )
90+ . map ( ( s ) => s . trim ( ) )
91+ . filter ( Boolean ) ,
92+ )
93+ const debugDumpMaxLines = Number ( process . env . SUMMARY_DEBUG_MAX_LINES || 400 )
94+
95+ function dumpSuiteOutput ( suiteName : string , stdout : string , stderr : string ) : void {
96+ const tail = ( s : string ) : string => {
97+ if ( ! s ) {
98+ return '(empty)'
99+ }
100+ const lines = s . split ( '\n' )
101+ if ( lines . length <= debugDumpMaxLines ) {
102+ return s
103+ }
104+ return (
105+ `... (${ lines . length - debugDumpMaxLines } earlier lines omitted) ...\n` +
106+ lines . slice ( - debugDumpMaxLines ) . join ( '\n' )
107+ )
108+ }
109+ console . log ( `\n${ '=' . repeat ( 80 ) } ` )
110+ console . log ( `🔬 DEBUG DUMP for suite: ${ suiteName } ` )
111+ console . log ( `${ '=' . repeat ( 80 ) } ` )
112+ console . log ( '--- stdout (tail) ---' )
113+ console . log ( tail ( stdout ) )
114+ console . log ( '--- stderr (tail) ---' )
115+ console . log ( tail ( stderr ) )
116+ console . log ( `${ '=' . repeat ( 80 ) } \n` )
117+ }
118+
82119function getVitestEnv ( options ?: { unsetPayloadDatabase ?: boolean } ) : NodeJS . ProcessEnv {
83120 const env = {
84121 ...process . env ,
@@ -362,6 +399,10 @@ function runTestSuite(suiteName: string): SuiteResult {
362399 duration : 0 ,
363400 }
364401
402+ let capturedStdout = ''
403+ let capturedStderr = ''
404+ let sawError = false
405+
365406 try {
366407 const testPath = path . join ( dirname , suiteName , 'int.spec.ts' )
367408 const command = `${ vitestBinary } run --project int ${ testPath } --reporter=json`
@@ -374,7 +415,7 @@ function runTestSuite(suiteName: string): SuiteResult {
374415 ...( isContentAPIMode ? { timeout : contentAPISuiteTimeout } : { } ) ,
375416 } )
376417
377- // Parse Jest output to extract test counts
418+ capturedStdout = output
378419 const parsed = parseTestResults ( output )
379420 result . passed = parsed . passed
380421 result . total = parsed . total
@@ -386,23 +427,29 @@ function runTestSuite(suiteName: string): SuiteResult {
386427 result . total = getStaticTestCount ( suiteName )
387428 }
388429 } catch ( error : unknown ) {
389- // Try to parse failure output from both stdout and stderr
430+ sawError = true
390431 let errorOutput = ''
391432 if ( error && typeof error === 'object' ) {
392433 if ( 'stdout' in error ) {
393434 const stdout = ( error as { stdout ?: unknown } ) . stdout
394435 if ( typeof stdout === 'string' ) {
395436 errorOutput += stdout
437+ capturedStdout = stdout
396438 } else if ( stdout && Buffer . isBuffer ( stdout ) ) {
397- errorOutput += stdout . toString ( 'utf8' )
439+ const s = stdout . toString ( 'utf8' )
440+ errorOutput += s
441+ capturedStdout = s
398442 }
399443 }
400444 if ( 'stderr' in error ) {
401445 const stderr = ( error as { stderr ?: unknown } ) . stderr
402446 if ( typeof stderr === 'string' ) {
403447 errorOutput += '\n' + stderr
448+ capturedStderr = stderr
404449 } else if ( stderr && Buffer . isBuffer ( stderr ) ) {
405- errorOutput += '\n' + stderr . toString ( 'utf8' )
450+ const s = stderr . toString ( 'utf8' )
451+ errorOutput += '\n' + s
452+ capturedStderr = s
406453 }
407454 }
408455 }
@@ -422,6 +469,13 @@ function runTestSuite(suiteName: string): SuiteResult {
422469 result . total = Math . max ( 0 , result . total - getExplicitSkippedTestCount ( suiteName ) )
423470 result . failed = result . passed < result . total
424471 result . duration = Date . now ( ) - startTime
472+
473+ const shouldDump =
474+ ( debugDumpEnabled || debugDumpSuites . has ( suiteName ) ) && ( result . passed === 0 || sawError )
475+ if ( shouldDump ) {
476+ dumpSuiteOutput ( suiteName , capturedStdout , capturedStderr )
477+ }
478+
425479 return result
426480}
427481
@@ -435,9 +489,75 @@ function formatDuration(ms: number): string {
435489 return `${ Math . floor ( ms / 60000 ) } m ${ Math . floor ( ( ms % 60000 ) / 1000 ) } s`
436490}
437491
438- function main ( ) {
492+ async function printDiagnostics ( ) : Promise < void > {
493+ console . log ( '=== Content API Diagnostics ===' )
494+ const envKeys = [
495+ 'PAYLOAD_DATABASE' ,
496+ 'NODE_ENV' ,
497+ 'CONTENT_API_URL' ,
498+ 'CONTENT_SYSTEM_ID' ,
499+ 'SUMMARY_DEBUG' ,
500+ 'SUMMARY_DEBUG_SUITES' ,
501+ ]
502+ for ( const k of envKeys ) {
503+ console . log ( ` env.${ k } = ${ process . env [ k ] ?? '(unset)' } ` )
504+ }
505+
506+ // Resolve @payloadcms /figma to understand which source vitest will use.
507+ const figmaSrcAlias = path . resolve (
508+ process . cwd ( ) ,
509+ '../enterprise-plugins/packages/figma/src/index.ts' ,
510+ )
511+ const hasFigmaSrc = fs . existsSync ( figmaSrcAlias )
512+ console . log ( ` enterprise-plugins sibling exists: ${ hasFigmaSrc } (${ figmaSrcAlias } )` )
513+ try {
514+ const req = createRequire ( path . join ( process . cwd ( ) , 'test/package.json' ) )
515+ const resolved = req . resolve ( '@payloadcms/figma' )
516+ console . log ( ` @payloadcms/figma resolves to: ${ resolved } ` )
517+ const pkgPath = path . join ( path . dirname ( resolved ) , 'package.json' )
518+ if ( fs . existsSync ( pkgPath ) ) {
519+ const pkg = JSON . parse ( fs . readFileSync ( pkgPath , 'utf8' ) )
520+ console . log ( ` @payloadcms/figma version: ${ pkg . version } ` )
521+ }
522+ } catch ( e ) {
523+ console . log ( ` @payloadcms/figma resolve FAILED: ${ ( e as Error ) . message } ` )
524+ }
525+
526+ // Ping Content API if we're targeting it, so we know it is reachable from the
527+ // node process that runs the tests (not just the docker healthcheck).
528+ if ( isContentAPIMode ) {
529+ const url = process . env . CONTENT_API_URL || 'http://localhost:8080'
530+ for ( const endpoint of [ '/health' , '/dev/jwt' ] ) {
531+ try {
532+ const init : RequestInit =
533+ endpoint === '/dev/jwt'
534+ ? {
535+ method : 'POST' ,
536+ headers : { 'content-type' : 'application/json' } ,
537+ body : JSON . stringify ( {
538+ contentSystemId :
539+ process . env . CONTENT_SYSTEM_ID || '00000000-0000-4000-8000-000000000001' ,
540+ } ) ,
541+ }
542+ : { }
543+ const res = await fetch ( `${ url } ${ endpoint } ` , init )
544+ const text = await res . text ( )
545+ console . log (
546+ ` GET ${ url } ${ endpoint } -> ${ res . status } ${ res . statusText } ; body: ${ text . slice ( 0 , 200 ) } ` ,
547+ )
548+ } catch ( e ) {
549+ console . log ( ` GET ${ url } ${ endpoint } -> ERROR ${ ( e as Error ) . message } ` )
550+ }
551+ }
552+ }
553+ console . log ( '================================\n' )
554+ }
555+
556+ async function main ( ) {
439557 console . log ( '🧪 Running integration tests suite by suite...\n' )
440558
559+ await printDiagnostics ( )
560+
441561 const testDirs = getTestDirectories ( )
442562 console . log ( `Found ${ testDirs . length } test suites\n` )
443563
@@ -520,9 +640,7 @@ function main() {
520640 }
521641}
522642
523- try {
524- main ( )
525- } catch ( error ) {
643+ main ( ) . catch ( ( error ) => {
526644 console . error ( 'Error running tests:' , error )
527645 process . exit ( 1 )
528- }
646+ } )
0 commit comments