1+ import { promises as fs } from 'fs' ;
2+ import path from 'path' ;
3+ import { ConformanceCheck } from './types.js' ;
4+ import { getClientScenario } from './scenarios/index.js' ;
5+
6+ async function ensureResultsDir ( ) : Promise < string > {
7+ const resultsDir = path . join ( process . cwd ( ) , 'results' ) ;
8+ await fs . mkdir ( resultsDir , { recursive : true } ) ;
9+ return resultsDir ;
10+ }
11+
12+ function createResultDir ( scenario : string ) : string {
13+ const timestamp = new Date ( ) . toISOString ( ) . replace ( / [: .] / g, '-' ) ;
14+ return path . join ( 'results' , `server-${ scenario } -${ timestamp } ` ) ;
15+ }
16+
17+ export async function runServerConformanceTest (
18+ serverUrl : string ,
19+ scenarioName : string
20+ ) : Promise < {
21+ checks : ConformanceCheck [ ] ;
22+ resultDir : string ;
23+ } > {
24+ await ensureResultsDir ( ) ;
25+ const resultDir = createResultDir ( scenarioName ) ;
26+ await fs . mkdir ( resultDir , { recursive : true } ) ;
27+
28+ const scenario = getClientScenario ( scenarioName ) ;
29+ if ( ! scenario ) {
30+ throw new Error ( `Unknown client scenario: ${ scenarioName } ` ) ;
31+ }
32+
33+ console . log ( `Running client scenario '${ scenarioName } ' against server: ${ serverUrl } ` ) ;
34+
35+ const checks = await scenario . run ( serverUrl ) ;
36+
37+ await fs . writeFile ( path . join ( resultDir , 'checks.json' ) , JSON . stringify ( checks , null , 2 ) ) ;
38+
39+ console . log ( `Results saved to ${ resultDir } ` ) ;
40+
41+ return {
42+ checks,
43+ resultDir
44+ } ;
45+ }
46+
47+ async function main ( ) : Promise < void > {
48+ const args = process . argv . slice ( 2 ) ;
49+ let serverUrl : string | null = null ;
50+ let scenario : string | null = null ;
51+
52+ for ( let i = 0 ; i < args . length ; i ++ ) {
53+ if ( args [ i ] === '--server-url' && i + 1 < args . length ) {
54+ serverUrl = args [ i + 1 ] ;
55+ i ++ ;
56+ } else if ( args [ i ] === '--scenario' && i + 1 < args . length ) {
57+ scenario = args [ i + 1 ] ;
58+ i ++ ;
59+ }
60+ }
61+
62+ if ( ! serverUrl || ! scenario ) {
63+ console . error ( 'Usage: server-runner --server-url <url> --scenario <scenario>' ) ;
64+ console . error ( 'Example: server-runner --server-url http://localhost:3000 --scenario initialize' ) ;
65+ process . exit ( 1 ) ;
66+ }
67+
68+ try {
69+ const result = await runServerConformanceTest ( serverUrl , scenario ) ;
70+
71+ const denominator = result . checks . filter ( c => c . status === 'SUCCESS' || c . status == 'FAILURE' ) . length ;
72+ const passed = result . checks . filter ( c => c . status === 'SUCCESS' ) . length ;
73+ const failed = result . checks . filter ( c => c . status === 'FAILURE' ) . length ;
74+
75+ console . log ( `Checks:\n${ JSON . stringify ( result . checks , null , 2 ) } ` ) ;
76+
77+ console . log ( `\nTest Results:` ) ;
78+ console . log ( `Passed: ${ passed } /${ denominator } , ${ failed } failed` ) ;
79+
80+ if ( failed > 0 ) {
81+ console . log ( '\nFailed Checks:' ) ;
82+ result . checks
83+ . filter ( c => c . status === 'FAILURE' )
84+ . forEach ( c => {
85+ console . log ( ` - ${ c . name } : ${ c . description } ` ) ;
86+ if ( c . errorMessage ) {
87+ console . log ( ` Error: ${ c . errorMessage } ` ) ;
88+ }
89+ } ) ;
90+ }
91+
92+ process . exit ( failed > 0 ? 1 : 0 ) ;
93+ } catch ( error ) {
94+ console . error ( 'Server test runner error:' , error ) ;
95+ process . exit ( 1 ) ;
96+ }
97+ }
98+
99+ if ( import . meta. url === `file://${ process . argv [ 1 ] } ` ) {
100+ main ( ) ;
101+ }
0 commit comments