Skip to content

Commit 23e48f3

Browse files
committed
chore: add aggregate tests
1 parent b959b6a commit 23e48f3

File tree

2 files changed

+102
-3
lines changed

2 files changed

+102
-3
lines changed

src/tools/mongodb/read/aggregate.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { z } from "zod";
22
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
33
import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
44
import { ToolArgs, OperationType } from "../../tool.js";
5+
import { EJSON } from "bson";
56

67
export const AggregateArgs = {
78
pipeline: z.array(z.object({}).passthrough()).describe("An array of aggregation stages to execute"),
8-
limit: z.number().optional().default(10).describe("The maximum number of documents to return"),
99
};
1010

1111
export class AggregateTool extends MongoDBToolBase {
@@ -27,12 +27,12 @@ export class AggregateTool extends MongoDBToolBase {
2727

2828
const content: Array<{ text: string; type: "text" }> = [
2929
{
30-
text: `Found ${documents.length} documents in the collection \`${collection}\`:`,
30+
text: `Found ${documents.length} documents in the collection "${collection}":`,
3131
type: "text",
3232
},
3333
...documents.map((doc) => {
3434
return {
35-
text: JSON.stringify(doc),
35+
text: EJSON.stringify(doc),
3636
type: "text",
3737
} as { text: string; type: "text" };
3838
}),
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { pipeline } from "stream";
2+
import {
3+
getResponseContent,
4+
databaseCollectionParameters,
5+
setupIntegrationTest,
6+
validateToolMetadata,
7+
validateAutoConnectBehavior,
8+
validateThrowsForInvalidArguments,
9+
getResponseElements,
10+
} from "../../../helpers.js";
11+
12+
describe("aggregate tool", () => {
13+
const integration = setupIntegrationTest();
14+
15+
validateToolMetadata(integration, "aggregate", "Run an aggregation against a MongoDB collection", [
16+
...databaseCollectionParameters,
17+
{
18+
name: "pipeline",
19+
description: "An array of aggregation stages to execute",
20+
type: "array",
21+
required: true,
22+
},
23+
]);
24+
25+
validateThrowsForInvalidArguments(integration, "aggregate", [
26+
{},
27+
{ database: "test", collection: "foo" },
28+
{ database: test, pipeline: [] },
29+
{ database: "test", collection: "foo", pipeline: {} },
30+
{ database: "test", collection: "foo", pipeline: [], extra: "extra" },
31+
{ database: "test", collection: [], pipeline: [] },
32+
{ database: 123, collection: "foo", pipeline: [] },
33+
]);
34+
35+
it("can run aggragation on non-existent database", async () => {
36+
await integration.connectMcpClient();
37+
const response = await integration.mcpClient().callTool({
38+
name: "aggregate",
39+
arguments: { database: "non-existent", collection: "people", pipeline: [{ $match: { name: "Peter" } }] },
40+
});
41+
42+
const elements = getResponseElements(response.content);
43+
expect(elements).toHaveLength(1);
44+
expect(elements[0].text).toEqual('Found 0 documents in the collection "people":');
45+
});
46+
47+
it("can run aggragation on an empty collection", async () => {
48+
await integration.mongoClient().db(integration.randomDbName()).createCollection("people");
49+
50+
await integration.connectMcpClient();
51+
const response = await integration.mcpClient().callTool({
52+
name: "aggregate",
53+
arguments: {
54+
database: integration.randomDbName(),
55+
collection: "people",
56+
pipeline: [{ $match: { name: "Peter" } }],
57+
},
58+
});
59+
60+
const elements = getResponseElements(response.content);
61+
expect(elements).toHaveLength(1);
62+
expect(elements[0].text).toEqual('Found 0 documents in the collection "people":');
63+
});
64+
65+
it("can run aggragation on an existing collection", async () => {
66+
const mongoClient = integration.mongoClient();
67+
await mongoClient
68+
.db(integration.randomDbName())
69+
.collection("people")
70+
.insertMany([
71+
{ name: "Peter", age: 5 },
72+
{ name: "Laura", age: 10 },
73+
{ name: "Søren", age: 15 },
74+
]);
75+
76+
await integration.connectMcpClient();
77+
const response = await integration.mcpClient().callTool({
78+
name: "aggregate",
79+
arguments: {
80+
database: integration.randomDbName(),
81+
collection: "people",
82+
pipeline: [{ $match: { age: { $gt: 8 } } }, { $sort: { name: -1 } }],
83+
},
84+
});
85+
86+
const elements = getResponseElements(response.content);
87+
expect(elements).toHaveLength(3);
88+
expect(elements[0].text).toEqual('Found 2 documents in the collection "people":');
89+
expect(JSON.parse(elements[1].text)).toEqual({ _id: expect.any(Object), name: "Søren", age: 15 });
90+
expect(JSON.parse(elements[2].text)).toEqual({ _id: expect.any(Object), name: "Laura", age: 10 });
91+
});
92+
93+
validateAutoConnectBehavior(integration, "aggregate", () => {
94+
return {
95+
args: { database: integration.randomDbName(), collection: "coll1" },
96+
expectedResponse: 'Found 0 documents in the collection "coll1"',
97+
};
98+
});
99+
});

0 commit comments

Comments
 (0)