mongodb-mcp-server 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/pull_request_template.md +5 -0
- package/.github/workflows/accuracy-tests.yml +55 -0
- package/.github/workflows/check.yml +1 -1
- package/.github/workflows/code_health.yaml +4 -4
- package/.github/workflows/code_health_fork.yaml +0 -14
- package/.github/workflows/dependabot_pr.yaml +26 -0
- package/.github/workflows/docker.yaml +1 -1
- package/.github/workflows/jira-issue.yml +72 -0
- package/.smithery/smithery.yaml +10 -0
- package/.vscode/extensions.json +1 -1
- package/.vscode/launch.json +11 -1
- package/.vscode/settings.json +1 -11
- package/Dockerfile +1 -0
- package/README.md +132 -31
- package/dist/common/atlas/accessListUtils.js +36 -0
- package/dist/common/atlas/accessListUtils.js.map +1 -0
- package/dist/common/atlas/apiClient.js +25 -6
- package/dist/common/atlas/apiClient.js.map +1 -1
- package/dist/common/atlas/cluster.js +1 -1
- package/dist/common/atlas/cluster.js.map +1 -1
- package/dist/{config.js → common/config.js} +8 -1
- package/dist/common/config.js.map +1 -0
- package/dist/{errors.js → common/errors.js} +1 -0
- package/dist/common/errors.js.map +1 -0
- package/dist/{logger.js → common/logger.js} +20 -19
- package/dist/common/logger.js.map +1 -0
- package/dist/common/managedTimeout.js +20 -0
- package/dist/common/managedTimeout.js.map +1 -0
- package/dist/common/packageInfo.js.map +1 -0
- package/dist/{session.js → common/session.js} +20 -21
- package/dist/common/session.js.map +1 -0
- package/dist/common/sessionStore.js +73 -0
- package/dist/common/sessionStore.js.map +1 -0
- package/dist/helpers/container.js +28 -0
- package/dist/helpers/container.js.map +1 -0
- package/dist/helpers/generatePassword.js.map +1 -0
- package/dist/helpers/indexCheck.js +63 -0
- package/dist/helpers/indexCheck.js.map +1 -0
- package/dist/index.js +30 -37
- package/dist/index.js.map +1 -1
- package/dist/server.js +44 -7
- package/dist/server.js.map +1 -1
- package/dist/telemetry/constants.js +1 -1
- package/dist/telemetry/constants.js.map +1 -1
- package/dist/telemetry/telemetry.js +86 -116
- package/dist/telemetry/telemetry.js.map +1 -1
- package/dist/tools/atlas/atlasTool.js +3 -3
- package/dist/tools/atlas/atlasTool.js.map +1 -1
- package/dist/tools/atlas/connect/connectCluster.js +198 -0
- package/dist/tools/atlas/connect/connectCluster.js.map +1 -0
- package/dist/tools/atlas/create/createAccessList.js +9 -10
- package/dist/tools/atlas/create/createAccessList.js.map +1 -1
- package/dist/tools/atlas/create/createDBUser.js +3 -1
- package/dist/tools/atlas/create/createDBUser.js.map +1 -1
- package/dist/tools/atlas/create/createFreeCluster.js +2 -0
- package/dist/tools/atlas/create/createFreeCluster.js.map +1 -1
- package/dist/tools/atlas/create/createProject.js.map +1 -1
- package/dist/tools/atlas/read/inspectAccessList.js.map +1 -1
- package/dist/tools/atlas/read/inspectCluster.js.map +1 -1
- package/dist/tools/atlas/read/listAlerts.js.map +1 -1
- package/dist/tools/atlas/read/listClusters.js.map +1 -1
- package/dist/tools/atlas/read/listDBUsers.js.map +1 -1
- package/dist/tools/atlas/read/listOrgs.js.map +1 -1
- package/dist/tools/atlas/read/listProjects.js.map +1 -1
- package/dist/tools/atlas/tools.js +1 -1
- package/dist/tools/atlas/tools.js.map +1 -1
- package/dist/tools/mongodb/{metadata → connect}/connect.js +7 -4
- package/dist/tools/mongodb/connect/connect.js.map +1 -0
- package/dist/tools/mongodb/create/createCollection.js.map +1 -1
- package/dist/tools/mongodb/create/createIndex.js +1 -1
- package/dist/tools/mongodb/create/createIndex.js.map +1 -1
- package/dist/tools/mongodb/create/insertMany.js +1 -1
- package/dist/tools/mongodb/create/insertMany.js.map +1 -1
- package/dist/tools/mongodb/delete/deleteMany.js +20 -1
- package/dist/tools/mongodb/delete/deleteMany.js.map +1 -1
- package/dist/tools/mongodb/delete/dropCollection.js.map +1 -1
- package/dist/tools/mongodb/delete/dropDatabase.js.map +1 -1
- package/dist/tools/mongodb/metadata/collectionSchema.js.map +1 -1
- package/dist/tools/mongodb/metadata/collectionStorageSize.js.map +1 -1
- package/dist/tools/mongodb/metadata/dbStats.js.map +1 -1
- package/dist/tools/mongodb/metadata/explain.js +2 -2
- package/dist/tools/mongodb/metadata/explain.js.map +1 -1
- package/dist/tools/mongodb/metadata/listCollections.js.map +1 -1
- package/dist/tools/mongodb/metadata/listDatabases.js.map +1 -1
- package/dist/tools/mongodb/metadata/logs.js.map +1 -1
- package/dist/tools/mongodb/mongodbTool.js +47 -10
- package/dist/tools/mongodb/mongodbTool.js.map +1 -1
- package/dist/tools/mongodb/read/aggregate.js +10 -1
- package/dist/tools/mongodb/read/aggregate.js.map +1 -1
- package/dist/tools/mongodb/read/collectionIndexes.js.map +1 -1
- package/dist/tools/mongodb/read/count.js +15 -1
- package/dist/tools/mongodb/read/count.js.map +1 -1
- package/dist/tools/mongodb/read/find.js +14 -4
- package/dist/tools/mongodb/read/find.js.map +1 -1
- package/dist/tools/mongodb/tools.js +1 -1
- package/dist/tools/mongodb/tools.js.map +1 -1
- package/dist/tools/mongodb/update/renameCollection.js.map +1 -1
- package/dist/tools/mongodb/update/updateMany.js +24 -2
- package/dist/tools/mongodb/update/updateMany.js.map +1 -1
- package/dist/tools/tool.js +12 -9
- package/dist/tools/tool.js.map +1 -1
- package/dist/transports/base.js +26 -0
- package/dist/transports/base.js.map +1 -0
- package/dist/{helpers/EJsonTransport.js → transports/stdio.js} +24 -2
- package/dist/transports/stdio.js.map +1 -0
- package/dist/transports/streamableHttp.js +140 -0
- package/dist/transports/streamableHttp.js.map +1 -0
- package/eslint.config.js +13 -4
- package/package.json +43 -33
- package/resources/test-summary-template.html +415 -0
- package/scripts/accuracy/generateTestSummary.ts +335 -0
- package/scripts/accuracy/runAccuracyTests.sh +45 -0
- package/scripts/accuracy/updateAccuracyRunStatus.ts +21 -0
- package/src/common/atlas/accessListUtils.ts +54 -0
- package/src/common/atlas/apiClient.ts +25 -6
- package/src/common/atlas/cluster.ts +1 -1
- package/src/{config.ts → common/config.ts} +16 -2
- package/src/{errors.ts → common/errors.ts} +1 -0
- package/src/{logger.ts → common/logger.ts} +21 -24
- package/src/common/managedTimeout.ts +27 -0
- package/src/{session.ts → common/session.ts} +24 -26
- package/src/common/sessionStore.ts +111 -0
- package/src/helpers/container.ts +35 -0
- package/src/helpers/indexCheck.ts +83 -0
- package/src/index.ts +30 -40
- package/src/server.ts +55 -11
- package/src/telemetry/constants.ts +1 -1
- package/src/telemetry/telemetry.ts +109 -153
- package/src/telemetry/types.ts +2 -1
- package/src/tools/atlas/atlasTool.ts +4 -4
- package/src/tools/atlas/connect/connectCluster.ts +259 -0
- package/src/tools/atlas/create/createAccessList.ts +15 -13
- package/src/tools/atlas/create/createDBUser.ts +5 -3
- package/src/tools/atlas/create/createFreeCluster.ts +4 -2
- package/src/tools/atlas/create/createProject.ts +2 -2
- package/src/tools/atlas/read/inspectAccessList.ts +2 -2
- package/src/tools/atlas/read/inspectCluster.ts +2 -2
- package/src/tools/atlas/read/listAlerts.ts +2 -2
- package/src/tools/atlas/read/listClusters.ts +2 -2
- package/src/tools/atlas/read/listDBUsers.ts +2 -2
- package/src/tools/atlas/read/listOrgs.ts +2 -2
- package/src/tools/atlas/read/listProjects.ts +2 -2
- package/src/tools/atlas/tools.ts +1 -1
- package/src/tools/mongodb/{metadata → connect}/connect.ts +12 -9
- package/src/tools/mongodb/create/createCollection.ts +2 -2
- package/src/tools/mongodb/create/createIndex.ts +3 -3
- package/src/tools/mongodb/create/insertMany.ts +3 -3
- package/src/tools/mongodb/delete/deleteMany.ts +24 -3
- package/src/tools/mongodb/delete/dropCollection.ts +2 -2
- package/src/tools/mongodb/delete/dropDatabase.ts +2 -2
- package/src/tools/mongodb/metadata/collectionSchema.ts +2 -2
- package/src/tools/mongodb/metadata/collectionStorageSize.ts +2 -2
- package/src/tools/mongodb/metadata/dbStats.ts +2 -2
- package/src/tools/mongodb/metadata/explain.ts +4 -4
- package/src/tools/mongodb/metadata/listCollections.ts +2 -2
- package/src/tools/mongodb/metadata/listDatabases.ts +2 -2
- package/src/tools/mongodb/metadata/logs.ts +2 -2
- package/src/tools/mongodb/mongodbTool.ts +60 -14
- package/src/tools/mongodb/read/aggregate.ts +14 -3
- package/src/tools/mongodb/read/collectionIndexes.ts +2 -2
- package/src/tools/mongodb/read/count.ts +19 -3
- package/src/tools/mongodb/read/find.ts +20 -6
- package/src/tools/mongodb/tools.ts +1 -1
- package/src/tools/mongodb/update/renameCollection.ts +2 -2
- package/src/tools/mongodb/update/updateMany.ts +28 -4
- package/src/tools/tool.ts +23 -18
- package/src/transports/base.ts +34 -0
- package/src/{helpers/EJsonTransport.ts → transports/stdio.ts} +30 -1
- package/src/transports/streamableHttp.ts +178 -0
- package/tests/accuracy/aggregate.test.ts +27 -0
- package/tests/accuracy/collectionIndexes.test.ts +40 -0
- package/tests/accuracy/collectionSchema.test.ts +28 -0
- package/tests/accuracy/collectionStorageSize.test.ts +41 -0
- package/tests/accuracy/count.test.ts +44 -0
- package/tests/accuracy/createCollection.test.ts +46 -0
- package/tests/accuracy/createIndex.test.ts +37 -0
- package/tests/accuracy/dbStats.test.ts +15 -0
- package/tests/accuracy/deleteMany.test.ts +44 -0
- package/tests/accuracy/dropCollection.test.ts +74 -0
- package/tests/accuracy/dropDatabase.test.ts +41 -0
- package/tests/accuracy/explain.test.ts +73 -0
- package/tests/accuracy/find.test.ts +114 -0
- package/tests/accuracy/insertMany.test.ts +48 -0
- package/tests/accuracy/listCollections.test.ts +60 -0
- package/tests/accuracy/listDatabases.test.ts +31 -0
- package/tests/accuracy/logs.test.ts +28 -0
- package/tests/accuracy/renameCollection.test.ts +31 -0
- package/tests/accuracy/sdk/accuracyResultStorage/diskStorage.ts +189 -0
- package/tests/accuracy/sdk/accuracyResultStorage/getAccuracyResultStorage.ts +11 -0
- package/tests/accuracy/sdk/accuracyResultStorage/mongodbStorage.ts +151 -0
- package/tests/accuracy/sdk/accuracyResultStorage/resultStorage.ts +117 -0
- package/tests/accuracy/sdk/accuracyScorer.ts +93 -0
- package/tests/accuracy/sdk/accuracyTestingClient.ts +94 -0
- package/tests/accuracy/sdk/agent.ts +56 -0
- package/tests/accuracy/sdk/constants.ts +26 -0
- package/tests/accuracy/sdk/describeAccuracyTests.ts +126 -0
- package/tests/accuracy/sdk/gitInfo.ts +7 -0
- package/tests/accuracy/sdk/matcher.ts +193 -0
- package/tests/accuracy/sdk/models.ts +95 -0
- package/tests/accuracy/test-data-dumps/comics.books.json +417 -0
- package/tests/accuracy/test-data-dumps/comics.characters.json +402 -0
- package/tests/accuracy/test-data-dumps/mflix.movies.json +496 -0
- package/tests/accuracy/test-data-dumps/mflix.shows.json +572 -0
- package/tests/accuracy/updateMany.test.ts +42 -0
- package/tests/integration/helpers.ts +9 -9
- package/tests/integration/indexCheck.test.ts +464 -0
- package/tests/integration/server.test.ts +6 -4
- package/tests/integration/telemetry.test.ts +29 -0
- package/tests/integration/tools/atlas/accessLists.test.ts +22 -2
- package/tests/integration/tools/atlas/alerts.test.ts +3 -2
- package/tests/integration/tools/atlas/atlasHelpers.ts +3 -0
- package/tests/integration/tools/atlas/clusters.test.ts +68 -16
- package/tests/integration/tools/atlas/dbUsers.test.ts +14 -1
- package/tests/integration/tools/atlas/orgs.test.ts +2 -1
- package/tests/integration/tools/atlas/projects.test.ts +4 -3
- package/tests/integration/tools/mongodb/{metadata → connect}/connect.test.ts +34 -3
- package/tests/integration/tools/mongodb/create/createCollection.test.ts +1 -0
- package/tests/integration/tools/mongodb/create/createIndex.test.ts +1 -0
- package/tests/integration/tools/mongodb/create/insertMany.test.ts +1 -0
- package/tests/integration/tools/mongodb/delete/deleteMany.test.ts +1 -0
- package/tests/integration/tools/mongodb/delete/dropCollection.test.ts +1 -1
- package/tests/integration/tools/mongodb/delete/dropDatabase.test.ts +1 -0
- package/tests/integration/tools/mongodb/metadata/collectionSchema.test.ts +1 -0
- package/tests/integration/tools/mongodb/metadata/collectionStorageSize.test.ts +1 -0
- package/tests/integration/tools/mongodb/metadata/dbStats.test.ts +1 -0
- package/tests/integration/tools/mongodb/metadata/explain.test.ts +1 -0
- package/tests/integration/tools/mongodb/metadata/listCollections.test.ts +1 -0
- package/tests/integration/tools/mongodb/metadata/listDatabases.test.ts +3 -2
- package/tests/integration/tools/mongodb/metadata/logs.test.ts +1 -0
- package/tests/integration/tools/mongodb/mongodbHelpers.ts +67 -2
- package/tests/integration/tools/mongodb/read/aggregate.test.ts +2 -1
- package/tests/integration/tools/mongodb/read/collectionIndexes.test.ts +1 -0
- package/tests/integration/tools/mongodb/read/count.test.ts +1 -0
- package/tests/integration/tools/mongodb/read/find.test.ts +2 -1
- package/tests/integration/tools/mongodb/update/renameCollection.test.ts +1 -0
- package/tests/integration/tools/mongodb/update/updateMany.test.ts +1 -0
- package/tests/integration/transports/stdio.test.ts +40 -0
- package/tests/integration/transports/streamableHttp.test.ts +56 -0
- package/tests/matchers/toIncludeSameMembers.test.ts +59 -0
- package/tests/matchers/toIncludeSameMembers.ts +12 -0
- package/tests/setup.ts +7 -0
- package/tests/unit/accessListUtils.test.ts +39 -0
- package/tests/unit/accuracyScorer.test.ts +390 -0
- package/tests/unit/{apiClient.test.ts → common/apiClient.test.ts} +15 -15
- package/tests/unit/common/managedTimeout.test.ts +67 -0
- package/tests/unit/{session.test.ts → common/session.test.ts} +7 -12
- package/tests/unit/helpers/indexCheck.test.ts +150 -0
- package/tests/unit/telemetry.test.ts +99 -137
- package/tests/unit/{EJsonTransport.test.ts → transports/stdio.test.ts} +4 -4
- package/tests/vitest.d.ts +11 -0
- package/tsconfig.json +0 -1
- package/{tsconfig.jest.json → tsconfig.test.json} +1 -2
- package/vitest.config.ts +41 -0
- package/dist/common/atlas/generatePassword.js.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/helpers/EJsonTransport.js.map +0 -1
- package/dist/helpers/packageInfo.js.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/session.js.map +0 -1
- package/dist/tools/atlas/metadata/connectCluster.js +0 -100
- package/dist/tools/atlas/metadata/connectCluster.js.map +0 -1
- package/dist/tools/mongodb/metadata/connect.js.map +0 -1
- package/global.d.ts +0 -1
- package/jest.config.cjs +0 -22
- package/src/tools/atlas/metadata/connectCluster.ts +0 -121
- /package/dist/{helpers → common}/packageInfo.js +0 -0
- /package/dist/{common/atlas → helpers}/generatePassword.js +0 -0
- /package/src/{helpers → common}/packageInfo.ts +0 -0
- /package/src/{common/atlas → helpers}/generatePassword.ts +0 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { defaultTestConfig, getResponseContent } from "./helpers.js";
|
|
2
|
+
import { describeWithMongoDB } from "./tools/mongodb/mongodbHelpers.js";
|
|
3
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
describe("IndexCheck integration tests", () => {
|
|
6
|
+
describe("with indexCheck enabled", () => {
|
|
7
|
+
describeWithMongoDB(
|
|
8
|
+
"indexCheck functionality",
|
|
9
|
+
(integration) => {
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
await integration.connectMcpClient();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("find operations", () => {
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
// Insert test data for find operations
|
|
17
|
+
await integration
|
|
18
|
+
.mongoClient()
|
|
19
|
+
.db(integration.randomDbName())
|
|
20
|
+
.collection("find-test-collection")
|
|
21
|
+
.insertMany([
|
|
22
|
+
{ name: "document1", value: 1, category: "A" },
|
|
23
|
+
{ name: "document2", value: 2, category: "B" },
|
|
24
|
+
{ name: "document3", value: 3, category: "A" },
|
|
25
|
+
]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should reject queries that perform collection scans", async () => {
|
|
29
|
+
const response = await integration.mcpClient().callTool({
|
|
30
|
+
name: "find",
|
|
31
|
+
arguments: {
|
|
32
|
+
database: integration.randomDbName(),
|
|
33
|
+
collection: "find-test-collection",
|
|
34
|
+
filter: { category: "A" }, // No index on category field
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const content = getResponseContent(response.content);
|
|
39
|
+
expect(content).toContain("Index check failed");
|
|
40
|
+
expect(content).toContain("collection scan (COLLSCAN)");
|
|
41
|
+
expect(content).toContain("MDB_MCP_INDEX_CHECK");
|
|
42
|
+
expect(response.isError).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should allow queries that use indexes", async () => {
|
|
46
|
+
// Create an index on the category field
|
|
47
|
+
await integration
|
|
48
|
+
.mongoClient()
|
|
49
|
+
.db(integration.randomDbName())
|
|
50
|
+
.collection("find-test-collection")
|
|
51
|
+
.createIndex({ category: 1 });
|
|
52
|
+
|
|
53
|
+
const response = await integration.mcpClient().callTool({
|
|
54
|
+
name: "find",
|
|
55
|
+
arguments: {
|
|
56
|
+
database: integration.randomDbName(),
|
|
57
|
+
collection: "find-test-collection",
|
|
58
|
+
filter: { category: "A" }, // Now has index
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(response.isError).toBeFalsy();
|
|
63
|
+
const content = getResponseContent(response.content);
|
|
64
|
+
expect(content).toContain("Found");
|
|
65
|
+
expect(content).toContain("documents");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should allow queries using _id (IDHACK)", async () => {
|
|
69
|
+
const docs = await integration
|
|
70
|
+
.mongoClient()
|
|
71
|
+
.db(integration.randomDbName())
|
|
72
|
+
.collection("find-test-collection")
|
|
73
|
+
.find({})
|
|
74
|
+
.toArray();
|
|
75
|
+
|
|
76
|
+
expect(docs.length).toBeGreaterThan(0);
|
|
77
|
+
|
|
78
|
+
const response = await integration.mcpClient().callTool({
|
|
79
|
+
name: "find",
|
|
80
|
+
arguments: {
|
|
81
|
+
database: integration.randomDbName(),
|
|
82
|
+
collection: "find-test-collection",
|
|
83
|
+
filter: { _id: docs[0]?._id }, // Uses _id index (IDHACK)
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(response.isError).toBeFalsy();
|
|
88
|
+
const content = getResponseContent(response.content);
|
|
89
|
+
expect(content).toContain("Found 1 documents");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("count operations", () => {
|
|
94
|
+
beforeEach(async () => {
|
|
95
|
+
// Insert test data for count operations
|
|
96
|
+
await integration
|
|
97
|
+
.mongoClient()
|
|
98
|
+
.db(integration.randomDbName())
|
|
99
|
+
.collection("count-test-collection")
|
|
100
|
+
.insertMany([
|
|
101
|
+
{ name: "document1", value: 1, category: "A" },
|
|
102
|
+
{ name: "document2", value: 2, category: "B" },
|
|
103
|
+
{ name: "document3", value: 3, category: "A" },
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should reject count queries that perform collection scans", async () => {
|
|
108
|
+
const response = await integration.mcpClient().callTool({
|
|
109
|
+
name: "count",
|
|
110
|
+
arguments: {
|
|
111
|
+
database: integration.randomDbName(),
|
|
112
|
+
collection: "count-test-collection",
|
|
113
|
+
query: { value: { $gt: 1 } }, // No index on value field
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const content = getResponseContent(response.content);
|
|
118
|
+
expect(content).toContain("Index check failed");
|
|
119
|
+
expect(content).toContain("count operation");
|
|
120
|
+
expect(response.isError).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should allow count queries with indexes", async () => {
|
|
124
|
+
// Create an index on the value field
|
|
125
|
+
await integration
|
|
126
|
+
.mongoClient()
|
|
127
|
+
.db(integration.randomDbName())
|
|
128
|
+
.collection("count-test-collection")
|
|
129
|
+
.createIndex({ value: 1 });
|
|
130
|
+
|
|
131
|
+
const response = await integration.mcpClient().callTool({
|
|
132
|
+
name: "count",
|
|
133
|
+
arguments: {
|
|
134
|
+
database: integration.randomDbName(),
|
|
135
|
+
collection: "count-test-collection",
|
|
136
|
+
query: { value: { $gt: 1 } }, // Now has index
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
expect(response.isError).toBeFalsy();
|
|
141
|
+
const content = getResponseContent(response.content);
|
|
142
|
+
expect(content).toContain("Found");
|
|
143
|
+
expect(content).toMatch(/\d+ documents/);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe("aggregate operations", () => {
|
|
148
|
+
beforeEach(async () => {
|
|
149
|
+
// Insert test data for aggregate operations
|
|
150
|
+
await integration
|
|
151
|
+
.mongoClient()
|
|
152
|
+
.db(integration.randomDbName())
|
|
153
|
+
.collection("aggregate-test-collection")
|
|
154
|
+
.insertMany([
|
|
155
|
+
{ name: "document1", value: 1, category: "A" },
|
|
156
|
+
{ name: "document2", value: 2, category: "B" },
|
|
157
|
+
{ name: "document3", value: 3, category: "A" },
|
|
158
|
+
]);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("should reject aggregation queries that perform collection scans", async () => {
|
|
162
|
+
const response = await integration.mcpClient().callTool({
|
|
163
|
+
name: "aggregate",
|
|
164
|
+
arguments: {
|
|
165
|
+
database: integration.randomDbName(),
|
|
166
|
+
collection: "aggregate-test-collection",
|
|
167
|
+
pipeline: [
|
|
168
|
+
{ $match: { category: "A" } }, // No index on category
|
|
169
|
+
{ $group: { _id: "$category", count: { $sum: 1 } } },
|
|
170
|
+
],
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const content = getResponseContent(response.content);
|
|
175
|
+
expect(content).toContain("Index check failed");
|
|
176
|
+
expect(content).toContain("aggregate operation");
|
|
177
|
+
expect(response.isError).toBe(true);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("should allow aggregation queries with indexes", async () => {
|
|
181
|
+
// Create an index on the category field
|
|
182
|
+
await integration
|
|
183
|
+
.mongoClient()
|
|
184
|
+
.db(integration.randomDbName())
|
|
185
|
+
.collection("aggregate-test-collection")
|
|
186
|
+
.createIndex({ category: 1 });
|
|
187
|
+
|
|
188
|
+
const response = await integration.mcpClient().callTool({
|
|
189
|
+
name: "aggregate",
|
|
190
|
+
arguments: {
|
|
191
|
+
database: integration.randomDbName(),
|
|
192
|
+
collection: "aggregate-test-collection",
|
|
193
|
+
pipeline: [
|
|
194
|
+
{ $match: { category: "A" } }, // Now has index
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
expect(response.isError).toBeFalsy();
|
|
200
|
+
const content = getResponseContent(response.content);
|
|
201
|
+
expect(content).toContain("Found");
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe("updateMany operations", () => {
|
|
206
|
+
beforeEach(async () => {
|
|
207
|
+
// Insert test data for updateMany operations
|
|
208
|
+
await integration
|
|
209
|
+
.mongoClient()
|
|
210
|
+
.db(integration.randomDbName())
|
|
211
|
+
.collection("update-test-collection")
|
|
212
|
+
.insertMany([
|
|
213
|
+
{ name: "document1", value: 1, category: "A" },
|
|
214
|
+
{ name: "document2", value: 2, category: "B" },
|
|
215
|
+
{ name: "document3", value: 3, category: "A" },
|
|
216
|
+
]);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("should reject updateMany queries that perform collection scans", async () => {
|
|
220
|
+
const response = await integration.mcpClient().callTool({
|
|
221
|
+
name: "update-many",
|
|
222
|
+
arguments: {
|
|
223
|
+
database: integration.randomDbName(),
|
|
224
|
+
collection: "update-test-collection",
|
|
225
|
+
filter: { category: "A" }, // No index on category
|
|
226
|
+
update: { $set: { updated: true } },
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const content = getResponseContent(response.content);
|
|
231
|
+
expect(content).toContain("Index check failed");
|
|
232
|
+
expect(content).toContain("updateMany operation");
|
|
233
|
+
expect(response.isError).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("should allow updateMany queries with indexes", async () => {
|
|
237
|
+
// Create an index on the category field
|
|
238
|
+
await integration
|
|
239
|
+
.mongoClient()
|
|
240
|
+
.db(integration.randomDbName())
|
|
241
|
+
.collection("update-test-collection")
|
|
242
|
+
.createIndex({ category: 1 });
|
|
243
|
+
|
|
244
|
+
const response = await integration.mcpClient().callTool({
|
|
245
|
+
name: "update-many",
|
|
246
|
+
arguments: {
|
|
247
|
+
database: integration.randomDbName(),
|
|
248
|
+
collection: "update-test-collection",
|
|
249
|
+
filter: { category: "A" }, // Now has index
|
|
250
|
+
update: { $set: { updated: true } },
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
expect(response.isError).toBeFalsy();
|
|
255
|
+
const content = getResponseContent(response.content);
|
|
256
|
+
expect(content).toContain("Matched");
|
|
257
|
+
expect(content).toContain("Modified");
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe("deleteMany operations", () => {
|
|
262
|
+
beforeEach(async () => {
|
|
263
|
+
// Insert test data for deleteMany operations
|
|
264
|
+
await integration
|
|
265
|
+
.mongoClient()
|
|
266
|
+
.db(integration.randomDbName())
|
|
267
|
+
.collection("delete-test-collection")
|
|
268
|
+
.insertMany([
|
|
269
|
+
{ name: "document1", value: 1, category: "A" },
|
|
270
|
+
{ name: "document2", value: 2, category: "B" },
|
|
271
|
+
{ name: "document3", value: 3, category: "A" },
|
|
272
|
+
]);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("should reject deleteMany queries that perform collection scans", async () => {
|
|
276
|
+
const response = await integration.mcpClient().callTool({
|
|
277
|
+
name: "delete-many",
|
|
278
|
+
arguments: {
|
|
279
|
+
database: integration.randomDbName(),
|
|
280
|
+
collection: "delete-test-collection",
|
|
281
|
+
filter: { value: { $lt: 2 } }, // No index on value
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const content = getResponseContent(response.content);
|
|
286
|
+
expect(content).toContain("Index check failed");
|
|
287
|
+
expect(content).toContain("deleteMany operation");
|
|
288
|
+
expect(response.isError).toBe(true);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("should allow deleteMany queries with indexes", async () => {
|
|
292
|
+
// Create an index on the value field
|
|
293
|
+
await integration
|
|
294
|
+
.mongoClient()
|
|
295
|
+
.db(integration.randomDbName())
|
|
296
|
+
.collection("delete-test-collection")
|
|
297
|
+
.createIndex({ value: 1 });
|
|
298
|
+
|
|
299
|
+
const response = await integration.mcpClient().callTool({
|
|
300
|
+
name: "delete-many",
|
|
301
|
+
arguments: {
|
|
302
|
+
database: integration.randomDbName(),
|
|
303
|
+
collection: "delete-test-collection",
|
|
304
|
+
filter: { value: { $lt: 2 } }, // Now has index
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
expect(response.isError).toBeFalsy();
|
|
309
|
+
const content = getResponseContent(response.content);
|
|
310
|
+
expect(content).toContain("Deleted");
|
|
311
|
+
expect(content).toMatch(/`\d+` document\(s\)/);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
},
|
|
315
|
+
() => ({
|
|
316
|
+
...defaultTestConfig,
|
|
317
|
+
indexCheck: true, // Enable indexCheck
|
|
318
|
+
})
|
|
319
|
+
);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
describe("with indexCheck disabled", () => {
|
|
323
|
+
describeWithMongoDB(
|
|
324
|
+
"indexCheck disabled functionality",
|
|
325
|
+
(integration) => {
|
|
326
|
+
beforeEach(async () => {
|
|
327
|
+
await integration.connectMcpClient();
|
|
328
|
+
|
|
329
|
+
// insert test data for disabled indexCheck tests
|
|
330
|
+
await integration
|
|
331
|
+
.mongoClient()
|
|
332
|
+
.db(integration.randomDbName())
|
|
333
|
+
.collection("disabled-test-collection")
|
|
334
|
+
.insertMany([
|
|
335
|
+
{ name: "document1", value: 1, category: "A" },
|
|
336
|
+
{ name: "document2", value: 2, category: "B" },
|
|
337
|
+
{ name: "document3", value: 3, category: "A" },
|
|
338
|
+
]);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("should allow all queries regardless of index usage", async () => {
|
|
342
|
+
// Test find operation without index
|
|
343
|
+
const findResponse = await integration.mcpClient().callTool({
|
|
344
|
+
name: "find",
|
|
345
|
+
arguments: {
|
|
346
|
+
database: integration.randomDbName(),
|
|
347
|
+
collection: "disabled-test-collection",
|
|
348
|
+
filter: { category: "A" }, // No index, but should be allowed
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
expect(findResponse.isError).toBeFalsy();
|
|
353
|
+
const findContent = getResponseContent(findResponse.content);
|
|
354
|
+
expect(findContent).toContain("Found");
|
|
355
|
+
expect(findContent).not.toContain("Index check failed");
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it("should allow count operations without indexes", async () => {
|
|
359
|
+
const response = await integration.mcpClient().callTool({
|
|
360
|
+
name: "count",
|
|
361
|
+
arguments: {
|
|
362
|
+
database: integration.randomDbName(),
|
|
363
|
+
collection: "disabled-test-collection",
|
|
364
|
+
query: { value: { $gt: 1 } }, // No index, but should be allowed
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
expect(response.isError).toBeFalsy();
|
|
369
|
+
const content = getResponseContent(response.content);
|
|
370
|
+
expect(content).toContain("Found");
|
|
371
|
+
expect(content).not.toContain("Index check failed");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("should allow aggregate operations without indexes", async () => {
|
|
375
|
+
const response = await integration.mcpClient().callTool({
|
|
376
|
+
name: "aggregate",
|
|
377
|
+
arguments: {
|
|
378
|
+
database: integration.randomDbName(),
|
|
379
|
+
collection: "disabled-test-collection",
|
|
380
|
+
pipeline: [
|
|
381
|
+
{ $match: { category: "A" } }, // No index, but should be allowed
|
|
382
|
+
{ $group: { _id: "$category", count: { $sum: 1 } } },
|
|
383
|
+
],
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
expect(response.isError).toBeFalsy();
|
|
388
|
+
const content = getResponseContent(response.content);
|
|
389
|
+
expect(content).toContain("Found");
|
|
390
|
+
expect(content).not.toContain("Index check failed");
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it("should allow updateMany operations without indexes", async () => {
|
|
394
|
+
const response = await integration.mcpClient().callTool({
|
|
395
|
+
name: "update-many",
|
|
396
|
+
arguments: {
|
|
397
|
+
database: integration.randomDbName(),
|
|
398
|
+
collection: "disabled-test-collection",
|
|
399
|
+
filter: { category: "A" }, // No index, but should be allowed
|
|
400
|
+
update: { $set: { updated: true } },
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
expect(response.isError).toBeFalsy();
|
|
405
|
+
const content = getResponseContent(response.content);
|
|
406
|
+
expect(content).toContain("Matched");
|
|
407
|
+
expect(content).not.toContain("Index check failed");
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it("should allow deleteMany operations without indexes", async () => {
|
|
411
|
+
const response = await integration.mcpClient().callTool({
|
|
412
|
+
name: "delete-many",
|
|
413
|
+
arguments: {
|
|
414
|
+
database: integration.randomDbName(),
|
|
415
|
+
collection: "disabled-test-collection",
|
|
416
|
+
filter: { value: { $lt: 2 } }, // No index, but should be allowed
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
expect(response.isError).toBeFalsy();
|
|
421
|
+
const content = getResponseContent(response.content);
|
|
422
|
+
expect(content).toContain("Deleted");
|
|
423
|
+
expect(content).not.toContain("Index check failed");
|
|
424
|
+
});
|
|
425
|
+
},
|
|
426
|
+
() => ({
|
|
427
|
+
...defaultTestConfig,
|
|
428
|
+
indexCheck: false, // Disable indexCheck
|
|
429
|
+
})
|
|
430
|
+
);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
describe("indexCheck configuration validation", () => {
|
|
434
|
+
describeWithMongoDB(
|
|
435
|
+
"default indexCheck behavior",
|
|
436
|
+
(integration) => {
|
|
437
|
+
it("should allow collection scans by default when indexCheck is not specified", async () => {
|
|
438
|
+
await integration.connectMcpClient();
|
|
439
|
+
|
|
440
|
+
await integration
|
|
441
|
+
.mongoClient()
|
|
442
|
+
.db(integration.randomDbName())
|
|
443
|
+
.collection("default-test-collection")
|
|
444
|
+
.insertOne({ name: "test", value: 1 });
|
|
445
|
+
|
|
446
|
+
const response = await integration.mcpClient().callTool({
|
|
447
|
+
name: "find",
|
|
448
|
+
arguments: {
|
|
449
|
+
database: integration.randomDbName(),
|
|
450
|
+
collection: "default-test-collection",
|
|
451
|
+
filter: { name: "test" }, // No index, should be allowed by default
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
expect(response.isError).toBeFalsy();
|
|
456
|
+
});
|
|
457
|
+
},
|
|
458
|
+
() => ({
|
|
459
|
+
...defaultTestConfig,
|
|
460
|
+
// indexCheck not specified, should default to false
|
|
461
|
+
})
|
|
462
|
+
);
|
|
463
|
+
});
|
|
464
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defaultTestConfig, expectDefined, setupIntegrationTest } from "./helpers.js";
|
|
2
2
|
import { describeWithMongoDB } from "./tools/mongodb/mongodbHelpers.js";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
3
4
|
|
|
4
5
|
describe("Server integration test", () => {
|
|
5
6
|
describeWithMongoDB(
|
|
@@ -47,11 +48,12 @@ describe("Server integration test", () => {
|
|
|
47
48
|
it("should return capabilities", () => {
|
|
48
49
|
const capabilities = integration.mcpClient().getServerCapabilities();
|
|
49
50
|
expectDefined(capabilities);
|
|
50
|
-
expect(capabilities.completions).toBeUndefined();
|
|
51
|
-
expect(capabilities.experimental).toBeUndefined();
|
|
52
|
-
expectDefined(capabilities?.tools);
|
|
53
51
|
expectDefined(capabilities?.logging);
|
|
54
|
-
|
|
52
|
+
expectDefined(capabilities?.completions);
|
|
53
|
+
expectDefined(capabilities?.tools);
|
|
54
|
+
expectDefined(capabilities?.resources);
|
|
55
|
+
expect(capabilities.experimental).toBeUndefined();
|
|
56
|
+
expect(capabilities.prompts).toBeUndefined();
|
|
55
57
|
});
|
|
56
58
|
});
|
|
57
59
|
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createHmac } from "crypto";
|
|
2
|
+
import { Telemetry } from "../../src/telemetry/telemetry.js";
|
|
3
|
+
import { Session } from "../../src/common/session.js";
|
|
4
|
+
import { config } from "../../src/common/config.js";
|
|
5
|
+
import nodeMachineId from "node-machine-id";
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
|
|
8
|
+
describe("Telemetry", () => {
|
|
9
|
+
it("should resolve the actual machine ID", async () => {
|
|
10
|
+
const actualId: string = await nodeMachineId.machineId(true);
|
|
11
|
+
|
|
12
|
+
const actualHashedId = createHmac("sha256", actualId.toUpperCase()).update("atlascli").digest("hex");
|
|
13
|
+
|
|
14
|
+
const telemetry = Telemetry.create(
|
|
15
|
+
new Session({
|
|
16
|
+
apiBaseUrl: "",
|
|
17
|
+
}),
|
|
18
|
+
config
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
expect(telemetry.getCommonProperties().device_id).toBe(undefined);
|
|
22
|
+
expect(telemetry["isBufferingEvents"]).toBe(true);
|
|
23
|
+
|
|
24
|
+
await telemetry.setupPromise;
|
|
25
|
+
|
|
26
|
+
expect(telemetry.getCommonProperties().device_id).toBe(actualHashedId);
|
|
27
|
+
expect(telemetry["isBufferingEvents"]).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import { describeWithAtlas, withProject } from "./atlasHelpers.js";
|
|
3
3
|
import { expectDefined } from "../../helpers.js";
|
|
4
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
5
|
+
import { ensureCurrentIpInAccessList } from "../../../../src/common/atlas/accessListUtils.js";
|
|
4
6
|
|
|
5
7
|
function generateRandomIp() {
|
|
6
8
|
const randomIp: number[] = [192];
|
|
@@ -65,7 +67,7 @@ describeWithAtlas("ip access lists", (integration) => {
|
|
|
65
67
|
currentIpAddress: true,
|
|
66
68
|
},
|
|
67
69
|
})) as CallToolResult;
|
|
68
|
-
expect(response.content).
|
|
70
|
+
expect(response.content).toBeInstanceOf(Array);
|
|
69
71
|
expect(response.content).toHaveLength(1);
|
|
70
72
|
expect(response.content[0]?.text).toContain("IP/CIDR ranges added to access list");
|
|
71
73
|
});
|
|
@@ -87,12 +89,30 @@ describeWithAtlas("ip access lists", (integration) => {
|
|
|
87
89
|
const response = (await integration
|
|
88
90
|
.mcpClient()
|
|
89
91
|
.callTool({ name: "atlas-inspect-access-list", arguments: { projectId } })) as CallToolResult;
|
|
90
|
-
expect(response.content).
|
|
92
|
+
expect(response.content).toBeInstanceOf(Array);
|
|
91
93
|
expect(response.content).toHaveLength(1);
|
|
92
94
|
for (const value of values) {
|
|
93
95
|
expect(response.content[0]?.text).toContain(value);
|
|
94
96
|
}
|
|
95
97
|
});
|
|
96
98
|
});
|
|
99
|
+
|
|
100
|
+
describe("ensureCurrentIpInAccessList helper", () => {
|
|
101
|
+
it("should add the current IP to the access list and be idempotent", async () => {
|
|
102
|
+
const apiClient = integration.mcpServer().session.apiClient;
|
|
103
|
+
const projectId = getProjectId();
|
|
104
|
+
const ipInfo = await apiClient.getIpInfo();
|
|
105
|
+
// First call should add the IP
|
|
106
|
+
await expect(ensureCurrentIpInAccessList(apiClient, projectId)).resolves.not.toThrow();
|
|
107
|
+
// Second call should be a no-op (idempotent)
|
|
108
|
+
await expect(ensureCurrentIpInAccessList(apiClient, projectId)).resolves.not.toThrow();
|
|
109
|
+
// Check that the IP is present in the access list
|
|
110
|
+
const accessList = await apiClient.listProjectIpAccessLists({
|
|
111
|
+
params: { path: { groupId: projectId } },
|
|
112
|
+
});
|
|
113
|
+
const found = accessList.results?.some((entry) => entry.ipAddress === ipInfo.currentIpv4Address);
|
|
114
|
+
expect(found).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
97
117
|
});
|
|
98
118
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import { expectDefined } from "../../helpers.js";
|
|
3
3
|
import { parseTable, describeWithAtlas, withProject } from "./atlasHelpers.js";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
4
5
|
|
|
5
6
|
describeWithAtlas("alerts", (integration) => {
|
|
6
7
|
describe("atlas-list-alerts", () => {
|
|
@@ -20,11 +21,11 @@ describeWithAtlas("alerts", (integration) => {
|
|
|
20
21
|
arguments: { projectId: getProjectId() },
|
|
21
22
|
})) as CallToolResult;
|
|
22
23
|
|
|
23
|
-
expect(response.content).
|
|
24
|
+
expect(response.content).toBeInstanceOf(Array);
|
|
24
25
|
expect(response.content).toHaveLength(1);
|
|
25
26
|
|
|
26
27
|
const data = parseTable(response.content[0]?.text as string);
|
|
27
|
-
expect(data).
|
|
28
|
+
expect(data).toBeInstanceOf(Array);
|
|
28
29
|
|
|
29
30
|
// Since we can't guarantee alerts will exist, we just verify the table structure
|
|
30
31
|
if (data.length > 0) {
|
|
@@ -2,6 +2,7 @@ import { ObjectId } from "mongodb";
|
|
|
2
2
|
import { Group } from "../../../../src/common/atlas/openapi.js";
|
|
3
3
|
import { ApiClient } from "../../../../src/common/atlas/apiClient.js";
|
|
4
4
|
import { setupIntegrationTest, IntegrationTest, defaultTestConfig } from "../../helpers.js";
|
|
5
|
+
import { afterAll, beforeAll, describe } from "vitest";
|
|
5
6
|
|
|
6
7
|
export type IntegrationTestFunction = (integration: IntegrationTest) => void;
|
|
7
8
|
|
|
@@ -19,8 +20,10 @@ export function describeWithAtlas(name: string, fn: IntegrationTestFunction) {
|
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
if (!process.env.MDB_MCP_API_CLIENT_ID?.length || !process.env.MDB_MCP_API_CLIENT_SECRET?.length) {
|
|
23
|
+
// eslint-disable-next-line vitest/valid-describe-callback
|
|
22
24
|
return describe.skip("atlas", testDefinition);
|
|
23
25
|
}
|
|
26
|
+
// eslint-disable-next-line vitest/no-identical-title, vitest/valid-describe-callback
|
|
24
27
|
return describe("atlas", testDefinition);
|
|
25
28
|
}
|
|
26
29
|
|