make-mp-data 1.4.5 → 1.5.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.
@@ -0,0 +1,619 @@
1
+ const dayjs = require("dayjs");
2
+ const utc = require("dayjs/plugin/utc");
3
+ const fs = require('fs');
4
+ const u = require('ak-tools');
5
+ dayjs.extend(utc);
6
+ require('dotenv').config();
7
+ const path = require('path');
8
+
9
+ /** @typedef {import('../types.js').Config} Config */
10
+ /** @typedef {import('../types.js').EventConfig} EventConfig */
11
+ /** @typedef {import("../types.js").EventSchema} EventSchema */
12
+ /** @typedef {import('../types.js').ValueValid} ValueValid */
13
+ /** @typedef {import('../types.js').HookedArray} hookArray */
14
+ /** @typedef {import('../types.js').hookArrayOptions} hookArrayOptions */
15
+ /** @typedef {import('../types.js').Person} Person */
16
+ /** @typedef {import('../types.js').Funnel} Funnel */
17
+ /** @typedef {import('../types.js').UserProfile} UserProfile */
18
+ /** @typedef {import('../types.js').SCDSchema} SCDSchema */
19
+ /** @typedef {import('../types.js').Storage} Storage */
20
+
21
+ const MAIN = require('../index.js');
22
+ const { generators, orchestrators, meta } = MAIN;
23
+ const { makeAdSpend, makeEvent, makeFunnel, makeProfile, makeSCD, makeMirror } = generators;
24
+ const { sendToMixpanel, userLoop, validateDungeonConfig } = orchestrators;
25
+ const { hookArray, inferFunnels } = meta;
26
+ const { validEvent } = require('../components/utils.js');
27
+
28
+
29
+ // Mock the global variables
30
+ let CAMPAIGNS;
31
+ let DEFAULTS;
32
+ let STORAGE;
33
+ let CONFIG;
34
+ const { campaigns, devices, locations } = require('../components/defaults.js');
35
+
36
+ beforeEach(async () => {
37
+ // Reset global variables before each test
38
+ CAMPAIGNS = [
39
+ { utm_campaign: ["campaign1", "campaign2"], utm_source: ["source1"], utm_medium: ["medium1"], utm_content: ["content1"], utm_term: ["term1"] }
40
+ ];
41
+ DEFAULTS = {
42
+ locationsUsers: () => ({ city: 'San Francisco' }),
43
+ locationsEvents: () => ({ city: 'San Francisco' }),
44
+ iOSDevices: () => 'iPhone',
45
+ androidDevices: () => 'Android',
46
+ desktopDevices: () => 'Desktop',
47
+ browsers: () => 'Chrome',
48
+ campaigns: () => 'campaign1'
49
+ };
50
+
51
+ /** @type {Storage} */
52
+ STORAGE = {
53
+ eventData: await hookArray([], {}),
54
+ userProfilesData: await hookArray([], {}),
55
+ adSpendData: await hookArray([], {}),
56
+ scdTableData: [await hookArray([], {})],
57
+ groupProfilesData: await hookArray([], {}),
58
+ lookupTableData: await hookArray([], {}),
59
+ mirrorEventData: await hookArray([], {})
60
+ };
61
+
62
+
63
+ /** @type {Config} */
64
+ CONFIG = {
65
+ numUsers: 10,
66
+ numEvents: 100,
67
+ numDays: 30,
68
+ simulationName: 'TestSimulation',
69
+ hook: (record) => record
70
+ };
71
+ global.CAMPAIGNS = CAMPAIGNS;
72
+ global.DEFAULTS = DEFAULTS;
73
+ global.STORAGE = STORAGE;
74
+ global.CONFIG = CONFIG;
75
+ const FIXED_NOW = dayjs('2024-02-02').unix();
76
+ global.FIXED_NOW = FIXED_NOW;
77
+
78
+ });
79
+
80
+ beforeEach(() => {
81
+
82
+ });
83
+
84
+ describe('generators', () => {
85
+
86
+ test('adspend: works', async () => {
87
+ const campaigns = [{
88
+ utm_source: ["foo"],
89
+ utm_campaign: ["one"],
90
+ utm_medium: ["two"],
91
+ utm_content: ["three"],
92
+ utm_term: ["four"]
93
+ },
94
+ {
95
+ utm_source: ["bar"],
96
+ utm_campaign: ["five"],
97
+ utm_medium: ["six"],
98
+ utm_content: ["seven"],
99
+ utm_term: ["eight"]
100
+ }];
101
+ const result = await makeAdSpend(dayjs().subtract(30, 'day').toISOString(), campaigns);
102
+ expect(result.length).toBe(2);
103
+ expect(result[0]).toHaveProperty('event', '$ad_spend');
104
+ expect(result[1]).toHaveProperty('event', '$ad_spend');
105
+ });
106
+
107
+ test('adspend: empty', async () => {
108
+ const result = await makeAdSpend(dayjs().subtract(30, 'day').toISOString(), []);
109
+ expect(result.length).toBe(0);
110
+ });
111
+
112
+ test('adspend: external', async () => {
113
+ const campaigns = [
114
+ { utm_source: ["source1"], utm_campaign: ["one"], utm_medium: ["two"], utm_content: ["three"], utm_term: ["four"] },
115
+ { utm_source: ["source2"], utm_campaign: ["two"], utm_medium: ["three"], utm_content: ["four"], utm_term: ["five"] }
116
+ ];
117
+ const result = await makeAdSpend(dayjs().subtract(30, 'day').toISOString(), campaigns);
118
+ expect(result.length).toBe(2);
119
+ result.forEach(event => {
120
+ expect(event).toHaveProperty('event', '$ad_spend');
121
+ expect(event).toHaveProperty('utm_campaign');
122
+ expect(event).toHaveProperty('utm_source');
123
+ expect(event).toHaveProperty('cost');
124
+ expect(event).toHaveProperty('clicks');
125
+ expect(event).toHaveProperty('impressions');
126
+ expect(event).toHaveProperty('views');
127
+ });
128
+ });
129
+
130
+
131
+ test('makeEvent: works', async () => {
132
+ /** @type {EventConfig} */
133
+ const eventConfig = {
134
+ event: "test_event",
135
+ properties: {
136
+ prop1: ["value1", "value2"],
137
+ prop2: ["value3", "value4"],
138
+ prop3: ["value5"]
139
+ },
140
+ };
141
+ const result = await makeEvent("known_id", dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), eventConfig, ["anon_id"], ["session_id"]);
142
+ expect(result).toHaveProperty('event', 'test_event');
143
+ expect(result).toHaveProperty('device_id', 'anon_id');
144
+ // expect(result).toHaveProperty('user_id', 'known_id'); // Known ID not always on the event
145
+ expect(result).toHaveProperty('session_id', 'session_id');
146
+ expect(result).toHaveProperty('source', 'dm4');
147
+ expect(result).toHaveProperty('insert_id');
148
+ expect(result).toHaveProperty('time');
149
+ expect(result).toHaveProperty('prop1');
150
+ expect(result).toHaveProperty('prop2');
151
+ expect(result.prop1 === "value1" || result.prop1 === "value2").toBeTruthy();
152
+ expect(result.prop2 === "value3" || result.prop2 === "value4").toBeTruthy();
153
+ expect(result).toHaveProperty('prop3', 'value5');
154
+ });
155
+
156
+ test('makeEvent: opt params', async () => {
157
+ const eventConfig = { event: "test_event", properties: {} };
158
+ const result = await makeEvent("known_id",dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), eventConfig);
159
+ expect(result).toHaveProperty('event', 'test_event');
160
+ expect(result).toHaveProperty('user_id', 'known_id');
161
+ expect(result).toHaveProperty('source', 'dm4');
162
+ expect(result).toHaveProperty('insert_id');
163
+ expect(result).toHaveProperty('time');
164
+ });
165
+
166
+ test('makeEvent: correct defaults', async () => {
167
+ const eventConfig = {
168
+ event: "test_event",
169
+ properties: {
170
+ prop1: ["value1", "value2"],
171
+ prop2: ["value3", "value4"]
172
+ },
173
+ };
174
+ const result = await makeEvent("known_id",dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), eventConfig, ["anon_id"], ["session_id"]);
175
+ expect(result.prop1 === "value1" || result.prop1 === "value2").toBeTruthy();
176
+ expect(result.prop2 === "value3" || result.prop2 === "value4").toBeTruthy();
177
+ });
178
+
179
+
180
+ test('makeFunnel: works', async () => {
181
+ const funnelConfig = {
182
+ sequence: ["step1", "step2"],
183
+ conversionRate: 100,
184
+ order: 'sequential'
185
+ };
186
+ /** @type {Person} */
187
+ const user = { distinct_id: "user1", name: "test", created: dayjs().toISOString(), anonymousIds: [], sessionIds: [] };
188
+ /** @type {UserProfile} */
189
+ const profile = { created: dayjs().toISOString(), distinct_id: "user1" };
190
+ /** @type {Record<string, SCDSchema[]>} */
191
+ const scd = { "scd_example": [{ distinct_id: "user1", insertTime: dayjs().toISOString(), startTime: dayjs().toISOString() }] };
192
+
193
+ const [result, converted] = await makeFunnel(funnelConfig, user,dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), profile, scd, {});
194
+ expect(result.length).toBe(2);
195
+ expect(converted).toBe(true);
196
+ expect(result.every(e => validEvent(e))).toBeTruthy();
197
+ });
198
+
199
+ test('makeFunnel: conversion rates', async () => {
200
+ const funnelConfig = {
201
+ sequence: ["step1", "step2", "step3"],
202
+ conversionRate: 50,
203
+ order: 'sequential'
204
+ };
205
+ const user = { distinct_id: "user1", name: "test", created: dayjs().toISOString(), anonymousIds: [], sessionIds: [] };
206
+ const profile = { created: dayjs().toISOString(), distinct_id: "user1" };
207
+ const scd = { "scd_example": [{ distinct_id: "user1", insertTime: dayjs().toISOString(), startTime: dayjs().toISOString() }] };
208
+
209
+ const [result, converted] = await makeFunnel(funnelConfig, user, dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), profile, scd, {});
210
+ expect(result.length).toBeGreaterThanOrEqual(1);
211
+ expect(result.length).toBeLessThanOrEqual(3);
212
+ expect(result.every(e => validEvent(e))).toBeTruthy();
213
+ });
214
+
215
+ test('makeFunnel: ordering', async () => {
216
+ const funnelConfig = {
217
+ sequence: ["step1", "step2", "step3"],
218
+ conversionRate: 100,
219
+ order: 'random'
220
+ };
221
+ const user = { distinct_id: "user1", name: "test", created: dayjs().toISOString(), anonymousIds: [], sessionIds: [] };
222
+ const profile = { created: dayjs().toISOString(), distinct_id: "user1" };
223
+ const scd = { "scd_example": [{ distinct_id: "user1", insertTime: dayjs().toISOString(), startTime: dayjs().toISOString() }] };
224
+
225
+ const [result, converted] = await makeFunnel(funnelConfig, user, dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), profile, scd, {});
226
+ expect(result.length).toBe(3);
227
+ expect(converted).toBe(true);
228
+ expect(result.every(e => validEvent(e))).toBeTruthy();
229
+ });
230
+
231
+
232
+ test('makeProfile: works', async () => {
233
+ const props = {
234
+ name: ["John", "Jane"],
235
+ age: [25, 30]
236
+ };
237
+ const result = await makeProfile(props, { foo: "bar" });
238
+ expect(result).toHaveProperty('name');
239
+ expect(result).toHaveProperty('age');
240
+ expect(result).toHaveProperty('foo', 'bar');
241
+ expect(result.name === "John" || result.name === "Jane").toBeTruthy();
242
+ expect(result.age === 25 || result.age === 30).toBeTruthy();
243
+ });
244
+
245
+ test('makeProfile: correct defaults', async () => {
246
+ const props = {
247
+ name: ["John", "Jane"],
248
+ age: [25, 30]
249
+ };
250
+ const result = await makeProfile(props);
251
+ expect(result).toHaveProperty('name');
252
+ expect(result).toHaveProperty('age');
253
+ expect(result.name === "John" || result.name === "Jane").toBeTruthy();
254
+ expect(result.age === 25 || result.age === 30).toBeTruthy();
255
+ });
256
+
257
+
258
+ test('makeSCD: works', async () => {
259
+ const result = await makeSCD(["value1", "value2"], "prop1", "distinct_id", 5, dayjs().toISOString());
260
+ expect(result.length).toBeGreaterThan(0);
261
+ const [first, second] = result;
262
+ expect(first).toHaveProperty('prop1');
263
+ expect(second).toHaveProperty('prop1');
264
+ expect(first).toHaveProperty('distinct_id', 'distinct_id');
265
+ expect(second).toHaveProperty('distinct_id', 'distinct_id');
266
+ expect(first).toHaveProperty('startTime');
267
+ expect(second).toHaveProperty('startTime');
268
+ expect(first).toHaveProperty('insertTime');
269
+ expect(second).toHaveProperty('insertTime');
270
+ expect(first.prop1 === "value1" || first.prop1 === "value2").toBeTruthy();
271
+ expect(second.prop1 === "value1" || second.prop1 === "value2").toBeTruthy();
272
+ expect(result[0]).toHaveProperty('distinct_id', 'distinct_id');
273
+ expect(result[0]).toHaveProperty('startTime');
274
+ expect(result[0]).toHaveProperty('insertTime');
275
+ });
276
+
277
+ test('makeSCD: no mutations', async () => {
278
+ const result = await makeSCD(["value1", "value2"], "prop1", "distinct_id", 0, dayjs().toISOString());
279
+ expect(result.length).toBe(0);
280
+ });
281
+
282
+ test('makeSCD: large mutations', async () => {
283
+ const result = await makeSCD(["value1", "value2"], "prop1", "distinct_id", 100, dayjs().subtract(100, 'd').toISOString());
284
+ expect(result.length).toBeGreaterThan(0);
285
+ result.forEach(entry => {
286
+ expect(entry).toHaveProperty('prop1');
287
+ expect(entry).toHaveProperty('distinct_id', 'distinct_id');
288
+ expect(entry).toHaveProperty('startTime');
289
+ expect(entry).toHaveProperty('insertTime');
290
+ expect(entry.prop1 === "value1" || entry.prop1 === "value2").toBeTruthy();
291
+ });
292
+ });
293
+
294
+ test('mirror: create', async () => {
295
+ /** @type {EventSchema} */
296
+ const oldEvent = {
297
+ event: "old",
298
+ insert_id: "test",
299
+ source: "test",
300
+ time: dayjs().toISOString(),
301
+ user_id: "test"
302
+ };
303
+
304
+ /** @type {Config} */
305
+ const config = {
306
+ mirrorProps: {
307
+ "newProp": {
308
+ events: "*",
309
+ strategy: "create",
310
+ values: ["new"]
311
+ }
312
+ }
313
+ };
314
+ await STORAGE.eventData.hookPush(oldEvent);
315
+ //ugh side fx
316
+ await makeMirror(config, STORAGE);
317
+ const [newData] = STORAGE.mirrorEventData;
318
+ expect(newData).toHaveProperty('newProp', "new");
319
+ });
320
+
321
+ test('mirror: delete', async () => {
322
+ /** @type {EventSchema} */
323
+ const oldEvent = {
324
+ event: "old",
325
+ insert_id: "test",
326
+ source: "test",
327
+ time: dayjs().toISOString(),
328
+ user_id: "test",
329
+ oldProp: "valueToDelete"
330
+ };
331
+
332
+ /** @type {Config} */
333
+ const config = {
334
+ mirrorProps: {
335
+ "oldProp": {
336
+ events: "*",
337
+ strategy: "delete"
338
+ }
339
+ }
340
+ };
341
+ await STORAGE.eventData.hookPush(oldEvent);
342
+
343
+ await makeMirror(config, STORAGE);
344
+ const [newData] = STORAGE.mirrorEventData;
345
+ expect(newData).not.toHaveProperty('oldProp');
346
+ });
347
+
348
+ test('mirror: fill', async () => {
349
+ /** @type {EventSchema} */
350
+ const oldEvent = {
351
+ event: "old",
352
+ insert_id: "test",
353
+ source: "test",
354
+ time: dayjs().subtract(8, 'days').toISOString(), // Set time to 8 days ago
355
+ user_id: "test",
356
+ fillProp: "initialValue"
357
+ };
358
+
359
+ /** @type {Config} */
360
+ const config = {
361
+ mirrorProps: {
362
+ "fillProp": {
363
+ events: "*",
364
+ strategy: "fill",
365
+ values: ["filledValue"],
366
+ daysUnfilled: 7
367
+ }
368
+ }
369
+ };
370
+ await STORAGE.eventData.hookPush(oldEvent);
371
+
372
+ await makeMirror(config, STORAGE);
373
+ const [newData] = STORAGE.mirrorEventData;
374
+ expect(newData).toHaveProperty('fillProp', "filledValue");
375
+ });
376
+
377
+ test('mirror: update', async () => {
378
+ /** @type {EventSchema} */
379
+ const oldEvent = {
380
+ event: "old",
381
+ insert_id: "test",
382
+ source: "test",
383
+ time: dayjs().toISOString(),
384
+ user_id: "test",
385
+ updateProp: "initialValue"
386
+ };
387
+
388
+ /** @type {Config} */
389
+ const config = {
390
+ mirrorProps: {
391
+ "updateProp": {
392
+ events: "*",
393
+ strategy: "update",
394
+ values: ["updatedValue"]
395
+ }
396
+ }
397
+ };
398
+ await STORAGE.eventData.hookPush(oldEvent);
399
+
400
+ await makeMirror(config, STORAGE);
401
+ const [newData] = STORAGE.mirrorEventData;
402
+ expect(newData).toHaveProperty('updateProp', "initialValue");
403
+ });
404
+
405
+ test('mirror: update nulls', async () => {
406
+ /** @type {EventSchema} */
407
+ const oldEvent = {
408
+ event: "old",
409
+ insert_id: "test",
410
+ source: "test",
411
+ time: dayjs().toISOString(),
412
+ user_id: "test"
413
+ // updateProp is not set initially
414
+ };
415
+
416
+ /** @type {Config} */
417
+ const config = {
418
+ mirrorProps: {
419
+ "updateProp": {
420
+ events: "*",
421
+ strategy: "update",
422
+ values: ["updatedValue"]
423
+ }
424
+ }
425
+ };
426
+ await STORAGE.eventData.hookPush(oldEvent);
427
+
428
+ await makeMirror(config, STORAGE);
429
+ const [newData] = STORAGE.mirrorEventData;
430
+ expect(newData).toHaveProperty('updateProp', "updatedValue");
431
+ });
432
+
433
+
434
+ test('mirror: update with no initial value', async () => {
435
+ /** @type {EventSchema} */
436
+ const oldEvent = {
437
+ event: "old",
438
+ insert_id: "test",
439
+ source: "test",
440
+ time: dayjs().toISOString(),
441
+ user_id: "test"
442
+ // updateProp is not set initially
443
+ };
444
+
445
+ /** @type {Config} */
446
+ const config = {
447
+ mirrorProps: {
448
+ "updateProp": {
449
+ events: "*",
450
+ strategy: "update",
451
+ values: ["updatedValue"]
452
+ }
453
+ }
454
+ };
455
+ await STORAGE.eventData.hookPush(oldEvent);
456
+
457
+ await makeMirror(config, STORAGE);
458
+ const [newData] = STORAGE.mirrorEventData;
459
+ expect(newData).toHaveProperty('updateProp', "updatedValue");
460
+ });
461
+
462
+
463
+
464
+ });
465
+
466
+ describe('orchestrators', () => {
467
+
468
+ test('sendToMixpanel: works', async () => {
469
+ CONFIG.token = "test_token";
470
+ const result = await sendToMixpanel(CONFIG, STORAGE);
471
+ expect(result).toHaveProperty('events');
472
+ expect(result).toHaveProperty('users');
473
+ expect(result).toHaveProperty('groups');
474
+ });
475
+
476
+ test('sendToMixpanel: no token', async () => {
477
+ CONFIG.token = null;
478
+ await expect(sendToMixpanel(CONFIG, STORAGE)).rejects.toThrow();
479
+ });
480
+
481
+ test('sendToMixpanel: empty storage', async () => {
482
+ CONFIG.token = "test_token";
483
+ STORAGE = {
484
+ eventData: await hookArray([], {}),
485
+ userProfilesData: await hookArray([], {}),
486
+ adSpendData: await hookArray([], {}),
487
+ scdTableData: [await hookArray([], {})],
488
+ groupProfilesData: await hookArray([], {}),
489
+ lookupTableData: await hookArray([], {}),
490
+ mirrorEventData: await hookArray([], {})
491
+ };
492
+ const result = await sendToMixpanel(CONFIG, STORAGE);
493
+ expect(result.events.success).toBe(0);
494
+ expect(result.users.success).toBe(0);
495
+ expect(result.groups).toHaveLength(0);
496
+ });
497
+
498
+
499
+ test('userLoop: works (no funnels)', async () => {
500
+ /** @type {Config} */
501
+ const config = {
502
+ numUsers: 2,
503
+ numEvents: 40,
504
+ numDays: 30,
505
+ userProps: {},
506
+ scdProps: {},
507
+ funnels: [],
508
+ isAnonymous: false,
509
+ hasAnonIds: false,
510
+ hasSessionIds: false,
511
+ hasLocation: false,
512
+ events: [{ event: "foo" }, { event: "bar" }, { event: "baz" }]
513
+ };
514
+ await userLoop(config, STORAGE);
515
+ expect(STORAGE.userProfilesData.length).toBe(2);
516
+ expect(STORAGE.eventData.length).toBeGreaterThan(15);
517
+ expect(STORAGE.eventData.every(e => validEvent(e))).toBeTruthy();
518
+ });
519
+
520
+
521
+ test('userLoop: works (funnels)', async () => {
522
+ /** @type {Config} */
523
+ const config = {
524
+ numUsers: 2,
525
+ numEvents: 50,
526
+ numDays: 30,
527
+ userProps: {},
528
+ scdProps: {},
529
+ events: [],
530
+ funnels: [{ sequence: ["step1", "step2"], conversionRate: 100, order: 'sequential' }],
531
+ };
532
+ await userLoop(config, STORAGE);
533
+ expect(STORAGE.userProfilesData.length).toBe(2);
534
+ expect(STORAGE.eventData.length).toBeGreaterThan(15);
535
+ expect(STORAGE.eventData.every(e => validEvent(e))).toBeTruthy();
536
+
537
+
538
+ });
539
+
540
+ test('userLoop: mixed config', async () => {
541
+ const config = {
542
+ numUsers: 3,
543
+ numEvents: 15,
544
+ numDays: 10,
545
+ userProps: { name: ["Alice", "Bob", "Charlie"] },
546
+ scdProps: { prop1: ["value1", "value2"] },
547
+ funnels: [],
548
+ events: [{ event: "event1" }, { event: "event2" }]
549
+ };
550
+ await userLoop(config, STORAGE);
551
+ expect(STORAGE.userProfilesData.length).toBe(3);
552
+ expect(STORAGE.eventData.length).toBeGreaterThan(0);
553
+ expect(STORAGE.eventData.every(e => validEvent(e))).toBeTruthy();
554
+ });
555
+
556
+ test('userLoop: no events', async () => {
557
+ const config = {
558
+ numUsers: 2,
559
+ numEvents: 0,
560
+ numDays: 30,
561
+ userProps: {},
562
+ scdProps: {},
563
+ funnels: [],
564
+ isAnonymous: false,
565
+ hasAnonIds: false,
566
+ hasSessionIds: false,
567
+ hasLocation: false,
568
+ events: []
569
+ };
570
+ await userLoop(config, STORAGE);
571
+ expect(STORAGE.userProfilesData.length).toBe(2);
572
+ expect(STORAGE.eventData.length).toBe(0);
573
+ });
574
+
575
+
576
+
577
+ test('validateDungeonConfig: works', async () => {
578
+ const config = {
579
+ numEvents: 100,
580
+ numUsers: 10,
581
+ numDays: 30
582
+ };
583
+ const result = await validateDungeonConfig(config);
584
+ expect(result).toHaveProperty('numEvents', 100);
585
+ expect(result).toHaveProperty('numUsers', 10);
586
+ expect(result).toHaveProperty('numDays', 30);
587
+ expect(result).toHaveProperty('events');
588
+ expect(result).toHaveProperty('superProps');
589
+ });
590
+
591
+ test('validateDungeonConfig: correct defaults', async () => {
592
+ const config = {};
593
+ const result = await validateDungeonConfig(config);
594
+ expect(result).toHaveProperty('numEvents', 100_000);
595
+ expect(result).toHaveProperty('numUsers', 1000);
596
+ expect(result).toHaveProperty('numDays', 30);
597
+ expect(result).toHaveProperty('events');
598
+ expect(result).toHaveProperty('superProps');
599
+ });
600
+
601
+ test('validateDungeonConfig: merges', async () => {
602
+ const config = {
603
+ numEvents: 100,
604
+ numUsers: 10,
605
+ numDays: 30,
606
+ events: [{ event: "test_event" }],
607
+ superProps: { luckyNumber: [7] }
608
+ };
609
+ const result = await validateDungeonConfig(config);
610
+ expect(result).toHaveProperty('numEvents', 100);
611
+ expect(result).toHaveProperty('numUsers', 10);
612
+ expect(result).toHaveProperty('numDays', 30);
613
+ expect(result).toHaveProperty('events', [{ event: "test_event" }]);
614
+ expect(result).toHaveProperty('superProps', { luckyNumber: [7] });
615
+ });
616
+
617
+
618
+
619
+ });
@@ -24,6 +24,14 @@ const jestConfig = {
24
24
  // @ts-ignore
25
25
  maxWorkers: "50%"
26
26
  },
27
+ {
28
+ displayName: "integration",
29
+ testMatch: [
30
+ "<rootDir>/tests/int.test.js"
31
+ ],
32
+ // @ts-ignore
33
+ maxWorkers: "50%"
34
+ },
27
35
  ],
28
36
  coverageDirectory: "./tests/coverage",
29
37
  };
@@ -226,4 +226,4 @@ const TEST_CASES = [
226
226
 
227
227
  ];
228
228
 
229
- module.exports = TEST_CASES;
229
+ export default TEST_CASES;
@@ -1,8 +1,9 @@
1
- import { generateLineChart } from '../core/chart.js';
2
- import { TimeSoup } from '../core/utils.js';
1
+ import { generateLineChart } from '../components/chart.js';
2
+ import { TimeSoup } from '../components/utils.js';
3
3
  import dayjs from 'dayjs';
4
4
  import { progress } from 'ak-tools';
5
5
  import TEST_CASES from './testCases.mjs';
6
+
6
7
  import execSync from 'child_process';
7
8
 
8
9
  async function genViz(soup) {
@@ -12,7 +13,7 @@ async function genViz(soup) {
12
13
  const end = dayjs().unix();
13
14
  console.log(`\n\nTEST CASE: ${name}\n\n`);
14
15
  for (let i = 0; i < 100_000; i++) {
15
- progress('processing', i);
16
+ progress([['processing', i]]);
16
17
  const time = TimeSoup(start, end, peaks, deviation, mean);
17
18
  data.push({ time });
18
19
  }
@@ -24,4 +25,4 @@ async function genViz(soup) {
24
25
  execSync.execSync('npm run prune');
25
26
  // @ts-ignore
26
27
  await Promise.all(TEST_CASES.map(genViz));
27
- // await genViz(TEST_CASES[0]);
28
+ console.log('done');