@@ -7,76 +7,124 @@ import fs from "fs/promises";
77import { Session } from "../../src/session.js" ;
88import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" ;
99
10- export async function setupIntegrationTest ( ) : Promise < {
11- client : Client ;
12- server : Server ;
13- teardown : ( ) => Promise < void > ;
14- } > {
15- const clientTransport = new InMemoryTransport ( ) ;
16- const serverTransport = new InMemoryTransport ( ) ;
17-
18- await serverTransport . start ( ) ;
19- await clientTransport . start ( ) ;
20-
21- clientTransport . output . pipeTo ( serverTransport . input ) ;
22- serverTransport . output . pipeTo ( clientTransport . input ) ;
23-
24- const client = new Client (
25- {
26- name : "test-client" ,
27- version : "1.2.3" ,
28- } ,
29- {
30- capabilities : { } ,
31- }
32- ) ;
33-
34- const server = new Server ( {
35- mcpServer : new McpServer ( {
36- name : "test-server" ,
37- version : "1.2.3" ,
38- } ) ,
39- session : new Session ( ) ,
10+ export function jestTestMCPClient ( ) : ( ) => Client {
11+ let client : Client | undefined ;
12+ let server : Server | undefined ;
13+
14+ beforeEach ( async ( ) => {
15+ const clientTransport = new InMemoryTransport ( ) ;
16+ const serverTransport = new InMemoryTransport ( ) ;
17+
18+ await serverTransport . start ( ) ;
19+ await clientTransport . start ( ) ;
20+
21+ clientTransport . output . pipeTo ( serverTransport . input ) ;
22+ serverTransport . output . pipeTo ( clientTransport . input ) ;
23+
24+ client = new Client (
25+ {
26+ name : "test-client" ,
27+ version : "1.2.3" ,
28+ } ,
29+ {
30+ capabilities : { } ,
31+ }
32+ ) ;
33+
34+ server = new Server ( {
35+ mcpServer : new McpServer ( {
36+ name : "test-server" ,
37+ version : "1.2.3" ,
38+ } ) ,
39+ session : new Session ( ) ,
40+ } ) ;
41+ await server . connect ( serverTransport ) ;
42+ await client . connect ( clientTransport ) ;
4043 } ) ;
41- await server . connect ( serverTransport ) ;
42- await client . connect ( clientTransport ) ;
43-
44- return {
45- client,
46- server,
47- teardown : async ( ) => {
48- await client . close ( ) ;
49- await server . close ( ) ;
50- } ,
44+
45+ afterEach ( async ( ) => {
46+ await client ?. close ( ) ;
47+ client = undefined ;
48+
49+ await server ?. close ( ) ;
50+ server = undefined ;
51+ } ) ;
52+
53+ return ( ) => {
54+ if ( ! client ) {
55+ throw new Error ( "beforeEach() hook not ran yet" ) ;
56+ }
57+
58+ return client ;
5159 } ;
5260}
5361
54- export async function runMongoDB ( ) : Promise < runner . MongoCluster > {
55- const tmpDir = path . join ( __dirname , ".." , "tmp" ) ;
56- await fs . mkdir ( tmpDir , { recursive : true } ) ;
62+ export function jestTestCluster ( ) : ( ) => runner . MongoCluster {
63+ let cluster : runner . MongoCluster | undefined ;
5764
58- try {
59- const cluster = await MongoCluster . start ( {
60- tmpDir : path . join ( tmpDir , "mongodb-runner" , "dbs" ) ,
61- logDir : path . join ( tmpDir , "mongodb-runner" , "logs" ) ,
62- topology : "standalone" ,
63- } ) ;
65+ function runMongodb ( ) { }
66+
67+ beforeAll ( async function ( ) {
68+ // Downloading Windows executables in CI takes a long time because
69+ // they include debug symbols...
70+ const tmpDir = path . join ( __dirname , ".." , "tmp" ) ;
71+ await fs . mkdir ( tmpDir , { recursive : true } ) ;
72+
73+ // On Windows, we may have a situation where mongod.exe is not fully released by the OS
74+ // before we attempt to run it again, so we add a retry.
75+ const dbsDir = path . join ( tmpDir , "mongodb-runner" , `dbs` ) ;
76+ for ( let i = 0 ; i < 10 ; i ++ ) {
77+ try {
78+ cluster = await MongoCluster . start ( {
79+ tmpDir : dbsDir ,
80+ logDir : path . join ( tmpDir , "mongodb-runner" , "logs" ) ,
81+ topology : "standalone" ,
82+ } ) ;
83+
84+ return ;
85+ } catch ( err ) {
86+ console . error ( `Failed to start cluster in ${ dbsDir } , attempt ${ i } : ${ err } ` ) ;
87+ await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ;
88+ }
89+ }
90+ } , 120_000 ) ;
91+
92+ afterAll ( async function ( ) {
93+ await cluster ?. close ( ) ;
94+ cluster = undefined ;
95+ } ) ;
96+
97+ return ( ) => {
98+ if ( ! cluster ) {
99+ throw new Error ( "beforeAll() hook not ran yet" ) ;
100+ }
64101
65102 return cluster ;
66- } catch ( err ) {
67- throw err ;
68- }
103+ } ;
104+ }
105+
106+ export function getResponseContent ( content : unknown ) : string {
107+ return getResponseElements ( content )
108+ . map ( ( item ) => item . text )
109+ . join ( "\n" ) ;
69110}
70111
71- export function validateToolResponse ( content : unknown ) : string {
112+ export function getResponseElements ( content : unknown ) : { type : string ; text : string } [ ] {
72113 expect ( Array . isArray ( content ) ) . toBe ( true ) ;
73114
74- const response = content as Array < { type : string ; text : string } > ;
115+ const response = content as { type : string ; text : string } [ ] ;
75116 for ( const item of response ) {
76117 expect ( item ) . toHaveProperty ( "type" ) ;
77118 expect ( item ) . toHaveProperty ( "text" ) ;
78119 expect ( item . type ) . toBe ( "text" ) ;
79120 }
80121
81- return response . map ( ( item ) => item . text ) . join ( "\n" ) ;
122+ return response ;
123+ }
124+
125+ export async function connect ( client : Client , cluster : runner . MongoCluster ) : Promise < void > {
126+ await client . callTool ( {
127+ name : "connect" ,
128+ arguments : { connectionStringOrClusterName : cluster . connectionString } ,
129+ } ) ;
82130}
0 commit comments