@ondc/automation-mock-runner 1.0.6 → 1.1.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/dist/lib/MockRunner.d.ts +42 -0
- package/dist/lib/MockRunner.js +57 -20
- package/dist/lib/configHelper.d.ts +2 -0
- package/dist/lib/configHelper.js +32 -0
- package/dist/test/MockRunner.test.js +30 -4
- package/dist/test/generateContext.test.d.ts +1 -0
- package/dist/test/generateContext.test.js +472 -0
- package/package.json +2 -1
package/dist/lib/MockRunner.d.ts
CHANGED
|
@@ -8,6 +8,48 @@ export declare class MockRunner {
|
|
|
8
8
|
logger: Logger;
|
|
9
9
|
constructor(config: MockPlaygroundConfigType, skipValidation?: boolean);
|
|
10
10
|
getRunnerInstance(): BaseCodeRunner;
|
|
11
|
+
getConfig(): {
|
|
12
|
+
meta: {
|
|
13
|
+
domain: string;
|
|
14
|
+
version: string;
|
|
15
|
+
flowId: string;
|
|
16
|
+
};
|
|
17
|
+
transaction_data: {
|
|
18
|
+
transaction_id: string;
|
|
19
|
+
latest_timestamp: string;
|
|
20
|
+
bap_id?: string | undefined;
|
|
21
|
+
bap_uri?: string | undefined;
|
|
22
|
+
bpp_id?: string | undefined;
|
|
23
|
+
bpp_uri?: string | undefined;
|
|
24
|
+
};
|
|
25
|
+
steps: {
|
|
26
|
+
api: string;
|
|
27
|
+
action_id: string;
|
|
28
|
+
owner: "BAP" | "BPP";
|
|
29
|
+
responseFor: string | null;
|
|
30
|
+
unsolicited: boolean;
|
|
31
|
+
description: string;
|
|
32
|
+
mock: {
|
|
33
|
+
generate: string;
|
|
34
|
+
validate: string;
|
|
35
|
+
requirements: string;
|
|
36
|
+
defaultPayload: any;
|
|
37
|
+
saveData: Record<string, string>;
|
|
38
|
+
inputs: {
|
|
39
|
+
id?: string | undefined;
|
|
40
|
+
jsonSchema?: any;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
repeatCount?: number | null | undefined;
|
|
44
|
+
}[];
|
|
45
|
+
transaction_history: {
|
|
46
|
+
action_id: string;
|
|
47
|
+
payload: any;
|
|
48
|
+
saved_info: Record<string, any>;
|
|
49
|
+
}[];
|
|
50
|
+
validationLib: string;
|
|
51
|
+
helperLib: string;
|
|
52
|
+
};
|
|
11
53
|
validateConfig(): {
|
|
12
54
|
success: boolean;
|
|
13
55
|
errors?: import("zod/v4/core").$ZodIssue[];
|
package/dist/lib/MockRunner.js
CHANGED
|
@@ -44,6 +44,9 @@ class MockRunner {
|
|
|
44
44
|
this.logger.debug("Code runner instance obtained successfully: " + this.runner.toString());
|
|
45
45
|
return this.runner;
|
|
46
46
|
}
|
|
47
|
+
getConfig() {
|
|
48
|
+
return this.config;
|
|
49
|
+
}
|
|
47
50
|
validateConfig() {
|
|
48
51
|
const res = (0, validateConfig_1.validateConfigWithErrors)(this.config);
|
|
49
52
|
return res;
|
|
@@ -319,50 +322,84 @@ class MockRunner {
|
|
|
319
322
|
};
|
|
320
323
|
}
|
|
321
324
|
generateContext(actionId, action, sessionData) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
326
|
-
const responseFor = step?.responseFor;
|
|
325
|
+
// Find the step configuration for this action
|
|
326
|
+
const step = this.config.steps.find((s) => s.action_id === actionId);
|
|
327
|
+
// Determine the message_id based on responseFor logic
|
|
327
328
|
let messageId = (0, uuid_1.v4)();
|
|
328
|
-
if (responseFor) {
|
|
329
|
+
if (step?.responseFor) {
|
|
330
|
+
// Priority 1: Get from sessionData if available
|
|
329
331
|
if (sessionData?.latestMessage_id &&
|
|
332
|
+
Array.isArray(sessionData.latestMessage_id) &&
|
|
330
333
|
sessionData.latestMessage_id.length > 0) {
|
|
331
334
|
messageId = sessionData.latestMessage_id[0];
|
|
332
335
|
}
|
|
336
|
+
// Priority 2: Fall back to transaction history
|
|
333
337
|
else {
|
|
334
|
-
const
|
|
335
|
-
if (
|
|
336
|
-
messageId =
|
|
338
|
+
const historyItem = this.config.transaction_history?.find((item) => item.action_id === step.responseFor);
|
|
339
|
+
if (historyItem?.payload?.context?.message_id) {
|
|
340
|
+
messageId = historyItem.payload.context.message_id;
|
|
337
341
|
}
|
|
338
342
|
}
|
|
339
343
|
}
|
|
344
|
+
// Safely extract transaction_id
|
|
345
|
+
const transactionId = (() => {
|
|
346
|
+
// Priority 1: Get from sessionData
|
|
347
|
+
if (sessionData?.transaction_id) {
|
|
348
|
+
const sessionTxnId = Array.isArray(sessionData.transaction_id)
|
|
349
|
+
? sessionData.transaction_id[0]
|
|
350
|
+
: sessionData.transaction_id;
|
|
351
|
+
// Only return if we got a valid non-empty value
|
|
352
|
+
if (sessionTxnId && sessionTxnId.trim().length > 0) {
|
|
353
|
+
return sessionTxnId;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// Priority 2: Get from transaction history (most recent)
|
|
357
|
+
if (this.config.transaction_history?.length > 0) {
|
|
358
|
+
const mostRecentHistory = this.config.transaction_history[this.config.transaction_history.length - 1];
|
|
359
|
+
const historyTxnId = mostRecentHistory?.payload?.context?.transaction_id;
|
|
360
|
+
if (historyTxnId && historyTxnId.trim().length > 0) {
|
|
361
|
+
return historyTxnId;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// Priority 3: Get from transaction_data
|
|
365
|
+
const configTxnId = this.config.transaction_data?.transaction_id;
|
|
366
|
+
if (configTxnId && configTxnId.trim().length > 0) {
|
|
367
|
+
return configTxnId;
|
|
368
|
+
}
|
|
369
|
+
// Priority 4: Generate new UUID as last resort
|
|
370
|
+
return (0, uuid_1.v4)();
|
|
371
|
+
})();
|
|
372
|
+
// Build base context
|
|
340
373
|
const baseContext = {
|
|
341
|
-
domain: this.config.meta
|
|
374
|
+
domain: this.config.meta?.domain || "",
|
|
342
375
|
action: action,
|
|
343
376
|
timestamp: new Date().toISOString(),
|
|
344
|
-
transaction_id:
|
|
345
|
-
this.config.transaction_data.transaction_id,
|
|
377
|
+
transaction_id: transactionId,
|
|
346
378
|
message_id: messageId,
|
|
347
|
-
bap_id: this.config.transaction_data
|
|
348
|
-
bap_uri: this.config.transaction_data
|
|
379
|
+
bap_id: this.config.transaction_data?.bap_id || "",
|
|
380
|
+
bap_uri: this.config.transaction_data?.bap_uri || "",
|
|
349
381
|
ttl: "PT30S",
|
|
350
382
|
};
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
baseContext.
|
|
383
|
+
// Add BPP details for non-search actions
|
|
384
|
+
if (action !== "search") {
|
|
385
|
+
baseContext.bpp_id = this.config.transaction_data?.bpp_id || "";
|
|
386
|
+
baseContext.bpp_uri = this.config.transaction_data?.bpp_uri || "";
|
|
354
387
|
}
|
|
355
|
-
|
|
388
|
+
// Version-specific context structure
|
|
389
|
+
const version = this.config.meta?.version || "2.0.0";
|
|
390
|
+
const majorVersion = parseInt(version.split(".")[0], 10);
|
|
391
|
+
if (majorVersion === 1) {
|
|
356
392
|
return {
|
|
357
393
|
...baseContext,
|
|
358
394
|
country: "IND",
|
|
359
395
|
city: "*",
|
|
360
|
-
core_version:
|
|
396
|
+
core_version: version,
|
|
361
397
|
};
|
|
362
398
|
}
|
|
399
|
+
// Version 2+ format
|
|
363
400
|
return {
|
|
364
401
|
...baseContext,
|
|
365
|
-
version:
|
|
402
|
+
version: version,
|
|
366
403
|
location: {
|
|
367
404
|
country: {
|
|
368
405
|
code: "IND",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { MockPlaygroundConfigType } from "./types/mock-config";
|
|
2
2
|
export declare function createInitialMockConfig(domain: string, version: string, flowId: string): MockPlaygroundConfigType;
|
|
3
3
|
export declare function convertToFlowConfig(config: MockPlaygroundConfigType): any;
|
|
4
|
+
export declare function createOptimizedMockConfig(config: MockPlaygroundConfigType): Promise<MockPlaygroundConfigType>;
|
|
5
|
+
export declare function getMinifiedCode(base64Code: string): Promise<string>;
|
package/dist/lib/configHelper.js
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createInitialMockConfig = createInitialMockConfig;
|
|
4
4
|
exports.convertToFlowConfig = convertToFlowConfig;
|
|
5
|
+
exports.createOptimizedMockConfig = createOptimizedMockConfig;
|
|
6
|
+
exports.getMinifiedCode = getMinifiedCode;
|
|
7
|
+
const MockRunner_1 = require("./MockRunner");
|
|
5
8
|
const uuid_1 = require("uuid");
|
|
9
|
+
const terser_1 = require("terser");
|
|
6
10
|
function createInitialMockConfig(domain, version, flowId) {
|
|
7
11
|
return {
|
|
8
12
|
meta: {
|
|
@@ -58,3 +62,31 @@ function convertToFlowConfig(config) {
|
|
|
58
62
|
}
|
|
59
63
|
return flowConfig;
|
|
60
64
|
}
|
|
65
|
+
async function createOptimizedMockConfig(config) {
|
|
66
|
+
const optimizedSteps = await Promise.all(config.steps.map(async (step) => {
|
|
67
|
+
return {
|
|
68
|
+
...step,
|
|
69
|
+
mock: {
|
|
70
|
+
...step.mock,
|
|
71
|
+
generate: await getMinifiedCode(step.mock.generate),
|
|
72
|
+
validate: await getMinifiedCode(step.mock.validate),
|
|
73
|
+
requirements: await getMinifiedCode(step.mock.requirements),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}));
|
|
77
|
+
const optimizedConfig = {
|
|
78
|
+
meta: config.meta,
|
|
79
|
+
transaction_history: [],
|
|
80
|
+
helperLib: config.helperLib,
|
|
81
|
+
validationLib: config.validationLib,
|
|
82
|
+
transaction_data: config.transaction_data,
|
|
83
|
+
steps: optimizedSteps,
|
|
84
|
+
};
|
|
85
|
+
return optimizedConfig;
|
|
86
|
+
}
|
|
87
|
+
async function getMinifiedCode(base64Code) {
|
|
88
|
+
const decodedCode = MockRunner_1.MockRunner.decodeBase64(base64Code);
|
|
89
|
+
const result = await (0, terser_1.minify)(decodedCode);
|
|
90
|
+
// If `minify` returns an object like { code: '...' }, return the string
|
|
91
|
+
return MockRunner_1.MockRunner.encodeBase64(result.code || decodedCode);
|
|
92
|
+
}
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* Tests for MockRunner class - ONDC Automation Mock Runner
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const configHelper_1 = require("../lib/configHelper");
|
|
6
7
|
const MockRunner_1 = require("../lib/MockRunner");
|
|
7
8
|
describe("MockRunner", () => {
|
|
8
9
|
let mockRunner;
|
|
9
10
|
let mockConfig;
|
|
10
|
-
beforeEach(() => {
|
|
11
|
+
beforeEach(async () => {
|
|
11
12
|
mockConfig = {
|
|
12
13
|
meta: {
|
|
13
14
|
domain: "ONDC:TRV14",
|
|
@@ -25,7 +26,10 @@ describe("MockRunner", () => {
|
|
|
25
26
|
};
|
|
26
27
|
try {
|
|
27
28
|
mockRunner = new MockRunner_1.MockRunner(mockConfig);
|
|
28
|
-
|
|
29
|
+
mockRunner
|
|
30
|
+
.getConfig()
|
|
31
|
+
.steps.push(mockRunner.getDefaultStep("search", "search_0"));
|
|
32
|
+
mockRunner = new MockRunner_1.MockRunner(await (0, configHelper_1.createOptimizedMockConfig)(mockRunner.getConfig()));
|
|
29
33
|
}
|
|
30
34
|
catch (error) {
|
|
31
35
|
throw error;
|
|
@@ -157,9 +161,31 @@ describe("MockRunner", () => {
|
|
|
157
161
|
b: 2,
|
|
158
162
|
};
|
|
159
163
|
const res = await MockRunner_1.MockRunner.runGetSave(ob, fun);
|
|
160
|
-
console.log(JSON.stringify(res, null, 2));
|
|
161
|
-
console.log(Object.keys(ob).length);
|
|
162
164
|
expect(res.result).toBe(ob.a + ob.b);
|
|
163
165
|
});
|
|
166
|
+
it("should timeout for long-running getSave functions", async () => {
|
|
167
|
+
const longRunningFunction = MockRunner_1.MockRunner.encodeBase64(`async function getSave(payload){
|
|
168
|
+
console.log('Starting long-running operation...');
|
|
169
|
+
// Simulate a long-running operation that takes more than 3 seconds
|
|
170
|
+
await new Promise(resolve => setTimeout(resolve, 4000));
|
|
171
|
+
console.log('Long-running operation completed');
|
|
172
|
+
return payload.result;
|
|
173
|
+
}`);
|
|
174
|
+
const payload = {
|
|
175
|
+
result: "This should timeout",
|
|
176
|
+
};
|
|
177
|
+
// This should timeout and return an error result
|
|
178
|
+
const res = await MockRunner_1.MockRunner.runGetSave(payload, longRunningFunction);
|
|
179
|
+
console.log("Timeout test result:", JSON.stringify(res, null, 2));
|
|
180
|
+
// Expect the operation to fail due to timeout
|
|
181
|
+
expect(res.success).toBe(false);
|
|
182
|
+
expect(res.error).toBeDefined();
|
|
183
|
+
if (res.error) {
|
|
184
|
+
expect(res.error.message.toLowerCase().includes("timeout") ||
|
|
185
|
+
res.error.message.includes("Timeout") ||
|
|
186
|
+
res.error.message.includes("TIMEOUT") ||
|
|
187
|
+
res.error.name.toLowerCase().includes("timeout")).toBe(true);
|
|
188
|
+
}
|
|
189
|
+
}, 10000); // Set Jest timeout to 10 seconds to allow for the timeout to occur
|
|
164
190
|
});
|
|
165
191
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const MockRunner_1 = require("../lib/MockRunner");
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
// Mock UUID to get predictable test results
|
|
6
|
+
jest.mock("uuid", () => ({
|
|
7
|
+
v4: jest.fn(() => "test-uuid-1234"),
|
|
8
|
+
}));
|
|
9
|
+
describe("MockRunner - generateContext", () => {
|
|
10
|
+
let mockRunner;
|
|
11
|
+
let mockConfig;
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
// Reset UUID mock
|
|
14
|
+
uuid_1.v4.mockReturnValue("test-uuid-1234");
|
|
15
|
+
mockConfig = {
|
|
16
|
+
meta: {
|
|
17
|
+
domain: "ONDC:TRV14",
|
|
18
|
+
version: "2.0.0",
|
|
19
|
+
flowId: "test-flow",
|
|
20
|
+
},
|
|
21
|
+
transaction_data: {
|
|
22
|
+
transaction_id: "txn-123",
|
|
23
|
+
latest_timestamp: "2025-11-11T10:00:00.000Z",
|
|
24
|
+
bap_id: "test-bap-id",
|
|
25
|
+
bap_uri: "https://test-bap.com",
|
|
26
|
+
bpp_id: "test-bpp-id",
|
|
27
|
+
bpp_uri: "https://test-bpp.com",
|
|
28
|
+
},
|
|
29
|
+
steps: [
|
|
30
|
+
{
|
|
31
|
+
api: "search",
|
|
32
|
+
action_id: "search_1",
|
|
33
|
+
owner: "BAP",
|
|
34
|
+
responseFor: null,
|
|
35
|
+
unsolicited: false,
|
|
36
|
+
description: "Search step",
|
|
37
|
+
mock: {
|
|
38
|
+
generate: "base64-encoded-generate",
|
|
39
|
+
validate: "base64-encoded-validate",
|
|
40
|
+
requirements: "base64-encoded-requirements",
|
|
41
|
+
defaultPayload: { context: {}, message: {} },
|
|
42
|
+
saveData: {},
|
|
43
|
+
inputs: {},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
api: "on_search",
|
|
48
|
+
action_id: "on_search_1",
|
|
49
|
+
owner: "BPP",
|
|
50
|
+
responseFor: "search_1",
|
|
51
|
+
unsolicited: false,
|
|
52
|
+
description: "On search response",
|
|
53
|
+
mock: {
|
|
54
|
+
generate: "base64-encoded-generate",
|
|
55
|
+
validate: "base64-encoded-validate",
|
|
56
|
+
requirements: "base64-encoded-requirements",
|
|
57
|
+
defaultPayload: { context: {}, message: {} },
|
|
58
|
+
saveData: {},
|
|
59
|
+
inputs: {},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
api: "select",
|
|
64
|
+
action_id: "select_1",
|
|
65
|
+
owner: "BAP",
|
|
66
|
+
responseFor: null,
|
|
67
|
+
unsolicited: false,
|
|
68
|
+
description: "Select step",
|
|
69
|
+
mock: {
|
|
70
|
+
generate: "base64-encoded-generate",
|
|
71
|
+
validate: "base64-encoded-validate",
|
|
72
|
+
requirements: "base64-encoded-requirements",
|
|
73
|
+
defaultPayload: { context: {}, message: {} },
|
|
74
|
+
saveData: {},
|
|
75
|
+
inputs: {},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
transaction_history: [
|
|
80
|
+
{
|
|
81
|
+
action_id: "search_1",
|
|
82
|
+
payload: {
|
|
83
|
+
context: {
|
|
84
|
+
message_id: "search-msg-123",
|
|
85
|
+
timestamp: "2025-11-11T10:00:00.000Z",
|
|
86
|
+
},
|
|
87
|
+
message: {},
|
|
88
|
+
},
|
|
89
|
+
saved_info: {},
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
validationLib: "",
|
|
93
|
+
helperLib: "",
|
|
94
|
+
};
|
|
95
|
+
mockRunner = new MockRunner_1.MockRunner(mockConfig, true); // Skip validation for tests
|
|
96
|
+
});
|
|
97
|
+
describe("Basic Context Generation", () => {
|
|
98
|
+
it("should generate context with basic required fields", () => {
|
|
99
|
+
const context = mockRunner.generateContext("search_1", "search");
|
|
100
|
+
expect(context).toHaveProperty("domain", "ONDC:TRV14");
|
|
101
|
+
expect(context).toHaveProperty("action", "search");
|
|
102
|
+
expect(context).toHaveProperty("timestamp");
|
|
103
|
+
expect(context).toHaveProperty("transaction_id", "txn-123");
|
|
104
|
+
expect(context).toHaveProperty("message_id");
|
|
105
|
+
expect(context).toHaveProperty("bap_id", "test-bap-id");
|
|
106
|
+
expect(context).toHaveProperty("bap_uri", "https://test-bap.com");
|
|
107
|
+
expect(context).toHaveProperty("ttl", "PT30S");
|
|
108
|
+
});
|
|
109
|
+
it("should generate timestamp in ISO format", () => {
|
|
110
|
+
const context = mockRunner.generateContext("search_1", "search");
|
|
111
|
+
expect(context.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/);
|
|
112
|
+
expect(new Date(context.timestamp).toISOString()).toBe(context.timestamp);
|
|
113
|
+
});
|
|
114
|
+
it("should use transaction_id from config", () => {
|
|
115
|
+
const context = mockRunner.generateContext("search_1", "search");
|
|
116
|
+
expect(context.transaction_id).toBe("txn-123");
|
|
117
|
+
});
|
|
118
|
+
it("should generate new UUID for message_id when no responseFor", () => {
|
|
119
|
+
const context = mockRunner.generateContext("search_1", "search");
|
|
120
|
+
expect(context.message_id).toBe("test-uuid-1234");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
describe("Version-based Context Structure", () => {
|
|
124
|
+
it("should generate v2.0.0 context structure", () => {
|
|
125
|
+
const context = mockRunner.generateContext("search_1", "search");
|
|
126
|
+
expect(context).toHaveProperty("version", "2.0.0");
|
|
127
|
+
expect(context).toHaveProperty("location");
|
|
128
|
+
expect(context.location).toEqual({
|
|
129
|
+
country: { code: "IND" },
|
|
130
|
+
city: { code: "*" },
|
|
131
|
+
});
|
|
132
|
+
expect(context).not.toHaveProperty("country");
|
|
133
|
+
expect(context).not.toHaveProperty("city");
|
|
134
|
+
expect(context).not.toHaveProperty("core_version");
|
|
135
|
+
});
|
|
136
|
+
it("should generate v1.x.x context structure", () => {
|
|
137
|
+
const v1Config = {
|
|
138
|
+
...mockConfig,
|
|
139
|
+
meta: { ...mockConfig.meta, version: "1.2.0" },
|
|
140
|
+
};
|
|
141
|
+
const v1Runner = new MockRunner_1.MockRunner(v1Config, true);
|
|
142
|
+
const context = v1Runner.generateContext("search_1", "search");
|
|
143
|
+
expect(context).toHaveProperty("core_version", "1.2.0");
|
|
144
|
+
expect(context).toHaveProperty("country", "IND");
|
|
145
|
+
expect(context).toHaveProperty("city", "*");
|
|
146
|
+
expect(context).not.toHaveProperty("version");
|
|
147
|
+
expect(context).not.toHaveProperty("location");
|
|
148
|
+
});
|
|
149
|
+
it("should handle version 1.0.0", () => {
|
|
150
|
+
const v1Config = {
|
|
151
|
+
...mockConfig,
|
|
152
|
+
meta: { ...mockConfig.meta, version: "1.0.0" },
|
|
153
|
+
};
|
|
154
|
+
const v1Runner = new MockRunner_1.MockRunner(v1Config, true);
|
|
155
|
+
const context = v1Runner.generateContext("search_1", "search");
|
|
156
|
+
expect(context.core_version).toBe("1.0.0");
|
|
157
|
+
expect(context.country).toBe("IND");
|
|
158
|
+
expect(context.city).toBe("*");
|
|
159
|
+
});
|
|
160
|
+
it("should handle version 1.10.5", () => {
|
|
161
|
+
const v1Config = {
|
|
162
|
+
...mockConfig,
|
|
163
|
+
meta: { ...mockConfig.meta, version: "1.10.5" },
|
|
164
|
+
};
|
|
165
|
+
const v1Runner = new MockRunner_1.MockRunner(v1Config, true);
|
|
166
|
+
const context = v1Runner.generateContext("search_1", "search");
|
|
167
|
+
expect(context.core_version).toBe("1.10.5");
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
describe("BPP Fields Handling", () => {
|
|
171
|
+
it("should not include bpp fields for search action", () => {
|
|
172
|
+
const context = mockRunner.generateContext("search_1", "search");
|
|
173
|
+
expect(context).not.toHaveProperty("bpp_id");
|
|
174
|
+
expect(context).not.toHaveProperty("bpp_uri");
|
|
175
|
+
});
|
|
176
|
+
it("should include bpp fields for non-search actions", () => {
|
|
177
|
+
const context = mockRunner.generateContext("select_1", "select");
|
|
178
|
+
expect(context).toHaveProperty("bpp_id", "test-bpp-id");
|
|
179
|
+
expect(context).toHaveProperty("bpp_uri", "https://test-bpp.com");
|
|
180
|
+
});
|
|
181
|
+
it("should include bpp fields for on_search action", () => {
|
|
182
|
+
const context = mockRunner.generateContext("on_search_1", "on_search");
|
|
183
|
+
expect(context).toHaveProperty("bpp_id", "test-bpp-id");
|
|
184
|
+
expect(context).toHaveProperty("bpp_uri", "https://test-bpp.com");
|
|
185
|
+
});
|
|
186
|
+
it("should handle empty bpp_id and bpp_uri gracefully", () => {
|
|
187
|
+
const configWithEmptyBpp = {
|
|
188
|
+
...mockConfig,
|
|
189
|
+
transaction_data: {
|
|
190
|
+
...mockConfig.transaction_data,
|
|
191
|
+
bpp_id: "",
|
|
192
|
+
bpp_uri: "",
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
const runnerWithEmptyBpp = new MockRunner_1.MockRunner(configWithEmptyBpp, true);
|
|
196
|
+
const context = runnerWithEmptyBpp.generateContext("select_1", "select");
|
|
197
|
+
expect(context.bpp_id).toBe("");
|
|
198
|
+
expect(context.bpp_uri).toBe("");
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
describe("Response Message ID Handling", () => {
|
|
202
|
+
it("should use message_id from responseFor step in transaction history", () => {
|
|
203
|
+
const context = mockRunner.generateContext("on_search_1", "on_search");
|
|
204
|
+
expect(context.message_id).toBe("search-msg-123");
|
|
205
|
+
});
|
|
206
|
+
it("should generate new UUID when responseFor not found in transaction history", () => {
|
|
207
|
+
const configWithMissingHistory = {
|
|
208
|
+
...mockConfig,
|
|
209
|
+
transaction_history: [], // Empty history
|
|
210
|
+
};
|
|
211
|
+
const runnerWithMissingHistory = new MockRunner_1.MockRunner(configWithMissingHistory, true);
|
|
212
|
+
// This will throw because the function tries to access responsePayload.context on undefined
|
|
213
|
+
expect(() => {
|
|
214
|
+
runnerWithMissingHistory.generateContext("on_search_1", "on_search");
|
|
215
|
+
}).not.toThrow("Cannot read properties of undefined (reading 'context')");
|
|
216
|
+
});
|
|
217
|
+
it("should generate new UUID when responseFor payload has no message_id", () => {
|
|
218
|
+
const configWithIncompleteHistory = {
|
|
219
|
+
...mockConfig,
|
|
220
|
+
transaction_history: [
|
|
221
|
+
{
|
|
222
|
+
action_id: "search_1",
|
|
223
|
+
payload: {
|
|
224
|
+
context: {}, // No message_id
|
|
225
|
+
message: {},
|
|
226
|
+
},
|
|
227
|
+
saved_info: {},
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
};
|
|
231
|
+
const runnerWithIncompleteHistory = new MockRunner_1.MockRunner(configWithIncompleteHistory, true);
|
|
232
|
+
const context = runnerWithIncompleteHistory.generateContext("on_search_1", "on_search");
|
|
233
|
+
expect(context.message_id).toBe("test-uuid-1234");
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
describe("Session Data Integration", () => {
|
|
237
|
+
it("should use transaction_id from sessionData when provided", () => {
|
|
238
|
+
const sessionData = {
|
|
239
|
+
transaction_id: ["session-txn-456"],
|
|
240
|
+
};
|
|
241
|
+
const context = mockRunner.generateContext("search_1", "search", sessionData);
|
|
242
|
+
expect(context.transaction_id).toBe("session-txn-456");
|
|
243
|
+
});
|
|
244
|
+
it("should use latestMessage_id from sessionData for response steps", () => {
|
|
245
|
+
const sessionData = {
|
|
246
|
+
transaction_id: ["session-txn-456"], // Need to provide transaction_id array
|
|
247
|
+
latestMessage_id: ["session-msg-789"],
|
|
248
|
+
};
|
|
249
|
+
const context = mockRunner.generateContext("on_search_1", "on_search", sessionData);
|
|
250
|
+
expect(context.message_id).toBe("session-msg-789");
|
|
251
|
+
});
|
|
252
|
+
it("should fallback to config transaction_id when sessionData has empty transaction_id", () => {
|
|
253
|
+
const sessionData = {
|
|
254
|
+
transaction_id: [],
|
|
255
|
+
};
|
|
256
|
+
const context = mockRunner.generateContext("search_1", "search", sessionData);
|
|
257
|
+
expect(context.transaction_id).toBe("txn-123");
|
|
258
|
+
});
|
|
259
|
+
it("should fallback to transaction history when sessionData has empty latestMessage_id", () => {
|
|
260
|
+
const sessionData = {
|
|
261
|
+
transaction_id: ["session-txn"], // Provide transaction_id array
|
|
262
|
+
latestMessage_id: [],
|
|
263
|
+
};
|
|
264
|
+
const context = mockRunner.generateContext("on_search_1", "on_search", sessionData);
|
|
265
|
+
expect(context.transaction_id).toBe("session-txn");
|
|
266
|
+
expect(context.message_id).toBe("search-msg-123");
|
|
267
|
+
});
|
|
268
|
+
it("should handle sessionData with null values", () => {
|
|
269
|
+
const sessionData = {
|
|
270
|
+
transaction_id: null,
|
|
271
|
+
latestMessage_id: null,
|
|
272
|
+
};
|
|
273
|
+
// This will throw because the function tries to access transaction_id[0] on null
|
|
274
|
+
expect(() => {
|
|
275
|
+
mockRunner.generateContext("on_search_1", "on_search", sessionData);
|
|
276
|
+
}).not.toThrow("Cannot read properties of null (reading '0')");
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
describe("Step Resolution", () => {
|
|
280
|
+
it("should handle non-existent actionId gracefully", () => {
|
|
281
|
+
const context = mockRunner.generateContext("non_existent", "unknown");
|
|
282
|
+
expect(context.message_id).toBe("test-uuid-1234"); // Should generate new UUID
|
|
283
|
+
expect(context.domain).toBe("ONDC:TRV14");
|
|
284
|
+
expect(context.action).toBe("unknown");
|
|
285
|
+
});
|
|
286
|
+
it("should handle step without responseFor", () => {
|
|
287
|
+
const context = mockRunner.generateContext("search_1", "search");
|
|
288
|
+
expect(context.message_id).toBe("test-uuid-1234");
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
describe("Empty and Default Values", () => {
|
|
292
|
+
it("should handle empty bap_id and bap_uri", () => {
|
|
293
|
+
const configWithEmptyBap = {
|
|
294
|
+
...mockConfig,
|
|
295
|
+
transaction_data: {
|
|
296
|
+
...mockConfig.transaction_data,
|
|
297
|
+
bap_id: "",
|
|
298
|
+
bap_uri: "",
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
const runnerWithEmptyBap = new MockRunner_1.MockRunner(configWithEmptyBap, true);
|
|
302
|
+
const context = runnerWithEmptyBap.generateContext("search_1", "search");
|
|
303
|
+
expect(context.bap_id).toBe("");
|
|
304
|
+
expect(context.bap_uri).toBe("");
|
|
305
|
+
});
|
|
306
|
+
it("should handle undefined bap_id and bap_uri", () => {
|
|
307
|
+
const configWithUndefinedBap = {
|
|
308
|
+
...mockConfig,
|
|
309
|
+
transaction_data: {
|
|
310
|
+
...mockConfig.transaction_data,
|
|
311
|
+
bap_id: undefined,
|
|
312
|
+
bap_uri: undefined,
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
const runnerWithUndefinedBap = new MockRunner_1.MockRunner(configWithUndefinedBap, true);
|
|
316
|
+
const context = runnerWithUndefinedBap.generateContext("search_1", "search");
|
|
317
|
+
expect(context.bap_id).toBe("");
|
|
318
|
+
expect(context.bap_uri).toBe("");
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
describe("Complex Scenarios", () => {
|
|
322
|
+
it("should handle multi-step flow with proper message_id chaining", () => {
|
|
323
|
+
const complexConfig = {
|
|
324
|
+
...mockConfig,
|
|
325
|
+
steps: [
|
|
326
|
+
...mockConfig.steps,
|
|
327
|
+
{
|
|
328
|
+
api: "on_select",
|
|
329
|
+
action_id: "on_select_1",
|
|
330
|
+
owner: "BPP",
|
|
331
|
+
responseFor: "select_1",
|
|
332
|
+
unsolicited: false,
|
|
333
|
+
description: "On select response",
|
|
334
|
+
mock: {
|
|
335
|
+
generate: "base64",
|
|
336
|
+
validate: "base64",
|
|
337
|
+
requirements: "base64",
|
|
338
|
+
defaultPayload: { context: {}, message: {} },
|
|
339
|
+
saveData: {},
|
|
340
|
+
inputs: {},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
],
|
|
344
|
+
transaction_history: [
|
|
345
|
+
...mockConfig.transaction_history,
|
|
346
|
+
{
|
|
347
|
+
action_id: "select_1",
|
|
348
|
+
payload: {
|
|
349
|
+
context: {
|
|
350
|
+
message_id: "select-msg-456",
|
|
351
|
+
timestamp: "2025-11-11T11:00:00.000Z",
|
|
352
|
+
},
|
|
353
|
+
message: {},
|
|
354
|
+
},
|
|
355
|
+
saved_info: {},
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
};
|
|
359
|
+
const complexRunner = new MockRunner_1.MockRunner(complexConfig, true);
|
|
360
|
+
const context = complexRunner.generateContext("on_select_1", "on_select");
|
|
361
|
+
expect(context.message_id).toBe("select-msg-456");
|
|
362
|
+
});
|
|
363
|
+
it("should generate unique timestamps for multiple calls", (done) => {
|
|
364
|
+
const context1 = mockRunner.generateContext("search_1", "search");
|
|
365
|
+
setTimeout(() => {
|
|
366
|
+
const context2 = mockRunner.generateContext("search_1", "search");
|
|
367
|
+
expect(context1.timestamp).not.toBe(context2.timestamp);
|
|
368
|
+
expect(new Date(context2.timestamp).getTime()).toBeGreaterThan(new Date(context1.timestamp).getTime());
|
|
369
|
+
done();
|
|
370
|
+
}, 1);
|
|
371
|
+
});
|
|
372
|
+
it("should handle mixed version formats", () => {
|
|
373
|
+
const configs = [
|
|
374
|
+
{ version: "2.0.0", expected: "version" },
|
|
375
|
+
{ version: "1.0.0", expected: "core_version" },
|
|
376
|
+
{ version: "1.5.2", expected: "core_version" },
|
|
377
|
+
{ version: "2.1.0", expected: "version" },
|
|
378
|
+
{ version: "0.9.0", expected: "version" },
|
|
379
|
+
];
|
|
380
|
+
configs.forEach(({ version, expected }) => {
|
|
381
|
+
const testConfig = {
|
|
382
|
+
...mockConfig,
|
|
383
|
+
meta: { ...mockConfig.meta, version },
|
|
384
|
+
};
|
|
385
|
+
const testRunner = new MockRunner_1.MockRunner(testConfig, true);
|
|
386
|
+
const context = testRunner.generateContext("search_1", "search");
|
|
387
|
+
if (expected === "core_version") {
|
|
388
|
+
expect(context).toHaveProperty("core_version", version);
|
|
389
|
+
expect(context).toHaveProperty("country", "IND");
|
|
390
|
+
expect(context).not.toHaveProperty("version");
|
|
391
|
+
expect(context).not.toHaveProperty("location");
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
expect(context).toHaveProperty("version", version);
|
|
395
|
+
expect(context).toHaveProperty("location");
|
|
396
|
+
expect(context).not.toHaveProperty("core_version");
|
|
397
|
+
expect(context).not.toHaveProperty("country");
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
describe("Error Handling and Edge Cases", () => {
|
|
403
|
+
it("should handle malformed transaction history", () => {
|
|
404
|
+
const configWithMalformedHistory = {
|
|
405
|
+
...mockConfig,
|
|
406
|
+
transaction_history: [
|
|
407
|
+
{
|
|
408
|
+
action_id: "search_1",
|
|
409
|
+
payload: null, // Malformed payload
|
|
410
|
+
saved_info: {},
|
|
411
|
+
},
|
|
412
|
+
],
|
|
413
|
+
};
|
|
414
|
+
const runnerWithMalformedHistory = new MockRunner_1.MockRunner(configWithMalformedHistory, true);
|
|
415
|
+
// This will throw because the function tries to access payload.context on null
|
|
416
|
+
expect(() => {
|
|
417
|
+
runnerWithMalformedHistory.generateContext("on_search_1", "on_search");
|
|
418
|
+
}).not.toThrow("Cannot read properties of null (reading 'context')");
|
|
419
|
+
});
|
|
420
|
+
it("should handle very long action names", () => {
|
|
421
|
+
const longActionId = "a".repeat(1000);
|
|
422
|
+
const context = mockRunner.generateContext(longActionId, "search");
|
|
423
|
+
expect(context.action).toBe("search");
|
|
424
|
+
expect(context.domain).toBe("ONDC:TRV14");
|
|
425
|
+
});
|
|
426
|
+
it("should handle special characters in action names", () => {
|
|
427
|
+
const specialActionId = "test-action_with.special@chars#123";
|
|
428
|
+
const context = mockRunner.generateContext(specialActionId, "special/action");
|
|
429
|
+
expect(context.action).toBe("special/action");
|
|
430
|
+
expect(context.message_id).toBe("test-uuid-1234");
|
|
431
|
+
});
|
|
432
|
+
it("should handle sessionData with proper array format", () => {
|
|
433
|
+
const sessionData = {
|
|
434
|
+
transaction_id: ["custom-txn-id"],
|
|
435
|
+
latestMessage_id: ["custom-msg-id"],
|
|
436
|
+
};
|
|
437
|
+
const context = mockRunner.generateContext("on_search_1", "on_search", sessionData);
|
|
438
|
+
expect(context.transaction_id).toBe("custom-txn-id");
|
|
439
|
+
expect(context.message_id).toBe("custom-msg-id");
|
|
440
|
+
});
|
|
441
|
+
it("should handle sessionData with undefined arrays", () => {
|
|
442
|
+
const sessionData = {
|
|
443
|
+
transaction_id: undefined,
|
|
444
|
+
latestMessage_id: undefined,
|
|
445
|
+
};
|
|
446
|
+
const context = mockRunner.generateContext("on_search_1", "on_search", sessionData);
|
|
447
|
+
expect(context.transaction_id).toBe("txn-123"); // Falls back to config
|
|
448
|
+
expect(context.message_id).toBe("search-msg-123"); // Falls back to transaction history
|
|
449
|
+
});
|
|
450
|
+
it("should handle properly formatted transaction history with no message_id", () => {
|
|
451
|
+
const configWithValidHistoryNoMsgId = {
|
|
452
|
+
...mockConfig,
|
|
453
|
+
transaction_history: [
|
|
454
|
+
{
|
|
455
|
+
action_id: "search_1",
|
|
456
|
+
payload: {
|
|
457
|
+
context: {
|
|
458
|
+
// No message_id field
|
|
459
|
+
timestamp: "2025-11-11T10:00:00.000Z",
|
|
460
|
+
},
|
|
461
|
+
message: {},
|
|
462
|
+
},
|
|
463
|
+
saved_info: {},
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
};
|
|
467
|
+
const runnerWithValidHistory = new MockRunner_1.MockRunner(configWithValidHistoryNoMsgId, true);
|
|
468
|
+
const context = runnerWithValidHistory.generateContext("on_search_1", "on_search");
|
|
469
|
+
expect(context.message_id).toBe("test-uuid-1234"); // Should generate new UUID
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ondc/automation-mock-runner",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A TypeScript library for ONDC automation mock runner",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"acorn-walk": "^8.3.4",
|
|
79
79
|
"base-64": "^1.0.0",
|
|
80
80
|
"jsonpath": "^1.1.1",
|
|
81
|
+
"terser": "^5.44.1",
|
|
81
82
|
"uuid": "^10.0.0",
|
|
82
83
|
"zod": "^4.1.12"
|
|
83
84
|
},
|