@ondc/automation-mock-runner 1.1.0 → 1.1.9

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.
@@ -80,7 +80,21 @@ class MockRunner {
80
80
  const context = this.generateContext(step.action_id, step.api);
81
81
  defaultPayload.context = context;
82
82
  const schema = (0, function_registry_1.getFunctionSchema)("generate");
83
- const result = await this.getRunnerInstance().execute(MockRunner.decodeBase64(step.mock.generate), schema, [defaultPayload, sessionData]);
83
+ const code = MockRunner.decodeBase64(step.mock.generate);
84
+ let helperLib = "";
85
+ try {
86
+ helperLib = MockRunner.decodeBase64(this.config.helperLib || "");
87
+ }
88
+ catch (e) {
89
+ this.logger.error("Failed to decode helper library", { actionId }, e);
90
+ helperLib = "";
91
+ }
92
+ // Combine helper library and main code
93
+ const fullCode = helperLib + "\n" + code;
94
+ const result = await this.getRunnerInstance().execute(fullCode, schema, [
95
+ defaultPayload,
96
+ sessionData,
97
+ ]);
84
98
  const executionTime = Date.now() - startTime;
85
99
  this.logger.logExecution(executionId, "Payload generation completed", {
86
100
  actionId,
@@ -25,9 +25,47 @@ function createInitialMockConfig(domain, version, flowId) {
25
25
  steps: [],
26
26
  transaction_history: [],
27
27
  validationLib: "",
28
- helperLib: "",
28
+ helperLib: MockRunner_1.MockRunner.encodeBase64(defaultHelpers),
29
29
  };
30
30
  }
31
+ const defaultHelpers = `/*
32
+ Custom helper functions available in all mock generation functions.
33
+ these are appended below the generate function for each step.
34
+ */
35
+
36
+ // Generates a UUID v4
37
+ function uuidv4() {
38
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
39
+ const r = Math.random() * 16 | 0;
40
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
41
+ return v.toString(16);
42
+ });
43
+ }
44
+
45
+ // Generate a 6 digit string ID
46
+ function generate6DigitId() {
47
+ return Math.floor(100000 + Math.random() * 900000).toString();
48
+ }
49
+
50
+ // Returns the current ISO timestamp
51
+ function currentTimestamp() {
52
+ return new Date().toISOString();
53
+ }
54
+
55
+ // Converts ISO 8601 duration string to total seconds
56
+ const isoDurToSec = (duration) => {
57
+ const durRE = /P((\d+)Y)?((\d+)M)?((\d+)W)?((\d+)D)?T?((\d+)H)?((\d+)M)?((\d+)S)?/;
58
+ const s = durRE.exec(duration);
59
+ if (!s) return 0;
60
+
61
+ return (Number(s?.[2]) || 0) * 31536000 +
62
+ (Number(s?.[4]) || 0) * 2628288 +
63
+ (Number(s?.[6]) || 0) * 604800 +
64
+ (Number(s?.[8]) || 0) * 86400 +
65
+ (Number(s?.[10]) || 0) * 3600 +
66
+ (Number(s?.[12]) || 0) * 60 +
67
+ (Number(s?.[14]) || 0);
68
+ };`;
31
69
  function convertToFlowConfig(config) {
32
70
  const flowConfig = {};
33
71
  flowConfig.id = config.meta.flowId;
@@ -25,7 +25,7 @@ exports.FUNCTION_REGISTRY = {
25
25
  description: "The generated payload object to be sent in the API request",
26
26
  },
27
27
  description: "Generates the mock payload for an API call",
28
- timeout: 60 * 1000,
28
+ timeout: 35 * 1000,
29
29
  defaultBody: ` return defaultPayload;`,
30
30
  template: (body) => `/**
31
31
  * Generates the mock payload for an API call in the transaction flow.
@@ -188,4 +188,430 @@ describe("MockRunner", () => {
188
188
  }
189
189
  }, 10000); // Set Jest timeout to 10 seconds to allow for the timeout to occur
190
190
  });
191
+ describe("Helper Library Functionality", () => {
192
+ it("should successfully use helper library functions in generate code", async () => {
193
+ // Create a helper library with utility functions
194
+ const helperLib = MockRunner_1.MockRunner.encodeBase64(`
195
+ function formatCurrency(amount) {
196
+ return "₹" + amount.toFixed(2);
197
+ }
198
+
199
+ function calculateDiscount(price, discountPercent) {
200
+ return price - (price * discountPercent / 100);
201
+ }
202
+ `);
203
+ // Create generate code that uses helper library functions
204
+ const generateCode = MockRunner_1.MockRunner.encodeBase64(`
205
+ async function generate(payload, sessionData) {
206
+ const originalPrice = 1000;
207
+ const discountedPrice = calculateDiscount(originalPrice, 10);
208
+
209
+ payload.message = {
210
+ price: formatCurrency(originalPrice),
211
+ discountedPrice: formatCurrency(discountedPrice),
212
+ savings: formatCurrency(originalPrice - discountedPrice)
213
+ };
214
+
215
+ return payload;
216
+ }
217
+ `);
218
+ // Create config with helper library
219
+ const configWithHelper = {
220
+ meta: {
221
+ domain: "ONDC:RET10",
222
+ version: "2.0.0",
223
+ flowId: "helper-test",
224
+ },
225
+ transaction_data: {
226
+ transaction_id: "test-txn-123",
227
+ latest_timestamp: "2024-01-01T00:00:00.000Z",
228
+ bap_id: "test-bap",
229
+ bap_uri: "https://test-bap.com",
230
+ },
231
+ steps: [
232
+ {
233
+ api: "select",
234
+ action_id: "select_0",
235
+ owner: "BAP",
236
+ responseFor: null,
237
+ unsolicited: false,
238
+ description: "Test helper library",
239
+ mock: {
240
+ generate: generateCode,
241
+ validate: MockRunner_1.MockRunner.encodeBase64("async function validate(payload, sessionData) { return { isValid: true, errors: [], warnings: [] }; }"),
242
+ requirements: MockRunner_1.MockRunner.encodeBase64("async function meetsRequirements(sessionData) { return { meets: true, reasons: [] }; }"),
243
+ defaultPayload: {
244
+ context: {},
245
+ message: {},
246
+ },
247
+ saveData: {},
248
+ inputs: {},
249
+ },
250
+ },
251
+ ],
252
+ transaction_history: [],
253
+ validationLib: "",
254
+ helperLib: helperLib,
255
+ };
256
+ const runner = new MockRunner_1.MockRunner(configWithHelper, true);
257
+ const result = await runner.runGeneratePayload("select_0", {});
258
+ expect(result.success).toBe(true);
259
+ expect(result.result).toBeDefined();
260
+ expect(result.result.message).toBeDefined();
261
+ expect(result.result.message.price).toBe("₹1000.00");
262
+ expect(result.result.message.discountedPrice).toBe("₹900.00");
263
+ expect(result.result.message.savings).toBe("₹100.00");
264
+ });
265
+ it("should work correctly when helper library is empty string", async () => {
266
+ const generateCode = MockRunner_1.MockRunner.encodeBase64(`
267
+ async function generate(payload, sessionData) {
268
+ payload.message = { test: "works without helper" };
269
+ return payload;
270
+ }
271
+ `);
272
+ const configNoHelper = {
273
+ meta: {
274
+ domain: "ONDC:RET10",
275
+ version: "2.0.0",
276
+ flowId: "no-helper-test",
277
+ },
278
+ transaction_data: {
279
+ transaction_id: "test-txn-456",
280
+ latest_timestamp: "2024-01-01T00:00:00.000Z",
281
+ },
282
+ steps: [
283
+ {
284
+ api: "search",
285
+ action_id: "search_0",
286
+ owner: "BAP",
287
+ responseFor: null,
288
+ unsolicited: false,
289
+ description: "Test without helper library",
290
+ mock: {
291
+ generate: generateCode,
292
+ validate: MockRunner_1.MockRunner.encodeBase64("async function validate(payload, sessionData) { return { isValid: true, errors: [], warnings: [] }; }"),
293
+ requirements: MockRunner_1.MockRunner.encodeBase64("async function meetsRequirements(sessionData) { return { meets: true, reasons: [] }; }"),
294
+ defaultPayload: {
295
+ context: {},
296
+ message: {},
297
+ },
298
+ saveData: {},
299
+ inputs: {},
300
+ },
301
+ },
302
+ ],
303
+ transaction_history: [],
304
+ validationLib: "",
305
+ helperLib: "",
306
+ };
307
+ const runner = new MockRunner_1.MockRunner(configNoHelper, true);
308
+ const result = await runner.runGeneratePayload("search_0", {});
309
+ expect(result.success).toBe(true);
310
+ expect(result.result).toBeDefined();
311
+ expect(result.result.message.test).toBe("works without helper");
312
+ });
313
+ it("should work correctly when helper library is null or undefined in config", async () => {
314
+ const generateCode = MockRunner_1.MockRunner.encodeBase64(`
315
+ async function generate(payload, sessionData) {
316
+ payload.message = { test: "works with null helper" };
317
+ return payload;
318
+ }
319
+ `);
320
+ // Test with empty string (same as undefined behavior)
321
+ const configNullHelper = {
322
+ meta: {
323
+ domain: "ONDC:RET10",
324
+ version: "2.0.0",
325
+ flowId: "null-helper-test",
326
+ },
327
+ transaction_data: {
328
+ transaction_id: "test-txn-789",
329
+ latest_timestamp: "2024-01-01T00:00:00.000Z",
330
+ },
331
+ steps: [
332
+ {
333
+ api: "search",
334
+ action_id: "search_0",
335
+ owner: "BAP",
336
+ responseFor: null,
337
+ unsolicited: false,
338
+ description: "Test with null/undefined helper library",
339
+ mock: {
340
+ generate: generateCode,
341
+ validate: MockRunner_1.MockRunner.encodeBase64("async function validate(payload, sessionData) { return { isValid: true, errors: [], warnings: [] }; }"),
342
+ requirements: MockRunner_1.MockRunner.encodeBase64("async function meetsRequirements(sessionData) { return { meets: true, reasons: [] }; }"),
343
+ defaultPayload: {
344
+ context: {},
345
+ message: {},
346
+ },
347
+ saveData: {},
348
+ inputs: {},
349
+ },
350
+ },
351
+ ],
352
+ transaction_history: [],
353
+ validationLib: "",
354
+ helperLib: "",
355
+ };
356
+ const runner = new MockRunner_1.MockRunner(configNullHelper, true);
357
+ const result = await runner.runGeneratePayload("search_0", {});
358
+ expect(result.success).toBe(true);
359
+ expect(result.result).toBeDefined();
360
+ expect(result.result.message.test).toBe("works with null helper");
361
+ });
362
+ it("should handle invalid base64 in helper library gracefully", async () => {
363
+ const generateCode = MockRunner_1.MockRunner.encodeBase64(`
364
+ async function generate(payload, sessionData) {
365
+ payload.message = { test: "should still work" };
366
+ return payload;
367
+ }
368
+ `);
369
+ const configInvalidHelper = {
370
+ meta: {
371
+ domain: "ONDC:RET10",
372
+ version: "2.0.0",
373
+ flowId: "invalid-helper-test",
374
+ },
375
+ transaction_data: {
376
+ transaction_id: "test-txn-invalid",
377
+ latest_timestamp: "2024-01-01T00:00:00.000Z",
378
+ },
379
+ steps: [
380
+ {
381
+ api: "search",
382
+ action_id: "search_0",
383
+ owner: "BAP",
384
+ responseFor: null,
385
+ unsolicited: false,
386
+ description: "Test with invalid helper library",
387
+ mock: {
388
+ generate: generateCode,
389
+ validate: MockRunner_1.MockRunner.encodeBase64("async function validate(payload, sessionData) { return { isValid: true, errors: [], warnings: [] }; }"),
390
+ requirements: MockRunner_1.MockRunner.encodeBase64("async function meetsRequirements(sessionData) { return { meets: true, reasons: [] }; }"),
391
+ defaultPayload: {
392
+ context: {},
393
+ message: {},
394
+ },
395
+ saveData: {},
396
+ inputs: {},
397
+ },
398
+ },
399
+ ],
400
+ transaction_history: [],
401
+ validationLib: "",
402
+ helperLib: "invalid-base64-@#$%",
403
+ };
404
+ const runner = new MockRunner_1.MockRunner(configInvalidHelper, true);
405
+ const result = await runner.runGeneratePayload("search_0", {});
406
+ // Should still execute successfully with empty helper lib
407
+ expect(result.success).toBe(true);
408
+ expect(result.result).toBeDefined();
409
+ expect(result.result.message.test).toBe("should still work");
410
+ });
411
+ it("should combine helper library with multiple utility functions", async () => {
412
+ // Helper library with multiple utilities
413
+ const helperLib = MockRunner_1.MockRunner.encodeBase64(`
414
+ const CONSTANTS = {
415
+ TAX_RATE: 0.18,
416
+ SHIPPING_COST: 50
417
+ };
418
+
419
+ function calculateTax(amount) {
420
+ return amount * CONSTANTS.TAX_RATE;
421
+ }
422
+
423
+ function calculateTotal(subtotal) {
424
+ const tax = calculateTax(subtotal);
425
+ return subtotal + tax + CONSTANTS.SHIPPING_COST;
426
+ }
427
+
428
+ function formatDate(dateStr) {
429
+ return new Date(dateStr).toLocaleDateString('en-IN');
430
+ }
431
+ `);
432
+ const generateCode = MockRunner_1.MockRunner.encodeBase64(`
433
+ async function generate(payload, sessionData) {
434
+ const subtotal = 1000;
435
+ const tax = calculateTax(subtotal);
436
+ const total = calculateTotal(subtotal);
437
+
438
+ payload.message = {
439
+ subtotal: subtotal,
440
+ tax: tax,
441
+ shipping: 50,
442
+ total: total,
443
+ orderDate: formatDate(payload.context.timestamp)
444
+ };
445
+
446
+ return payload;
447
+ }
448
+ `);
449
+ const config = {
450
+ meta: {
451
+ domain: "ONDC:RET10",
452
+ version: "2.0.0",
453
+ flowId: "multi-helper-test",
454
+ },
455
+ transaction_data: {
456
+ transaction_id: "test-txn-multi",
457
+ latest_timestamp: "2024-01-15T00:00:00.000Z",
458
+ },
459
+ steps: [
460
+ {
461
+ api: "init",
462
+ action_id: "init_0",
463
+ owner: "BAP",
464
+ responseFor: null,
465
+ unsolicited: false,
466
+ description: "Test multiple helper functions",
467
+ mock: {
468
+ generate: generateCode,
469
+ validate: MockRunner_1.MockRunner.encodeBase64("async function validate(payload, sessionData) { return { isValid: true, errors: [], warnings: [] }; }"),
470
+ requirements: MockRunner_1.MockRunner.encodeBase64("async function meetsRequirements(sessionData) { return { meets: true, reasons: [] }; }"),
471
+ defaultPayload: {
472
+ context: {},
473
+ message: {},
474
+ },
475
+ saveData: {},
476
+ inputs: {},
477
+ },
478
+ },
479
+ ],
480
+ transaction_history: [],
481
+ validationLib: "",
482
+ helperLib: helperLib,
483
+ };
484
+ const runner = new MockRunner_1.MockRunner(config, true);
485
+ const result = await runner.runGeneratePayload("init_0", {});
486
+ expect(result.success).toBe(true);
487
+ expect(result.result.message).toBeDefined();
488
+ expect(result.result.message.subtotal).toBe(1000);
489
+ expect(result.result.message.tax).toBe(180); // 18% of 1000
490
+ expect(result.result.message.shipping).toBe(50);
491
+ expect(result.result.message.total).toBe(1230); // 1000 + 180 + 50
492
+ expect(result.result.message.orderDate).toBeDefined();
493
+ });
494
+ it("should handle syntax errors in helper library", async () => {
495
+ // Helper library with syntax error
496
+ const helperLibWithError = MockRunner_1.MockRunner.encodeBase64(`
497
+ function brokenFunction( {
498
+ // Missing closing parenthesis
499
+ return "broken";
500
+ }
501
+ `);
502
+ const generateCode = MockRunner_1.MockRunner.encodeBase64(`
503
+ async function generate(payload, sessionData) {
504
+ payload.message = { test: "value" };
505
+ return payload;
506
+ }
507
+ `);
508
+ const config = {
509
+ meta: {
510
+ domain: "ONDC:RET10",
511
+ version: "2.0.0",
512
+ flowId: "error-helper-test",
513
+ },
514
+ transaction_data: {
515
+ transaction_id: "test-txn-error",
516
+ latest_timestamp: "2024-01-01T00:00:00.000Z",
517
+ },
518
+ steps: [
519
+ {
520
+ api: "search",
521
+ action_id: "search_0",
522
+ owner: "BAP",
523
+ responseFor: null,
524
+ unsolicited: false,
525
+ description: "Test helper library with syntax error",
526
+ mock: {
527
+ generate: generateCode,
528
+ validate: MockRunner_1.MockRunner.encodeBase64("async function validate(payload, sessionData) { return { isValid: true, errors: [], warnings: [] }; }"),
529
+ requirements: MockRunner_1.MockRunner.encodeBase64("async function meetsRequirements(sessionData) { return { meets: true, reasons: [] }; }"),
530
+ defaultPayload: {
531
+ context: {},
532
+ message: {},
533
+ },
534
+ saveData: {},
535
+ inputs: {},
536
+ },
537
+ },
538
+ ],
539
+ transaction_history: [],
540
+ validationLib: "",
541
+ helperLib: helperLibWithError,
542
+ };
543
+ const runner = new MockRunner_1.MockRunner(config, true);
544
+ const result = await runner.runGeneratePayload("search_0", {});
545
+ // Should fail due to syntax error in combined code
546
+ expect(result.success).toBe(false);
547
+ expect(result.error).toBeDefined();
548
+ });
549
+ it("should allow helper library to access sessionData through generate function", async () => {
550
+ const helperLib = MockRunner_1.MockRunner.encodeBase64(`
551
+ function getUserDiscount(userType) {
552
+ const discounts = {
553
+ 'premium': 20,
554
+ 'regular': 10,
555
+ 'new': 5
556
+ };
557
+ return discounts[userType] || 0;
558
+ }
559
+ `);
560
+ const generateCode = MockRunner_1.MockRunner.encodeBase64(`
561
+ async function generate(payload, sessionData) {
562
+ const userType = sessionData.user_inputs?.userType || 'new';
563
+ const discount = getUserDiscount(userType);
564
+
565
+ payload.message = {
566
+ userType: userType,
567
+ discountPercent: discount
568
+ };
569
+
570
+ return payload;
571
+ }
572
+ `);
573
+ const config = {
574
+ meta: {
575
+ domain: "ONDC:RET10",
576
+ version: "2.0.0",
577
+ flowId: "session-helper-test",
578
+ },
579
+ transaction_data: {
580
+ transaction_id: "test-txn-session",
581
+ latest_timestamp: "2024-01-01T00:00:00.000Z",
582
+ },
583
+ steps: [
584
+ {
585
+ api: "select",
586
+ action_id: "select_0",
587
+ owner: "BAP",
588
+ responseFor: null,
589
+ unsolicited: false,
590
+ description: "Test helper with session data",
591
+ mock: {
592
+ generate: generateCode,
593
+ validate: MockRunner_1.MockRunner.encodeBase64("async function validate(payload, sessionData) { return { isValid: true, errors: [], warnings: [] }; }"),
594
+ requirements: MockRunner_1.MockRunner.encodeBase64("async function meetsRequirements(sessionData) { return { meets: true, reasons: [] }; }"),
595
+ defaultPayload: {
596
+ context: {},
597
+ message: {},
598
+ },
599
+ saveData: {},
600
+ inputs: {},
601
+ },
602
+ },
603
+ ],
604
+ transaction_history: [],
605
+ validationLib: "",
606
+ helperLib: helperLib,
607
+ };
608
+ const runner = new MockRunner_1.MockRunner(config, true);
609
+ const result = await runner.runGeneratePayload("select_0", {
610
+ userType: "premium",
611
+ });
612
+ expect(result.success).toBe(true);
613
+ expect(result.result.message.userType).toBe("premium");
614
+ expect(result.result.message.discountPercent).toBe(20);
615
+ });
616
+ });
191
617
  });
@@ -460,7 +460,6 @@ describe("configHelper", () => {
460
460
  expect(result.steps).toEqual([]);
461
461
  expect(result.transaction_history).toEqual([]);
462
462
  expect(result.validationLib).toBe("");
463
- expect(result.helperLib).toBe("");
464
463
  });
465
464
  it("should handle different domain formats", () => {
466
465
  const result1 = (0, configHelper_1.createInitialMockConfig)("ONDC:RET10", "1.0.0", "test");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ondc/automation-mock-runner",
3
- "version": "1.1.0",
3
+ "version": "1.1.9",
4
4
  "description": "A TypeScript library for ONDC automation mock runner",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -108,8 +108,8 @@ function createSandbox() {
108
108
  decodeURIComponent,
109
109
  // Utility functions for ONDC operations
110
110
  setTimeout: (fn, delay) => {
111
- if (delay < 1 || delay > 1000) {
112
- throw new Error("Timeout must be between 1-1000ms");
111
+ if (delay < 1 || delay > 35 * 1000) {
112
+ throw new Error("Timeout must be between 1-35000ms");
113
113
  }
114
114
  return setTimeout(fn, delay);
115
115
  },
@@ -151,7 +151,7 @@ parentPort?.on("message", async (message) => {
151
151
 
152
152
  // Execute the script
153
153
  script.runInContext(context, {
154
- timeout: timeout || 5000,
154
+ timeout: timeout || 35000,
155
155
  breakOnSigint: true,
156
156
  });
157
157
 
@@ -168,8 +168,8 @@ parentPort?.on("message", async (message) => {
168
168
  new Promise((_, reject) =>
169
169
  setTimeout(
170
170
  () => reject(new Error("Function execution timeout")),
171
- timeout || 5000
172
- )
171
+ timeout || 5000,
172
+ ),
173
173
  ),
174
174
  ]);
175
175