@ondc/automation-mock-runner 1.3.48 → 1.3.50

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.
@@ -15,9 +15,12 @@ export declare class CodeValidator {
15
15
  * Validate that return statements match expected structure
16
16
  */
17
17
  /**
18
- * Collect ReturnStatement arguments belonging only to the outer (top-level)
19
- * function. Nested function/arrow bodies are skipped their returns are
20
- * not the function's contract and would otherwise produce false positives.
18
+ * Collect ReturnStatement arguments belonging only to the target function.
19
+ * When `targetFnName` is given and a top-level `function <name>(...)` exists,
20
+ * we walk only that body sibling helpers like `function x(){return "hi"}`
21
+ * declared next to `validate` are ignored. Returns inside nested
22
+ * functions/arrows are also skipped (they aren't the contract).
23
+ * Falls back to the whole AST when no named match is found.
21
24
  */
22
25
  private static collectTopLevelReturns;
23
26
  private static validateReturnStructure;
@@ -139,7 +139,7 @@ class CodeValidator {
139
139
  });
140
140
  // 5. Validate return type structure (for validate and meetsRequirements)
141
141
  if (schema.returnType.properties) {
142
- const returnValidation = this.validateReturnStructure(ast, schema.returnType.properties);
142
+ const returnValidation = this.validateReturnStructure(ast, schema.returnType.properties, schema.name);
143
143
  errors.push(...returnValidation);
144
144
  }
145
145
  // 6. Check for best practices
@@ -172,24 +172,27 @@ class CodeValidator {
172
172
  * Validate that return statements match expected structure
173
173
  */
174
174
  /**
175
- * Collect ReturnStatement arguments belonging only to the outer (top-level)
176
- * function. Nested function/arrow bodies are skipped their returns are
177
- * not the function's contract and would otherwise produce false positives.
175
+ * Collect ReturnStatement arguments belonging only to the target function.
176
+ * When `targetFnName` is given and a top-level `function <name>(...)` exists,
177
+ * we walk only that body sibling helpers like `function x(){return "hi"}`
178
+ * declared next to `validate` are ignored. Returns inside nested
179
+ * functions/arrows are also skipped (they aren't the contract).
180
+ * Falls back to the whole AST when no named match is found.
178
181
  */
179
- static collectTopLevelReturns(ast) {
182
+ static collectTopLevelReturns(ast, targetFnName) {
180
183
  const returns = [];
181
- let depth = 0;
182
- const enterFn = (node, _st, c) => {
183
- if (depth === 0) {
184
- depth++;
185
- c(node.body, null);
186
- depth--;
187
- }
188
- };
189
- walk.recursive(ast, null, {
190
- FunctionDeclaration: enterFn,
191
- FunctionExpression: enterFn,
192
- ArrowFunctionExpression: enterFn,
184
+ const programBody = Array.isArray(ast.body)
185
+ ? ast.body
186
+ : [];
187
+ const targetFn = targetFnName
188
+ ? programBody.find((n) => n.type === "FunctionDeclaration" && n.id?.name === targetFnName)
189
+ : undefined;
190
+ const walkRoot = targetFn ? targetFn.body : ast;
191
+ const skip = () => { };
192
+ walk.recursive(walkRoot, null, {
193
+ FunctionDeclaration: skip,
194
+ FunctionExpression: skip,
195
+ ArrowFunctionExpression: skip,
193
196
  ReturnStatement(node) {
194
197
  if (node.argument)
195
198
  returns.push(node.argument);
@@ -197,35 +200,60 @@ class CodeValidator {
197
200
  });
198
201
  return returns;
199
202
  }
200
- static validateReturnStructure(ast, expectedProperties) {
203
+ static validateReturnStructure(ast, expectedProperties, targetFnName) {
201
204
  const warnings = [];
202
- const foundReturns = this.collectTopLevelReturns(ast);
205
+ const foundReturns = this.collectTopLevelReturns(ast, targetFnName);
203
206
  // Check if we have return statements
204
207
  if (foundReturns.length === 0) {
205
208
  warnings.push(`Function should return an object with properties: ${Object.keys(expectedProperties).join(", ")}`);
206
209
  return warnings;
207
210
  }
208
- // Check the structure of returned objects
209
- foundReturns.forEach((returnArg) => {
210
- if (returnArg.type === "ObjectExpression") {
211
- const returnedProps = new Set(returnArg.properties.map((p) => p.key.name));
212
- const expectedProps = Object.keys(expectedProperties);
213
- // Check for missing properties
214
- expectedProps.forEach((prop) => {
215
- if (!returnedProps.has(prop)) {
216
- warnings.push(`Return object is missing property '${prop}' (expected: ${expectedProperties[prop].type})`);
217
- }
218
- });
219
- // Check for extra properties
220
- returnedProps.forEach((prop) => {
221
- if (!expectedProps.includes(prop)) {
222
- warnings.push(`Return object has unexpected property '${prop}'`);
223
- }
224
- });
225
- }
226
- else {
227
- warnings.push(`Function should return an object literal with properties: ${Object.keys(expectedProperties).join(", ")}`);
211
+ // Flatten conditional/logical/sequence/parenthesized wrappers so that
212
+ // minifier output like `return cond ? {a:1} : {a:2}` or `return x && {...}`
213
+ // is treated as the set of object-literal branches it actually returns.
214
+ const objectBranches = [];
215
+ const stack = [...foundReturns];
216
+ let nonObjectFound = false;
217
+ while (stack.length) {
218
+ const node = stack.pop();
219
+ switch (node.type) {
220
+ case "ObjectExpression":
221
+ objectBranches.push(node);
222
+ break;
223
+ case "ConditionalExpression":
224
+ stack.push(node.consequent, node.alternate);
225
+ break;
226
+ case "LogicalExpression":
227
+ stack.push(node.left, node.right);
228
+ break;
229
+ case "SequenceExpression":
230
+ stack.push(node.expressions[node.expressions.length - 1]);
231
+ break;
232
+ case "ParenthesizedExpression":
233
+ stack.push(node.expression);
234
+ break;
235
+ default:
236
+ nonObjectFound = true;
228
237
  }
238
+ }
239
+ if (objectBranches.length === 0 || nonObjectFound) {
240
+ warnings.push(`Function should return an object literal with properties: ${Object.keys(expectedProperties).join(", ")}`);
241
+ }
242
+ objectBranches.forEach((returnArg) => {
243
+ const returnedProps = new Set(returnArg.properties.map((p) => p.key.name));
244
+ const expectedProps = Object.keys(expectedProperties);
245
+ // Check for missing properties
246
+ expectedProps.forEach((prop) => {
247
+ if (!returnedProps.has(prop)) {
248
+ warnings.push(`Return object is missing property '${prop}' (expected: ${expectedProperties[prop].type})`);
249
+ }
250
+ });
251
+ // Check for extra properties
252
+ returnedProps.forEach((prop) => {
253
+ if (!expectedProps.includes(prop)) {
254
+ warnings.push(`Return object has unexpected property '${prop}'`);
255
+ }
256
+ });
229
257
  });
230
258
  return warnings;
231
259
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const code_validator_1 = require("../lib/validators/code-validator");
4
4
  const function_registry_1 = require("../lib/constants/function-registry");
5
5
  const validateSchema = (0, function_registry_1.getFunctionSchema)("validate");
6
+ const meetsRequirementsSchema = (0, function_registry_1.getFunctionSchema)("meetsRequirements");
6
7
  describe("CodeValidator.validate — return structure", () => {
7
8
  it("accepts an outer return with the full expected shape", () => {
8
9
  const code = `
@@ -57,6 +58,40 @@ describe("CodeValidator.validate — return structure", () => {
57
58
  expect(result.isValid).toBe(false);
58
59
  expect(result.errors.some((e) => e.includes("Function should return an object literal"))).toBe(true);
59
60
  });
61
+ it("accepts a minified conditional return: return i ? {...} : {...}", () => {
62
+ const code = `function validate(i,d){return i?{valid:!0,code:200,description:"Valid request"}:{valid:!1,code:200,description:"oh no"}}`;
63
+ const result = code_validator_1.CodeValidator.validate(code, validateSchema);
64
+ expect(result.errors).toEqual([]);
65
+ expect(result.isValid).toBe(true);
66
+ });
67
+ it("ignores sibling top-level helper function returns (validate)", () => {
68
+ const code = `
69
+ function x() {
70
+ return "hello";
71
+ }
72
+ function validate(targetPayload, sessionData) {
73
+ let some = x();
74
+ return { valid: true, code: 200, description: "Valid request" };
75
+ }
76
+ `;
77
+ const result = code_validator_1.CodeValidator.validate(code, validateSchema);
78
+ expect(result.errors).toEqual([]);
79
+ expect(result.isValid).toBe(true);
80
+ });
81
+ it("ignores sibling top-level helper function returns (meetsRequirements)", () => {
82
+ const code = `
83
+ function helper() {
84
+ return 123;
85
+ }
86
+ function meetsRequirements(sessionData) {
87
+ const n = helper();
88
+ return { valid: true, code: 200, description: "Requirements met" };
89
+ }
90
+ `;
91
+ const result = code_validator_1.CodeValidator.validate(code, meetsRequirementsSchema);
92
+ expect(result.errors).toEqual([]);
93
+ expect(result.isValid).toBe(true);
94
+ });
60
95
  it("warns when only a nested helper returns and the outer function has no return", () => {
61
96
  const code = `
62
97
  function validate(targetPayload, sessionData) {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const MockRunner_1 = require("../lib/MockRunner");
4
+ describe("scratch: user-supplied validate fn", () => {
5
+ afterEach(async () => {
6
+ const shared = MockRunner_1.MockRunner.sharedRunner;
7
+ if (shared?.terminate)
8
+ await shared.terminate();
9
+ MockRunner_1.MockRunner.sharedRunner = undefined;
10
+ });
11
+ it("runs the user's validate() against a payload and a null payload", async () => {
12
+ const validateSrc = `function validate(targetPayload, sessionData) {
13
+ if(!targetPayload){
14
+ return {valid: false, code: 200, description: "oh no"};
15
+ }
16
+ return { valid: true, code: 200, description: "Valid request" };
17
+ }`;
18
+ const config = {
19
+ meta: { domain: "ONDC:TRV14", version: "2.0.0", flowId: "testing" },
20
+ transaction_data: {
21
+ transaction_id: "e9e0b5cb-3f15-48a1-9d86-d4d643f0909d",
22
+ latest_timestamp: "1970-01-01T00:00:00.000Z",
23
+ },
24
+ steps: [],
25
+ transaction_history: [],
26
+ validationLib: "",
27
+ helperLib: "",
28
+ };
29
+ const runner = new MockRunner_1.MockRunner(config);
30
+ const step = runner.getDefaultStep("search", "search_0");
31
+ step.mock.validate = MockRunner_1.MockRunner.encodeBase64(validateSrc);
32
+ runner.getConfig().steps.push(step);
33
+ const r1 = await runner.runValidatePayloadWithSession("search_0", { context: {} }, {});
34
+ console.log("WITH_PAYLOAD:", JSON.stringify(r1, null, 2));
35
+ const r2 = await runner.runValidatePayloadWithSession("search_0", null, {});
36
+ console.log("WITH_NULL:", JSON.stringify(r2, null, 2));
37
+ expect(r1.success).toBe(true);
38
+ expect(r2.success).toBe(true);
39
+ }, 30000);
40
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ondc/automation-mock-runner",
3
- "version": "1.3.48",
3
+ "version": "1.3.50",
4
4
  "description": "A TypeScript library for ONDC automation mock runner",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",