@ondc/automation-mock-runner 0.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/LICENSE +15 -0
- package/README.md +372 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +43 -0
- package/dist/lib/MockRunner.d.ts +18 -0
- package/dist/lib/MockRunner.js +335 -0
- package/dist/lib/configHelper.d.ts +2 -0
- package/dist/lib/configHelper.js +101 -0
- package/dist/lib/constants/function-registry.d.ts +26 -0
- package/dist/lib/constants/function-registry.js +132 -0
- package/dist/lib/runners/base-runner.d.ts +6 -0
- package/dist/lib/runners/base-runner.js +6 -0
- package/dist/lib/runners/browser-runner.d.ts +12 -0
- package/dist/lib/runners/browser-runner.js +91 -0
- package/dist/lib/runners/node-runner.d.ts +16 -0
- package/dist/lib/runners/node-runner.js +189 -0
- package/dist/lib/runners/runner-factory.d.ts +25 -0
- package/dist/lib/runners/runner-factory.js +113 -0
- package/dist/lib/types/execution-results.d.ts +30 -0
- package/dist/lib/types/execution-results.js +2 -0
- package/dist/lib/types/mock-config.d.ts +107 -0
- package/dist/lib/types/mock-config.js +52 -0
- package/dist/lib/utils/errors.d.ts +47 -0
- package/dist/lib/utils/errors.js +84 -0
- package/dist/lib/utils/input-validator.d.ts +18 -0
- package/dist/lib/utils/input-validator.js +146 -0
- package/dist/lib/utils/logger.d.ts +36 -0
- package/dist/lib/utils/logger.js +86 -0
- package/dist/lib/utils/performance-monitor.d.ts +34 -0
- package/dist/lib/utils/performance-monitor.js +98 -0
- package/dist/lib/utils/validateConfig.d.ts +6 -0
- package/dist/lib/utils/validateConfig.js +16 -0
- package/dist/lib/validators/code-validator.d.ts +30 -0
- package/dist/lib/validators/code-validator.js +327 -0
- package/dist/lib/worker/worker-factory.d.ts +3 -0
- package/dist/lib/worker/worker-factory.js +81 -0
- package/dist/test/MockRunner.test.d.ts +4 -0
- package/dist/test/MockRunner.test.js +144 -0
- package/dist/test/__mocks__/uuid.d.ts +5 -0
- package/dist/test/__mocks__/uuid.js +8 -0
- package/dist/test/setup.d.ts +3 -0
- package/dist/test/setup.js +43 -0
- package/package.json +84 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// lib/code-runner/code-validator.ts
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.CodeValidator = void 0;
|
|
38
|
+
const acorn = __importStar(require("acorn"));
|
|
39
|
+
const walk = __importStar(require("acorn-walk"));
|
|
40
|
+
class CodeValidator {
|
|
41
|
+
/**
|
|
42
|
+
* Get code statistics
|
|
43
|
+
*/
|
|
44
|
+
static getCodeStatistics(code) {
|
|
45
|
+
const stats = {
|
|
46
|
+
lines: code.split("\n").length,
|
|
47
|
+
functions: 0,
|
|
48
|
+
complexity: 1,
|
|
49
|
+
loops: 0,
|
|
50
|
+
conditionals: 0,
|
|
51
|
+
};
|
|
52
|
+
try {
|
|
53
|
+
const ast = acorn.parse(code, {
|
|
54
|
+
ecmaVersion: 2020,
|
|
55
|
+
sourceType: "script",
|
|
56
|
+
});
|
|
57
|
+
walk.simple(ast, {
|
|
58
|
+
FunctionDeclaration() {
|
|
59
|
+
stats.functions++;
|
|
60
|
+
},
|
|
61
|
+
FunctionExpression() {
|
|
62
|
+
stats.functions++;
|
|
63
|
+
},
|
|
64
|
+
ArrowFunctionExpression() {
|
|
65
|
+
stats.functions++;
|
|
66
|
+
},
|
|
67
|
+
WhileStatement() {
|
|
68
|
+
stats.loops++;
|
|
69
|
+
stats.complexity++;
|
|
70
|
+
},
|
|
71
|
+
DoWhileStatement() {
|
|
72
|
+
stats.loops++;
|
|
73
|
+
stats.complexity++;
|
|
74
|
+
},
|
|
75
|
+
ForStatement() {
|
|
76
|
+
stats.loops++;
|
|
77
|
+
stats.complexity++;
|
|
78
|
+
},
|
|
79
|
+
ForInStatement() {
|
|
80
|
+
stats.loops++;
|
|
81
|
+
stats.complexity++;
|
|
82
|
+
},
|
|
83
|
+
ForOfStatement() {
|
|
84
|
+
stats.loops++;
|
|
85
|
+
stats.complexity++;
|
|
86
|
+
},
|
|
87
|
+
IfStatement() {
|
|
88
|
+
stats.conditionals++;
|
|
89
|
+
stats.complexity++;
|
|
90
|
+
},
|
|
91
|
+
ConditionalExpression() {
|
|
92
|
+
stats.conditionals++;
|
|
93
|
+
stats.complexity++;
|
|
94
|
+
},
|
|
95
|
+
SwitchCase(node) {
|
|
96
|
+
if (node.test) {
|
|
97
|
+
stats.conditionals++;
|
|
98
|
+
stats.complexity++;
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
LogicalExpression(node) {
|
|
102
|
+
if (node.operator === "||" || node.operator === "&&") {
|
|
103
|
+
stats.complexity++;
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
// Ignore parse errors, return partial stats
|
|
110
|
+
console.error("Error parsing code for statistics:", e);
|
|
111
|
+
}
|
|
112
|
+
return stats;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Main validation method - now handles function bodies
|
|
116
|
+
*/
|
|
117
|
+
static validate(functionBody, schema) {
|
|
118
|
+
const errors = [];
|
|
119
|
+
const warnings = [];
|
|
120
|
+
// 1. Wrap the body with function declaration
|
|
121
|
+
const wrappedCode = functionBody;
|
|
122
|
+
// const wrappedCode = schema.template(functionBody);
|
|
123
|
+
try {
|
|
124
|
+
// 2. Parse complete code into AST
|
|
125
|
+
const ast = acorn.parse(wrappedCode, {
|
|
126
|
+
ecmaVersion: 2020,
|
|
127
|
+
sourceType: "script",
|
|
128
|
+
locations: true,
|
|
129
|
+
});
|
|
130
|
+
// 3. Security analysis on the complete code
|
|
131
|
+
const securityIssues = this.analyzeSecurityIssues(ast);
|
|
132
|
+
securityIssues.forEach((issue) => {
|
|
133
|
+
errors.push(`[Line ${issue.line}] ${issue.message}`);
|
|
134
|
+
});
|
|
135
|
+
// 4. Check for dangerous patterns
|
|
136
|
+
const dangerousPatterns = this.detectDangerousPatterns(ast);
|
|
137
|
+
dangerousPatterns.forEach((pattern) => {
|
|
138
|
+
errors.push(`[Line ${pattern.line}] ${pattern.message}`);
|
|
139
|
+
});
|
|
140
|
+
// 5. Validate return type structure (for validate and meetsRequirements)
|
|
141
|
+
if (schema.returnType.properties) {
|
|
142
|
+
const returnValidation = this.validateReturnStructure(ast, schema.returnType.properties);
|
|
143
|
+
errors.push(...returnValidation);
|
|
144
|
+
}
|
|
145
|
+
// 6. Check for best practices
|
|
146
|
+
const practiceWarnings = this.checkBestPractices(ast, schema);
|
|
147
|
+
warnings.push(...practiceWarnings);
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
// Syntax error during parsing
|
|
151
|
+
let errorMessage = `Syntax Error: ${e.message}`;
|
|
152
|
+
// Try to extract line number and adjust for function body
|
|
153
|
+
const lineMatch = e.message.match(/\((\d+):(\d+)\)/);
|
|
154
|
+
if (lineMatch) {
|
|
155
|
+
const line = parseInt(lineMatch[1]);
|
|
156
|
+
const col = lineMatch[2];
|
|
157
|
+
// Subtract the number of lines in the template before the body
|
|
158
|
+
const templateLines = schema.template("").split("\n").length - 1;
|
|
159
|
+
const actualLine = Math.max(1, line - templateLines);
|
|
160
|
+
errorMessage = `Syntax Error at line ${actualLine}, column ${col}: ${e.message.split("(")[0]}`;
|
|
161
|
+
}
|
|
162
|
+
errors.push(errorMessage);
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
isValid: errors.length === 0,
|
|
166
|
+
errors,
|
|
167
|
+
warnings,
|
|
168
|
+
wrappedCode: errors.length === 0 ? wrappedCode : undefined,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Validate that return statements match expected structure
|
|
173
|
+
*/
|
|
174
|
+
static validateReturnStructure(ast, expectedProperties) {
|
|
175
|
+
const warnings = [];
|
|
176
|
+
const foundReturns = [];
|
|
177
|
+
walk.simple(ast, {
|
|
178
|
+
ReturnStatement(node) {
|
|
179
|
+
if (node.argument) {
|
|
180
|
+
foundReturns.push(node.argument);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
// Check if we have return statements
|
|
185
|
+
if (foundReturns.length === 0) {
|
|
186
|
+
warnings.push(`Function should return an object with properties: ${Object.keys(expectedProperties).join(", ")}`);
|
|
187
|
+
return warnings;
|
|
188
|
+
}
|
|
189
|
+
// Check the structure of returned objects
|
|
190
|
+
foundReturns.forEach((returnArg) => {
|
|
191
|
+
if (returnArg.type === "ObjectExpression") {
|
|
192
|
+
const returnedProps = new Set(returnArg.properties.map((p) => p.key.name));
|
|
193
|
+
const expectedProps = Object.keys(expectedProperties);
|
|
194
|
+
// Check for missing properties
|
|
195
|
+
expectedProps.forEach((prop) => {
|
|
196
|
+
if (!returnedProps.has(prop)) {
|
|
197
|
+
warnings.push(`Return object is missing property '${prop}' (expected: ${expectedProperties[prop].type})`);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
// Check for extra properties
|
|
201
|
+
returnedProps.forEach((prop) => {
|
|
202
|
+
if (!expectedProps.includes(prop)) {
|
|
203
|
+
warnings.push(`Return object has unexpected property '${prop}'`);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
warnings.push(`Function should return an object literal with properties: ${Object.keys(expectedProperties).join(", ")}`);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
return warnings;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Security analysis - checks for forbidden functions and properties
|
|
215
|
+
*/
|
|
216
|
+
static analyzeSecurityIssues(ast) {
|
|
217
|
+
const issues = [];
|
|
218
|
+
walk.simple(ast, {
|
|
219
|
+
CallExpression(node) {
|
|
220
|
+
if (node.callee.type === "Identifier") {
|
|
221
|
+
const name = node.callee.name;
|
|
222
|
+
if (CodeValidator.FORBIDDEN_GLOBALS.includes(name)) {
|
|
223
|
+
issues.push({
|
|
224
|
+
type: "forbidden_function",
|
|
225
|
+
message: `Forbidden function call: ${name}()`,
|
|
226
|
+
line: node.loc?.start.line || 0,
|
|
227
|
+
column: node.loc?.start.column || 0,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
NewExpression(node) {
|
|
233
|
+
if (node.callee.type === "Identifier" &&
|
|
234
|
+
node.callee.name === "Function") {
|
|
235
|
+
issues.push({
|
|
236
|
+
type: "function_constructor",
|
|
237
|
+
message: "Using Function constructor is forbidden",
|
|
238
|
+
line: node.loc?.start.line || 0,
|
|
239
|
+
column: node.loc?.start.column || 0,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
MemberExpression(node) {
|
|
244
|
+
if (node.property.type === "Identifier") {
|
|
245
|
+
const propName = node.property.name;
|
|
246
|
+
if (CodeValidator.FORBIDDEN_PROPERTIES.includes(propName)) {
|
|
247
|
+
issues.push({
|
|
248
|
+
type: "forbidden_property",
|
|
249
|
+
message: `Access to '${propName}' is forbidden`,
|
|
250
|
+
line: node.loc?.start.line || 0,
|
|
251
|
+
column: node.loc?.start.column || 0,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
return issues;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Detect dangerous patterns like infinite loops
|
|
261
|
+
*/
|
|
262
|
+
static detectDangerousPatterns(ast) {
|
|
263
|
+
const issues = [];
|
|
264
|
+
walk.simple(ast, {
|
|
265
|
+
WhileStatement(node) {
|
|
266
|
+
if (node.test.type === "Literal" && node.test.value === true) {
|
|
267
|
+
issues.push({
|
|
268
|
+
type: "infinite_loop",
|
|
269
|
+
message: "Potential infinite loop: while(true)",
|
|
270
|
+
line: node.loc?.start.line || 0,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
ForStatement(node) {
|
|
275
|
+
if (!node.test) {
|
|
276
|
+
issues.push({
|
|
277
|
+
type: "infinite_loop",
|
|
278
|
+
message: "Potential infinite loop: for(;;)",
|
|
279
|
+
line: node.loc?.start.line || 0,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
WithStatement(node) {
|
|
284
|
+
issues.push({
|
|
285
|
+
type: "with_statement",
|
|
286
|
+
message: "'with' statement is forbidden",
|
|
287
|
+
line: node.loc?.start.line || 0,
|
|
288
|
+
});
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
return issues;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Check for best practices
|
|
295
|
+
*/
|
|
296
|
+
static checkBestPractices(ast, schema) {
|
|
297
|
+
const warnings = [];
|
|
298
|
+
let hasReturn = false;
|
|
299
|
+
walk.simple(ast, {
|
|
300
|
+
ReturnStatement() {
|
|
301
|
+
hasReturn = true;
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
if (!hasReturn) {
|
|
305
|
+
warnings.push(`Function should return a value (expected: ${schema.returnType.description})`);
|
|
306
|
+
}
|
|
307
|
+
return warnings;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
exports.CodeValidator = CodeValidator;
|
|
311
|
+
CodeValidator.FORBIDDEN_GLOBALS = [
|
|
312
|
+
"eval",
|
|
313
|
+
"Function",
|
|
314
|
+
"importScripts",
|
|
315
|
+
"Worker",
|
|
316
|
+
"SharedWorker",
|
|
317
|
+
"WebSocket",
|
|
318
|
+
"XMLHttpRequest",
|
|
319
|
+
"fetch",
|
|
320
|
+
];
|
|
321
|
+
CodeValidator.FORBIDDEN_PROPERTIES = [
|
|
322
|
+
"localStorage",
|
|
323
|
+
"sessionStorage",
|
|
324
|
+
"indexedDB",
|
|
325
|
+
"webkitStorageInfo",
|
|
326
|
+
"__proto__",
|
|
327
|
+
];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WorkerFactory = void 0;
|
|
4
|
+
// lib/src/workers/worker-factory.ts
|
|
5
|
+
class WorkerFactory {
|
|
6
|
+
static createCodeRunnerWorker() {
|
|
7
|
+
const workerCode = `
|
|
8
|
+
// Complete worker implementation
|
|
9
|
+
self.addEventListener('message', (event) => {
|
|
10
|
+
const { id, code, functionName, args } = event.data;
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const startTime = Date.now();
|
|
14
|
+
const logs = [];
|
|
15
|
+
|
|
16
|
+
// Capture console outputs
|
|
17
|
+
const originalConsole = {
|
|
18
|
+
log: console.log,
|
|
19
|
+
error: console.error,
|
|
20
|
+
warn: console.warn
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
console.log = (...args) => {
|
|
24
|
+
logs.push({
|
|
25
|
+
type: 'log',
|
|
26
|
+
message: args.join(' '),
|
|
27
|
+
timestamp: Date.now()
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
console.error = (...args) => {
|
|
32
|
+
logs.push({
|
|
33
|
+
type: 'error',
|
|
34
|
+
message: args.join(' '),
|
|
35
|
+
timestamp: Date.now()
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
console.warn = (...args) => {
|
|
40
|
+
logs.push({
|
|
41
|
+
type: 'warn',
|
|
42
|
+
message: args.join(' '),
|
|
43
|
+
timestamp: Date.now()
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Execute the code
|
|
48
|
+
const func = new Function('args', code);
|
|
49
|
+
const result = func(args);
|
|
50
|
+
|
|
51
|
+
// Restore console
|
|
52
|
+
Object.assign(console, originalConsole);
|
|
53
|
+
|
|
54
|
+
self.postMessage({
|
|
55
|
+
id,
|
|
56
|
+
success: true,
|
|
57
|
+
result,
|
|
58
|
+
logs,
|
|
59
|
+
executionTime: Date.now() - startTime
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
} catch (error) {
|
|
63
|
+
self.postMessage({
|
|
64
|
+
id,
|
|
65
|
+
success: false,
|
|
66
|
+
error: {
|
|
67
|
+
message: error.message,
|
|
68
|
+
name: error.name,
|
|
69
|
+
stack: error.stack
|
|
70
|
+
},
|
|
71
|
+
logs,
|
|
72
|
+
executionTime: Date.now() - startTime
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
`;
|
|
77
|
+
const blob = new Blob([workerCode], { type: "application/javascript" });
|
|
78
|
+
return new Worker(URL.createObjectURL(blob));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.WorkerFactory = WorkerFactory;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for MockRunner class - ONDC Automation Mock Runner
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const MockRunner_1 = require("../lib/MockRunner");
|
|
7
|
+
describe("MockRunner", () => {
|
|
8
|
+
let mockRunner;
|
|
9
|
+
let mockConfig;
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
mockConfig = {
|
|
12
|
+
meta: {
|
|
13
|
+
domain: "ONDC:TRV14",
|
|
14
|
+
version: "2.0.0",
|
|
15
|
+
flowId: "testing",
|
|
16
|
+
},
|
|
17
|
+
transaction_data: {
|
|
18
|
+
transaction_id: "e9e0b5cb-3f15-48a1-9d86-d4d643f0909d",
|
|
19
|
+
latest_timestamp: "1970-01-01T00:00:00.000Z",
|
|
20
|
+
},
|
|
21
|
+
steps: [],
|
|
22
|
+
transaction_history: [],
|
|
23
|
+
validationLib: "",
|
|
24
|
+
helperLib: "",
|
|
25
|
+
};
|
|
26
|
+
try {
|
|
27
|
+
mockRunner = new MockRunner_1.MockRunner(mockConfig);
|
|
28
|
+
mockConfig.steps.push(mockRunner.getDefaultStep("search", "search_0"));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
jest.clearAllMocks();
|
|
36
|
+
});
|
|
37
|
+
describe("Constructor", () => {
|
|
38
|
+
it("should create MockRunner with valid config", () => {
|
|
39
|
+
expect(mockRunner).toBeDefined();
|
|
40
|
+
expect(mockRunner).toBeInstanceOf(MockRunner_1.MockRunner);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("Config Validation", () => {
|
|
44
|
+
it("should validate correct config successfully", () => {
|
|
45
|
+
const validation = mockRunner.validateConfig();
|
|
46
|
+
expect(validation.success).toBe(true);
|
|
47
|
+
expect(validation.errors).toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
it("should detect validation errors in invalid config", () => {
|
|
50
|
+
expect(() => {
|
|
51
|
+
new MockRunner_1.MockRunner({
|
|
52
|
+
meta: { domain: "", version: "", flowId: "" },
|
|
53
|
+
transaction_data: {
|
|
54
|
+
transaction_id: "",
|
|
55
|
+
latest_timestamp: "",
|
|
56
|
+
bap_id: "",
|
|
57
|
+
bap_uri: "invalid-url",
|
|
58
|
+
bpp_id: "",
|
|
59
|
+
bpp_uri: "invalid-url",
|
|
60
|
+
},
|
|
61
|
+
steps: [],
|
|
62
|
+
transaction_history: [],
|
|
63
|
+
validationLib: "",
|
|
64
|
+
helperLib: "",
|
|
65
|
+
});
|
|
66
|
+
}).toThrow();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe("Generate Payload", () => {
|
|
70
|
+
it("should generate payload successfully", async () => {
|
|
71
|
+
const result = await mockRunner.runGeneratePayload("search_0", {
|
|
72
|
+
category: "Electronics",
|
|
73
|
+
});
|
|
74
|
+
expect(result).toBeDefined();
|
|
75
|
+
expect(result.success).toBe(true);
|
|
76
|
+
expect(result.result).toBeDefined();
|
|
77
|
+
expect(result.timestamp).toBeDefined();
|
|
78
|
+
expect(typeof result.executionTime).toBe("number");
|
|
79
|
+
});
|
|
80
|
+
it("should fail with non-existent action ID", async () => {
|
|
81
|
+
const result = await mockRunner.runGeneratePayload("non-existent", {});
|
|
82
|
+
expect(result.success).toBe(false);
|
|
83
|
+
expect(result.error).toBeDefined();
|
|
84
|
+
expect(result.error.message).toContain("not found");
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe("Validate Payload", () => {
|
|
88
|
+
it("should validate payload successfully", async () => {
|
|
89
|
+
const targetPayload = {
|
|
90
|
+
context: { domain: "retail", action: "search" },
|
|
91
|
+
message: {
|
|
92
|
+
intent: { category: { descriptor: { name: "Electronics" } } },
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
const result = await mockRunner.runValidatePayload("search_0", targetPayload);
|
|
96
|
+
expect(result).toBeDefined();
|
|
97
|
+
expect(result.success).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
it("should fail validation with invalid action ID", async () => {
|
|
100
|
+
const result = await mockRunner.runValidatePayload("invalid-id", {});
|
|
101
|
+
expect(result.success).toBe(false);
|
|
102
|
+
expect(result.error).toBeDefined();
|
|
103
|
+
expect(result.error.name).toBe("PayloadValidationError");
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe("Context Generation", () => {
|
|
107
|
+
it("should generate context for search action (v2.0)", () => {
|
|
108
|
+
const context = mockRunner.generateContext("search_0", "search");
|
|
109
|
+
expect(context).toBeDefined();
|
|
110
|
+
expect(context.domain).toBe("ONDC:TRV14");
|
|
111
|
+
expect(context.action).toBe("search");
|
|
112
|
+
expect(context.transaction_id).toBe("e9e0b5cb-3f15-48a1-9d86-d4d643f0909d");
|
|
113
|
+
expect(context.message_id).toBeDefined();
|
|
114
|
+
expect(context.timestamp).toBeDefined();
|
|
115
|
+
expect(context.bap_id).toBe("");
|
|
116
|
+
expect(context.bap_uri).toBe("");
|
|
117
|
+
// v2.0 specific fields
|
|
118
|
+
expect(context.version).toBe("2.0.0");
|
|
119
|
+
expect(context.location).toBeDefined();
|
|
120
|
+
expect(context.location.country.code).toBe("IND");
|
|
121
|
+
// Search action should not have BPP details
|
|
122
|
+
expect(context.bpp_id).toBeUndefined();
|
|
123
|
+
expect(context.bpp_uri).toBeUndefined();
|
|
124
|
+
});
|
|
125
|
+
it("should generate context for non-search action", () => {
|
|
126
|
+
// Add a select step first
|
|
127
|
+
mockConfig.steps.push(mockRunner.getDefaultStep("select", "select_0"));
|
|
128
|
+
const context = mockRunner.generateContext("select_0", "select");
|
|
129
|
+
expect(context.bpp_id).toBe("");
|
|
130
|
+
expect(context.bpp_uri).toBe("");
|
|
131
|
+
expect(context.action).toBe("select");
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe("Session Data Management", () => {
|
|
135
|
+
it("should extract session data correctly", () => {
|
|
136
|
+
const sessionData = mockRunner.getSessionDataUpToStep(0, mockConfig);
|
|
137
|
+
expect(sessionData).toEqual({});
|
|
138
|
+
});
|
|
139
|
+
it("should handle invalid step indices", () => {
|
|
140
|
+
const sessionData = mockRunner.getSessionDataUpToStep(-1, mockConfig);
|
|
141
|
+
expect(sessionData).toEqual({});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.v4 = void 0;
|
|
4
|
+
// Mock implementation of uuid for Jest tests
|
|
5
|
+
exports.v4 = jest.fn(() => "mocked-uuid-v4-12345678-1234-1234-1234-123456789012");
|
|
6
|
+
exports.default = {
|
|
7
|
+
v4: exports.v4,
|
|
8
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Jest setup file for ONDC Mock Runner tests
|
|
4
|
+
*/
|
|
5
|
+
// Global test configuration
|
|
6
|
+
beforeAll(() => {
|
|
7
|
+
// Set up any global test configuration
|
|
8
|
+
process.env.NODE_ENV = "test";
|
|
9
|
+
});
|
|
10
|
+
afterAll(() => {
|
|
11
|
+
// Clean up after all tests
|
|
12
|
+
});
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
// Reset any mocks or state before each test
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
// Clean up after each test
|
|
19
|
+
});
|
|
20
|
+
// Extend Jest matchers
|
|
21
|
+
expect.extend({
|
|
22
|
+
toBeValidExecutionResult(received) {
|
|
23
|
+
const pass = received &&
|
|
24
|
+
typeof received === "object" &&
|
|
25
|
+
typeof received.success === "boolean" &&
|
|
26
|
+
typeof received.timestamp === "string" &&
|
|
27
|
+
Array.isArray(received.logs) &&
|
|
28
|
+
received.validation &&
|
|
29
|
+
typeof received.validation.isValid === "boolean";
|
|
30
|
+
if (pass) {
|
|
31
|
+
return {
|
|
32
|
+
message: () => `Expected ${received} not to be a valid ExecutionResult`,
|
|
33
|
+
pass: true,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return {
|
|
38
|
+
message: () => `Expected ${received} to be a valid ExecutionResult`,
|
|
39
|
+
pass: false,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
});
|