make-mp-data 1.3.3 → 1.4.0

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.
@@ -1,19 +1,54 @@
1
1
  const generate = require('../index.js');
2
2
  const dayjs = require("dayjs");
3
3
  const utc = require("dayjs/plugin/utc");
4
+ const fs = require('fs');
4
5
  const u = require('ak-tools');
5
6
  dayjs.extend(utc);
6
- const { timeSoup } = generate;
7
7
  require('dotenv').config();
8
8
 
9
-
10
-
11
- describe('timeSoup', () => {
12
- test('always positive dates', () => {
9
+ const { applySkew,
10
+ boxMullerRandom,
11
+ choose,
12
+ date,
13
+ dates,
14
+ day,
15
+ exhaust,
16
+ generateEmoji,
17
+ getUniqueKeys,
18
+ integer,
19
+ mapToRange,
20
+ person,
21
+ pick,
22
+ range,
23
+ pickAWinner,
24
+ weightedRange,
25
+ enrichArray,
26
+ fixFirstAndLast,
27
+ generateUser,
28
+ openFinder,
29
+ progress,
30
+ shuffleArray,
31
+ shuffleExceptFirst,
32
+ shuffleExceptLast,
33
+ shuffleMiddle,
34
+ shuffleOutside,
35
+ streamCSV,
36
+ streamJSON,
37
+ weighArray,
38
+ weighFunnels,
39
+ buildFileNames,
40
+ TimeSoup,
41
+ getChance,
42
+ initChance
43
+ } = require('../utils');
44
+
45
+
46
+ describe('timesoup', () => {
47
+ test('always valid times', () => {
13
48
  const dates = [];
14
- for (let i = 0; i < 20000; i++) {
15
- const earliest = dayjs().subtract(u.rand(2, 360), 'D');
16
- dates.push(timeSoup());
49
+ for (let i = 0; i < 10000; i++) {
50
+ const earliest = dayjs().subtract(u.rand(5, 50), 'D');
51
+ dates.push(TimeSoup());
17
52
  }
18
53
  const tooOld = dates.filter(d => dayjs(d).isBefore(dayjs.unix(0)));
19
54
  const badYear = dates.filter(d => !d.startsWith('202'));
@@ -24,8 +59,207 @@ describe('timeSoup', () => {
24
59
  });
25
60
 
26
61
 
62
+ describe('naming things', () => {
63
+
64
+ test('default config', () => {
65
+ const config = { simulationName: 'testSim' };
66
+ const result = buildFileNames(config);
67
+ expect(result.eventFiles).toEqual(['testSim-EVENTS.csv']);
68
+ expect(result.userFiles).toEqual(['testSim-USERS.csv']);
69
+ expect(result.scdFiles).toEqual([]);
70
+ expect(result.groupFiles).toEqual([]);
71
+ expect(result.lookupFiles).toEqual([]);
72
+ expect(result.mirrorFiles).toEqual([]);
73
+ expect(result.folder).toEqual('./');
74
+ });
75
+
76
+ test('json format', () => {
77
+ const config = { simulationName: 'testSim', format: 'json' };
78
+ const result = buildFileNames(config);
79
+ expect(result.eventFiles).toEqual(['testSim-EVENTS.json']);
80
+ expect(result.userFiles).toEqual(['testSim-USERS.json']);
81
+ });
82
+
83
+ test('with scdProps', () => {
84
+ const config = {
85
+ simulationName: 'testSim',
86
+ scdProps: { prop1: {}, prop2: {} }
87
+ };
88
+ const result = buildFileNames(config);
89
+ expect(result.scdFiles).toEqual([
90
+ 'testSim-prop1-SCD.csv',
91
+ 'testSim-prop2-SCD.csv'
92
+ ]);
93
+ });
94
+
95
+ test('with groupKeys', () => {
96
+ const config = {
97
+ simulationName: 'testSim',
98
+ groupKeys: [['group1'], ['group2']]
99
+ };
100
+ const result = buildFileNames(config);
101
+ expect(result.groupFiles).toEqual([
102
+ 'testSim-group1-GROUP.csv',
103
+ 'testSim-group2-GROUP.csv'
104
+ ]);
105
+ });
106
+
107
+ test('with lookupTables', () => {
108
+ const config = {
109
+ simulationName: 'testSim',
110
+ lookupTables: [{ key: 'lookup1' }, { key: 'lookup2' }]
111
+ };
112
+ const result = buildFileNames(config);
113
+ expect(result.lookupFiles).toEqual([
114
+ 'testSim-lookup1-LOOKUP.csv',
115
+ 'testSim-lookup2-LOOKUP.csv'
116
+ ]);
117
+ });
118
+
119
+ test('with mirrorProps', () => {
120
+ const config = {
121
+ simulationName: 'testSim',
122
+ mirrorProps: { prop1: {} }
123
+ };
124
+ const result = buildFileNames(config);
125
+ expect(result.mirrorFiles).toEqual(['testSim-MIRROR.csv']);
126
+ });
127
+
128
+ test('writeToDisk', async () => {
129
+ const config = { simulationName: 'testSim', writeToDisk: true };
130
+ const result = await buildFileNames(config);
131
+ expect(result.folder).toBeDefined();
132
+
133
+ });
134
+
135
+
136
+ test('invalid simName', () => {
137
+ const config = { simulationName: 123 };
138
+ expect(() => buildFileNames(config)).toThrow('simName must be a string');
139
+ });
140
+
141
+
142
+ test('streamJSON: writes to file', async () => {
143
+ const path = 'test.json';
144
+ const data = [{ a: 1, b: 2 }, { a: 3, b: 4 }];
145
+ await streamJSON(path, data);
146
+ const content = fs.readFileSync(path, 'utf8');
147
+ const lines = content.trim().split('\n').map(line => JSON.parse(line));
148
+ expect(lines).toEqual(data);
149
+ fs.unlinkSync(path);
150
+ });
151
+
152
+ test('streamCSV: writes to file', async () => {
153
+ const path = 'test.csv';
154
+ const data = [{ a: 1, b: 2 }, { a: 3, b: 4 }];
155
+ await streamCSV(path, data);
156
+ const content = fs.readFileSync(path, 'utf8');
157
+ const lines = content.trim().split('\n');
158
+ expect(lines.length).toBe(3); // Including header
159
+ fs.unlinkSync(path);
160
+ });
161
+
162
+
163
+ test('generateUser: works', () => {
164
+ const uuid = { guid: jest.fn().mockReturnValue('uuid-123') };
165
+ const numDays = 30;
166
+ const user = generateUser(numDays);
167
+ expect(user).toHaveProperty('distinct_id');
168
+ expect(user).toHaveProperty('name');
169
+ expect(user).toHaveProperty('email');
170
+ expect(user).toHaveProperty('avatar');
171
+ });
172
+
173
+ test('enrichArray: works', () => {
174
+ const arr = [];
175
+ const enrichedArray = enrichArray(arr);
176
+ enrichedArray.hookPush(1);
177
+ enrichedArray.hookPush(2);
178
+ const match = JSON.stringify(enrichedArray) === JSON.stringify([1, 2]);
179
+ expect(match).toEqual(true);
180
+ });
181
+
182
+ });
183
+
184
+
185
+ describe('determined random', () => {
186
+ test('initializes RNG with seed from environment variable', () => {
187
+ process.env.SEED = 'test-seed';
188
+ // @ts-ignore
189
+ initChance();
190
+ const chance = getChance();
191
+ expect(chance).toBeDefined();
192
+ expect(chance.random()).toBeGreaterThanOrEqual(0);
193
+ expect(chance.random()).toBeLessThanOrEqual(1);
194
+
195
+ });
196
+
197
+ test('initializes RNG only once', () => {
198
+ const seed = 'initial-seed';
199
+ initChance(seed);
200
+ const chance1 = getChance();
201
+ initChance('new-seed');
202
+ const chance2 = getChance();
203
+ expect(chance1).toBe(chance2);
204
+
205
+ });
206
+ });
207
+
208
+
209
+ describe('generateUser', () => {
210
+ test('creates a user with valid fields', () => {
211
+ const numDays = 30;
212
+ const user = generateUser('uuid-123', numDays);
213
+ expect(user).toHaveProperty('distinct_id');
214
+ expect(user).toHaveProperty('name');
215
+ expect(user).toHaveProperty('email');
216
+ expect(user).toHaveProperty('avatar');
217
+ expect(user).toHaveProperty('created');
218
+ expect(user).toHaveProperty('anonymousIds');
219
+ expect(user).toHaveProperty('sessionIds');
220
+ });
221
+
222
+ test('creates a user with a created date within the specified range', () => {
223
+ const numDays = 30;
224
+ const user = generateUser('uuid-123', numDays);
225
+ const createdDate = dayjs(user.created, 'YYYY-MM-DD');
226
+ expect(createdDate.isValid()).toBeTruthy();
227
+ expect(createdDate.isBefore(dayjs.unix(global.NOW))).toBeTruthy();
228
+ });
229
+ });
230
+
231
+
232
+
233
+ describe('enrich array', () => {
234
+ test('hook works', () => {
235
+ const arr = [];
236
+ const hook = (item) => item * 2;
237
+ const enrichedArray = enrichArray(arr, { hook });
238
+ enrichedArray.hookPush(1);
239
+ enrichedArray.hookPush(2);
240
+ expect(enrichedArray.includes(2)).toBeTruthy();
241
+ expect(enrichedArray.includes(4)).toBeTruthy();
242
+ });
243
+
244
+ test('filter empties', () => {
245
+ const arr = [];
246
+ const hook = (item) => item ? item.toString() : item;
247
+ const enrichedArray = enrichArray(arr, { hook });
248
+ enrichedArray.hookPush(null);
249
+ enrichedArray.hookPush(undefined);
250
+ enrichedArray.hookPush({});
251
+ enrichedArray.hookPush({ a: 1 });
252
+ enrichedArray.hookPush([1, 2]);
253
+ expect(enrichedArray).toHaveLength(3);
254
+ expect(enrichedArray.includes('null')).toBeFalsy();
255
+ expect(enrichedArray.includes('undefined')).toBeFalsy();
256
+ expect(enrichedArray.includes('[object Object]')).toBeTruthy();
257
+ expect(enrichedArray.includes('1')).toBeTruthy();
258
+ expect(enrichedArray.includes('2')).toBeTruthy();
259
+
260
+ });
261
+ });
27
262
 
28
- const { applySkew, boxMullerRandom, choose, date, dates, day, exhaust, generateEmoji, getUniqueKeys, integer, makeHashTags, makeProducts, mapToRange, person, pick, range, weighList, weightedRange } = require('../utils');
29
263
 
30
264
  describe('utils', () => {
31
265
 
@@ -58,9 +292,9 @@ describe('utils', () => {
58
292
 
59
293
  test('person: fields', () => {
60
294
  const generatedPerson = person();
61
- expect(generatedPerson).toHaveProperty('$name');
62
- expect(generatedPerson).toHaveProperty('$email');
63
- expect(generatedPerson).toHaveProperty('$avatar');
295
+ expect(generatedPerson).toHaveProperty('name');
296
+ expect(generatedPerson).toHaveProperty('email');
297
+ expect(generatedPerson).toHaveProperty('avatar');
64
298
  });
65
299
 
66
300
 
@@ -73,7 +307,7 @@ describe('utils', () => {
73
307
  test('date: future', () => {
74
308
  const futureDate = date(10, false, 'YYYY-MM-DD')();
75
309
  expect(dayjs(futureDate, 'YYYY-MM-DD').isValid()).toBeTruthy();
76
- expect(dayjs(futureDate).isAfter(dayjs())).toBeTruthy();
310
+ expect(dayjs(futureDate).isAfter(dayjs.unix(global.NOW))).toBeTruthy();
77
311
  });
78
312
 
79
313
  test('dates: pairs', () => {
@@ -107,7 +341,7 @@ describe('utils', () => {
107
341
  });
108
342
 
109
343
  test('weightedRange: within range', () => {
110
- const values = weightedRange(5, 15, 100);
344
+ const values = weightedRange(5, 15);
111
345
  expect(values.every(v => v >= 5 && v <= 15)).toBe(true);
112
346
  expect(values.length).toBe(100);
113
347
  });
@@ -124,8 +358,8 @@ describe('utils', () => {
124
358
  const sd = 5;
125
359
  const mappedValue = mapToRange(value, mean, sd);
126
360
  expect(mappedValue).toBe(10);
127
- });
128
-
361
+ });
362
+
129
363
 
130
364
 
131
365
  test('exhaust: elements', () => {
@@ -146,6 +380,19 @@ describe('utils', () => {
146
380
  });
147
381
 
148
382
 
383
+ test('times', () => {
384
+ const dates = [];
385
+ for (let i = 0; i < 10000; i++) {
386
+ const earliest = dayjs().subtract(u.rand(5, 50), 'D');
387
+ dates.push(TimeSoup());
388
+ }
389
+ const tooOld = dates.filter(d => dayjs(d).isBefore(dayjs.unix(0)));
390
+ const badYear = dates.filter(d => !d.startsWith('202'));
391
+ expect(dates.every(d => dayjs(d).isAfter(dayjs.unix(0)))).toBe(true);
392
+ expect(dates.every(d => d.startsWith('202'))).toBe(true);
393
+
394
+ });
395
+
149
396
  test('date', () => {
150
397
  const result = date();
151
398
  expect(dayjs(result()).isValid()).toBe(true);
@@ -184,16 +431,110 @@ describe('utils', () => {
184
431
  test('emoji: works', () => {
185
432
  const emojis = generateEmoji(5)();
186
433
  expect(typeof emojis).toBe('string');
187
- expect(emojis.split(', ').length).toBeLessThanOrEqual(5);
434
+ if (!Array.isArray(emojis)) {
435
+ expect(emojis.split(', ').length).toBeLessThanOrEqual(5);
436
+ }
437
+ if (Array.isArray(emojis)) {
438
+ expect(emojis.length).toBeLessThanOrEqual(5);
439
+ }
188
440
  });
189
441
 
190
442
  test('emoji: length', () => {
191
443
  const result = generateEmoji();
192
444
  const emojis = result();
193
445
  expect(typeof emojis).toBe('string');
194
- const emojiArray = emojis.split(', ');
195
- expect(emojiArray.length).toBeLessThanOrEqual(10); // Assuming max default is 10
446
+ if (!Array.isArray(emojis)) {
447
+ expect(emojis.split(', ').length).toBeLessThanOrEqual(10);
448
+ }
449
+ if (Array.isArray(emojis)) {
450
+ expect(emojis.length).toBeLessThanOrEqual(10);
451
+ }
452
+
453
+ });
454
+
196
455
 
456
+ test('weighArray: works', () => {
457
+ const arr = ['a', 'b', 'c'];
458
+ const weightedArr = weighArray(arr);
459
+ expect(weightedArr.length).toBeGreaterThanOrEqual(arr.length);
460
+ });
461
+
462
+ test('weighFunnels: works', () => {
463
+ const acc = [];
464
+ const funnel = { weight: 3 };
465
+ const result = weighFunnels(acc, funnel);
466
+ expect(result.length).toBe(3);
467
+ });
468
+
469
+ test('progress: outputs correctly', () => {
470
+ // @ts-ignore
471
+ const mockStdoutWrite = jest.spyOn(process.stdout, 'write').mockImplementation(() => { });
472
+ progress('test', 50);
473
+ expect(mockStdoutWrite).toHaveBeenCalled();
474
+ mockStdoutWrite.mockRestore();
475
+ });
476
+
477
+ test('range: works', () => {
478
+ const result = [];
479
+ range.call(result, 1, 5);
480
+ expect(result).toEqual([1, 2, 3, 4, 5]);
481
+ });
482
+
483
+
484
+
485
+ test('shuffleArray: works', () => {
486
+ const arr = [1, 2, 3, 4, 5];
487
+ const shuffled = shuffleArray([...arr]);
488
+ expect(shuffled).not.toEqual(arr);
489
+ expect(shuffled.sort()).toEqual(arr.sort());
490
+ });
491
+
492
+ test('shuffleExceptFirst: works', () => {
493
+ const arr = [1, 2, 3, 4, 5];
494
+ const shuffled = shuffleExceptFirst([...arr]);
495
+ expect(shuffled[0]).toBe(arr[0]);
496
+ expect(shuffled.slice(1).sort()).toEqual(arr.slice(1).sort());
497
+ });
498
+
499
+ test('shuffleExceptLast: works', () => {
500
+ const arr = [1, 2, 3, 4, 5];
501
+ const shuffled = shuffleExceptLast([...arr]);
502
+ expect(shuffled[shuffled.length - 1]).toBe(arr[arr.length - 1]);
503
+ expect(shuffled.slice(0, -1).sort()).toEqual(arr.slice(0, -1).sort());
504
+ });
505
+
506
+ test('fixFirstAndLast: works', () => {
507
+ const arr = [1, 2, 3, 4, 5];
508
+ const shuffled = fixFirstAndLast([...arr]);
509
+ expect(shuffled[0]).toBe(arr[0]);
510
+ expect(shuffled[shuffled.length - 1]).toBe(arr[arr.length - 1]);
511
+ expect(shuffled.slice(1, -1).sort()).toEqual(arr.slice(1, -1).sort());
512
+ });
513
+
514
+ test('shuffleMiddle: works', () => {
515
+ const arr = [1, 2, 3, 4, 5];
516
+ const shuffled = shuffleMiddle([...arr]);
517
+ expect(shuffled[0]).toBe(arr[0]);
518
+ expect(shuffled[shuffled.length - 1]).toBe(arr[arr.length - 1]);
519
+ expect(shuffled.slice(1, -1).sort()).toEqual(arr.slice(1, -1).sort());
520
+ });
521
+
522
+ test('shuffleOutside: works', () => {
523
+ const arr = [1, 2, 3, 4, 5];
524
+ const shuffled = shuffleOutside([...arr]);
525
+ expect(shuffled.slice(1, -1)).toEqual(arr.slice(1, -1));
526
+ });
527
+
528
+ test('box normal distribution', () => {
529
+ const values = [];
530
+ for (let i = 0; i < 10000; i++) {
531
+ values.push(boxMullerRandom());
532
+ }
533
+ const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
534
+ const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length;
535
+ const stdDev = Math.sqrt(variance);
536
+ expect(mean).toBeCloseTo(0, 1);
537
+ expect(stdDev).toBeCloseTo(1, 1);
197
538
  });
198
539
 
199
540
 
package/tmp/.gitkeep ADDED
File without changes
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "CommonJS",
5
+ "allowJs": true,
6
+ "checkJs": true,
7
+ "noEmit": true,
8
+ "strict": false,
9
+ "esModuleInterop": true,
10
+ "resolveJsonModule": true
11
+ },
12
+ "include": [
13
+ "**/*.js"
14
+ , "soupTemplates.mjs", "testSoup.mjs", "testCases.mjs" ],
15
+ "exclude": [
16
+ "node_modules"
17
+ ]
18
+ }