Skip to content

Commit 80258d4

Browse files
committed
chore(tests): use actual integration structure
1 parent 7cf9386 commit 80258d4

File tree

2 files changed

+72
-168
lines changed

2 files changed

+72
-168
lines changed

tests/integration/elicitation.test.ts

Lines changed: 54 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,35 @@
11
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
2-
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
3-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5-
import { Server } from "../../src/server.js";
6-
import { Session } from "../../src/common/session.js";
7-
import { CompositeLogger } from "../../src/common/logger.js";
8-
import { MCPConnectionManager } from "../../src/common/connectionManager.js";
9-
import { DeviceId } from "../../src/helpers/deviceId.js";
10-
import { ExportsManager } from "../../src/common/exportsManager.js";
11-
import { Telemetry } from "../../src/telemetry/telemetry.js";
12-
import { Keychain } from "../../src/common/keychain.js";
13-
import { InMemoryTransport } from "./inMemoryTransport.js";
14-
import { connectionErrorHandler } from "../../src/common/connectionErrorHandler.js";
2+
import { describe, it, expect } from "vitest";
153
import { defaultDriverOptions, type UserConfig } from "../../src/common/config.js";
16-
import { defaultTestConfig } from "./helpers.js";
4+
import { defaultTestConfig, setupIntegrationTest } from "./helpers.js";
175
import { Elicitation } from "../../src/elicitation.js";
18-
import { type MockClientCapabilities, createMockElicitInput } from "../utils/elicitationMocks.js";
6+
import { createMockElicitInput } from "../utils/elicitationMocks.js";
197

208
describe("Elicitation Integration Tests", () => {
21-
let mcpClient: Client;
22-
let mcpServer: Server;
23-
let deviceId: DeviceId;
24-
let mockElicitInput: ReturnType<typeof createMockElicitInput>;
25-
26-
async function setupWithConfig(
27-
config: Partial<UserConfig> = {},
28-
clientCapabilities: MockClientCapabilities = {}
29-
): Promise<void> {
30-
const userConfig: UserConfig = {
9+
function createTestConfig(config: Partial<UserConfig> = {}): UserConfig {
10+
return {
3111
...defaultTestConfig,
3212
telemetry: "disabled",
3313
// Add fake API credentials so Atlas tools get registered
3414
apiClientId: "test-client-id",
3515
apiClientSecret: "test-client-secret",
3616
...config,
3717
};
38-
39-
const driverOptions = defaultDriverOptions;
40-
const logger = new CompositeLogger();
41-
const exportsManager = ExportsManager.init(userConfig, logger);
42-
deviceId = DeviceId.create(logger);
43-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
44-
const connectionManager = new MCPConnectionManager(userConfig, driverOptions, logger, deviceId);
45-
const session = new Session({
46-
apiBaseUrl: userConfig.apiBaseUrl,
47-
apiClientId: userConfig.apiClientId,
48-
apiClientSecret: userConfig.apiClientSecret,
49-
logger,
50-
exportsManager,
51-
connectionManager,
52-
keychain: new Keychain(),
53-
});
54-
// Mock API validation for tests
55-
const mockFn = vi.fn().mockResolvedValue(true);
56-
session.apiClient.validateAccessToken = mockFn;
57-
58-
const telemetry = Telemetry.create(session, userConfig, deviceId);
59-
60-
const clientTransport = new InMemoryTransport();
61-
const serverTransport = new InMemoryTransport();
62-
63-
await serverTransport.start();
64-
await clientTransport.start();
65-
66-
void clientTransport.output.pipeTo(serverTransport.input);
67-
void serverTransport.output.pipeTo(clientTransport.input);
68-
69-
mockElicitInput = createMockElicitInput();
70-
71-
mcpClient = new Client(
72-
{
73-
name: "test-client",
74-
version: "1.2.3",
75-
},
76-
{
77-
capabilities: clientCapabilities,
78-
}
79-
);
80-
81-
const mockMcpServer = new McpServer({
82-
name: "test-server",
83-
version: "5.2.3",
84-
});
85-
86-
// Mock the elicitInput method on the server instance
87-
Object.assign(mockMcpServer.server, { elicitInput: mockElicitInput.mock });
88-
89-
// Create elicitation instance
90-
const elicitation = new Elicitation({ server: mockMcpServer.server });
91-
92-
mcpServer = new Server({
93-
session,
94-
userConfig,
95-
telemetry,
96-
mcpServer: mockMcpServer,
97-
connectionErrorHandler,
98-
elicitation,
99-
});
100-
101-
await mcpServer.connect(serverTransport);
102-
await mcpClient.connect(clientTransport);
10318
}
10419

105-
async function cleanup(): Promise<void> {
106-
await mcpServer?.session.disconnect();
107-
await mcpClient?.close();
108-
deviceId?.close();
109-
}
110-
111-
afterEach(async () => {
112-
await cleanup();
113-
vi.clearAllMocks();
114-
});
115-
11620
describe("with elicitation support", () => {
117-
beforeEach(async () => {
118-
await setupWithConfig({}, { elicitation: {} });
119-
});
21+
const mockElicitInput = createMockElicitInput();
22+
const integration = setupIntegrationTest(
23+
() => createTestConfig(),
24+
() => defaultDriverOptions,
25+
{ elicitInput: mockElicitInput }
26+
);
12027

12128
describe("tools requiring confirmation", () => {
12229
it("should request confirmation for drop-database tool and proceed when confirmed", async () => {
12330
mockElicitInput.confirmYes();
12431

125-
const result = await mcpClient.callTool({
32+
const result = await integration.mcpClient().callTool({
12633
name: "drop-database",
12734
arguments: { database: "test-db" },
12835
});
@@ -160,7 +67,7 @@ describe("Elicitation Integration Tests", () => {
16067
it("should not proceed when user declines confirmation", async () => {
16168
mockElicitInput.confirmNo();
16269

163-
const result = await mcpClient.callTool({
70+
const result = await integration.mcpClient().callTool({
16471
name: "drop-database",
16572
arguments: { database: "test-db" },
16673
});
@@ -178,7 +85,7 @@ describe("Elicitation Integration Tests", () => {
17885
it("should request confirmation for drop-collection tool", async () => {
17986
mockElicitInput.confirmYes();
18087

181-
await mcpClient.callTool({
88+
await integration.mcpClient().callTool({
18289
name: "drop-collection",
18390
arguments: { database: "test-db", collection: "test-collection" },
18491
});
@@ -193,7 +100,7 @@ describe("Elicitation Integration Tests", () => {
193100
it("should request confirmation for delete-many tool", async () => {
194101
mockElicitInput.confirmYes();
195102

196-
await mcpClient.callTool({
103+
await integration.mcpClient().callTool({
197104
name: "delete-many",
198105
arguments: {
199106
database: "test-db",
@@ -212,10 +119,10 @@ describe("Elicitation Integration Tests", () => {
212119
it("should request confirmation for create-db-user tool", async () => {
213120
mockElicitInput.confirmYes();
214121

215-
await mcpClient.callTool({
122+
await integration.mcpClient().callTool({
216123
name: "atlas-create-db-user",
217124
arguments: {
218-
projectId: "test-project",
125+
projectId: "507f1f77bcf86cd799439011", // Valid 24-char hex string
219126
username: "test-user",
220127
roles: [{ roleName: "read", databaseName: "test-db" }],
221128
},
@@ -231,10 +138,10 @@ describe("Elicitation Integration Tests", () => {
231138
it("should request confirmation for create-access-list tool", async () => {
232139
mockElicitInput.confirmYes();
233140

234-
await mcpClient.callTool({
141+
await integration.mcpClient().callTool({
235142
name: "atlas-create-access-list",
236143
arguments: {
237-
projectId: "test-project",
144+
projectId: "507f1f77bcf86cd799439011", // Valid 24-char hex string
238145
ipAddresses: ["192.168.1.1"],
239146
},
240147
});
@@ -249,7 +156,7 @@ describe("Elicitation Integration Tests", () => {
249156

250157
describe("tools not requiring confirmation", () => {
251158
it("should not request confirmation for read operations", async () => {
252-
const result = await mcpClient.callTool({
159+
const result = await integration.mcpClient().callTool({
253160
name: "list-databases",
254161
arguments: {},
255162
});
@@ -260,7 +167,7 @@ describe("Elicitation Integration Tests", () => {
260167
});
261168

262169
it("should not request confirmation for find operations", async () => {
263-
const result = await mcpClient.callTool({
170+
const result = await integration.mcpClient().callTool({
264171
name: "find",
265172
arguments: {
266173
database: "test-db",
@@ -276,17 +183,19 @@ describe("Elicitation Integration Tests", () => {
276183
});
277184

278185
describe("without elicitation support", () => {
279-
beforeEach(async () => {
280-
await setupWithConfig({}, {}); // No elicitation capability
281-
});
186+
const integration = setupIntegrationTest(
187+
() => createTestConfig(),
188+
() => defaultDriverOptions,
189+
{ getClientCapabilities: () => ({}) }
190+
);
282191

283192
it("should proceed without confirmation for destructive tools when client lacks elicitation support", async () => {
284-
const result = await mcpClient.callTool({
193+
const result = await integration.mcpClient().callTool({
285194
name: "drop-database",
286195
arguments: { database: "test-db" },
287196
});
288197

289-
expect(mockElicitInput.mock).not.toHaveBeenCalled();
198+
// Note: No mock assertions needed since elicitation is disabled
290199
// Should fail with connection error since we're not connected, but confirms flow bypassed confirmation
291200
expect(result.isError).toBe(true);
292201
expect(result.content).toEqual(
@@ -301,12 +210,17 @@ describe("Elicitation Integration Tests", () => {
301210
});
302211

303212
describe("custom confirmation configuration", () => {
304-
it("should respect custom confirmationRequiredTools configuration", async () => {
305-
await setupWithConfig({ confirmationRequiredTools: ["list-databases"] }, { elicitation: {} });
213+
const mockElicitInput = createMockElicitInput();
214+
const integration = setupIntegrationTest(
215+
() => createTestConfig({ confirmationRequiredTools: ["list-databases"] }),
216+
() => defaultDriverOptions,
217+
{ elicitInput: mockElicitInput }
218+
);
306219

220+
it("should respect custom confirmationRequiredTools configuration", async () => {
307221
mockElicitInput.confirmYes();
308222

309-
await mcpClient.callTool({
223+
await integration.mcpClient().callTool({
310224
name: "list-databases",
311225
arguments: {},
312226
});
@@ -315,12 +229,7 @@ describe("Elicitation Integration Tests", () => {
315229
});
316230

317231
it("should not request confirmation when tool is removed from confirmationRequiredTools", async () => {
318-
await setupWithConfig(
319-
{ confirmationRequiredTools: [] }, // Empty list
320-
{ elicitation: {} }
321-
);
322-
323-
const result = await mcpClient.callTool({
232+
const result = await integration.mcpClient().callTool({
324233
name: "drop-database",
325234
arguments: { database: "test-db" },
326235
});
@@ -329,47 +238,23 @@ describe("Elicitation Integration Tests", () => {
329238
// Should fail with connection error since we're not connected
330239
expect(result.isError).toBe(true);
331240
});
332-
333-
it("should work with partial confirmation lists", async () => {
334-
await setupWithConfig(
335-
{ confirmationRequiredTools: ["drop-database"] }, // Only drop-database requires confirmation
336-
{ elicitation: {} }
337-
);
338-
339-
mockElicitInput.confirmYes();
340-
341-
// This should require confirmation
342-
await mcpClient.callTool({
343-
name: "drop-database",
344-
arguments: { database: "test-db" },
345-
});
346-
347-
expect(mockElicitInput.mock).toHaveBeenCalledTimes(1);
348-
349-
mockElicitInput.clear();
350-
351-
// This should not require confirmation
352-
await mcpClient.callTool({
353-
name: "drop-collection",
354-
arguments: { database: "test-db", collection: "test-collection" },
355-
});
356-
357-
expect(mockElicitInput.mock).not.toHaveBeenCalled();
358-
});
359241
});
360242

361243
describe("confirmation message content validation", () => {
362-
beforeEach(async () => {
363-
await setupWithConfig({}, { elicitation: {} });
364-
});
244+
const mockElicitInput = createMockElicitInput();
245+
const integration = setupIntegrationTest(
246+
() => createTestConfig(),
247+
() => defaultDriverOptions,
248+
{ elicitInput: mockElicitInput }
249+
);
365250

366251
it("should include specific details in create-db-user confirmation", async () => {
367252
mockElicitInput.confirmYes();
368253

369-
await mcpClient.callTool({
254+
await integration.mcpClient().callTool({
370255
name: "atlas-create-db-user",
371256
arguments: {
372-
projectId: "my-project-123",
257+
projectId: "507f1f77bcf86cd799439011", // Valid 24-char hex string
373258
username: "myuser",
374259
password: "mypassword",
375260
roles: [
@@ -381,15 +266,15 @@ describe("Elicitation Integration Tests", () => {
381266
});
382267

383268
expect(mockElicitInput.mock).toHaveBeenCalledWith({
384-
message: expect.stringMatching(/project.*my-project-123/),
269+
message: expect.stringMatching(/project.*507f1f77bcf86cd799439011/),
385270
requestedSchema: expect.objectContaining(Elicitation.CONFIRMATION_SCHEMA),
386271
});
387272
});
388273

389274
it("should include filter details in delete-many confirmation", async () => {
390275
mockElicitInput.confirmYes();
391276

392-
await mcpClient.callTool({
277+
await integration.mcpClient().callTool({
393278
name: "delete-many",
394279
arguments: {
395280
database: "mydb",
@@ -406,14 +291,17 @@ describe("Elicitation Integration Tests", () => {
406291
});
407292

408293
describe("error handling in confirmation flow", () => {
409-
beforeEach(async () => {
410-
await setupWithConfig({}, { elicitation: {} });
411-
});
294+
const mockElicitInput = createMockElicitInput();
295+
const integration = setupIntegrationTest(
296+
() => createTestConfig(),
297+
() => defaultDriverOptions,
298+
{ elicitInput: mockElicitInput }
299+
);
412300

413301
it("should handle confirmation errors gracefully", async () => {
414302
mockElicitInput.rejectWith(new Error("Confirmation service unavailable"));
415303

416-
const result = await mcpClient.callTool({
304+
const result = await integration.mcpClient().callTool({
417305
name: "drop-database",
418306
arguments: { database: "test-db" },
419307
});

0 commit comments

Comments
 (0)