Skip to content

Commit d7021bd

Browse files
authored
chore: add integration tests for insert-many (#85)
1 parent b842fa4 commit d7021bd

File tree

4 files changed

+142
-41
lines changed

4 files changed

+142
-41
lines changed

Diff for: src/tools/mongodb/create/insertMany.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export class InsertManyTool extends MongoDBToolBase {
2727
return {
2828
content: [
2929
{
30-
text: `Inserted \`${result.insertedCount}\` documents into collection \`${collection}\``,
30+
text: `Inserted \`${result.insertedCount}\` document(s) into collection "${collection}"`,
3131
type: "text",
3232
},
3333
{

Diff for: src/tools/mongodb/create/insertOne.ts

-38
This file was deleted.

Diff for: src/tools/mongodb/tools.ts

-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { CollectionIndexesTool } from "./read/collectionIndexes.js";
44
import { ListDatabasesTool } from "./metadata/listDatabases.js";
55
import { CreateIndexTool } from "./create/createIndex.js";
66
import { CollectionSchemaTool } from "./metadata/collectionSchema.js";
7-
import { InsertOneTool } from "./create/insertOne.js";
87
import { FindTool } from "./read/find.js";
98
import { InsertManyTool } from "./create/insertMany.js";
109
import { DeleteManyTool } from "./delete/deleteMany.js";
@@ -28,7 +27,6 @@ export const MongoDbTools = [
2827
CollectionIndexesTool,
2928
CreateIndexTool,
3029
CollectionSchemaTool,
31-
InsertOneTool,
3230
FindTool,
3331
InsertManyTool,
3432
DeleteManyTool,
+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import {
2+
getResponseContent,
3+
validateParameters,
4+
dbOperationParameters,
5+
setupIntegrationTest,
6+
} from "../../../helpers.js";
7+
import { McpError } from "@modelcontextprotocol/sdk/types.js";
8+
import config from "../../../../../src/config.js";
9+
10+
describe("insertMany tool", () => {
11+
const integration = setupIntegrationTest();
12+
13+
it("should have correct metadata", async () => {
14+
const { tools } = await integration.mcpClient().listTools();
15+
const insertMany = tools.find((tool) => tool.name === "insert-many")!;
16+
expect(insertMany).toBeDefined();
17+
expect(insertMany.description).toBe("Insert an array of documents into a MongoDB collection");
18+
19+
validateParameters(insertMany, [
20+
...dbOperationParameters,
21+
{
22+
name: "documents",
23+
type: "array",
24+
description:
25+
"The array of documents to insert, matching the syntax of the document argument of db.collection.insertMany()",
26+
required: true,
27+
},
28+
]);
29+
});
30+
31+
describe("with invalid arguments", () => {
32+
const args = [
33+
{},
34+
{ collection: "bar", database: 123, documents: [] },
35+
{ collection: [], database: "test", documents: [] },
36+
{ collection: "bar", database: "test", documents: "my-document" },
37+
{ collection: "bar", database: "test", documents: { name: "Peter" } },
38+
];
39+
for (const arg of args) {
40+
it(`throws a schema error for: ${JSON.stringify(arg)}`, async () => {
41+
await integration.connectMcpClient();
42+
try {
43+
await integration.mcpClient().callTool({ name: "insert-many", arguments: arg });
44+
expect.fail("Expected an error to be thrown");
45+
} catch (error) {
46+
expect(error).toBeInstanceOf(McpError);
47+
const mcpError = error as McpError;
48+
expect(mcpError.code).toEqual(-32602);
49+
expect(mcpError.message).toContain("Invalid arguments for tool insert-many");
50+
}
51+
});
52+
}
53+
});
54+
55+
const validateDocuments = async (collection: string, expectedDocuments: object[]) => {
56+
const collections = await integration.mongoClient().db(integration.randomDbName()).listCollections().toArray();
57+
expect(collections.find((c) => c.name === collection)).toBeDefined();
58+
59+
const docs = await integration
60+
.mongoClient()
61+
.db(integration.randomDbName())
62+
.collection(collection)
63+
.find()
64+
.toArray();
65+
66+
expect(docs).toHaveLength(expectedDocuments.length);
67+
for (const expectedDocument of expectedDocuments) {
68+
expect(docs).toContainEqual(expect.objectContaining(expectedDocument));
69+
}
70+
};
71+
72+
it("creates the namespace if necessary", async () => {
73+
await integration.connectMcpClient();
74+
const response = await integration.mcpClient().callTool({
75+
name: "insert-many",
76+
arguments: {
77+
database: integration.randomDbName(),
78+
collection: "coll1",
79+
documents: [{ prop1: "value1" }],
80+
},
81+
});
82+
83+
const content = getResponseContent(response.content);
84+
expect(content).toContain('Inserted `1` document(s) into collection "coll1"');
85+
86+
await validateDocuments("coll1", [{ prop1: "value1" }]);
87+
});
88+
89+
it("returns an error when inserting duplicates", async () => {
90+
const { insertedIds } = await integration
91+
.mongoClient()
92+
.db(integration.randomDbName())
93+
.collection("coll1")
94+
.insertMany([{ prop1: "value1" }]);
95+
96+
await integration.connectMcpClient();
97+
const response = await integration.mcpClient().callTool({
98+
name: "insert-many",
99+
arguments: {
100+
database: integration.randomDbName(),
101+
collection: "coll1",
102+
documents: [{ prop1: "value1", _id: insertedIds[0] }],
103+
},
104+
});
105+
106+
const content = getResponseContent(response.content);
107+
expect(content).toContain("Error running insert-many");
108+
expect(content).toContain("duplicate key error");
109+
expect(content).toContain(insertedIds[0].toString());
110+
});
111+
112+
describe("when not connected", () => {
113+
it("connects automatically if connection string is configured", async () => {
114+
config.connectionString = integration.connectionString();
115+
116+
const response = await integration.mcpClient().callTool({
117+
name: "insert-many",
118+
arguments: {
119+
database: integration.randomDbName(),
120+
collection: "coll1",
121+
documents: [{ prop1: "value1" }],
122+
},
123+
});
124+
const content = getResponseContent(response.content);
125+
expect(content).toContain('Inserted `1` document(s) into collection "coll1"');
126+
});
127+
128+
it("throw an error if connection string is not configured", async () => {
129+
const response = await integration.mcpClient().callTool({
130+
name: "insert-many",
131+
arguments: {
132+
database: integration.randomDbName(),
133+
collection: "coll1",
134+
documents: [{ prop1: "value1" }],
135+
},
136+
});
137+
const content = getResponseContent(response.content);
138+
expect(content).toContain("You need to connect to a MongoDB instance before you can access its data.");
139+
});
140+
});
141+
});

0 commit comments

Comments
 (0)