Skip to content

Commit 91f49d9

Browse files
committed
test: Integration test dual-server support
1 parent 6380f5d commit 91f49d9

14 files changed

Lines changed: 1152 additions & 1041 deletions

File tree

packages/toolbox-adk/test/e2e/jest.globalSetup.ts

Lines changed: 85 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -57,102 +57,134 @@ export default async function globalSetup(): Promise<void> {
5757

5858
const localToolboxPath = path.resolve(__dirname, TOOLBOX_BINARY_NAME);
5959

60+
const bucketName = 'mcp-toolbox-for-databases';
6061
console.log(
61-
`Downloading toolbox binary from gs://mcp-toolbox-for-databases/${toolboxGcsPath} to ${localToolboxPath}...`,
62-
);
63-
await downloadBlob(
64-
'mcp-toolbox-for-databases',
65-
toolboxGcsPath,
66-
localToolboxPath,
62+
`Downloading toolbox binary from gs://${bucketName}/${toolboxGcsPath} to ${localToolboxPath}...`,
6763
);
64+
await downloadBlob(bucketName, toolboxGcsPath, localToolboxPath);
6865
console.log('Toolbox binary downloaded successfully.');
6966

7067
// Make toolbox executable
7168
await fs.chmod(localToolboxPath, 0o700);
7269

73-
// Start toolbox server
74-
console.log('Starting toolbox server process...');
75-
const serverProcess = spawn(
70+
// Start toolbox servers
71+
console.log('Starting toolbox server processes...');
72+
const serverProcess1 = spawn(
7673
localToolboxPath,
77-
['--tools-file', toolsFilePath],
74+
['--port', '5000', '--tools-file', toolsFilePath],
7875
{
79-
stdio: ['ignore', 'pipe', 'pipe'], // ignore stdin, pipe stdout/stderr
76+
stdio: ['ignore', 'pipe', 'pipe'],
77+
},
78+
);
79+
const serverProcess2 = spawn(
80+
localToolboxPath,
81+
['--port', '5001', '--tools-file', toolsFilePath, '--enable-draft-specs'],
82+
{
83+
stdio: ['ignore', 'pipe', 'pipe'],
8084
},
8185
);
8286

83-
(globalThis as CustomGlobal).__TOOLBOX_SERVER_PROCESS__ = serverProcess;
87+
(globalThis as CustomGlobal).__TOOLBOX_SERVER_PROCESS__ = serverProcess1;
88+
(globalThis as CustomGlobal).__TOOLBOX_SERVER_PROCESS_2__ = serverProcess2;
8489

85-
serverProcess.stdout?.on('data', (data: Buffer) => {
86-
console.log(`[ToolboxServer STDOUT]: ${data.toString().trim()}`);
90+
serverProcess1.stdout?.on('data', (data: Buffer) => {
91+
console.log(`[ToolboxServer1 STDOUT]: ${data.toString().trim()}`);
8792
});
88-
89-
serverProcess.stderr?.on('data', (data: Buffer) => {
90-
console.error(`[ToolboxServer STDERR]: ${data.toString().trim()}`);
93+
serverProcess1.stderr?.on('data', (data: Buffer) => {
94+
console.error(`[ToolboxServer1 STDERR]: ${data.toString().trim()}`);
9195
});
92-
93-
serverProcess.on('error', err => {
94-
console.error('Toolbox server process error:', err);
95-
throw new Error('Failed to start toolbox server process.');
96+
serverProcess1.on('error', err => {
97+
console.error('Toolbox server 1 process error:', err);
98+
throw new Error('Failed to start toolbox server 1 process.');
9699
});
97-
98-
serverProcess.on('exit', (code, signal) => {
100+
serverProcess1.on('exit', (code, signal) => {
99101
console.log(
100-
`Toolbox server process exited with code ${code}, signal ${signal}.`,
102+
`Toolbox server 1 process exited with code ${code}, signal ${signal}.`,
101103
);
102104
if (
103105
(globalThis as CustomGlobal).__TOOLBOX_SERVER_PROCESS__ &&
104106
!(globalThis as CustomGlobal).__SERVER_TEARDOWN_INITIATED__
105107
) {
106-
console.error('Toolbox server exited prematurely during setup.');
108+
console.error('Toolbox server 1 exited prematurely during setup.');
109+
}
110+
});
111+
112+
serverProcess2.stdout?.on('data', (data: Buffer) => {
113+
console.log(`[ToolboxServer2 STDOUT]: ${data.toString().trim()}`);
114+
});
115+
serverProcess2.stderr?.on('data', (data: Buffer) => {
116+
console.error(`[ToolboxServer2 STDERR]: ${data.toString().trim()}`);
117+
});
118+
serverProcess2.on('error', err => {
119+
console.error('Toolbox server 2 process error:', err);
120+
throw new Error('Failed to start toolbox server 2 process.');
121+
});
122+
serverProcess2.on('exit', (code, signal) => {
123+
console.log(
124+
`Toolbox server 2 process exited with code ${code}, signal ${signal}.`,
125+
);
126+
if (
127+
(globalThis as CustomGlobal).__TOOLBOX_SERVER_PROCESS_2__ &&
128+
!(globalThis as CustomGlobal).__SERVER_TEARDOWN_INITIATED__
129+
) {
130+
console.error('Toolbox server 2 exited prematurely during setup.');
107131
}
108132
});
109133

110-
// Wait for server to start (basic poll check)
111-
let started = false;
134+
// Wait for servers to start (basic poll check)
135+
let started1 = false;
136+
let started2 = false;
112137
const startTime = Date.now();
113138
while (Date.now() - startTime < SERVER_READY_TIMEOUT_MS) {
114139
if (
115-
serverProcess.pid &&
116-
!serverProcess.killed &&
117-
serverProcess.exitCode === null
140+
serverProcess1.pid &&
141+
!serverProcess1.killed &&
142+
serverProcess1.exitCode === null
118143
) {
144+
started1 = true;
145+
}
146+
if (
147+
serverProcess2.pid &&
148+
!serverProcess2.killed &&
149+
serverProcess2.exitCode === null
150+
) {
151+
started2 = true;
152+
}
153+
if (started1 && started2) {
119154
console.log(
120-
'Toolbox server process appears to be running. Polling for stability...',
155+
'Both Toolbox servers started successfully (processes are active).',
121156
);
122-
await delay(SERVER_READY_POLL_INTERVAL_MS * 2);
123-
if (serverProcess.exitCode === null) {
124-
console.log(
125-
'Toolbox server started successfully (process is active).',
126-
);
127-
started = true;
128-
break;
129-
} else {
130-
console.log('Toolbox server process exited after initial start.');
131-
break;
132-
}
157+
break;
133158
}
134159
await delay(SERVER_READY_POLL_INTERVAL_MS);
135-
console.log('Checking if toolbox server is started...');
160+
console.log('Checking if toolbox servers are started...');
136161
}
137162

138-
if (!started) {
139-
if (serverProcess && !serverProcess.killed) {
140-
serverProcess.kill('SIGTERM');
141-
}
163+
if (!started1 || !started2) {
164+
if (serverProcess1 && !serverProcess1.killed)
165+
serverProcess1.kill('SIGTERM');
166+
if (serverProcess2 && !serverProcess2.killed)
167+
serverProcess2.kill('SIGTERM');
142168
throw new Error(
143-
`Toolbox server failed to start within ${SERVER_READY_TIMEOUT_MS / 1000} seconds.`,
169+
`Toolbox servers failed to start within ${SERVER_READY_TIMEOUT_MS / 1000} seconds.`,
144170
);
145171
}
146172

147173
console.log('Jest Global Setup: Completed successfully.');
148174
} catch (error) {
149175
console.error('Jest Global Setup Failed:', error);
150-
// Attempt to kill server if it started partially
151-
const serverProcess = (globalThis as CustomGlobal)
176+
// Attempt to kill servers if they started partially
177+
const serverProcess1 = (globalThis as CustomGlobal)
152178
.__TOOLBOX_SERVER_PROCESS__;
153-
if (serverProcess && !serverProcess.killed) {
154-
console.log('Attempting to terminate partially started server...');
155-
serverProcess.kill('SIGKILL');
179+
const serverProcess2 = (globalThis as CustomGlobal)
180+
.__TOOLBOX_SERVER_PROCESS_2__;
181+
if (serverProcess1 && !serverProcess1.killed) {
182+
console.log('Attempting to terminate partially started server 1...');
183+
serverProcess1.kill('SIGKILL');
184+
}
185+
if (serverProcess2 && !serverProcess2.killed) {
186+
console.log('Attempting to terminate partially started server 2...');
187+
serverProcess2.kill('SIGKILL');
156188
}
157189
// Clean up temp file if created
158190
const toolsFilePath = (globalThis as CustomGlobal).__TOOLS_FILE_PATH__;

packages/toolbox-adk/test/e2e/jest.globalTeardown.ts

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
import * as fs from 'fs-extra';
16+
import {ChildProcess} from 'child_process';
1617
import {CustomGlobal} from './types.js';
1718

1819
const SERVER_TERMINATE_TIMEOUT_MS = 10000; // 10 seconds
@@ -22,50 +23,67 @@ export default async function globalTeardown(): Promise<void> {
2223
(globalThis as CustomGlobal).__SERVER_TEARDOWN_INITIATED__ = true;
2324

2425
const customGlobal = globalThis as CustomGlobal;
25-
const serverProcess = customGlobal.__TOOLBOX_SERVER_PROCESS__;
26+
const serverProcess1 = customGlobal.__TOOLBOX_SERVER_PROCESS__;
27+
const serverProcess2 = customGlobal.__TOOLBOX_SERVER_PROCESS_2__;
2628
const toolsFilePath = customGlobal.__TOOLS_FILE_PATH__;
2729

28-
if (serverProcess && !serverProcess.killed) {
29-
console.log('Stopping toolbox server process...');
30-
serverProcess.kill('SIGTERM'); // Graceful termination
30+
const killServer = async (
31+
serverProcess: ChildProcess | undefined,
32+
name: string,
33+
) => {
34+
if (serverProcess && !serverProcess.killed) {
35+
console.log(`Stopping toolbox server ${name} process...`);
36+
serverProcess.kill('SIGTERM'); // Graceful termination
3137

32-
// Wait for the process to exit
33-
const stopPromise = new Promise<void>((resolve, reject) => {
34-
const timeout = setTimeout(() => {
35-
if (!serverProcess.killed) {
36-
console.warn(
37-
'Toolbox server did not terminate gracefully, sending SIGKILL.',
38-
);
39-
serverProcess.kill('SIGKILL');
40-
}
41-
// Resolve even if SIGKILL is needed, as we want teardown to finish
42-
resolve();
43-
}, SERVER_TERMINATE_TIMEOUT_MS);
38+
// Wait for the process to exit
39+
const stopPromise = new Promise<void>((resolve, reject) => {
40+
const timeout = setTimeout(() => {
41+
if (!serverProcess.killed) {
42+
console.warn(
43+
`Toolbox server ${name} did not terminate gracefully, sending SIGKILL.`,
44+
);
45+
serverProcess.kill('SIGKILL');
46+
}
47+
// Resolve even if SIGKILL is needed, as we want teardown to finish
48+
resolve();
49+
}, SERVER_TERMINATE_TIMEOUT_MS);
4450

45-
serverProcess.on('exit', (code, signal) => {
46-
clearTimeout(timeout);
47-
console.log(
48-
`Toolbox server process exited with code ${code}, signal ${signal} during teardown.`,
51+
serverProcess.on(
52+
'exit',
53+
(code: number | null, signal: NodeJS.Signals | null) => {
54+
clearTimeout(timeout);
55+
console.log(
56+
`Toolbox server ${name} process exited with code ${code}, signal ${signal} during teardown.`,
57+
);
58+
resolve();
59+
},
4960
);
50-
resolve();
51-
});
52-
serverProcess.on('error', err => {
53-
// Should not happen if already running
54-
clearTimeout(timeout);
55-
console.error('Error during server process termination:', err);
56-
reject(err);
61+
serverProcess.on('error', (err: Error) => {
62+
// Should not happen if already running
63+
clearTimeout(timeout);
64+
console.error(
65+
`Error during server ${name} process termination:`,
66+
err,
67+
);
68+
reject(err);
69+
});
5770
});
58-
});
5971

60-
try {
61-
await stopPromise;
62-
} catch (error) {
63-
console.error('Error while waiting for server to stop:', error);
64-
if (!serverProcess.killed) serverProcess.kill('SIGKILL'); // Ensure it's killed
72+
try {
73+
await stopPromise;
74+
} catch (error) {
75+
console.error(`Error while waiting for server ${name} to stop:`, error);
76+
if (!serverProcess.killed) serverProcess.kill('SIGKILL'); // Ensure it's killed
77+
}
78+
} else {
79+
console.log(
80+
`Toolbox server ${name} process was not running or already handled.`,
81+
);
6582
}
66-
} else {
67-
console.log('Toolbox server process was not running or already handled.');
68-
}
83+
};
84+
85+
await killServer(serverProcess1, '1');
86+
await killServer(serverProcess2, '2');
6987

7088
if (toolsFilePath) {
7189
try {
@@ -79,6 +97,7 @@ export default async function globalTeardown(): Promise<void> {
7997
}
8098
}
8199
customGlobal.__TOOLBOX_SERVER_PROCESS__ = undefined;
100+
customGlobal.__TOOLBOX_SERVER_PROCESS_2__ = undefined;
82101
customGlobal.__TOOLS_FILE_PATH__ = undefined;
83102
customGlobal.__GOOGLE_CLOUD_PROJECT__ = undefined;
84103

0 commit comments

Comments
 (0)