mcp-database-inspector 2.0.1
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/README.md +197 -0
- package/dist/database/connection.d.ts +13 -0
- package/dist/database/connection.d.ts.map +1 -0
- package/dist/database/connection.js +155 -0
- package/dist/database/connection.js.map +1 -0
- package/dist/database/manager.d.ts +28 -0
- package/dist/database/manager.d.ts.map +1 -0
- package/dist/database/manager.js +621 -0
- package/dist/database/manager.js.map +1 -0
- package/dist/database/postgres-connection.d.ts +10 -0
- package/dist/database/postgres-connection.d.ts.map +1 -0
- package/dist/database/postgres-connection.js +113 -0
- package/dist/database/postgres-connection.js.map +1 -0
- package/dist/database/types.d.ts +84 -0
- package/dist/database/types.d.ts.map +1 -0
- package/dist/database/types.js +6 -0
- package/dist/database/types.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +120 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +186 -0
- package/dist/server.js.map +1 -0
- package/dist/test-defaults.d.ts +2 -0
- package/dist/test-defaults.d.ts.map +1 -0
- package/dist/test-defaults.js +57 -0
- package/dist/test-defaults.js.map +1 -0
- package/dist/tools/analyze-query.d.ts +27 -0
- package/dist/tools/analyze-query.d.ts.map +1 -0
- package/dist/tools/analyze-query.js +71 -0
- package/dist/tools/analyze-query.js.map +1 -0
- package/dist/tools/execute-query.d.ts +33 -0
- package/dist/tools/execute-query.d.ts.map +1 -0
- package/dist/tools/execute-query.js +57 -0
- package/dist/tools/execute-query.js.map +1 -0
- package/dist/tools/get-foreign-keys.d.ts +38 -0
- package/dist/tools/get-foreign-keys.d.ts.map +1 -0
- package/dist/tools/get-foreign-keys.js +391 -0
- package/dist/tools/get-foreign-keys.js.map +1 -0
- package/dist/tools/get-indexes.d.ts +38 -0
- package/dist/tools/get-indexes.d.ts.map +1 -0
- package/dist/tools/get-indexes.js +472 -0
- package/dist/tools/get-indexes.js.map +1 -0
- package/dist/tools/information-schema-query.d.ts +33 -0
- package/dist/tools/information-schema-query.d.ts.map +1 -0
- package/dist/tools/information-schema-query.js +76 -0
- package/dist/tools/information-schema-query.js.map +1 -0
- package/dist/tools/inspect-table.d.ts +38 -0
- package/dist/tools/inspect-table.d.ts.map +1 -0
- package/dist/tools/inspect-table.js +351 -0
- package/dist/tools/inspect-table.js.map +1 -0
- package/dist/tools/list-databases.d.ts +14 -0
- package/dist/tools/list-databases.d.ts.map +1 -0
- package/dist/tools/list-databases.js +83 -0
- package/dist/tools/list-databases.js.map +1 -0
- package/dist/tools/list-tables.d.ts +19 -0
- package/dist/tools/list-tables.d.ts.map +1 -0
- package/dist/tools/list-tables.js +130 -0
- package/dist/tools/list-tables.js.map +1 -0
- package/dist/utils/errors.d.ts +32 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +98 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +28 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +132 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/validators/input-validator.d.ts +76 -0
- package/dist/validators/input-validator.d.ts.map +1 -0
- package/dist/validators/input-validator.js +295 -0
- package/dist/validators/input-validator.js.map +1 -0
- package/dist/validators/query-validator.d.ts +19 -0
- package/dist/validators/query-validator.d.ts.map +1 -0
- package/dist/validators/query-validator.js +229 -0
- package/dist/validators/query-validator.js.map +1 -0
- package/enhanced_sql_prompt.md +324 -0
- package/examples/claude-config.json +23 -0
- package/examples/roo-config.json +16 -0
- package/package.json +42 -0
- package/src/database/connection.ts +165 -0
- package/src/database/manager.ts +682 -0
- package/src/database/postgres-connection.ts +123 -0
- package/src/database/types.ts +93 -0
- package/src/index.ts +136 -0
- package/src/server.ts +254 -0
- package/src/test-defaults.ts +63 -0
- package/src/tools/analyze-query.test.ts +100 -0
- package/src/tools/analyze-query.ts +112 -0
- package/src/tools/execute-query.ts +91 -0
- package/src/tools/get-foreign-keys.test.ts +51 -0
- package/src/tools/get-foreign-keys.ts +488 -0
- package/src/tools/get-indexes.test.ts +51 -0
- package/src/tools/get-indexes.ts +570 -0
- package/src/tools/information-schema-query.ts +125 -0
- package/src/tools/inspect-table.test.ts +59 -0
- package/src/tools/inspect-table.ts +440 -0
- package/src/tools/list-databases.ts +119 -0
- package/src/tools/list-tables.ts +181 -0
- package/src/utils/errors.ts +103 -0
- package/src/utils/logger.ts +158 -0
- package/src/validators/input-validator.ts +318 -0
- package/src/validators/query-validator.ts +267 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { handleAnalyzeQuery } from './analyze-query.js';
|
|
3
|
+
import { DatabaseManager } from '../database/manager.js';
|
|
4
|
+
|
|
5
|
+
describe('analyze_query', () => {
|
|
6
|
+
let mockDbManager: any;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
mockDbManager = {
|
|
10
|
+
analyzeQuery: vi.fn()
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should analyze a MySQL query and return execution plan', async () => {
|
|
15
|
+
const mockAnalysis = {
|
|
16
|
+
database: 'testdb',
|
|
17
|
+
type: 'MySQL',
|
|
18
|
+
query: 'SELECT * FROM users',
|
|
19
|
+
plan: { query_block: { cost_info: { query_cost: 1.5 } } },
|
|
20
|
+
summary: {
|
|
21
|
+
cost: 1.5,
|
|
22
|
+
operations: ['ALL'],
|
|
23
|
+
potentialIssues: ['Full table scan on users']
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
mockDbManager.analyzeQuery.mockResolvedValueOnce(mockAnalysis);
|
|
28
|
+
|
|
29
|
+
const result = await handleAnalyzeQuery(
|
|
30
|
+
{ database: 'testdb', query: 'SELECT * FROM users' },
|
|
31
|
+
mockDbManager
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
35
|
+
expect(parsed.summary.cost).toBe(1.5);
|
|
36
|
+
expect(parsed.summary.potentialIssues).toContain('Full table scan on users');
|
|
37
|
+
expect(parsed.summary.recommendation).toContain('potential performance issues');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should analyze a PostgreSQL query and return execution plan', async () => {
|
|
41
|
+
const mockAnalysis = {
|
|
42
|
+
database: 'testdb',
|
|
43
|
+
type: 'PostgreSQL',
|
|
44
|
+
query: 'SELECT * FROM products',
|
|
45
|
+
plan: { Plan: { 'Total Cost': 2.5, 'Node Type': 'Seq Scan' } },
|
|
46
|
+
summary: {
|
|
47
|
+
cost: 2.5,
|
|
48
|
+
operations: ['Seq Scan'],
|
|
49
|
+
potentialIssues: ['Full table scan on products']
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
mockDbManager.analyzeQuery.mockResolvedValueOnce(mockAnalysis);
|
|
54
|
+
|
|
55
|
+
const result = await handleAnalyzeQuery(
|
|
56
|
+
{ database: 'testdb', query: 'SELECT * FROM products' },
|
|
57
|
+
mockDbManager
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
61
|
+
expect(parsed.summary.cost).toBe(2.5);
|
|
62
|
+
expect(parsed.summary.operations).toContain('Seq Scan');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should return positive recommendation when no issues found', async () => {
|
|
66
|
+
const mockAnalysis = {
|
|
67
|
+
database: 'testdb',
|
|
68
|
+
type: 'MySQL',
|
|
69
|
+
query: 'SELECT * FROM users WHERE id = 1',
|
|
70
|
+
plan: { query_block: { cost_info: { query_cost: 0.35 } } },
|
|
71
|
+
summary: {
|
|
72
|
+
cost: 0.35,
|
|
73
|
+
operations: ['const'],
|
|
74
|
+
potentialIssues: []
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
mockDbManager.analyzeQuery.mockResolvedValueOnce(mockAnalysis);
|
|
79
|
+
|
|
80
|
+
const result = await handleAnalyzeQuery(
|
|
81
|
+
{ database: 'testdb', query: 'SELECT * FROM users WHERE id = 1' },
|
|
82
|
+
mockDbManager
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
86
|
+
expect(parsed.summary.recommendation).toContain('No major performance issues');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should throw error if neither database nor query is provided', async () => {
|
|
90
|
+
await expect(() =>
|
|
91
|
+
handleAnalyzeQuery({ database: 'testdb' }, mockDbManager)
|
|
92
|
+
).rejects.toThrow(/Invalid arguments/);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should throw error for invalid database name', async () => {
|
|
96
|
+
await expect(() =>
|
|
97
|
+
handleAnalyzeQuery({ database: '', query: 'SELECT 1' }, mockDbManager)
|
|
98
|
+
).rejects.toThrow(/Invalid arguments/);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { DatabaseManager } from '../database/manager.js';
|
|
3
|
+
import { InputValidator } from '../validators/input-validator.js';
|
|
4
|
+
import { Logger } from '../utils/logger.js';
|
|
5
|
+
import { ToolError } from '../utils/errors.js';
|
|
6
|
+
|
|
7
|
+
// Tool schema
|
|
8
|
+
export const AnalyzeQueryArgsSchema = z.object({
|
|
9
|
+
database: z.string().min(1, 'Database name is required'),
|
|
10
|
+
query: z.string().min(1, 'SQL query is required')
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export interface AnalyzeQueryTool {
|
|
14
|
+
name: 'analyze_query';
|
|
15
|
+
description: 'Analyze a SQL query performance and provide recommendations';
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object';
|
|
18
|
+
properties: {
|
|
19
|
+
database: {
|
|
20
|
+
type: 'string';
|
|
21
|
+
description: 'Name of the database to run the analysis against';
|
|
22
|
+
};
|
|
23
|
+
query: {
|
|
24
|
+
type: 'string';
|
|
25
|
+
description: 'The SQL query to analyze';
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
required: ['database', 'query'];
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const analyzeQueryToolDefinition: AnalyzeQueryTool = {
|
|
33
|
+
name: 'analyze_query',
|
|
34
|
+
description: 'Analyze a SQL query performance and provide recommendations',
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
database: {
|
|
39
|
+
type: 'string',
|
|
40
|
+
description: 'Name of the database to run the analysis against'
|
|
41
|
+
},
|
|
42
|
+
query: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
description: 'The SQL query to analyze'
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
required: ['database', 'query']
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export async function handleAnalyzeQuery(
|
|
52
|
+
args: unknown,
|
|
53
|
+
dbManager: DatabaseManager
|
|
54
|
+
): Promise<any> {
|
|
55
|
+
try {
|
|
56
|
+
Logger.info('Executing analyze_query tool');
|
|
57
|
+
|
|
58
|
+
// Validate arguments
|
|
59
|
+
const validationResult = AnalyzeQueryArgsSchema.safeParse(args);
|
|
60
|
+
if (!validationResult.success) {
|
|
61
|
+
Logger.warn('Invalid arguments for analyze_query', validationResult.error);
|
|
62
|
+
throw new ToolError(
|
|
63
|
+
`Invalid arguments: ${validationResult.error.issues.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`,
|
|
64
|
+
'analyze_query'
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const { database, query } = validationResult.data;
|
|
69
|
+
|
|
70
|
+
// Sanitize database name
|
|
71
|
+
const sanitizedDatabase = InputValidator.sanitizeString(database);
|
|
72
|
+
|
|
73
|
+
Logger.info(`Analyzing query for database: ${sanitizedDatabase}`);
|
|
74
|
+
|
|
75
|
+
const analysis = await dbManager.analyzeQuery(sanitizedDatabase, query);
|
|
76
|
+
|
|
77
|
+
const response = {
|
|
78
|
+
database: sanitizedDatabase,
|
|
79
|
+
query: analysis.query,
|
|
80
|
+
type: analysis.type,
|
|
81
|
+
summary: {
|
|
82
|
+
cost: analysis.summary.cost,
|
|
83
|
+
operations: analysis.summary.operations,
|
|
84
|
+
potentialIssues: analysis.summary.potentialIssues,
|
|
85
|
+
recommendation: analysis.summary.potentialIssues.length > 0
|
|
86
|
+
? `Found ${analysis.summary.potentialIssues.length} potential performance issues. Consider adding indexes or refactoring the query.`
|
|
87
|
+
: 'No major performance issues detected in the execution plan.'
|
|
88
|
+
},
|
|
89
|
+
executionPlan: analysis.plan
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
content: [{
|
|
94
|
+
type: 'text',
|
|
95
|
+
text: JSON.stringify(response, null, 2)
|
|
96
|
+
}]
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
} catch (error) {
|
|
100
|
+
Logger.error('Error in analyze_query tool', error);
|
|
101
|
+
|
|
102
|
+
if (error instanceof ToolError) {
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
throw new ToolError(
|
|
107
|
+
`Failed to analyze query: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
108
|
+
'analyze_query',
|
|
109
|
+
error instanceof Error ? error : undefined
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { DatabaseManager } from '../database/manager.js';
|
|
3
|
+
import { InputValidator } from '../validators/input-validator.js';
|
|
4
|
+
import { Logger } from '../utils/logger.js';
|
|
5
|
+
import { ToolError } from '../utils/errors.js';
|
|
6
|
+
|
|
7
|
+
// Zod schema for arguments
|
|
8
|
+
const ExecuteQueryArgsSchema = z.object({
|
|
9
|
+
database: z.string().min(1, 'Database name is required'),
|
|
10
|
+
query: z.string().min(1, 'SQL query is required'),
|
|
11
|
+
params: z.array(z.any()).optional(),
|
|
12
|
+
limit: z.number().int().min(1).max(1000).optional()
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export interface ExecuteQueryTool {
|
|
16
|
+
name: 'execute_query';
|
|
17
|
+
description: 'Execute a safe read-only SQL query (SELECT, SHOW, DESCRIBE, EXPLAIN). Automatic row limits are applied for security.';
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: 'object';
|
|
20
|
+
properties: {
|
|
21
|
+
database: { type: 'string'; description: 'Database name' };
|
|
22
|
+
query: { type: 'string'; description: 'SQL query to execute' };
|
|
23
|
+
params?: { type: 'array'; items: { type: 'any' }; description: 'Optional parameters for the query' };
|
|
24
|
+
limit?: { type: 'number'; description: 'Optional limit for number of rows (default 1000, max 1000)' };
|
|
25
|
+
};
|
|
26
|
+
required: ['database', 'query'];
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const executeQueryToolDefinition: ExecuteQueryTool = {
|
|
31
|
+
name: 'execute_query',
|
|
32
|
+
description: 'Execute a safe read-only SQL query (SELECT, SHOW, DESCRIBE, EXPLAIN). Automatic row limits are applied for security.',
|
|
33
|
+
inputSchema: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
database: { type: 'string', description: 'Database name' },
|
|
37
|
+
query: { type: 'string', description: 'SQL query to execute' },
|
|
38
|
+
params: { type: 'array', items: { type: 'any' }, description: 'Optional parameters for the query' },
|
|
39
|
+
limit: { type: 'number', description: 'Optional limit for number of rows (default 1000, max 1000)' }
|
|
40
|
+
},
|
|
41
|
+
required: ['database', 'query']
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export async function handleExecuteQuery(
|
|
46
|
+
args: unknown,
|
|
47
|
+
dbManager: DatabaseManager
|
|
48
|
+
): Promise<any> {
|
|
49
|
+
try {
|
|
50
|
+
Logger.info('Executing execute_query tool');
|
|
51
|
+
const validationResult = ExecuteQueryArgsSchema.safeParse(args);
|
|
52
|
+
if (!validationResult.success) {
|
|
53
|
+
Logger.warn('Invalid arguments for execute_query', validationResult.error);
|
|
54
|
+
throw new ToolError(
|
|
55
|
+
`Invalid arguments: ${validationResult.error.issues.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`,
|
|
56
|
+
'execute_query'
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const { database, query, params, limit } = validationResult.data;
|
|
61
|
+
|
|
62
|
+
// Sanitize and validate database name
|
|
63
|
+
const sanitizedDatabase = InputValidator.sanitizeString(database);
|
|
64
|
+
const dbNameValidation = InputValidator.validateDatabaseName(sanitizedDatabase);
|
|
65
|
+
if (!dbNameValidation.isValid) {
|
|
66
|
+
throw new ToolError(
|
|
67
|
+
`Invalid database name: ${dbNameValidation.error}`,
|
|
68
|
+
'execute_query'
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Logger.info(`Executing query on database ${sanitizedDatabase}`);
|
|
73
|
+
|
|
74
|
+
const result = await dbManager.executeQuery(sanitizedDatabase, query, params);
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
content: [{
|
|
78
|
+
type: 'text',
|
|
79
|
+
text: JSON.stringify(result.rows, null, 2)
|
|
80
|
+
}]
|
|
81
|
+
};
|
|
82
|
+
} catch (error) {
|
|
83
|
+
Logger.error('Error in execute_query tool', error);
|
|
84
|
+
if (error instanceof ToolError) throw error;
|
|
85
|
+
throw new ToolError(
|
|
86
|
+
`Failed to execute query: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
87
|
+
'execute_query',
|
|
88
|
+
error instanceof Error ? error : undefined
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
import { handleGetForeignKeys } from './get-foreign-keys';
|
|
4
|
+
import { DatabaseManager } from '../database/manager';
|
|
5
|
+
|
|
6
|
+
const mockDbManager = {
|
|
7
|
+
getForeignKeys: vi.fn()
|
|
8
|
+
} as unknown as DatabaseManager;
|
|
9
|
+
|
|
10
|
+
describe('get_foreign_keys', () => {
|
|
11
|
+
it('should return foreign keys for a single table', async () => {
|
|
12
|
+
mockDbManager.getForeignKeys.mockResolvedValueOnce([{ constraintName: 'fk_orders_users', tableName: 'orders', columnName: 'user_id', referencedTableName: 'users', referencedColumnName: 'id' }]);
|
|
13
|
+
const args = { database: 'testdb', table: 'orders' };
|
|
14
|
+
const result = await handleGetForeignKeys(args, mockDbManager);
|
|
15
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
16
|
+
expect(parsed.table).toBe('orders');
|
|
17
|
+
expect(parsed.foreignKeys[0].sourceColumn).toBe('user_id');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should return foreign keys for multiple tables', async () => {
|
|
21
|
+
mockDbManager.getForeignKeys
|
|
22
|
+
.mockResolvedValueOnce([{ constraintName: 'fk_orders_users', tableName: 'orders', columnName: 'user_id', referencedTableName: 'users', referencedColumnName: 'id' }])
|
|
23
|
+
.mockResolvedValueOnce([{ constraintName: 'fk_items_orders', tableName: 'order_items', columnName: 'order_id', referencedTableName: 'orders', referencedColumnName: 'id' }]);
|
|
24
|
+
const args = { database: 'testdb', tables: ['orders', 'order_items'] };
|
|
25
|
+
const result = await handleGetForeignKeys(args, mockDbManager);
|
|
26
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
27
|
+
expect(Object.keys(parsed.results)).toHaveLength(2);
|
|
28
|
+
expect(parsed.results.orders.table).toBe('orders');
|
|
29
|
+
expect(parsed.results.order_items.table).toBe('order_items');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should throw error if neither table nor tables is provided', async () => {
|
|
33
|
+
await expect(() =>
|
|
34
|
+
handleGetForeignKeys({ database: 'testdb' }, mockDbManager)
|
|
35
|
+
).rejects.toThrow(/Either 'table' or non-empty 'tables'/);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should throw error for invalid table name', async () => {
|
|
39
|
+
await expect(() =>
|
|
40
|
+
handleGetForeignKeys({ database: 'testdb', table: 'invalid;DROP' }, mockDbManager)
|
|
41
|
+
).rejects.toThrow(/Invalid table name/);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should return empty result if table not found', async () => {
|
|
45
|
+
mockDbManager.getForeignKeys.mockResolvedValueOnce([]);
|
|
46
|
+
const result = await handleGetForeignKeys({ database: 'testdb', table: 'notfound' }, mockDbManager);
|
|
47
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
48
|
+
expect(parsed.foreignKeys).toHaveLength(0);
|
|
49
|
+
expect(parsed.summary.message).toMatch(/No foreign key relationships found/);
|
|
50
|
+
});
|
|
51
|
+
});
|