@neverinfamous/mysql-mcp 2.2.0 → 2.3.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/workflows/docker-publish.yml +1 -2
- package/CHANGELOG.md +85 -0
- package/CODE_MODE.md +245 -0
- package/DOCKER_README.md +59 -36
- package/README.md +65 -42
- package/VERSION +1 -1
- package/dist/adapters/mysql/MySQLAdapter.d.ts +4 -0
- package/dist/adapters/mysql/MySQLAdapter.d.ts.map +1 -1
- package/dist/adapters/mysql/MySQLAdapter.js +9 -0
- package/dist/adapters/mysql/MySQLAdapter.js.map +1 -1
- package/dist/adapters/mysql/prompts/index.d.ts +8 -1
- package/dist/adapters/mysql/prompts/index.d.ts.map +1 -1
- package/dist/adapters/mysql/prompts/index.js +8 -1
- package/dist/adapters/mysql/prompts/index.js.map +1 -1
- package/dist/adapters/mysql/prompts/routerSetup.d.ts.map +1 -1
- package/dist/adapters/mysql/prompts/routerSetup.js +5 -0
- package/dist/adapters/mysql/prompts/routerSetup.js.map +1 -1
- package/dist/adapters/mysql/resources/capabilities.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/capabilities.js +6 -5
- package/dist/adapters/mysql/resources/capabilities.js.map +1 -1
- package/dist/adapters/mysql/resources/index.d.ts +9 -1
- package/dist/adapters/mysql/resources/index.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/index.js +9 -1
- package/dist/adapters/mysql/resources/index.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/backup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/backup.js +3 -3
- package/dist/adapters/mysql/tools/admin/backup.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/maintenance.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/maintenance.js +5 -5
- package/dist/adapters/mysql/tools/admin/maintenance.js.map +1 -1
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.js +26 -5
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.js.map +1 -1
- package/dist/adapters/mysql/tools/codemode/index.d.ts +38 -0
- package/dist/adapters/mysql/tools/codemode/index.d.ts.map +1 -0
- package/dist/adapters/mysql/tools/codemode/index.js +203 -0
- package/dist/adapters/mysql/tools/codemode/index.js.map +1 -0
- package/dist/adapters/mysql/tools/core.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/core.js +32 -20
- package/dist/adapters/mysql/tools/core.js.map +1 -1
- package/dist/adapters/mysql/tools/events.js +18 -6
- package/dist/adapters/mysql/tools/events.js.map +1 -1
- package/dist/adapters/mysql/tools/json/core.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/core.js +5 -5
- package/dist/adapters/mysql/tools/json/core.js.map +1 -1
- package/dist/adapters/mysql/tools/json/helpers.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/helpers.js +9 -3
- package/dist/adapters/mysql/tools/json/helpers.js.map +1 -1
- package/dist/adapters/mysql/tools/partitioning.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/partitioning.js +38 -6
- package/dist/adapters/mysql/tools/partitioning.js.map +1 -1
- package/dist/adapters/mysql/tools/performance/analysis.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/performance/analysis.js +67 -20
- package/dist/adapters/mysql/tools/performance/analysis.js.map +1 -1
- package/dist/adapters/mysql/tools/performance/optimization.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/performance/optimization.js +36 -6
- package/dist/adapters/mysql/tools/performance/optimization.js.map +1 -1
- package/dist/adapters/mysql/tools/security/data-protection.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/data-protection.js +9 -4
- package/dist/adapters/mysql/tools/security/data-protection.js.map +1 -1
- package/dist/adapters/mysql/tools/shell/common.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/shell/common.js +28 -2
- package/dist/adapters/mysql/tools/shell/common.js.map +1 -1
- package/dist/adapters/mysql/tools/shell/restore.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/shell/restore.js +54 -4
- package/dist/adapters/mysql/tools/shell/restore.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/operations.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/operations.js +10 -2
- package/dist/adapters/mysql/tools/spatial/operations.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/setup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/setup.js +18 -0
- package/dist/adapters/mysql/tools/spatial/setup.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/resources.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/resources.js +5 -0
- package/dist/adapters/mysql/tools/sysschema/resources.js.map +1 -1
- package/dist/adapters/mysql/tools/text/fulltext.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/text/fulltext.js +6 -4
- package/dist/adapters/mysql/tools/text/fulltext.js.map +1 -1
- package/dist/adapters/mysql/tools/text/processing.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/text/processing.js +10 -45
- package/dist/adapters/mysql/tools/text/processing.js.map +1 -1
- package/dist/adapters/mysql/tools/transactions.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/transactions.js +8 -8
- package/dist/adapters/mysql/tools/transactions.js.map +1 -1
- package/dist/adapters/mysql/types.d.ts +968 -78
- package/dist/adapters/mysql/types.d.ts.map +1 -1
- package/dist/adapters/mysql/types.js +1084 -78
- package/dist/adapters/mysql/types.js.map +1 -1
- package/dist/auth/scopes.d.ts.map +1 -1
- package/dist/auth/scopes.js +1 -0
- package/dist/auth/scopes.js.map +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +12 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/codemode/api.d.ts +69 -0
- package/dist/codemode/api.d.ts.map +1 -0
- package/dist/codemode/api.js +1035 -0
- package/dist/codemode/api.js.map +1 -0
- package/dist/codemode/index.d.ts +13 -0
- package/dist/codemode/index.d.ts.map +1 -0
- package/dist/codemode/index.js +17 -0
- package/dist/codemode/index.js.map +1 -0
- package/dist/codemode/sandbox-factory.d.ts +72 -0
- package/dist/codemode/sandbox-factory.d.ts.map +1 -0
- package/dist/codemode/sandbox-factory.js +88 -0
- package/dist/codemode/sandbox-factory.js.map +1 -0
- package/dist/codemode/sandbox.d.ts +96 -0
- package/dist/codemode/sandbox.d.ts.map +1 -0
- package/dist/codemode/sandbox.js +345 -0
- package/dist/codemode/sandbox.js.map +1 -0
- package/dist/codemode/security.d.ts +44 -0
- package/dist/codemode/security.d.ts.map +1 -0
- package/dist/codemode/security.js +149 -0
- package/dist/codemode/security.js.map +1 -0
- package/dist/codemode/types.d.ts +137 -0
- package/dist/codemode/types.d.ts.map +1 -0
- package/dist/codemode/types.js +46 -0
- package/dist/codemode/types.js.map +1 -0
- package/dist/codemode/worker-sandbox.d.ts +82 -0
- package/dist/codemode/worker-sandbox.d.ts.map +1 -0
- package/dist/codemode/worker-sandbox.js +244 -0
- package/dist/codemode/worker-sandbox.js.map +1 -0
- package/dist/codemode/worker-script.d.ts +8 -0
- package/dist/codemode/worker-script.d.ts.map +1 -0
- package/dist/codemode/worker-script.js +113 -0
- package/dist/codemode/worker-script.js.map +1 -0
- package/dist/constants/ServerInstructions.d.ts +1 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +33 -9
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/filtering/ToolConstants.d.ts +11 -11
- package/dist/filtering/ToolConstants.d.ts.map +1 -1
- package/dist/filtering/ToolConstants.js +37 -19
- package/dist/filtering/ToolConstants.js.map +1 -1
- package/dist/filtering/ToolFilter.d.ts.map +1 -1
- package/dist/filtering/ToolFilter.js +12 -0
- package/dist/filtering/ToolFilter.js.map +1 -1
- package/dist/server/McpServer.js +1 -1
- package/dist/server/McpServer.js.map +1 -1
- package/dist/types/modules/server.d.ts +2 -0
- package/dist/types/modules/server.d.ts.map +1 -1
- package/dist/types/modules/tools.d.ts +1 -1
- package/dist/types/modules/tools.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/package.json +12 -7
- package/releases/v2.2.0-release-notes.md +18 -18
- package/releases/v2.3.0-release-notes.md +191 -0
- package/src/__tests__/perf.test.ts +12 -12
- package/src/adapters/mysql/MySQLAdapter.ts +10 -0
- package/src/adapters/mysql/__tests__/MySQLAdapter.test.ts +1 -1
- package/src/adapters/mysql/prompts/index.ts +8 -1
- package/src/adapters/mysql/prompts/routerSetup.ts +5 -0
- package/src/adapters/mysql/resources/__tests__/capabilities.test.ts +50 -1
- package/src/adapters/mysql/resources/capabilities.ts +6 -4
- package/src/adapters/mysql/resources/index.ts +9 -1
- package/src/adapters/mysql/tools/__tests__/core.test.ts +68 -0
- package/src/adapters/mysql/tools/__tests__/events.test.ts +56 -2
- package/src/adapters/mysql/tools/__tests__/json_core.test.ts +1 -1
- package/src/adapters/mysql/tools/__tests__/json_helpers.test.ts +46 -4
- package/src/adapters/mysql/tools/__tests__/replication.test.ts +144 -42
- package/src/adapters/mysql/tools/__tests__/security.test.ts +39 -0
- package/src/adapters/mysql/tools/__tests__/spatial.test.ts +39 -7
- package/src/adapters/mysql/tools/__tests__/spatial_handler.test.ts +35 -3
- package/src/adapters/mysql/tools/__tests__/transactions.test.ts +3 -5
- package/src/adapters/mysql/tools/admin/backup.ts +8 -3
- package/src/adapters/mysql/tools/admin/maintenance.ts +8 -4
- package/src/adapters/mysql/tools/cluster/__tests__/innodb-cluster.test.ts +35 -0
- package/src/adapters/mysql/tools/cluster/innodb-cluster.ts +26 -5
- package/src/adapters/mysql/tools/codemode/index.ts +249 -0
- package/src/adapters/mysql/tools/core.ts +44 -27
- package/src/adapters/mysql/tools/events.ts +23 -7
- package/src/adapters/mysql/tools/json/__tests__/helpers.test.ts +59 -14
- package/src/adapters/mysql/tools/json/core.ts +8 -4
- package/src/adapters/mysql/tools/json/helpers.ts +13 -3
- package/src/adapters/mysql/tools/partitioning.ts +53 -6
- package/src/adapters/mysql/tools/performance/__tests__/analysis.test.ts +227 -4
- package/src/adapters/mysql/tools/performance/__tests__/optimization.test.ts +35 -0
- package/src/adapters/mysql/tools/performance/analysis.ts +75 -21
- package/src/adapters/mysql/tools/performance/optimization.ts +44 -6
- package/src/adapters/mysql/tools/security/data-protection.ts +10 -4
- package/src/adapters/mysql/tools/shell/__tests__/common.test.ts +46 -0
- package/src/adapters/mysql/tools/shell/__tests__/restore.test.ts +28 -1
- package/src/adapters/mysql/tools/shell/common.ts +34 -2
- package/src/adapters/mysql/tools/shell/restore.ts +70 -7
- package/src/adapters/mysql/tools/spatial/__tests__/operations.test.ts +29 -0
- package/src/adapters/mysql/tools/spatial/operations.ts +13 -2
- package/src/adapters/mysql/tools/spatial/setup.ts +23 -0
- package/src/adapters/mysql/tools/sysschema/__tests__/resources.test.ts +21 -0
- package/src/adapters/mysql/tools/sysschema/resources.ts +5 -0
- package/src/adapters/mysql/tools/text/fulltext.ts +13 -5
- package/src/adapters/mysql/tools/text/processing.ts +20 -49
- package/src/adapters/mysql/tools/transactions.ts +11 -7
- package/src/adapters/mysql/types.ts +1241 -87
- package/src/auth/scopes.ts +1 -0
- package/src/cli/args.ts +14 -0
- package/src/codemode/api.ts +1224 -0
- package/src/codemode/index.ts +51 -0
- package/src/codemode/sandbox-factory.ts +146 -0
- package/src/codemode/sandbox.ts +450 -0
- package/src/codemode/security.ts +188 -0
- package/src/codemode/types.ts +194 -0
- package/src/codemode/worker-sandbox.ts +326 -0
- package/src/codemode/worker-script.ts +144 -0
- package/src/constants/ServerInstructions.ts +33 -9
- package/src/filtering/ToolConstants.ts +37 -19
- package/src/filtering/ToolFilter.ts +15 -0
- package/src/filtering/__tests__/ToolFilter.test.ts +65 -38
- package/src/server/McpServer.ts +1 -1
- package/src/types/modules/server.ts +3 -0
- package/src/types/modules/tools.ts +2 -1
- package/src/utils/logger.ts +2 -1
|
@@ -10,7 +10,13 @@ import {
|
|
|
10
10
|
interface CapabilitiesResult {
|
|
11
11
|
server: {
|
|
12
12
|
version: string;
|
|
13
|
-
features: {
|
|
13
|
+
features: {
|
|
14
|
+
json: boolean;
|
|
15
|
+
fulltext: boolean;
|
|
16
|
+
partitioning: boolean;
|
|
17
|
+
replication: boolean;
|
|
18
|
+
gtid: boolean;
|
|
19
|
+
};
|
|
14
20
|
};
|
|
15
21
|
toolGroups: unknown[];
|
|
16
22
|
metaGroups: unknown[];
|
|
@@ -64,6 +70,49 @@ describe("Capabilities Resource", () => {
|
|
|
64
70
|
expect(result.server.features).toHaveProperty("partitioning");
|
|
65
71
|
});
|
|
66
72
|
|
|
73
|
+
it("should detect features correctly for MySQL 9.x", async () => {
|
|
74
|
+
mockAdapter.executeQuery.mockResolvedValue(
|
|
75
|
+
createMockQueryResult([{ version: "9.6.0" }]),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const resource = createCapabilitiesResource(
|
|
79
|
+
mockAdapter as unknown as MySQLAdapter,
|
|
80
|
+
);
|
|
81
|
+
const result = (await resource.handler(
|
|
82
|
+
"mysql://capabilities",
|
|
83
|
+
mockContext,
|
|
84
|
+
)) as CapabilitiesResult;
|
|
85
|
+
|
|
86
|
+
expect(result.server.version).toBe("9.6.0");
|
|
87
|
+
expect(result.server.features.json).toBe(true);
|
|
88
|
+
expect(result.server.features.gtid).toBe(true);
|
|
89
|
+
expect(result.server.features.fulltext).toBe(true);
|
|
90
|
+
expect(result.server.features.replication).toBe(true);
|
|
91
|
+
expect(result.server.features.partitioning).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should disable version-dependent features for unknown version", async () => {
|
|
95
|
+
mockAdapter.executeQuery.mockResolvedValue(
|
|
96
|
+
createMockQueryResult([{ version: "unknown" }]),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const resource = createCapabilitiesResource(
|
|
100
|
+
mockAdapter as unknown as MySQLAdapter,
|
|
101
|
+
);
|
|
102
|
+
const result = (await resource.handler(
|
|
103
|
+
"mysql://capabilities",
|
|
104
|
+
mockContext,
|
|
105
|
+
)) as CapabilitiesResult;
|
|
106
|
+
|
|
107
|
+
expect(result.server.version).toBe("unknown");
|
|
108
|
+
expect(result.server.features.json).toBe(false);
|
|
109
|
+
expect(result.server.features.gtid).toBe(false);
|
|
110
|
+
// Always-true features remain true
|
|
111
|
+
expect(result.server.features.fulltext).toBe(true);
|
|
112
|
+
expect(result.server.features.replication).toBe(true);
|
|
113
|
+
expect(result.server.features.partitioning).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
67
116
|
it("should have correct metadata", () => {
|
|
68
117
|
const resource = createCapabilitiesResource(
|
|
69
118
|
mockAdapter as unknown as MySQLAdapter,
|
|
@@ -37,16 +37,18 @@ export function createCapabilitiesResource(
|
|
|
37
37
|
const version =
|
|
38
38
|
(versionResult.rows?.[0]?.["version"] as string) ?? "unknown";
|
|
39
39
|
|
|
40
|
-
//
|
|
40
|
+
// Parse major version for feature detection (handles 8.x, 9.x, 10.x+)
|
|
41
|
+
const majorVersion = parseInt(version.split(".")[0] ?? "0", 10) || 0;
|
|
42
|
+
|
|
41
43
|
const features = {
|
|
42
|
-
json:
|
|
44
|
+
json: majorVersion >= 8 || version.startsWith("5.7"),
|
|
43
45
|
fulltext: true,
|
|
44
46
|
partitioning: true,
|
|
45
47
|
replication: true,
|
|
46
48
|
gtid:
|
|
49
|
+
majorVersion >= 8 ||
|
|
47
50
|
version.startsWith("5.6") ||
|
|
48
|
-
version.startsWith("5.7")
|
|
49
|
-
version.startsWith("8."),
|
|
51
|
+
version.startsWith("5.7"),
|
|
50
52
|
};
|
|
51
53
|
|
|
52
54
|
// Get tool groups and meta-groups info
|
|
@@ -32,7 +32,7 @@ import { createSpatialResource } from "./spatial.js";
|
|
|
32
32
|
import { createDocstoreResource } from "./docstore.js";
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Get all MySQL resources (
|
|
35
|
+
* Get all MySQL resources (18 total)
|
|
36
36
|
*
|
|
37
37
|
* Core (6):
|
|
38
38
|
* - mysql://schema - Full database schema
|
|
@@ -49,6 +49,14 @@ import { createDocstoreResource } from "./docstore.js";
|
|
|
49
49
|
* - mysql://indexes - Index usage and statistics
|
|
50
50
|
* - mysql://replication - Replication status and lag
|
|
51
51
|
* - mysql://innodb - InnoDB buffer pool and engine metrics
|
|
52
|
+
*
|
|
53
|
+
* New (6):
|
|
54
|
+
* - mysql://events - Event Scheduler status and scheduled events
|
|
55
|
+
* - mysql://sysschema - sys schema diagnostics summary
|
|
56
|
+
* - mysql://locks - InnoDB lock contention detection
|
|
57
|
+
* - mysql://cluster - Group Replication/InnoDB Cluster status
|
|
58
|
+
* - mysql://spatial - Spatial columns and indexes
|
|
59
|
+
* - mysql://docstore - Document Store collections
|
|
52
60
|
*/
|
|
53
61
|
export function getMySQLResources(adapter: MySQLAdapter): ResourceDefinition[] {
|
|
54
62
|
return [
|
|
@@ -180,6 +180,40 @@ describe("Handler Execution", () => {
|
|
|
180
180
|
"txn-123",
|
|
181
181
|
);
|
|
182
182
|
});
|
|
183
|
+
|
|
184
|
+
it("should return structured error for nonexistent table", async () => {
|
|
185
|
+
mockAdapter.executeReadQuery.mockRejectedValue(
|
|
186
|
+
new Error("Table 'testdb.nonexistent' doesn't exist"),
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const tool = tools.find((t) => t.name === "mysql_read_query")!;
|
|
190
|
+
const result = await tool.handler(
|
|
191
|
+
{ query: "SELECT * FROM nonexistent" },
|
|
192
|
+
mockContext,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
expect(result).toHaveProperty("success", false);
|
|
196
|
+
expect((result as Record<string, unknown>).error).toContain(
|
|
197
|
+
"doesn't exist",
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("should return structured error for non-table errors in read query", async () => {
|
|
202
|
+
mockAdapter.executeReadQuery.mockRejectedValue(
|
|
203
|
+
new Error("Access denied"),
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const tool = tools.find((t) => t.name === "mysql_read_query")!;
|
|
207
|
+
const result = await tool.handler(
|
|
208
|
+
{ query: "SELECT * FROM users" },
|
|
209
|
+
mockContext,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
expect((result as Record<string, unknown>).success).toBe(false);
|
|
213
|
+
expect((result as Record<string, unknown>).error).toContain(
|
|
214
|
+
"Access denied",
|
|
215
|
+
);
|
|
216
|
+
});
|
|
183
217
|
});
|
|
184
218
|
|
|
185
219
|
describe("mysql_write_query", () => {
|
|
@@ -223,6 +257,40 @@ describe("Handler Execution", () => {
|
|
|
223
257
|
"txn-456",
|
|
224
258
|
);
|
|
225
259
|
});
|
|
260
|
+
|
|
261
|
+
it("should return structured error for nonexistent table", async () => {
|
|
262
|
+
mockAdapter.executeWriteQuery.mockRejectedValue(
|
|
263
|
+
new Error("Table 'testdb.nonexistent' doesn't exist"),
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
const tool = tools.find((t) => t.name === "mysql_write_query")!;
|
|
267
|
+
const result = await tool.handler(
|
|
268
|
+
{ query: "INSERT INTO nonexistent (id) VALUES (1)" },
|
|
269
|
+
mockContext,
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
expect(result).toHaveProperty("success", false);
|
|
273
|
+
expect((result as Record<string, unknown>).error).toContain(
|
|
274
|
+
"doesn't exist",
|
|
275
|
+
);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("should return structured error for non-table errors in write query", async () => {
|
|
279
|
+
mockAdapter.executeWriteQuery.mockRejectedValue(
|
|
280
|
+
new Error("Access denied"),
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const tool = tools.find((t) => t.name === "mysql_write_query")!;
|
|
284
|
+
const result = await tool.handler(
|
|
285
|
+
{ query: "INSERT INTO users VALUES (1)" },
|
|
286
|
+
mockContext,
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
expect((result as Record<string, unknown>).success).toBe(false);
|
|
290
|
+
expect((result as Record<string, unknown>).error).toContain(
|
|
291
|
+
"Access denied",
|
|
292
|
+
);
|
|
293
|
+
});
|
|
226
294
|
});
|
|
227
295
|
|
|
228
296
|
describe("mysql_list_tables", () => {
|
|
@@ -146,6 +146,31 @@ describe("Handler Execution", () => {
|
|
|
146
146
|
const call = mockAdapter.executeQuery.mock.calls[0][0] as string;
|
|
147
147
|
expect(call).toContain("RENAME TO");
|
|
148
148
|
});
|
|
149
|
+
|
|
150
|
+
it("should place RENAME TO before COMMENT and DO body in combined alter", async () => {
|
|
151
|
+
mockAdapter.executeQuery.mockResolvedValue(createMockQueryResult([]));
|
|
152
|
+
|
|
153
|
+
const tool = tools.find((t) => t.name === "mysql_event_alter")!;
|
|
154
|
+
await tool.handler(
|
|
155
|
+
{
|
|
156
|
+
name: "old_event",
|
|
157
|
+
newName: "new_event",
|
|
158
|
+
body: "SELECT 1",
|
|
159
|
+
comment: "updated",
|
|
160
|
+
},
|
|
161
|
+
mockContext,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const call = mockAdapter.executeQuery.mock.calls[0][0] as string;
|
|
165
|
+
const renameIndex = call.indexOf("RENAME TO");
|
|
166
|
+
const commentIndex = call.indexOf("COMMENT");
|
|
167
|
+
const doIndex = call.indexOf("DO ");
|
|
168
|
+
expect(renameIndex).toBeGreaterThan(-1);
|
|
169
|
+
expect(commentIndex).toBeGreaterThan(-1);
|
|
170
|
+
expect(doIndex).toBeGreaterThan(-1);
|
|
171
|
+
expect(renameIndex).toBeLessThan(commentIndex);
|
|
172
|
+
expect(commentIndex).toBeLessThan(doIndex);
|
|
173
|
+
});
|
|
149
174
|
});
|
|
150
175
|
|
|
151
176
|
describe("mysql_event_drop", () => {
|
|
@@ -351,7 +376,9 @@ describe("Event Create Advanced", () => {
|
|
|
351
376
|
});
|
|
352
377
|
|
|
353
378
|
it("should add IF NOT EXISTS clause when specified", async () => {
|
|
354
|
-
|
|
379
|
+
// Pre-check returns empty (event doesn't exist), then CREATE succeeds
|
|
380
|
+
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([]));
|
|
381
|
+
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([]));
|
|
355
382
|
|
|
356
383
|
const tool = tools.find((t) => t.name === "mysql_event_create")!;
|
|
357
384
|
await tool.handler(
|
|
@@ -364,10 +391,37 @@ describe("Event Create Advanced", () => {
|
|
|
364
391
|
mockContext,
|
|
365
392
|
);
|
|
366
393
|
|
|
367
|
-
const call = mockAdapter.executeQuery.mock.calls[
|
|
394
|
+
const call = mockAdapter.executeQuery.mock.calls[1][0] as string;
|
|
368
395
|
expect(call).toContain("IF NOT EXISTS");
|
|
369
396
|
});
|
|
370
397
|
|
|
398
|
+
it("should return skipped when ifNotExists is true and event already exists", async () => {
|
|
399
|
+
// Pre-check returns existing event
|
|
400
|
+
mockAdapter.executeQuery.mockResolvedValueOnce(
|
|
401
|
+
createMockQueryResult([{ EVENT_NAME: "my_event" }]),
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
const tool = tools.find((t) => t.name === "mysql_event_create")!;
|
|
405
|
+
const result = await tool.handler(
|
|
406
|
+
{
|
|
407
|
+
name: "my_event",
|
|
408
|
+
schedule: { type: "ONE TIME", executeAt: "2024-12-31 23:59:59" },
|
|
409
|
+
body: "DELETE FROM temp",
|
|
410
|
+
ifNotExists: true,
|
|
411
|
+
},
|
|
412
|
+
mockContext,
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
expect(result).toEqual({
|
|
416
|
+
success: true,
|
|
417
|
+
skipped: true,
|
|
418
|
+
reason: "Event already exists",
|
|
419
|
+
eventName: "my_event",
|
|
420
|
+
});
|
|
421
|
+
// Should only have the pre-check query, no CREATE
|
|
422
|
+
expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
|
|
423
|
+
});
|
|
424
|
+
|
|
371
425
|
it("should include STARTS and ENDS for recurring events", async () => {
|
|
372
426
|
mockAdapter.executeQuery.mockResolvedValue(createMockQueryResult([]));
|
|
373
427
|
|
|
@@ -72,19 +72,61 @@ describe("JSON Helper Handler Execution", () => {
|
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
describe("mysql_json_validate", () => {
|
|
75
|
-
it("should validate JSON
|
|
75
|
+
it("should validate valid JSON", async () => {
|
|
76
76
|
mockAdapter.executeReadQuery.mockResolvedValue(
|
|
77
77
|
createMockQueryResult([{ is_valid: 1 }]),
|
|
78
78
|
);
|
|
79
79
|
|
|
80
80
|
const tool = tools.find((t) => t.name === "mysql_json_validate")!;
|
|
81
|
-
const result = await tool.handler(
|
|
81
|
+
const result = (await tool.handler(
|
|
82
82
|
{ value: '{"name":"test"}' },
|
|
83
83
|
mockContext,
|
|
84
|
-
);
|
|
84
|
+
)) as { valid: boolean };
|
|
85
85
|
|
|
86
86
|
expect(mockAdapter.executeReadQuery).toHaveBeenCalled();
|
|
87
|
-
expect(result).
|
|
87
|
+
expect(result.valid).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("should return valid: false for malformed JSON", async () => {
|
|
91
|
+
mockAdapter.executeReadQuery.mockResolvedValue(
|
|
92
|
+
createMockQueryResult([{ is_valid: 0 }]),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const tool = tools.find((t) => t.name === "mysql_json_validate")!;
|
|
96
|
+
const result = (await tool.handler(
|
|
97
|
+
{ value: '{"broken": true' },
|
|
98
|
+
mockContext,
|
|
99
|
+
)) as { valid: boolean };
|
|
100
|
+
|
|
101
|
+
expect(result.valid).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should return valid: false for bare strings", async () => {
|
|
105
|
+
mockAdapter.executeReadQuery.mockResolvedValue(
|
|
106
|
+
createMockQueryResult([{ is_valid: 0 }]),
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const tool = tools.find((t) => t.name === "mysql_json_validate")!;
|
|
110
|
+
const result = (await tool.handler({ value: "hello" }, mockContext)) as {
|
|
111
|
+
valid: boolean;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
expect(result.valid).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("should return structured error on MySQL failure", async () => {
|
|
118
|
+
mockAdapter.executeReadQuery.mockRejectedValue(
|
|
119
|
+
new Error("Validation query failed"),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const tool = tools.find((t) => t.name === "mysql_json_validate")!;
|
|
123
|
+
const result = (await tool.handler(
|
|
124
|
+
{ value: "\x00invalid" },
|
|
125
|
+
mockContext,
|
|
126
|
+
)) as { valid: boolean; error: string };
|
|
127
|
+
|
|
128
|
+
expect(result.valid).toBe(false);
|
|
129
|
+
expect(result.error).toBe("Validation query failed");
|
|
88
130
|
});
|
|
89
131
|
});
|
|
90
132
|
|
|
@@ -297,7 +297,9 @@ describe("Partitioning Handler Execution", () => {
|
|
|
297
297
|
|
|
298
298
|
describe("mysql_add_partition", () => {
|
|
299
299
|
it("should add a RANGE partition", async () => {
|
|
300
|
-
mockAdapter.executeQuery
|
|
300
|
+
mockAdapter.executeQuery
|
|
301
|
+
.mockResolvedValueOnce(createMockQueryResult([{ TABLE_NAME: "logs" }]))
|
|
302
|
+
.mockResolvedValueOnce(createMockQueryResult([]));
|
|
301
303
|
|
|
302
304
|
const tool = tools.find((t) => t.name === "mysql_add_partition")!;
|
|
303
305
|
const result = await tool.handler(
|
|
@@ -310,15 +312,19 @@ describe("Partitioning Handler Execution", () => {
|
|
|
310
312
|
mockContext,
|
|
311
313
|
);
|
|
312
314
|
|
|
313
|
-
expect(mockAdapter.executeQuery).
|
|
314
|
-
const call = mockAdapter.executeQuery.mock.calls[
|
|
315
|
+
expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(2);
|
|
316
|
+
const call = mockAdapter.executeQuery.mock.calls[1][0] as string;
|
|
315
317
|
expect(call).toContain("ADD PARTITION");
|
|
316
318
|
expect(call).toContain("VALUES LESS THAN");
|
|
317
319
|
expect(result).toHaveProperty("success", true);
|
|
318
320
|
});
|
|
319
321
|
|
|
320
322
|
it("should add a LIST partition", async () => {
|
|
321
|
-
mockAdapter.executeQuery
|
|
323
|
+
mockAdapter.executeQuery
|
|
324
|
+
.mockResolvedValueOnce(
|
|
325
|
+
createMockQueryResult([{ TABLE_NAME: "regions" }]),
|
|
326
|
+
)
|
|
327
|
+
.mockResolvedValueOnce(createMockQueryResult([]));
|
|
322
328
|
|
|
323
329
|
const tool = tools.find((t) => t.name === "mysql_add_partition")!;
|
|
324
330
|
await tool.handler(
|
|
@@ -331,12 +337,14 @@ describe("Partitioning Handler Execution", () => {
|
|
|
331
337
|
mockContext,
|
|
332
338
|
);
|
|
333
339
|
|
|
334
|
-
const call = mockAdapter.executeQuery.mock.calls[
|
|
340
|
+
const call = mockAdapter.executeQuery.mock.calls[1][0] as string;
|
|
335
341
|
expect(call).toContain("VALUES IN");
|
|
336
342
|
});
|
|
337
343
|
|
|
338
344
|
it("should add HASH partitions", async () => {
|
|
339
|
-
mockAdapter.executeQuery
|
|
345
|
+
mockAdapter.executeQuery
|
|
346
|
+
.mockResolvedValueOnce(createMockQueryResult([{ TABLE_NAME: "data" }]))
|
|
347
|
+
.mockResolvedValueOnce(createMockQueryResult([]));
|
|
340
348
|
|
|
341
349
|
const tool = tools.find((t) => t.name === "mysql_add_partition")!;
|
|
342
350
|
await tool.handler(
|
|
@@ -349,12 +357,14 @@ describe("Partitioning Handler Execution", () => {
|
|
|
349
357
|
mockContext,
|
|
350
358
|
);
|
|
351
359
|
|
|
352
|
-
const call = mockAdapter.executeQuery.mock.calls[
|
|
360
|
+
const call = mockAdapter.executeQuery.mock.calls[1][0] as string;
|
|
353
361
|
expect(call).toContain("PARTITIONS 4");
|
|
354
362
|
});
|
|
355
363
|
|
|
356
364
|
it("should add KEY partitions", async () => {
|
|
357
|
-
mockAdapter.executeQuery
|
|
365
|
+
mockAdapter.executeQuery
|
|
366
|
+
.mockResolvedValueOnce(createMockQueryResult([{ TABLE_NAME: "data" }]))
|
|
367
|
+
.mockResolvedValueOnce(createMockQueryResult([]));
|
|
358
368
|
|
|
359
369
|
const tool = tools.find((t) => t.name === "mysql_add_partition")!;
|
|
360
370
|
await tool.handler(
|
|
@@ -367,14 +377,16 @@ describe("Partitioning Handler Execution", () => {
|
|
|
367
377
|
mockContext,
|
|
368
378
|
);
|
|
369
379
|
|
|
370
|
-
const call = mockAdapter.executeQuery.mock.calls[
|
|
380
|
+
const call = mockAdapter.executeQuery.mock.calls[1][0] as string;
|
|
371
381
|
expect(call).toContain("PARTITIONS 8");
|
|
372
382
|
});
|
|
373
383
|
});
|
|
374
384
|
|
|
375
385
|
describe("mysql_drop_partition", () => {
|
|
376
386
|
it("should drop a partition", async () => {
|
|
377
|
-
mockAdapter.executeQuery
|
|
387
|
+
mockAdapter.executeQuery
|
|
388
|
+
.mockResolvedValueOnce(createMockQueryResult([{ TABLE_NAME: "logs" }]))
|
|
389
|
+
.mockResolvedValueOnce(createMockQueryResult([]));
|
|
378
390
|
|
|
379
391
|
const tool = tools.find((t) => t.name === "mysql_drop_partition")!;
|
|
380
392
|
const result = await tool.handler(
|
|
@@ -385,8 +397,8 @@ describe("Partitioning Handler Execution", () => {
|
|
|
385
397
|
mockContext,
|
|
386
398
|
);
|
|
387
399
|
|
|
388
|
-
expect(mockAdapter.executeQuery).
|
|
389
|
-
const call = mockAdapter.executeQuery.mock.calls[
|
|
400
|
+
expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(2);
|
|
401
|
+
const call = mockAdapter.executeQuery.mock.calls[1][0] as string;
|
|
390
402
|
expect(call).toContain("DROP PARTITION");
|
|
391
403
|
expect(result).toHaveProperty("success", true);
|
|
392
404
|
});
|
|
@@ -394,7 +406,9 @@ describe("Partitioning Handler Execution", () => {
|
|
|
394
406
|
|
|
395
407
|
describe("mysql_reorganize_partition", () => {
|
|
396
408
|
it("should reorganize partitions", async () => {
|
|
397
|
-
mockAdapter.executeQuery
|
|
409
|
+
mockAdapter.executeQuery
|
|
410
|
+
.mockResolvedValueOnce(createMockQueryResult([{ TABLE_NAME: "logs" }]))
|
|
411
|
+
.mockResolvedValueOnce(createMockQueryResult([]));
|
|
398
412
|
|
|
399
413
|
const tool = tools.find((t) => t.name === "mysql_reorganize_partition")!;
|
|
400
414
|
const result = await tool.handler(
|
|
@@ -410,18 +424,20 @@ describe("Partitioning Handler Execution", () => {
|
|
|
410
424
|
mockContext,
|
|
411
425
|
);
|
|
412
426
|
|
|
413
|
-
expect(mockAdapter.executeQuery).
|
|
414
|
-
const call = mockAdapter.executeQuery.mock.calls[
|
|
427
|
+
expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(2);
|
|
428
|
+
const call = mockAdapter.executeQuery.mock.calls[1][0] as string;
|
|
415
429
|
expect(call).toContain("REORGANIZE PARTITION");
|
|
416
430
|
expect(result).toHaveProperty("success", true);
|
|
417
431
|
});
|
|
418
432
|
|
|
419
433
|
it("should return structured error for non-partitioned table", async () => {
|
|
420
|
-
mockAdapter.executeQuery
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
434
|
+
mockAdapter.executeQuery
|
|
435
|
+
.mockResolvedValueOnce(createMockQueryResult([{ TABLE_NAME: "users" }]))
|
|
436
|
+
.mockRejectedValueOnce(
|
|
437
|
+
new Error(
|
|
438
|
+
"Partition management on a not partitioned table is not possible",
|
|
439
|
+
),
|
|
440
|
+
);
|
|
425
441
|
|
|
426
442
|
const tool = tools.find((t) => t.name === "mysql_reorganize_partition")!;
|
|
427
443
|
const result = (await tool.handler(
|
|
@@ -439,9 +455,11 @@ describe("Partitioning Handler Execution", () => {
|
|
|
439
455
|
});
|
|
440
456
|
|
|
441
457
|
it("should return structured error for nonexistent partition", async () => {
|
|
442
|
-
mockAdapter.executeQuery
|
|
443
|
-
|
|
444
|
-
|
|
458
|
+
mockAdapter.executeQuery
|
|
459
|
+
.mockResolvedValueOnce(createMockQueryResult([{ TABLE_NAME: "logs" }]))
|
|
460
|
+
.mockRejectedValueOnce(
|
|
461
|
+
new Error("Error in list of partitions to REORGANIZE"),
|
|
462
|
+
);
|
|
445
463
|
|
|
446
464
|
const tool = tools.find((t) => t.name === "mysql_reorganize_partition")!;
|
|
447
465
|
const result = (await tool.handler(
|
|
@@ -458,6 +476,76 @@ describe("Partitioning Handler Execution", () => {
|
|
|
458
476
|
expect(result.fromPartitions).toEqual(["nonexistent"]);
|
|
459
477
|
expect(result.error).toContain("do not exist");
|
|
460
478
|
});
|
|
479
|
+
|
|
480
|
+
it("should return structured error for unsupported partition type (HASH)", async () => {
|
|
481
|
+
const tool = tools.find((t) => t.name === "mysql_reorganize_partition")!;
|
|
482
|
+
const result = (await tool.handler(
|
|
483
|
+
{
|
|
484
|
+
table: "data",
|
|
485
|
+
fromPartitions: ["p1"],
|
|
486
|
+
partitionType: "HASH",
|
|
487
|
+
toPartitions: [{ name: "p1a", value: "50" }],
|
|
488
|
+
},
|
|
489
|
+
mockContext,
|
|
490
|
+
)) as { success: boolean; error: string };
|
|
491
|
+
|
|
492
|
+
expect(result.success).toBe(false);
|
|
493
|
+
expect(result.error).toContain("HASH/KEY");
|
|
494
|
+
expect(result.error).toContain("cannot be reorganized");
|
|
495
|
+
// Should NOT have called executeQuery — error caught at validation
|
|
496
|
+
expect(mockAdapter.executeQuery).not.toHaveBeenCalled();
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
describe("Partitioning P154 existence checks", () => {
|
|
501
|
+
it("should return exists: false for nonexistent table in add_partition", async () => {
|
|
502
|
+
mockAdapter.executeQuery.mockResolvedValue(createMockQueryResult([]));
|
|
503
|
+
|
|
504
|
+
const tool = tools.find((t) => t.name === "mysql_add_partition")!;
|
|
505
|
+
const result = (await tool.handler(
|
|
506
|
+
{
|
|
507
|
+
table: "nonexistent",
|
|
508
|
+
partitionName: "p1",
|
|
509
|
+
partitionType: "RANGE",
|
|
510
|
+
value: "100",
|
|
511
|
+
},
|
|
512
|
+
mockContext,
|
|
513
|
+
)) as { exists: boolean; table: string };
|
|
514
|
+
|
|
515
|
+
expect(result.exists).toBe(false);
|
|
516
|
+
expect(result.table).toBe("nonexistent");
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it("should return exists: false for nonexistent table in drop_partition", async () => {
|
|
520
|
+
mockAdapter.executeQuery.mockResolvedValue(createMockQueryResult([]));
|
|
521
|
+
|
|
522
|
+
const tool = tools.find((t) => t.name === "mysql_drop_partition")!;
|
|
523
|
+
const result = (await tool.handler(
|
|
524
|
+
{ table: "nonexistent", partitionName: "p1" },
|
|
525
|
+
mockContext,
|
|
526
|
+
)) as { exists: boolean; table: string };
|
|
527
|
+
|
|
528
|
+
expect(result.exists).toBe(false);
|
|
529
|
+
expect(result.table).toBe("nonexistent");
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it("should return exists: false for nonexistent table in reorganize_partition", async () => {
|
|
533
|
+
mockAdapter.executeQuery.mockResolvedValue(createMockQueryResult([]));
|
|
534
|
+
|
|
535
|
+
const tool = tools.find((t) => t.name === "mysql_reorganize_partition")!;
|
|
536
|
+
const result = (await tool.handler(
|
|
537
|
+
{
|
|
538
|
+
table: "nonexistent",
|
|
539
|
+
fromPartitions: ["p1"],
|
|
540
|
+
partitionType: "RANGE",
|
|
541
|
+
toPartitions: [{ name: "p1a", value: "50" }],
|
|
542
|
+
},
|
|
543
|
+
mockContext,
|
|
544
|
+
)) as { exists: boolean; table: string };
|
|
545
|
+
|
|
546
|
+
expect(result.exists).toBe(false);
|
|
547
|
+
expect(result.table).toBe("nonexistent");
|
|
548
|
+
});
|
|
461
549
|
});
|
|
462
550
|
|
|
463
551
|
describe("mysql_partition_info existence check", () => {
|
|
@@ -477,11 +565,13 @@ describe("Partitioning Handler Execution", () => {
|
|
|
477
565
|
|
|
478
566
|
describe("mysql_add_partition error handling", () => {
|
|
479
567
|
it("should return structured error for non-partitioned table", async () => {
|
|
480
|
-
mockAdapter.executeQuery
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
568
|
+
mockAdapter.executeQuery
|
|
569
|
+
.mockResolvedValueOnce(createMockQueryResult([{ TABLE_NAME: "users" }]))
|
|
570
|
+
.mockRejectedValueOnce(
|
|
571
|
+
new Error(
|
|
572
|
+
"Partition management on a not partitioned table is not possible",
|
|
573
|
+
),
|
|
574
|
+
);
|
|
485
575
|
|
|
486
576
|
const tool = tools.find((t) => t.name === "mysql_add_partition")!;
|
|
487
577
|
const result = (await tool.handler(
|
|
@@ -499,9 +589,11 @@ describe("Partitioning Handler Execution", () => {
|
|
|
499
589
|
});
|
|
500
590
|
|
|
501
591
|
it("should return structured error for MAXVALUE conflict", async () => {
|
|
502
|
-
mockAdapter.executeQuery
|
|
503
|
-
|
|
504
|
-
|
|
592
|
+
mockAdapter.executeQuery
|
|
593
|
+
.mockResolvedValueOnce(createMockQueryResult([{ TABLE_NAME: "logs" }]))
|
|
594
|
+
.mockRejectedValueOnce(
|
|
595
|
+
new Error("MAXVALUE can only be used in last partition definition"),
|
|
596
|
+
);
|
|
505
597
|
|
|
506
598
|
const tool = tools.find((t) => t.name === "mysql_add_partition")!;
|
|
507
599
|
const result = (await tool.handler(
|
|
@@ -520,9 +612,15 @@ describe("Partitioning Handler Execution", () => {
|
|
|
520
612
|
});
|
|
521
613
|
|
|
522
614
|
it("should return structured error for duplicate partition values", async () => {
|
|
523
|
-
mockAdapter.executeQuery
|
|
524
|
-
|
|
525
|
-
|
|
615
|
+
mockAdapter.executeQuery
|
|
616
|
+
.mockResolvedValueOnce(
|
|
617
|
+
createMockQueryResult([{ TABLE_NAME: "regions" }]),
|
|
618
|
+
)
|
|
619
|
+
.mockRejectedValueOnce(
|
|
620
|
+
new Error(
|
|
621
|
+
"Multiple definition of same constant in list partitioning",
|
|
622
|
+
),
|
|
623
|
+
);
|
|
526
624
|
|
|
527
625
|
const tool = tools.find((t) => t.name === "mysql_add_partition")!;
|
|
528
626
|
const result = (await tool.handler(
|
|
@@ -542,11 +640,13 @@ describe("Partitioning Handler Execution", () => {
|
|
|
542
640
|
|
|
543
641
|
describe("mysql_drop_partition error handling", () => {
|
|
544
642
|
it("should return structured error for non-partitioned table", async () => {
|
|
545
|
-
mockAdapter.executeQuery
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
643
|
+
mockAdapter.executeQuery
|
|
644
|
+
.mockResolvedValueOnce(createMockQueryResult([{ TABLE_NAME: "users" }]))
|
|
645
|
+
.mockRejectedValueOnce(
|
|
646
|
+
new Error(
|
|
647
|
+
"Partition management on a not partitioned table is not possible",
|
|
648
|
+
),
|
|
649
|
+
);
|
|
550
650
|
|
|
551
651
|
const tool = tools.find((t) => t.name === "mysql_drop_partition")!;
|
|
552
652
|
const result = (await tool.handler(
|
|
@@ -559,9 +659,11 @@ describe("Partitioning Handler Execution", () => {
|
|
|
559
659
|
});
|
|
560
660
|
|
|
561
661
|
it("should return structured error for nonexistent partition", async () => {
|
|
562
|
-
mockAdapter.executeQuery
|
|
563
|
-
|
|
564
|
-
|
|
662
|
+
mockAdapter.executeQuery
|
|
663
|
+
.mockResolvedValueOnce(createMockQueryResult([{ TABLE_NAME: "logs" }]))
|
|
664
|
+
.mockRejectedValueOnce(
|
|
665
|
+
new Error("Error in list of partitions to DROP"),
|
|
666
|
+
);
|
|
565
667
|
|
|
566
668
|
const tool = tools.find((t) => t.name === "mysql_drop_partition")!;
|
|
567
669
|
const result = (await tool.handler(
|