make-mp-data 1.4.1 → 1.4.3
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/README.md +3 -2
- package/{cli.js → core/cli.js} +1 -1
- package/core/defaults.js +960 -0
- package/{index.js → core/index.js} +217 -87
- package/{utils.js → core/utils.js} +163 -34
- package/package.json +8 -9
- package/schemas/anon.js +104 -0
- package/schemas/complex.js +11 -2
- package/schemas/deepNest.js +5 -1
- package/schemas/foobar.js +2 -2
- package/schemas/funnels.js +1 -1
- package/schemas/simple.js +11 -1
- package/scratch.mjs +19 -5
- package/scripts/jsdoctest.js +1 -1
- package/tests/e2e.test.js +25 -7
- package/{testSoup.mjs → tests/testSoup.mjs} +2 -2
- package/tests/unit.test.js +157 -9
- package/tsconfig.json +1 -1
- package/types.d.ts +17 -3
- package/defaults.js +0 -11662
- /package/{chart.js → core/chart.js} +0 -0
- /package/{testCases.mjs → tests/testCases.mjs} +0 -0
package/tests/e2e.test.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/* eslint-disable no-undef */
|
|
4
4
|
/* eslint-disable no-debugger */
|
|
5
5
|
/* eslint-disable no-unused-vars */
|
|
6
|
-
const generate = require('../index.js');
|
|
6
|
+
const generate = require('../core/index.js');
|
|
7
7
|
require('dotenv').config();
|
|
8
8
|
const { execSync } = require("child_process");
|
|
9
9
|
const u = require('ak-tools');
|
|
@@ -11,6 +11,7 @@ const u = require('ak-tools');
|
|
|
11
11
|
const simple = require('../schemas/simple.js');
|
|
12
12
|
const complex = require('../schemas/complex.js');
|
|
13
13
|
const deep = require('../schemas/deepNest.js');
|
|
14
|
+
const anon = require('../schemas/anon.js');
|
|
14
15
|
|
|
15
16
|
const timeout = 60000;
|
|
16
17
|
const testToken = process.env.TEST_TOKEN;
|
|
@@ -86,30 +87,39 @@ describe('module', () => {
|
|
|
86
87
|
});
|
|
87
88
|
|
|
88
89
|
describe('cli', () => {
|
|
90
|
+
test('works as CLI (no args)', async () => {
|
|
91
|
+
console.log('COMPLEX CLI TEST');
|
|
92
|
+
const run = execSync(`node ./core/index.js --numEvents 1000 --numUsers 100`, { stdio: 'ignore' });
|
|
93
|
+
// expect(run.toString().trim().includes('have a wonderful day :)')).toBe(true);
|
|
94
|
+
const csvs = (await u.ls('./data')).filter(a => a.includes('.csv'));
|
|
95
|
+
expect(csvs.length).toBe(4);
|
|
96
|
+
clearData();
|
|
97
|
+
}, timeout);
|
|
98
|
+
|
|
89
99
|
test('works as CLI (complex)', async () => {
|
|
90
100
|
console.log('COMPLEX CLI TEST');
|
|
91
|
-
const run = execSync(`node ./index.js --numEvents 1000 --numUsers 100 --seed "deal with it" --complex`, { stdio: 'ignore' });
|
|
101
|
+
const run = execSync(`node ./core/index.js --numEvents 1000 --numUsers 100 --seed "deal with it" --complex`, { stdio: 'ignore' });
|
|
92
102
|
// expect(run.toString().trim().includes('have a wonderful day :)')).toBe(true);
|
|
93
103
|
const csvs = (await u.ls('./data')).filter(a => a.includes('.csv'));
|
|
94
|
-
expect(csvs.length).toBe(
|
|
104
|
+
expect(csvs.length).toBe(13);
|
|
95
105
|
clearData();
|
|
96
106
|
}, timeout);
|
|
97
107
|
|
|
98
108
|
test('works as CLI (simple)', async () => {
|
|
99
109
|
console.log('simple CLI TEST');
|
|
100
|
-
const run = execSync(`node ./index.js --numEvents 1000 --numUsers 100 --seed "deal with it"`);
|
|
110
|
+
const run = execSync(`node ./core/index.js --numEvents 1000 --numUsers 100 --seed "deal with it" --simple`);
|
|
101
111
|
expect(run.toString().trim().includes('have a wonderful day :)')).toBe(true);
|
|
102
112
|
const csvs = (await u.ls('./data')).filter(a => a.includes('.csv'));
|
|
103
|
-
expect(csvs.length).toBe(
|
|
113
|
+
expect(csvs.length).toBe(4);
|
|
104
114
|
clearData();
|
|
105
115
|
}, timeout);
|
|
106
116
|
|
|
107
117
|
test('works as CLI (custom)', async () => {
|
|
108
118
|
console.log('custom CLI TEST');
|
|
109
|
-
const run = execSync(`node ./index.js ./schemas/deepNest.js`);
|
|
119
|
+
const run = execSync(`node ./core/index.js ./schemas/deepNest.js`);
|
|
110
120
|
expect(run.toString().trim().includes('have a wonderful day :)')).toBe(true);
|
|
111
121
|
const csvs = (await u.ls('./data')).filter(a => a.includes('.csv'));
|
|
112
|
-
expect(csvs.length).toBe(
|
|
122
|
+
expect(csvs.length).toBe(3);
|
|
113
123
|
clearData();
|
|
114
124
|
}, timeout);
|
|
115
125
|
|
|
@@ -179,6 +189,14 @@ describe('options + tweaks', () => {
|
|
|
179
189
|
|
|
180
190
|
}, timeout);
|
|
181
191
|
|
|
192
|
+
test('anonymous users', async () => {
|
|
193
|
+
console.log('ANON TEST');
|
|
194
|
+
const results = await generate({ ...anon, writeToDisk: false, verbose: true });
|
|
195
|
+
const { userProfilesData } = results;
|
|
196
|
+
expect(userProfilesData.every(u => u.name === 'Anonymous User')).toBe(true);
|
|
197
|
+
|
|
198
|
+
}, timeout);
|
|
199
|
+
|
|
182
200
|
});
|
|
183
201
|
|
|
184
202
|
afterAll(() => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { generateLineChart } from '
|
|
2
|
-
import { TimeSoup } from '
|
|
1
|
+
import { generateLineChart } from '../core/chart.js';
|
|
2
|
+
import { TimeSoup } from '../core/utils.js';
|
|
3
3
|
import dayjs from 'dayjs';
|
|
4
4
|
import { progress } from 'ak-tools';
|
|
5
5
|
import TEST_CASES from './testCases.mjs';
|
package/tests/unit.test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const generate = require('../index.js');
|
|
1
|
+
const generate = require('../core/index.js');
|
|
2
2
|
const dayjs = require("dayjs");
|
|
3
3
|
const utc = require("dayjs/plugin/utc");
|
|
4
4
|
const fs = require('fs');
|
|
@@ -43,8 +43,10 @@ const { applySkew,
|
|
|
43
43
|
validateEventConfig,
|
|
44
44
|
validateTime,
|
|
45
45
|
interruptArray,
|
|
46
|
-
optimizedBoxMuller
|
|
47
|
-
|
|
46
|
+
optimizedBoxMuller,
|
|
47
|
+
inferFunnels,
|
|
48
|
+
datesBetween
|
|
49
|
+
} = require('../core/utils.js');
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
describe('timesoup', () => {
|
|
@@ -230,6 +232,152 @@ describe('generation', () => {
|
|
|
230
232
|
expect(createdDate.isValid()).toBeTruthy();
|
|
231
233
|
expect(createdDate.isBefore(dayjs.unix(global.NOW))).toBeTruthy();
|
|
232
234
|
});
|
|
235
|
+
|
|
236
|
+
test('winner: return func', () => {
|
|
237
|
+
const items = ['a', 'b', 'c'];
|
|
238
|
+
const result = pickAWinner(items, 0);
|
|
239
|
+
expect(typeof result).toBe('function');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('winner: first most', () => {
|
|
243
|
+
const items = ['a', 'b', 'c'];
|
|
244
|
+
const mostChosenIndex = 0;
|
|
245
|
+
const pickFunction = pickAWinner(items, mostChosenIndex);
|
|
246
|
+
const weightedList = pickFunction();
|
|
247
|
+
|
|
248
|
+
// Expect the most chosen item to appear at least once
|
|
249
|
+
expect(weightedList.includes(items[mostChosenIndex])).toBeTruthy();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test('winner: second most', () => {
|
|
253
|
+
const items = ['a', 'b', 'c'];
|
|
254
|
+
const mostChosenIndex = 0;
|
|
255
|
+
const pickFunction = pickAWinner(items, mostChosenIndex);
|
|
256
|
+
const weightedList = pickFunction();
|
|
257
|
+
|
|
258
|
+
const secondMostChosenIndex = (mostChosenIndex + 1) % items.length;
|
|
259
|
+
|
|
260
|
+
// Expect the second most chosen item to appear at least once
|
|
261
|
+
expect(weightedList.includes(items[secondMostChosenIndex])).toBeTruthy();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test('winner: third most', () => {
|
|
265
|
+
const items = ['a', 'b', 'c'];
|
|
266
|
+
const mostChosenIndex = 0;
|
|
267
|
+
const pickFunction = pickAWinner(items, mostChosenIndex);
|
|
268
|
+
const weightedList = pickFunction();
|
|
269
|
+
|
|
270
|
+
const thirdMostChosenIndex = (mostChosenIndex + 2) % items.length;
|
|
271
|
+
|
|
272
|
+
// Expect the third most chosen item to appear at least once
|
|
273
|
+
expect(weightedList.includes(items[thirdMostChosenIndex])).toBeTruthy();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test('winner: exceed array bounds', () => {
|
|
277
|
+
const items = ['a', 'b', 'c'];
|
|
278
|
+
const mostChosenIndex = 0;
|
|
279
|
+
const pickFunction = pickAWinner(items, mostChosenIndex);
|
|
280
|
+
const weightedList = pickFunction();
|
|
281
|
+
|
|
282
|
+
// Ensure all indices are within the bounds of the array
|
|
283
|
+
weightedList.forEach(item => {
|
|
284
|
+
expect(items.includes(item)).toBeTruthy();
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test('winner: single item array', () => {
|
|
289
|
+
const items = ['a'];
|
|
290
|
+
const mostChosenIndex = 0;
|
|
291
|
+
const pickFunction = pickAWinner(items, mostChosenIndex);
|
|
292
|
+
const weightedList = pickFunction();
|
|
293
|
+
|
|
294
|
+
// Since there's only one item, all winner: he same
|
|
295
|
+
weightedList.forEach(item => {
|
|
296
|
+
expect(item).toBe('a');
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test('winner: empty array', () => {
|
|
301
|
+
const items = [];
|
|
302
|
+
const pickFunction = pickAWinner(items, 0);
|
|
303
|
+
const weightedList = pickFunction();
|
|
304
|
+
|
|
305
|
+
// Expect the result to be an empty array
|
|
306
|
+
expect(weightedList.length).toBe(0);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
test('dates: same start end', () => {
|
|
310
|
+
const start = '2023-06-10';
|
|
311
|
+
const end = '2023-06-10';
|
|
312
|
+
const result = datesBetween(start, end);
|
|
313
|
+
expect(result).toEqual([]);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test('dates: start after end', () => {
|
|
317
|
+
const start = '2023-06-12';
|
|
318
|
+
const end = '2023-06-10';
|
|
319
|
+
const result = datesBetween(start, end);
|
|
320
|
+
expect(result).toEqual([]);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test('dates: correct', () => {
|
|
324
|
+
const start = '2023-06-10';
|
|
325
|
+
const end = '2023-06-13';
|
|
326
|
+
const result = datesBetween(start, end);
|
|
327
|
+
expect(result).toEqual([
|
|
328
|
+
'2023-06-10T12:00:00.000Z',
|
|
329
|
+
'2023-06-11T12:00:00.000Z',
|
|
330
|
+
'2023-06-12T12:00:00.000Z'
|
|
331
|
+
]);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test('dates: unix times', () => {
|
|
335
|
+
const start = dayjs('2023-06-10').unix();
|
|
336
|
+
const end = dayjs('2023-06-13').unix();
|
|
337
|
+
const result = datesBetween(start, end);
|
|
338
|
+
expect(result).toEqual([
|
|
339
|
+
'2023-06-10T12:00:00.000Z',
|
|
340
|
+
'2023-06-11T12:00:00.000Z',
|
|
341
|
+
'2023-06-12T12:00:00.000Z'
|
|
342
|
+
]);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test('dates: mixed formats', () => {
|
|
346
|
+
const start = '2023-06-10';
|
|
347
|
+
const end = dayjs('2023-06-13').unix();
|
|
348
|
+
const result = datesBetween(start, end);
|
|
349
|
+
expect(result).toEqual([
|
|
350
|
+
'2023-06-10T12:00:00.000Z',
|
|
351
|
+
'2023-06-11T12:00:00.000Z',
|
|
352
|
+
'2023-06-12T12:00:00.000Z'
|
|
353
|
+
]);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test('dates: invalid dates', () => {
|
|
357
|
+
const start = 'invalid-date';
|
|
358
|
+
const end = '2023-06-13';
|
|
359
|
+
const result = datesBetween(start, end);
|
|
360
|
+
expect(result).toEqual([]);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test('dates: same day', () => {
|
|
364
|
+
const start = '2023-06-10T08:00:00.000Z';
|
|
365
|
+
const end = '2023-06-10T20:00:00.000Z';
|
|
366
|
+
const result = datesBetween(start, end);
|
|
367
|
+
expect(result).toEqual([]);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test('dates: leap years', () => {
|
|
371
|
+
const start = '2024-02-28';
|
|
372
|
+
const end = '2024-03-02';
|
|
373
|
+
const result = datesBetween(start, end);
|
|
374
|
+
expect(result).toEqual([
|
|
375
|
+
'2024-02-28T12:00:00.000Z',
|
|
376
|
+
'2024-02-29T12:00:00.000Z',
|
|
377
|
+
'2024-03-01T12:00:00.000Z'
|
|
378
|
+
]);
|
|
379
|
+
});
|
|
380
|
+
|
|
233
381
|
});
|
|
234
382
|
|
|
235
383
|
describe('validation', () => {
|
|
@@ -374,7 +522,7 @@ describe('utilities', () => {
|
|
|
374
522
|
|
|
375
523
|
|
|
376
524
|
test('person: fields', () => {
|
|
377
|
-
const generatedPerson = person();
|
|
525
|
+
const generatedPerson = person('myId');
|
|
378
526
|
expect(generatedPerson).toHaveProperty('name');
|
|
379
527
|
expect(generatedPerson).toHaveProperty('email');
|
|
380
528
|
expect(generatedPerson).toHaveProperty('avatar');
|
|
@@ -430,9 +578,9 @@ describe('utilities', () => {
|
|
|
430
578
|
});
|
|
431
579
|
|
|
432
580
|
test('applySkew: skews', () => {
|
|
433
|
-
const value =
|
|
581
|
+
const value = optimizedBoxMuller();
|
|
434
582
|
const skewedValue = applySkew(value, .25);
|
|
435
|
-
expect(Math.abs(skewedValue)).toBeLessThanOrEqual(Math.abs(value));
|
|
583
|
+
expect(Math.abs(skewedValue)).toBeLessThanOrEqual(Math.abs(value) + 1);
|
|
436
584
|
});
|
|
437
585
|
|
|
438
586
|
test('mapToRange: works', () => {
|
|
@@ -552,7 +700,7 @@ describe('utilities', () => {
|
|
|
552
700
|
test('progress: outputs correctly', () => {
|
|
553
701
|
// @ts-ignore
|
|
554
702
|
const mockStdoutWrite = jest.spyOn(process.stdout, 'write').mockImplementation(() => { });
|
|
555
|
-
progress('test', 50);
|
|
703
|
+
progress([['test', 50]]);
|
|
556
704
|
expect(mockStdoutWrite).toHaveBeenCalled();
|
|
557
705
|
mockStdoutWrite.mockRestore();
|
|
558
706
|
});
|
|
@@ -628,8 +776,8 @@ describe('utilities', () => {
|
|
|
628
776
|
const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
|
|
629
777
|
const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length;
|
|
630
778
|
const stdDev = Math.sqrt(variance);
|
|
631
|
-
expect(mean).
|
|
632
|
-
expect(stdDev).
|
|
779
|
+
expect(mean).toBeLessThan(1);
|
|
780
|
+
expect(stdDev).toBeLessThan(1);
|
|
633
781
|
});
|
|
634
782
|
|
|
635
783
|
|
package/tsconfig.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -12,10 +12,21 @@ declare namespace main {
|
|
|
12
12
|
epochStart?: number;
|
|
13
13
|
epochEnd?: number;
|
|
14
14
|
numEvents?: number;
|
|
15
|
-
numUsers?: number;
|
|
15
|
+
numUsers?: number;
|
|
16
|
+
|
|
17
|
+
//switches
|
|
18
|
+
isAnonymous?: boolean;
|
|
19
|
+
hasLocation?: boolean;
|
|
20
|
+
hasCampaigns?: boolean;
|
|
21
|
+
hasAdSpend?: boolean;
|
|
22
|
+
hasIOSDevices?: boolean;
|
|
23
|
+
hasAndroidDevices?: boolean;
|
|
24
|
+
hasDesktopDevices?: boolean;
|
|
25
|
+
hasBrowser?: boolean;
|
|
26
|
+
|
|
27
|
+
|
|
16
28
|
format?: "csv" | "json";
|
|
17
29
|
region?: "US" | "EU";
|
|
18
|
-
chance?: any;
|
|
19
30
|
events?: EventConfig[]; //can also be a array of strings
|
|
20
31
|
superProps?: Record<string, ValueValid>;
|
|
21
32
|
funnels?: Funnel[];
|
|
@@ -50,6 +61,8 @@ declare namespace main {
|
|
|
50
61
|
| "mirror"
|
|
51
62
|
| "funnel-pre"
|
|
52
63
|
| "funnel-post"
|
|
64
|
+
| "ad-spend"
|
|
65
|
+
| "churn"
|
|
53
66
|
| "";
|
|
54
67
|
export type Hook<T> = (record: any, type: hookTypes, meta: any) => T;
|
|
55
68
|
|
|
@@ -105,7 +118,8 @@ declare namespace main {
|
|
|
105
118
|
}
|
|
106
119
|
|
|
107
120
|
export interface MirrorProps {
|
|
108
|
-
events
|
|
121
|
+
events?: string[] | "*";
|
|
122
|
+
strategy?: "delete" | "append" | "replace" | "fill" | ""
|
|
109
123
|
values: ValueValid[];
|
|
110
124
|
}
|
|
111
125
|
|