@skyramp/mcp 0.2.8 → 0.2.150-rc.local.de

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.
Files changed (93) hide show
  1. package/build/commands/commandLibrary.d.ts +1 -0
  2. package/build/commands/commandLibrary.js +18 -17
  3. package/build/commands/localDevTestChangesCommand.d.ts +15 -0
  4. package/build/commands/localDevTestChangesCommand.js +175 -0
  5. package/build/index.js +74 -13
  6. package/build/prompts/enhance-assertions/sharedAssertionRules.js +1 -0
  7. package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +7 -6
  8. package/build/prompts/local-dev/local-dev-plan.d.ts +25 -0
  9. package/build/prompts/local-dev/local-dev-plan.js +414 -0
  10. package/build/prompts/local-dev/local-dev-prompts.d.ts +4 -0
  11. package/build/prompts/local-dev/local-dev-prompts.js +152 -0
  12. package/build/prompts/prompt-utils.d.ts +8 -0
  13. package/build/prompts/prompt-utils.js +33 -0
  14. package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +3 -1
  15. package/build/prompts/sut-setup/modes/dockerComposePrompt.js +3 -1
  16. package/build/prompts/sut-setup/shared.d.ts +20 -0
  17. package/build/prompts/sut-setup/shared.js +69 -7
  18. package/build/prompts/test-recommendation/analysisOutputPrompt.js +21 -29
  19. package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +79 -16
  20. package/build/prompts/test-recommendation/test-recommendation-prompt.js +41 -4
  21. package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +34 -1
  22. package/build/prompts/testbot/testbot-prompts.d.ts +0 -5
  23. package/build/prompts/testbot/testbot-prompts.js +4 -34
  24. package/build/resources/testbotResource.js +2 -1
  25. package/build/services/TestExecutionService.d.ts +10 -2
  26. package/build/services/TestExecutionService.js +220 -28
  27. package/build/services/TestExecutionService.test.js +447 -16
  28. package/build/services/TestGenerationService.d.ts +2 -2
  29. package/build/tool-phases.js +4 -0
  30. package/build/tools/code-refactor/enhanceAssertionsTool.js +34 -2
  31. package/build/tools/enrichTestWithMocksTool.d.ts +15 -0
  32. package/build/tools/enrichTestWithMocksTool.js +414 -0
  33. package/build/tools/enrichTestWithMocksTool.test.d.ts +1 -0
  34. package/build/tools/enrichTestWithMocksTool.test.js +397 -0
  35. package/build/tools/executeSkyrampTestTool.d.ts +3 -0
  36. package/build/tools/executeSkyrampTestTool.js +73 -18
  37. package/build/tools/executeSkyrampTestTool.test.d.ts +1 -0
  38. package/build/tools/executeSkyrampTestTool.test.js +111 -0
  39. package/build/tools/generate-tests/batchMockGenerationTool.d.ts +72 -0
  40. package/build/tools/generate-tests/batchMockGenerationTool.js +382 -0
  41. package/build/tools/generate-tests/batchMockGenerationTool.test.d.ts +1 -0
  42. package/build/tools/generate-tests/batchMockGenerationTool.test.js +514 -0
  43. package/build/tools/generate-tests/generateContractRestTool.js +2 -2
  44. package/build/tools/generate-tests/generateMockRestTool.d.ts +56 -6
  45. package/build/tools/generate-tests/generateMockRestTool.js +193 -21
  46. package/build/tools/generate-tests/generateMockRestTool.test.d.ts +1 -0
  47. package/build/tools/generate-tests/generateMockRestTool.test.js +340 -0
  48. package/build/tools/generateEnrichedIntegrationTestTool.d.ts +24 -0
  49. package/build/tools/generateEnrichedIntegrationTestTool.js +209 -0
  50. package/build/tools/generateEnrichedIntegrationTestTool.test.d.ts +1 -0
  51. package/build/tools/generateEnrichedIntegrationTestTool.test.js +199 -0
  52. package/build/tools/one-click/oneClickTool.d.ts +10 -0
  53. package/build/tools/one-click/oneClickTool.js +182 -40
  54. package/build/tools/one-click/oneClickTool.test.d.ts +1 -0
  55. package/build/tools/one-click/oneClickTool.test.js +204 -0
  56. package/build/tools/preflightMockCheckTool.d.ts +2 -0
  57. package/build/tools/preflightMockCheckTool.js +91 -0
  58. package/build/tools/test-management/analyzeChangesTool.d.ts +2 -1
  59. package/build/tools/test-management/analyzeChangesTool.js +59 -38
  60. package/build/tools/workspace/initializeWorkspaceTool.js +2 -9
  61. package/build/tools/workspace/initializeWorkspaceTool.test.js +9 -4
  62. package/build/types/OneClickCommands.d.ts +1 -1
  63. package/build/types/RepositoryAnalysis.d.ts +117 -0
  64. package/build/types/RepositoryAnalysis.js +16 -2
  65. package/build/types/TestTypes.d.ts +11 -2
  66. package/build/types/TestTypes.js +11 -1
  67. package/build/utils/analyze-openapi.js +18 -1
  68. package/build/utils/analyze-openapi.test.d.ts +1 -0
  69. package/build/utils/analyze-openapi.test.js +19 -0
  70. package/build/utils/branchDiff.d.ts +17 -1
  71. package/build/utils/branchDiff.js +96 -14
  72. package/build/utils/branchDiff.test.d.ts +1 -0
  73. package/build/utils/branchDiff.test.js +107 -0
  74. package/build/utils/docker.test.js +1 -1
  75. package/build/utils/featureFlags.d.ts +15 -1
  76. package/build/utils/featureFlags.js +19 -2
  77. package/build/utils/featureFlags.test.js +57 -2
  78. package/build/utils/grpcMockValidation.d.ts +1 -0
  79. package/build/utils/grpcMockValidation.js +49 -0
  80. package/build/utils/grpcMockValidation.test.d.ts +1 -0
  81. package/build/utils/grpcMockValidation.test.js +41 -0
  82. package/build/utils/httpMethodValidation.d.ts +4 -0
  83. package/build/utils/httpMethodValidation.js +13 -0
  84. package/build/utils/mockCompatibility.d.ts +49 -0
  85. package/build/utils/mockCompatibility.js +80 -0
  86. package/build/utils/mockCompatibility.test.d.ts +1 -0
  87. package/build/utils/mockCompatibility.test.js +88 -0
  88. package/build/utils/versions.d.ts +3 -3
  89. package/build/utils/versions.js +1 -1
  90. package/build/workspace/workspace.d.ts +35 -21
  91. package/build/workspace/workspace.js +8 -4
  92. package/build/workspace/workspace.test.js +65 -6
  93. package/package.json +2 -2
@@ -0,0 +1,514 @@
1
+ import { jest, describe, it, expect, beforeEach, afterEach, } from "@jest/globals";
2
+ import * as fs from "fs";
3
+ import * as os from "os";
4
+ import * as path from "path";
5
+ // Mock the SkyrampClient
6
+ const mockGenerateRestMock = jest.fn();
7
+ const mockGenerateGrpcMock = jest.fn();
8
+ const mockGenerateKafkaMock = jest.fn();
9
+ jest.unstable_mockModule("@skyramp/skyramp", () => ({
10
+ SkyrampClient: jest.fn().mockImplementation(() => ({
11
+ generateRestMock: mockGenerateRestMock,
12
+ generateGrpcMock: mockGenerateGrpcMock,
13
+ generateKafkaMock: mockGenerateKafkaMock,
14
+ })),
15
+ }));
16
+ jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({
17
+ AnalyticsService: { pushTestGenerationToolEvent: jest.fn() },
18
+ }));
19
+ jest.unstable_mockModule("../../utils/telemetry.js", () => ({
20
+ getEntryPoint: () => "mcp",
21
+ }));
22
+ const { executeBatchMockGeneration, MockProtocol, BATCH_MOCK_PROTOCOL_EXAMPLES, } = await import("./batchMockGenerationTool.js");
23
+ describe("batchMockGenerationTool", () => {
24
+ let tempDir;
25
+ beforeEach(() => {
26
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "skyramp-batch-mock-"));
27
+ mockGenerateRestMock.mockReset();
28
+ mockGenerateGrpcMock.mockReset();
29
+ mockGenerateKafkaMock.mockReset();
30
+ });
31
+ afterEach(() => {
32
+ fs.rmSync(tempDir, { recursive: true, force: true });
33
+ });
34
+ it("documents batch protocol-specific required fields with Claude-style example tags", () => {
35
+ expect(BATCH_MOCK_PROTOCOL_EXAMPLES).toContain('<example protocol="rest">');
36
+ expect(BATCH_MOCK_PROTOCOL_EXAMPLES).toContain('<example protocol="grpc">');
37
+ expect(BATCH_MOCK_PROTOCOL_EXAMPLES).toContain('<example protocol="kafka-producer">');
38
+ expect(BATCH_MOCK_PROTOCOL_EXAMPLES).toContain('<example protocol="kafka-consumer">');
39
+ expect(BATCH_MOCK_PROTOCOL_EXAMPLES).toContain("endpointURL, method unless apiSchema is provided");
40
+ expect(BATCH_MOCK_PROTOCOL_EXAMPLES).toContain("protoPath");
41
+ expect(BATCH_MOCK_PROTOCOL_EXAMPLES).toContain("grpcServiceName");
42
+ expect(BATCH_MOCK_PROTOCOL_EXAMPLES).toContain("kafkaTopic");
43
+ expect(BATCH_MOCK_PROTOCOL_EXAMPLES).toContain("Do not pass method");
44
+ expect(BATCH_MOCK_PROTOCOL_EXAMPLES).toContain("Kafka producer validation item");
45
+ expect(BATCH_MOCK_PROTOCOL_EXAMPLES).toContain("Kafka consumer message item");
46
+ });
47
+ it("requires REST method in batch generation unless apiSchema is provided", async () => {
48
+ const result = await executeBatchMockGeneration({
49
+ mocks: [
50
+ {
51
+ protocol: MockProtocol.Rest,
52
+ endpointURL: "http://ams:4000/api/v1/validate",
53
+ method: "",
54
+ },
55
+ ],
56
+ outputDir: tempDir,
57
+ language: "python",
58
+ });
59
+ expect(result.isError).toBe(true);
60
+ expect(mockGenerateRestMock).not.toHaveBeenCalled();
61
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
62
+ expect(text).toContain("REST mock method is required unless apiSchema is provided for schema-wide generation");
63
+ });
64
+ it("allows empty REST method in batch generation when apiSchema is provided", async () => {
65
+ mockGenerateRestMock.mockResolvedValue("success: rest mock generated");
66
+ const result = await executeBatchMockGeneration({
67
+ mocks: [
68
+ {
69
+ protocol: MockProtocol.Rest,
70
+ endpointURL: "http://ams:4000",
71
+ method: "",
72
+ apiSchema: "/repo/openapi.json",
73
+ },
74
+ ],
75
+ outputDir: tempDir,
76
+ language: "python",
77
+ });
78
+ expect(result.isError).toBe(false);
79
+ expect(mockGenerateRestMock).toHaveBeenCalledTimes(1);
80
+ });
81
+ it("blocks loopback REST mocks in Docker local-dev mode", async () => {
82
+ const result = await executeBatchMockGeneration({
83
+ mocks: [
84
+ {
85
+ protocol: MockProtocol.Rest,
86
+ endpointURL: "http://localhost:4000/api/v1/accounts",
87
+ method: "GET",
88
+ },
89
+ ],
90
+ outputDir: tempDir,
91
+ language: "python",
92
+ runtime: "docker",
93
+ });
94
+ expect(result.isError).toBe(true);
95
+ expect(mockGenerateRestMock).not.toHaveBeenCalled();
96
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
97
+ expect(text).toContain("REST_LOOPBACK_URL");
98
+ });
99
+ it("uses Docker local-dev compatibility checks when dockerNetwork is provided", async () => {
100
+ const result = await executeBatchMockGeneration({
101
+ mocks: [
102
+ {
103
+ protocol: MockProtocol.Rest,
104
+ endpointURL: "http://host.docker.internal:4000/api/v1/accounts",
105
+ method: "GET",
106
+ },
107
+ ],
108
+ outputDir: tempDir,
109
+ language: "python",
110
+ dockerNetwork: "app_default",
111
+ });
112
+ expect(result.isError).toBe(true);
113
+ expect(mockGenerateRestMock).not.toHaveBeenCalled();
114
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
115
+ expect(text).toContain("REST_LOOPBACK_URL");
116
+ });
117
+ it("allows loopback REST mocks outside Docker local-dev mode", async () => {
118
+ mockGenerateRestMock.mockResolvedValue("success: rest mock generated");
119
+ const result = await executeBatchMockGeneration({
120
+ mocks: [
121
+ {
122
+ protocol: MockProtocol.Rest,
123
+ endpointURL: "http://localhost:4000/api/v1/accounts",
124
+ method: "GET",
125
+ },
126
+ ],
127
+ outputDir: tempDir,
128
+ language: "python",
129
+ });
130
+ expect(result.isError).toBe(false);
131
+ expect(mockGenerateRestMock).toHaveBeenCalledTimes(1);
132
+ });
133
+ it("generates multiple REST mocks successfully", async () => {
134
+ mockGenerateRestMock.mockResolvedValue("success: rest mock generated");
135
+ const result = await executeBatchMockGeneration({
136
+ mocks: [
137
+ {
138
+ protocol: MockProtocol.Rest,
139
+ endpointURL: "http://accounts-service:4000/api/v1/accounts",
140
+ method: "GET",
141
+ responseData: JSON.stringify({ id: "acc_123", name: "Test" }),
142
+ },
143
+ {
144
+ protocol: MockProtocol.Rest,
145
+ endpointURL: "http://payments-service:4001/api/v1/payments",
146
+ method: "POST",
147
+ responseData: JSON.stringify({ payment_id: "pay_456" }),
148
+ },
149
+ {
150
+ protocol: MockProtocol.Rest,
151
+ endpointURL: "http://notifications-service:4002/api/v1/notify",
152
+ method: "POST",
153
+ responseData: JSON.stringify({ status: "sent" }),
154
+ },
155
+ ],
156
+ outputDir: tempDir,
157
+ language: "python",
158
+ runtime: "docker",
159
+ dockerNetwork: "my-network",
160
+ });
161
+ expect(result.isError).toBe(false);
162
+ expect(mockGenerateRestMock).toHaveBeenCalledTimes(3);
163
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
164
+ expect(text).toContain("Succeeded:** 3");
165
+ expect(text).toContain("Failed:** 0");
166
+ expect(text).toContain("accounts-service");
167
+ expect(text).toContain("payments-service");
168
+ expect(text).toContain("notifications-service");
169
+ });
170
+ it("rejects gRPC error-shaped responseData in batch generation", async () => {
171
+ const result = await executeBatchMockGeneration({
172
+ mocks: [
173
+ {
174
+ protocol: MockProtocol.Grpc,
175
+ endpointURL: "partner-accounts:50051",
176
+ method: "GetCustomerConfigurations",
177
+ protoPath: "/repo/proto/customer_configurations.proto",
178
+ grpcServiceName: "CustomerConfigurationsService",
179
+ responseData: JSON.stringify({ error: "DEADLINE_EXCEEDED" }),
180
+ },
181
+ ],
182
+ outputDir: tempDir,
183
+ language: "python",
184
+ runtime: "docker",
185
+ });
186
+ expect(result.isError).toBe(true);
187
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
188
+ expect(text).toContain("gRPC mock responseData cannot simulate gRPC status errors");
189
+ expect(mockGenerateGrpcMock).not.toHaveBeenCalled();
190
+ });
191
+ it("generates mixed REST + gRPC mocks when gRPC runs on the downstream service port", async () => {
192
+ mockGenerateRestMock.mockResolvedValue("success: rest mock generated");
193
+ mockGenerateGrpcMock.mockResolvedValue("success: grpc mock generated");
194
+ const result = await executeBatchMockGeneration({
195
+ mocks: [
196
+ {
197
+ protocol: MockProtocol.Rest,
198
+ endpointURL: "http://accounts-service:4000/api/v1/accounts",
199
+ method: "GET",
200
+ responseData: JSON.stringify({ id: "acc_123" }),
201
+ },
202
+ {
203
+ protocol: MockProtocol.Grpc,
204
+ endpointURL: "partner-accounts:50051",
205
+ method: "GetCustomerConfiguration",
206
+ protoPath: "/repo/proto/partner_accounts.proto",
207
+ grpcServiceName: "PartnerAccountsService",
208
+ responseData: JSON.stringify({ config: { enabled: true } }),
209
+ },
210
+ ],
211
+ outputDir: tempDir,
212
+ language: "python",
213
+ });
214
+ expect(result.isError).toBe(false);
215
+ expect(mockGenerateRestMock).toHaveBeenCalledTimes(1);
216
+ expect(mockGenerateGrpcMock).toHaveBeenCalledTimes(1);
217
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
218
+ expect(text).toContain("Succeeded:** 2");
219
+ expect(text).toContain("Failed:** 0");
220
+ });
221
+ it("allows gRPC mocks on non-8080 downstream service ports", async () => {
222
+ mockGenerateRestMock.mockResolvedValue("success: rest mock generated");
223
+ mockGenerateGrpcMock.mockResolvedValue("success: grpc mock generated");
224
+ const result = await executeBatchMockGeneration({
225
+ mocks: [
226
+ {
227
+ protocol: MockProtocol.Rest,
228
+ endpointURL: "http://accounts-service:4000/api/v1/accounts",
229
+ method: "GET",
230
+ responseData: JSON.stringify({ id: "acc_123" }),
231
+ },
232
+ {
233
+ protocol: MockProtocol.Grpc,
234
+ endpointURL: "partner-accounts:9090",
235
+ method: "GetCustomerConfiguration",
236
+ protoPath: "/repo/proto/partner_accounts.proto",
237
+ grpcServiceName: "PartnerAccountsService",
238
+ responseData: JSON.stringify({ config: { enabled: true } }),
239
+ },
240
+ ],
241
+ outputDir: tempDir,
242
+ language: "python",
243
+ });
244
+ expect(result.isError).toBe(false);
245
+ expect(mockGenerateRestMock).toHaveBeenCalledTimes(1);
246
+ expect(mockGenerateGrpcMock).toHaveBeenCalledTimes(1);
247
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
248
+ expect(text).toContain("Succeeded:** 2");
249
+ expect(text).toContain("Failed:** 0");
250
+ expect(text).not.toContain("GRPC_PORT_MISMATCH");
251
+ });
252
+ it("handles partial failures gracefully", async () => {
253
+ mockGenerateRestMock.mockResolvedValue("success: rest mock generated");
254
+ mockGenerateGrpcMock.mockRejectedValue(new Error("Proto file not found"));
255
+ const result = await executeBatchMockGeneration({
256
+ mocks: [
257
+ {
258
+ protocol: MockProtocol.Rest,
259
+ endpointURL: "http://accounts-service:4000/api/v1/accounts",
260
+ method: "GET",
261
+ responseData: JSON.stringify({ id: "acc_123" }),
262
+ },
263
+ {
264
+ protocol: MockProtocol.Grpc,
265
+ endpointURL: "partner-accounts:8080",
266
+ method: "GetConfig",
267
+ protoPath: "/repo/proto/missing.proto",
268
+ grpcServiceName: "PartnerAccountsService",
269
+ responseData: JSON.stringify({}),
270
+ },
271
+ ],
272
+ outputDir: tempDir,
273
+ language: "python",
274
+ });
275
+ // Not a total failure — some succeeded
276
+ expect(result.isError).toBe(false);
277
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
278
+ expect(text).toContain("Succeeded:** 1");
279
+ expect(text).toContain("Failed:** 1");
280
+ expect(text).toContain("Proto file not found");
281
+ });
282
+ it("generates Kafka mocks without forwarding a method field", async () => {
283
+ mockGenerateKafkaMock.mockResolvedValue("success: kafka mock generated");
284
+ const result = await executeBatchMockGeneration({
285
+ mocks: [
286
+ {
287
+ protocol: MockProtocol.Kafka,
288
+ endpointURL: "redpanda:9092",
289
+ kafkaTopic: "events",
290
+ requestData: JSON.stringify({ name: "account.created" }),
291
+ },
292
+ ],
293
+ outputDir: tempDir,
294
+ language: "python",
295
+ runtime: "docker",
296
+ });
297
+ expect(result.isError).toBe(false);
298
+ expect(mockGenerateKafkaMock).toHaveBeenCalledTimes(1);
299
+ const kafkaOptions = mockGenerateKafkaMock.mock.calls[0][0];
300
+ expect(kafkaOptions).toEqual(expect.objectContaining({
301
+ protocol: MockProtocol.Kafka,
302
+ url: "redpanda:9092",
303
+ topic: "events",
304
+ requestData: JSON.stringify({ name: "account.created" }),
305
+ }));
306
+ expect(kafkaOptions).not.toHaveProperty("method");
307
+ });
308
+ it("generates Kafka consumer mocks without forwarding empty requestData", async () => {
309
+ mockGenerateKafkaMock.mockResolvedValue("success: kafka mock generated");
310
+ const result = await executeBatchMockGeneration({
311
+ mocks: [
312
+ {
313
+ protocol: MockProtocol.Kafka,
314
+ endpointURL: "redpanda:9092",
315
+ kafkaTopic: "events",
316
+ requestData: "",
317
+ responseData: JSON.stringify({ accepted: true }),
318
+ },
319
+ ],
320
+ outputDir: tempDir,
321
+ language: "python",
322
+ runtime: "docker",
323
+ });
324
+ expect(result.isError).toBe(false);
325
+ expect(mockGenerateKafkaMock).toHaveBeenCalledTimes(1);
326
+ const kafkaOptions = mockGenerateKafkaMock.mock.calls[0][0];
327
+ expect(kafkaOptions).toEqual(expect.objectContaining({
328
+ protocol: MockProtocol.Kafka,
329
+ url: "redpanda:9092",
330
+ topic: "events",
331
+ responseData: JSON.stringify({ accepted: true }),
332
+ }));
333
+ expect(kafkaOptions.requestData).toBeUndefined();
334
+ expect(kafkaOptions.requestData).not.toBe("");
335
+ expect(kafkaOptions).not.toHaveProperty("method");
336
+ });
337
+ it("rejects Kafka mocks with both requestData and responseData in batch generation", async () => {
338
+ const result = await executeBatchMockGeneration({
339
+ mocks: [
340
+ {
341
+ protocol: MockProtocol.Kafka,
342
+ endpointURL: "redpanda:9092",
343
+ kafkaTopic: "events",
344
+ requestData: JSON.stringify({ type: "account.created" }),
345
+ responseData: JSON.stringify({ accepted: true }),
346
+ },
347
+ ],
348
+ outputDir: tempDir,
349
+ language: "python",
350
+ runtime: "docker",
351
+ });
352
+ expect(result.isError).toBe(true);
353
+ expect(mockGenerateKafkaMock).not.toHaveBeenCalled();
354
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
355
+ expect(text).toContain("Kafka mock requires exactly one of requestData");
356
+ });
357
+ it("rejects Kafka mocks with neither requestData nor responseData in batch generation", async () => {
358
+ const result = await executeBatchMockGeneration({
359
+ mocks: [
360
+ {
361
+ protocol: MockProtocol.Kafka,
362
+ endpointURL: "redpanda:9092",
363
+ kafkaTopic: "events",
364
+ },
365
+ ],
366
+ outputDir: tempDir,
367
+ language: "python",
368
+ runtime: "docker",
369
+ });
370
+ expect(result.isError).toBe(true);
371
+ expect(mockGenerateKafkaMock).not.toHaveBeenCalled();
372
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
373
+ expect(text).toContain("Kafka mock requires exactly one of requestData");
374
+ });
375
+ it("rejects Kafka method in batch generation", async () => {
376
+ const result = await executeBatchMockGeneration({
377
+ mocks: [
378
+ {
379
+ protocol: MockProtocol.Kafka,
380
+ endpointURL: "redpanda:9092",
381
+ kafkaTopic: "events",
382
+ method: "produce",
383
+ },
384
+ ],
385
+ outputDir: tempDir,
386
+ language: "python",
387
+ runtime: "docker",
388
+ });
389
+ expect(result.isError).toBe(true);
390
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
391
+ expect(text).toContain("Kafka mock method must be empty");
392
+ expect(mockGenerateKafkaMock).not.toHaveBeenCalled();
393
+ });
394
+ it("reports validation errors without calling the client", async () => {
395
+ const result = await executeBatchMockGeneration({
396
+ mocks: [
397
+ {
398
+ protocol: MockProtocol.Grpc,
399
+ endpointURL: "partner-accounts:50051",
400
+ method: "GetConfig",
401
+ // Missing protoPath and grpcServiceName
402
+ },
403
+ {
404
+ protocol: MockProtocol.Kafka,
405
+ endpointURL: "redpanda:9092",
406
+ method: "",
407
+ // Missing kafkaTopic
408
+ },
409
+ ],
410
+ outputDir: tempDir,
411
+ language: "python",
412
+ });
413
+ // All failed = isError true
414
+ expect(result.isError).toBe(true);
415
+ expect(mockGenerateGrpcMock).not.toHaveBeenCalled();
416
+ expect(mockGenerateKafkaMock).not.toHaveBeenCalled();
417
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
418
+ expect(text).toContain("Failed:** 2");
419
+ expect(text).toContain("protoPath");
420
+ expect(text).toContain("grpcServiceName");
421
+ expect(text).toContain("kafkaTopic");
422
+ });
423
+ it("rejects invalid REST HTTP methods without calling the client", async () => {
424
+ const result = await executeBatchMockGeneration({
425
+ mocks: [
426
+ {
427
+ protocol: MockProtocol.Rest,
428
+ endpointURL: "http://svc:4000/api",
429
+ method: "POTS",
430
+ },
431
+ ],
432
+ outputDir: tempDir,
433
+ language: "python",
434
+ });
435
+ expect(result.isError).toBe(true);
436
+ expect(mockGenerateRestMock).not.toHaveBeenCalled();
437
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
438
+ expect(text).toContain('REST mock method must be one of GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS or empty string (got "POTS")');
439
+ });
440
+ it("creates output directory if it does not exist", async () => {
441
+ const nestedDir = path.join(tempDir, "nested", "output", "mocks");
442
+ mockGenerateRestMock.mockResolvedValue("success: mock created");
443
+ await executeBatchMockGeneration({
444
+ mocks: [
445
+ {
446
+ protocol: MockProtocol.Rest,
447
+ endpointURL: "http://svc:4000/api",
448
+ method: "GET",
449
+ responseData: "{}",
450
+ },
451
+ ],
452
+ outputDir: nestedDir,
453
+ language: "python",
454
+ });
455
+ expect(fs.existsSync(nestedDir)).toBe(true);
456
+ });
457
+ it("marks result as total failure when all mocks fail", async () => {
458
+ mockGenerateRestMock.mockRejectedValue(new Error("connection refused"));
459
+ const result = await executeBatchMockGeneration({
460
+ mocks: [
461
+ {
462
+ protocol: MockProtocol.Rest,
463
+ endpointURL: "http://svc:4000/api",
464
+ method: "GET",
465
+ },
466
+ ],
467
+ outputDir: tempDir,
468
+ language: "python",
469
+ });
470
+ expect(result.isError).toBe(true);
471
+ const text = result.content[0].type === "text" ? result.content[0].text : "";
472
+ expect(text).toContain("Failed:** 1");
473
+ expect(text).toContain("connection refused");
474
+ });
475
+ it("normalizes padded lowercase REST HTTP methods before backend generation", async () => {
476
+ mockGenerateRestMock.mockResolvedValue("success: rest mock generated");
477
+ await executeBatchMockGeneration({
478
+ mocks: [
479
+ {
480
+ protocol: MockProtocol.Rest,
481
+ endpointURL: "http://svc:4000/api",
482
+ method: " post\n",
483
+ },
484
+ ],
485
+ outputDir: tempDir,
486
+ language: "python",
487
+ });
488
+ expect(mockGenerateRestMock).toHaveBeenCalledWith(expect.objectContaining({ method: "POST" }));
489
+ });
490
+ it("passes shared params to all generated options", async () => {
491
+ mockGenerateRestMock.mockResolvedValue("success: mock created");
492
+ await executeBatchMockGeneration({
493
+ mocks: [
494
+ {
495
+ protocol: MockProtocol.Rest,
496
+ endpointURL: "http://svc:4000/api",
497
+ method: "GET",
498
+ },
499
+ ],
500
+ outputDir: tempDir,
501
+ runtime: "docker",
502
+ dockerNetwork: "test-net",
503
+ language: "typescript",
504
+ framework: "playwright",
505
+ });
506
+ expect(mockGenerateRestMock).toHaveBeenCalledWith(expect.objectContaining({
507
+ outputDir: tempDir,
508
+ runtime: "docker",
509
+ dockerNetwork: "test-net",
510
+ language: "typescript",
511
+ framework: "playwright",
512
+ }));
513
+ });
514
+ });
@@ -357,8 +357,8 @@ const TOOL_NAME = "skyramp_contract_test_generation";
357
357
  const MODES_SECTION = CONSUMER_MODE_ENABLED
358
358
  ? `**Modes:**
359
359
  - Default (no mode set): both \`providerMode\` and \`consumerMode\` default to false. This generates both provider and consumer contract tests — equivalent to setting both modes to true.
360
- - \`providerMode\`: set to true ONLY if the user explicitly requests a provider-side contract test. Optionally specify \`providerOutput\` for the output file path.
361
- - \`consumerMode\`: set to true ONLY if the user explicitly requests a consumer-side contract test. Optionally specify \`consumerOutput\` for the output file path.
360
+ - \`providerMode\`: set to true ONLY if the user explicitly requests a provider-side contract test. Optionally specify \`providerOutput\` for the output file path. Provider tests hit the real endpoint — no mocks.
361
+ - \`consumerMode\`: set to true ONLY if the user explicitly requests a consumer-side contract test. Optionally specify \`consumerOutput\` for the output file path. Consumer tests include mocks wired inline (\`client.apply_mock(mock)\` in the test function) — no separate \`skyramp_enrich_test_with_mocks\` call needed.
362
362
  - Both \`providerMode\` and \`consumerMode\` can be enabled simultaneously to generate both sides.`
363
363
  : `**Mode:**
364
364
  - Only provider-side contract tests are generated. Pass \`providerMode: true\` (or omit it — the tool always behaves as provider-only). Optionally specify \`providerOutput\` for the output file path.`;
@@ -1,9 +1,17 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { TestType } from "../../types/TestTypes.js";
2
+ import { MockProtocol } from "../../types/TestTypes.js";
3
+ export { MockProtocol } from "../../types/TestTypes.js";
3
4
  import { TestGenerationService } from "../../services/TestGenerationService.js";
4
5
  import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
6
+ export declare const MOCK_PROTOCOL_EXAMPLES = "\n<example protocol=\"rest\">\nGenerate a REST mock for one HTTP endpoint. Required fields: protocol=\"rest\", endpointURL, method. Use apiSchema with an empty method only when intentionally generating all schema methods.\n{\n \"protocol\": \"rest\",\n \"endpointURL\": \"http://payment-service:5001/orders/:order_id/payment_authorizations\",\n \"method\": \"PUT\",\n \"requestAware\": true,\n \"requestData\": \"{\"payment_method_id\":\"pm_123\"}\",\n \"responseData\": \"{\"accepted\":true}\",\n \"responseStatusCode\": \"200\"\n}\n</example>\n\n<example protocol=\"grpc\">\nGenerate a gRPC mock. Required fields: protocol=\"grpc\", endpointURL as host:port, protoPath, grpcServiceName. method is the RPC method name; omit or leave empty to generate all service methods. responseData must be a successful protobuf-shaped response, not a gRPC status error.\n{\n \"protocol\": \"grpc\",\n \"endpointURL\": \"profile-service:50051\",\n \"protoPath\": \"/repo/proto/profiles.proto\",\n \"grpcServiceName\": \"ProfileService\",\n \"method\": \"GetProfile\",\n \"responseData\": \"{\"eligible\":true}\"\n}\n</example>\n\n<example protocol=\"kafka-producer\">\nGenerate a Kafka producer validation mock. Required fields: protocol=\"kafka\", endpointURL as broker host:port, kafkaTopic, and requestData. Do not pass method or responseData.\n{\n \"protocol\": \"kafka\",\n \"endpointURL\": \"broker:9092\",\n \"kafkaTopic\": \"events\",\n \"requestData\": \"{\"name\":\"order.created\"}\"\n}\n</example>\n\n<example protocol=\"kafka-consumer\">\nGenerate a Kafka consumer message mock. Required fields: protocol=\"kafka\", endpointURL as broker host:port, kafkaTopic, and responseData. Do not pass method or requestData.\n{\n \"protocol\": \"kafka\",\n \"endpointURL\": \"broker:9092\",\n \"kafkaTopic\": \"events\",\n \"responseData\": \"{\"accepted\":true}\"\n}\n</example>";
5
7
  type MockParams = {
8
+ protocol?: MockProtocol;
6
9
  endpointURL?: string;
10
+ protoPath?: string;
11
+ grpcServiceName?: string;
12
+ protobufExtraFiles?: string[];
13
+ protobufExtraImportPath?: string[];
14
+ kafkaTopic?: string;
7
15
  method?: string;
8
16
  apiSchema?: string;
9
17
  responseData?: string;
@@ -29,13 +37,18 @@ type MockParams = {
29
37
  prompt?: string;
30
38
  };
31
39
  export declare class MockGenerationService extends TestGenerationService {
32
- protected getTestType(): TestType;
40
+ protected getTestType(): "mock";
33
41
  generateTest(params: MockParams & Record<string, any>): Promise<CallToolResult>;
34
42
  private buildReorganizationInstructions;
43
+ protected validateInputs(params: MockParams & Record<string, any>): CallToolResult;
35
44
  protected buildGenerationOptions(params: MockParams): {
36
- uri: string | undefined;
37
45
  method: string | undefined;
38
- apiSchema: string[];
46
+ url: string | undefined;
47
+ protoPath: string | undefined;
48
+ service: string | undefined;
49
+ extraFiles: string[];
50
+ extraImports: string[];
51
+ protocol: MockProtocol;
39
52
  language: string;
40
53
  framework: string;
41
54
  output: string | undefined;
@@ -48,17 +61,54 @@ export declare class MockGenerationService extends TestGenerationService {
48
61
  k8sNamespace: string | undefined;
49
62
  k8sConfig: string | undefined;
50
63
  k8sContext: string | undefined;
64
+ responseData: string | undefined;
65
+ entrypoint: string;
66
+ } | {
67
+ url: string | undefined;
68
+ topic: string | undefined;
51
69
  requestData: string | undefined;
70
+ protocol: MockProtocol;
71
+ language: string;
72
+ framework: string;
73
+ output: string | undefined;
74
+ outputDir: string;
75
+ force: boolean;
76
+ deployDashboard: boolean | undefined;
77
+ runtime: string | undefined;
78
+ dockerNetwork: string | undefined;
79
+ dockerWorkerPort: string;
80
+ k8sNamespace: string | undefined;
81
+ k8sConfig: string | undefined;
82
+ k8sContext: string | undefined;
52
83
  responseData: string | undefined;
84
+ entrypoint: string;
85
+ } | {
86
+ method: string;
87
+ uri: string | undefined;
88
+ apiSchema: string[];
89
+ requestData: string | undefined;
53
90
  responseStatusCode: string | undefined;
54
91
  formParams: string | undefined;
55
92
  requestAware: boolean | undefined;
56
93
  traceFilePath: string | undefined;
57
- entrypoint: string;
58
94
  mockPort: number;
59
95
  optionalFields: boolean;
96
+ protocol: MockProtocol;
97
+ language: string;
98
+ framework: string;
99
+ output: string | undefined;
100
+ outputDir: string;
101
+ force: boolean;
102
+ deployDashboard: boolean | undefined;
103
+ runtime: string | undefined;
104
+ dockerNetwork: string | undefined;
105
+ dockerWorkerPort: string;
106
+ k8sNamespace: string | undefined;
107
+ k8sConfig: string | undefined;
108
+ k8sContext: string | undefined;
109
+ responseData: string | undefined;
110
+ entrypoint: string;
60
111
  };
61
112
  protected executeGeneration(generateOptions: any): Promise<string>;
62
113
  }
63
114
  export declare function registerMockTool(server: McpServer): void;
64
- export {};