posthog-node 2.0.0-alpha9 → 2.0.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,3192 @@
1
+ // import PostHog from '../'
2
+ // Uncomment below line while developing to not compile code everytime
3
+ import { PostHogGlobal as PostHog } from '../src/posthog-node'
4
+ import { matchProperty, InconclusiveMatchError } from '../src/feature-flags'
5
+ jest.mock('undici')
6
+ import undici from 'undici'
7
+
8
+ jest.spyOn(global.console, 'debug').mockImplementation()
9
+
10
+ const mockedUndici = jest.mocked(undici, true)
11
+
12
+ export const localEvaluationImplementation =
13
+ (flags: any) =>
14
+ (url: any): Promise<any> => {
15
+ if ((url as any).includes('api/feature_flag/local_evaluation?token=TEST_API_KEY')) {
16
+ return Promise.resolve({
17
+ statusCode: 200,
18
+ body: {
19
+ text: () => Promise.resolve('ok'),
20
+ json: () => Promise.resolve(flags),
21
+ },
22
+ }) as any
23
+ }
24
+
25
+ return Promise.resolve({
26
+ statusCode: 401,
27
+ body: {
28
+ text: () => Promise.resolve('ok'),
29
+ json: () =>
30
+ Promise.resolve({
31
+ statusCode: 'ok',
32
+ }),
33
+ },
34
+ }) as any
35
+ }
36
+
37
+ export const decideImplementation =
38
+ (flags: any, decideStatus: number = 200) =>
39
+ (url: any): Promise<any> => {
40
+ if ((url as any).includes('/decide/')) {
41
+ return Promise.resolve({
42
+ status: decideStatus,
43
+ text: () => Promise.resolve('ok'),
44
+ json: () => {
45
+ if (decideStatus !== 200) {
46
+ return Promise.resolve(flags)
47
+ } else {
48
+ return Promise.resolve({
49
+ featureFlags: flags,
50
+ })
51
+ }
52
+ },
53
+ }) as any
54
+ }
55
+
56
+ return Promise.resolve({
57
+ status: 400,
58
+ text: () => Promise.resolve('ok'),
59
+ json: () =>
60
+ Promise.resolve({
61
+ status: 'ok',
62
+ }),
63
+ }) as any
64
+ }
65
+
66
+ describe('local evaluation', () => {
67
+ let posthog: PostHog
68
+
69
+ jest.useFakeTimers()
70
+
71
+ afterEach(async () => {
72
+ // ensure clean shutdown & no test interdependencies
73
+ await posthog.shutdownAsync()
74
+ })
75
+
76
+ it('evaluates person properties', async () => {
77
+ const flags = {
78
+ flags: [
79
+ {
80
+ id: 1,
81
+ name: 'Beta Feature',
82
+ key: 'person-flag',
83
+ is_simple_flag: true,
84
+ active: true,
85
+ filters: {
86
+ groups: [
87
+ {
88
+ properties: [
89
+ {
90
+ key: 'region',
91
+ operator: 'exact',
92
+ value: ['USA'],
93
+ type: 'person',
94
+ },
95
+ ],
96
+ rollout_percentage: null,
97
+ },
98
+ ],
99
+ },
100
+ },
101
+ ],
102
+ }
103
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
104
+
105
+ posthog = new PostHog('TEST_API_KEY', {
106
+ host: 'http://example.com',
107
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
108
+ })
109
+
110
+ expect(
111
+ await posthog.getFeatureFlag('person-flag', 'some-distinct-id', { personProperties: { region: 'USA' } })
112
+ ).toEqual(true)
113
+ expect(
114
+ await posthog.getFeatureFlag('person-flag', 'some-distinct-id', { personProperties: { region: 'Canada' } })
115
+ ).toEqual(false)
116
+ expect(mockedUndici.request).toHaveBeenCalled()
117
+ })
118
+
119
+ it('evaluates group properties', async () => {
120
+ const flags = {
121
+ flags: [
122
+ {
123
+ id: 1,
124
+ name: 'Beta Feature',
125
+ key: 'group-flag',
126
+ is_simple_flag: true,
127
+ active: true,
128
+ filters: {
129
+ aggregation_group_type_index: 0,
130
+ groups: [
131
+ {
132
+ properties: [
133
+ {
134
+ group_type_index: 0,
135
+ key: 'name',
136
+ operator: 'exact',
137
+ value: ['Project Name 1'],
138
+ type: 'group',
139
+ },
140
+ ],
141
+ rollout_percentage: 35,
142
+ },
143
+ ],
144
+ },
145
+ },
146
+ ],
147
+ group_type_mapping: { '0': 'company', '1': 'project' },
148
+ }
149
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
150
+
151
+ posthog = new PostHog('TEST_API_KEY', {
152
+ host: 'http://example.com',
153
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
154
+ })
155
+
156
+ // # groups not passed in, hence false
157
+ expect(
158
+ await posthog.getFeatureFlag('group-flag', 'some-distinct-id', {
159
+ groupProperties: { company: { name: 'Project Name 1' } },
160
+ })
161
+ ).toEqual(false)
162
+ expect(
163
+ await posthog.getFeatureFlag('group-flag', 'some-distinct-2', {
164
+ groupProperties: { company: { name: 'Project Name 2' } },
165
+ })
166
+ ).toEqual(false)
167
+
168
+ // # this is good
169
+ expect(
170
+ await posthog.getFeatureFlag('group-flag', 'some-distinct-2', {
171
+ groups: { company: 'amazon_without_rollout' },
172
+ groupProperties: { company: { name: 'Project Name 1' } },
173
+ })
174
+ ).toEqual(true)
175
+
176
+ // # rollout % not met
177
+ expect(
178
+ await posthog.getFeatureFlag('group-flag', 'some-distinct-2', {
179
+ groups: { company: 'amazon' },
180
+ groupProperties: { company: { name: 'Project Name 1' } },
181
+ })
182
+ ).toEqual(false)
183
+
184
+ // # property mismatch
185
+ expect(
186
+ await posthog.getFeatureFlag('group-flag', 'some-distinct-2', {
187
+ groups: { company: 'amazon_without_rollout' },
188
+ groupProperties: { company: { name: 'Project Name 2' } },
189
+ })
190
+ ).toEqual(false)
191
+
192
+ expect(mockedUndici.request).toHaveBeenCalled()
193
+ // decide not called
194
+ expect(mockedUndici.fetch).not.toHaveBeenCalled()
195
+ })
196
+
197
+ it('evaluates group properties and falls back to decide when group_type_mappings not present', async () => {
198
+ const flags = {
199
+ flags: [
200
+ {
201
+ id: 1,
202
+ name: 'Beta Feature',
203
+ key: 'group-flag',
204
+ is_simple_flag: true,
205
+ active: true,
206
+ filters: {
207
+ aggregation_group_type_index: 0,
208
+ groups: [
209
+ {
210
+ properties: [
211
+ {
212
+ group_type_index: 0,
213
+ key: 'name',
214
+ operator: 'exact',
215
+ value: ['Project Name 1'],
216
+ type: 'group',
217
+ },
218
+ ],
219
+ rollout_percentage: 35,
220
+ },
221
+ ],
222
+ },
223
+ },
224
+ ],
225
+ // "group_type_mapping": {"0": "company", "1": "project"}
226
+ }
227
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
228
+
229
+ mockedUndici.fetch.mockImplementation(decideImplementation({ 'group-flag': 'decide-fallback-value' }))
230
+
231
+ posthog = new PostHog('TEST_API_KEY', {
232
+ host: 'http://example.com',
233
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
234
+ })
235
+ // # group_type_mappings not present, so fallback to `/decide`
236
+ expect(
237
+ await posthog.getFeatureFlag('group-flag', 'some-distinct-2', {
238
+ groupProperties: {
239
+ company: { name: 'Project Name 1' },
240
+ },
241
+ })
242
+ ).toEqual('decide-fallback-value')
243
+ })
244
+
245
+ it('evaluates flag with complex definition', async () => {
246
+ const flags = {
247
+ flags: [
248
+ {
249
+ id: 1,
250
+ name: 'Beta Feature',
251
+ key: 'complex-flag',
252
+ is_simple_flag: false,
253
+ active: true,
254
+ filters: {
255
+ groups: [
256
+ {
257
+ properties: [
258
+ {
259
+ key: 'region',
260
+ operator: 'exact',
261
+ value: ['USA'],
262
+ type: 'person',
263
+ },
264
+ {
265
+ key: 'name',
266
+ operator: 'exact',
267
+ value: ['Aloha'],
268
+ type: 'person',
269
+ },
270
+ ],
271
+ rollout_percentage: undefined,
272
+ },
273
+ {
274
+ properties: [
275
+ {
276
+ key: 'email',
277
+ operator: 'exact',
278
+ value: ['a@b.com', 'b@c.com'],
279
+ type: 'person',
280
+ },
281
+ ],
282
+ rollout_percentage: 30,
283
+ },
284
+ {
285
+ properties: [
286
+ {
287
+ key: 'doesnt_matter',
288
+ operator: 'exact',
289
+ value: ['1', '2'],
290
+ type: 'person',
291
+ },
292
+ ],
293
+ rollout_percentage: 0,
294
+ },
295
+ ],
296
+ },
297
+ },
298
+ ],
299
+ }
300
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
301
+
302
+ mockedUndici.fetch.mockImplementation(decideImplementation({ 'complex-flag': 'decide-fallback-value' }))
303
+
304
+ posthog = new PostHog('TEST_API_KEY', {
305
+ host: 'http://example.com',
306
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
307
+ })
308
+
309
+ expect(
310
+ await posthog.getFeatureFlag('complex-flag', 'some-distinct-id', {
311
+ personProperties: { region: 'USA', name: 'Aloha' },
312
+ })
313
+ ).toEqual(true)
314
+ expect(mockedUndici.fetch).not.toHaveBeenCalled()
315
+
316
+ // # this distinctIDs hash is < rollout %
317
+ expect(
318
+ await posthog.getFeatureFlag('complex-flag', 'some-distinct-id_within_rollout?', {
319
+ personProperties: { region: 'USA', email: 'a@b.com' },
320
+ })
321
+ ).toEqual(true)
322
+ expect(mockedUndici.fetch).not.toHaveBeenCalled()
323
+
324
+ // # will fall back on `/decide`, as all properties present for second group, but that group resolves to false
325
+ expect(
326
+ await posthog.getFeatureFlag('complex-flag', 'some-distinct-id_outside_rollout?', {
327
+ personProperties: { region: 'USA', email: 'a@b.com' },
328
+ })
329
+ ).toEqual('decide-fallback-value')
330
+ expect(mockedUndici.fetch).toHaveBeenCalledWith(
331
+ 'http://example.com/decide/?v=2',
332
+ expect.objectContaining({
333
+ body: JSON.stringify({
334
+ token: 'TEST_API_KEY',
335
+ distinct_id: 'some-distinct-id_outside_rollout?',
336
+ groups: {},
337
+ person_properties: { region: 'USA', email: 'a@b.com' },
338
+ group_properties: {},
339
+ }),
340
+ })
341
+ )
342
+ mockedUndici.fetch.mockClear()
343
+
344
+ // # same as above
345
+ expect(
346
+ await posthog.getFeatureFlag('complex-flag', 'some-distinct-id', { personProperties: { doesnt_matter: '1' } })
347
+ ).toEqual('decide-fallback-value')
348
+ expect(mockedUndici.fetch).toHaveBeenCalledWith(
349
+ 'http://example.com/decide/?v=2',
350
+ expect.objectContaining({
351
+ body: JSON.stringify({
352
+ token: 'TEST_API_KEY',
353
+ distinct_id: 'some-distinct-id',
354
+ groups: {},
355
+ person_properties: { doesnt_matter: '1' },
356
+ group_properties: {},
357
+ }),
358
+ })
359
+ )
360
+ mockedUndici.fetch.mockClear()
361
+
362
+ expect(
363
+ await posthog.getFeatureFlag('complex-flag', 'some-distinct-id', { personProperties: { region: 'USA' } })
364
+ ).toEqual('decide-fallback-value')
365
+ expect(mockedUndici.fetch).toHaveBeenCalledTimes(1)
366
+ mockedUndici.fetch.mockClear()
367
+
368
+ // # won't need to fallback when all values are present, and resolves to False
369
+ expect(
370
+ await posthog.getFeatureFlag('complex-flag', 'some-distinct-id_outside_rollout?', {
371
+ personProperties: { region: 'USA', email: 'a@b.com', name: 'X', doesnt_matter: '1' },
372
+ })
373
+ ).toEqual(false)
374
+ expect(mockedUndici.fetch).not.toHaveBeenCalled()
375
+ })
376
+
377
+ it('falls back to decide', async () => {
378
+ const flags = {
379
+ flags: [
380
+ {
381
+ id: 1,
382
+ name: 'Beta Feature',
383
+ key: 'beta-feature',
384
+ active: true,
385
+ filters: {
386
+ groups: [
387
+ {
388
+ properties: [{ key: 'id', value: 98, operator: undefined, type: 'cohort' }],
389
+ rollout_percentage: 100,
390
+ },
391
+ ],
392
+ },
393
+ },
394
+ {
395
+ id: 2,
396
+ name: 'Beta Feature',
397
+ key: 'beta-feature2',
398
+ active: true,
399
+ filters: {
400
+ groups: [
401
+ {
402
+ properties: [
403
+ {
404
+ key: 'region',
405
+ operator: 'exact',
406
+ value: ['USA'],
407
+ type: 'person',
408
+ },
409
+ ],
410
+ rollout_percentage: 100,
411
+ },
412
+ ],
413
+ },
414
+ },
415
+ ],
416
+ }
417
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
418
+
419
+ mockedUndici.fetch.mockImplementation(
420
+ decideImplementation({ 'beta-feature': 'alakazam', 'beta-feature2': 'alakazam2' })
421
+ )
422
+
423
+ posthog = new PostHog('TEST_API_KEY', {
424
+ host: 'http://example.com',
425
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
426
+ })
427
+
428
+ // # beta-feature fallbacks to decide because property type is unknown
429
+ expect(await posthog.getFeatureFlag('beta-feature', 'some-distinct-id')).toEqual('alakazam')
430
+ expect(mockedUndici.fetch).toHaveBeenCalledTimes(1)
431
+ mockedUndici.fetch.mockClear()
432
+
433
+ // # beta-feature2 fallbacks to decide because region property not given with call
434
+ expect(await posthog.getFeatureFlag('beta-feature2', 'some-distinct-id')).toEqual('alakazam2')
435
+ expect(mockedUndici.fetch).toHaveBeenCalledTimes(1)
436
+ })
437
+
438
+ it('dont fall back to decide when local evaluation is set', async () => {
439
+ const flags = {
440
+ flags: [
441
+ {
442
+ id: 1,
443
+ name: 'Beta Feature',
444
+ key: 'beta-feature',
445
+ active: true,
446
+ filters: {
447
+ groups: [
448
+ {
449
+ properties: [{ key: 'id', value: 98, operator: undefined, type: 'cohort' }],
450
+ rollout_percentage: 100,
451
+ },
452
+ ],
453
+ },
454
+ },
455
+ {
456
+ id: 2,
457
+ name: 'Beta Feature',
458
+ key: 'beta-feature2',
459
+ active: true,
460
+ filters: {
461
+ groups: [
462
+ {
463
+ properties: [
464
+ {
465
+ key: 'region',
466
+ operator: 'exact',
467
+ value: ['USA'],
468
+ type: 'person',
469
+ },
470
+ ],
471
+ rollout_percentage: 100,
472
+ },
473
+ ],
474
+ },
475
+ },
476
+ ],
477
+ }
478
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
479
+
480
+ mockedUndici.fetch.mockImplementation(
481
+ decideImplementation({ 'beta-feature': 'alakazam', 'beta-feature2': 'alakazam2' })
482
+ )
483
+
484
+ posthog = new PostHog('TEST_API_KEY', {
485
+ host: 'http://example.com',
486
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
487
+ })
488
+
489
+ // # beta-feature should fallback to decide because property type is unknown
490
+ // # but doesn't because only_evaluate_locally is true
491
+ expect(await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', { onlyEvaluateLocally: true })).toEqual(
492
+ undefined
493
+ )
494
+ expect(await posthog.isFeatureEnabled('beta-feature', 'some-distinct-id', { onlyEvaluateLocally: true })).toEqual(
495
+ undefined
496
+ )
497
+ expect(mockedUndici.fetch).not.toHaveBeenCalled()
498
+
499
+ // # beta-feature2 should fallback to decide because region property not given with call
500
+ // # but doesn't because only_evaluate_locally is true
501
+ expect(await posthog.getFeatureFlag('beta-feature2', 'some-distinct-id', { onlyEvaluateLocally: true })).toEqual(
502
+ undefined
503
+ )
504
+ expect(await posthog.isFeatureEnabled('beta-feature2', 'some-distinct-id', { onlyEvaluateLocally: true })).toEqual(
505
+ undefined
506
+ )
507
+ expect(mockedUndici.fetch).not.toHaveBeenCalled()
508
+ })
509
+
510
+ it("doesn't return undefined when flag is evaluated successfully", async () => {
511
+ const flags = {
512
+ flags: [
513
+ {
514
+ id: 1,
515
+ name: 'Beta Feature',
516
+ key: 'beta-feature',
517
+ is_simple_flag: true,
518
+ active: true,
519
+ filters: {
520
+ groups: [
521
+ {
522
+ properties: [],
523
+ rollout_percentage: 0,
524
+ },
525
+ ],
526
+ },
527
+ },
528
+ ],
529
+ }
530
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
531
+
532
+ mockedUndici.fetch.mockImplementation(decideImplementation({}))
533
+
534
+ posthog = new PostHog('TEST_API_KEY', {
535
+ host: 'http://example.com',
536
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
537
+ })
538
+
539
+ // # beta-feature resolves to False
540
+ expect(await posthog.getFeatureFlag('beta-feature', 'some-distinct-id')).toEqual(false)
541
+ expect(await posthog.isFeatureEnabled('beta-feature', 'some-distinct-id')).toEqual(false)
542
+ expect(mockedUndici.fetch).not.toHaveBeenCalled()
543
+
544
+ // # beta-feature2 falls back to decide, and whatever decide returns is the value
545
+ expect(await posthog.getFeatureFlag('beta-feature2', 'some-distinct-id')).toEqual(false)
546
+ expect(await posthog.isFeatureEnabled('beta-feature2', 'some-distinct-id')).toEqual(false)
547
+ expect(mockedUndici.fetch).toHaveBeenCalledTimes(2)
548
+ })
549
+
550
+ it('returns undefined when decide errors out', async () => {
551
+ const flags = {
552
+ flags: [
553
+ {
554
+ id: 1,
555
+ name: 'Beta Feature',
556
+ key: 'beta-feature',
557
+ is_simple_flag: true,
558
+ active: true,
559
+ filters: {
560
+ groups: [
561
+ {
562
+ properties: [],
563
+ rollout_percentage: 0,
564
+ },
565
+ ],
566
+ },
567
+ },
568
+ ],
569
+ }
570
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
571
+
572
+ mockedUndici.fetch.mockImplementation(decideImplementation({ error: 'went wrong' }, 400))
573
+
574
+ posthog = new PostHog('TEST_API_KEY', {
575
+ host: 'http://example.com',
576
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
577
+ })
578
+
579
+ // # beta-feature2 falls back to decide, which on error returns undefined
580
+ expect(await posthog.getFeatureFlag('beta-feature2', 'some-distinct-id')).toEqual(undefined)
581
+ expect(await posthog.isFeatureEnabled('beta-feature2', 'some-distinct-id')).toEqual(undefined)
582
+ expect(mockedUndici.fetch).toHaveBeenCalledTimes(2)
583
+ })
584
+
585
+ it('experience continuity flags are not evaluated locally', async () => {
586
+ const flags = {
587
+ flags: [
588
+ {
589
+ id: 1,
590
+ name: 'Beta Feature',
591
+ key: 'beta-feature',
592
+ is_simple_flag: true,
593
+ active: true,
594
+ ensure_experience_continuity: true,
595
+ filters: {
596
+ groups: [
597
+ {
598
+ properties: [],
599
+ rollout_percentage: 0,
600
+ },
601
+ ],
602
+ },
603
+ },
604
+ ],
605
+ }
606
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
607
+
608
+ mockedUndici.fetch.mockImplementation(decideImplementation({ 'beta-feature': 'decide-fallback-value' }))
609
+
610
+ posthog = new PostHog('TEST_API_KEY', {
611
+ host: 'http://example.com',
612
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
613
+ })
614
+
615
+ // # beta-feature2 falls back to decide, which on error returns default
616
+ expect(await posthog.getFeatureFlag('beta-feature', 'some-distinct-id')).toEqual('decide-fallback-value')
617
+ expect(mockedUndici.fetch).toHaveBeenCalledTimes(1)
618
+ })
619
+
620
+ it('get all flags with fallback', async () => {
621
+ const flags = {
622
+ flags: [
623
+ {
624
+ id: 1,
625
+ name: 'Beta Feature',
626
+ key: 'beta-feature',
627
+ is_simple_flag: false,
628
+ active: true,
629
+ rollout_percentage: 100,
630
+ filters: {
631
+ groups: [
632
+ {
633
+ properties: [],
634
+ rollout_percentage: 100,
635
+ },
636
+ ],
637
+ },
638
+ },
639
+ {
640
+ id: 2,
641
+ name: 'Beta Feature',
642
+ key: 'disabled-feature',
643
+ is_simple_flag: false,
644
+ active: true,
645
+ filters: {
646
+ groups: [
647
+ {
648
+ properties: [],
649
+ rollout_percentage: 0,
650
+ },
651
+ ],
652
+ },
653
+ },
654
+ {
655
+ id: 3,
656
+ name: 'Beta Feature',
657
+ key: 'beta-feature2',
658
+ is_simple_flag: false,
659
+ active: true,
660
+ filters: {
661
+ groups: [
662
+ {
663
+ properties: [{ key: 'country', value: 'US' }],
664
+ rollout_percentage: 0,
665
+ },
666
+ ],
667
+ },
668
+ },
669
+ ],
670
+ }
671
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
672
+
673
+ mockedUndici.fetch.mockImplementation(
674
+ decideImplementation({ 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' })
675
+ )
676
+
677
+ posthog = new PostHog('TEST_API_KEY', {
678
+ host: 'http://example.com',
679
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
680
+ })
681
+
682
+ // # beta-feature value overridden by /decide
683
+ expect(await posthog.getAllFlags('distinct-id')).toEqual({
684
+ 'beta-feature': 'variant-1',
685
+ 'beta-feature2': 'variant-2',
686
+ 'disabled-feature': false,
687
+ })
688
+ expect(mockedUndici.fetch).toHaveBeenCalledTimes(1)
689
+ mockedUndici.fetch.mockClear()
690
+ })
691
+
692
+ it('get all flags with fallback but only_locally_evaluated set', async () => {
693
+ const flags = {
694
+ flags: [
695
+ {
696
+ id: 1,
697
+ name: 'Beta Feature',
698
+ key: 'beta-feature',
699
+ is_simple_flag: false,
700
+ active: true,
701
+ rollout_percentage: 100,
702
+ filters: {
703
+ groups: [
704
+ {
705
+ properties: [],
706
+ rollout_percentage: 100,
707
+ },
708
+ ],
709
+ },
710
+ },
711
+ {
712
+ id: 2,
713
+ name: 'Beta Feature',
714
+ key: 'disabled-feature',
715
+ is_simple_flag: false,
716
+ active: true,
717
+ filters: {
718
+ groups: [
719
+ {
720
+ properties: [],
721
+ rollout_percentage: 0,
722
+ },
723
+ ],
724
+ },
725
+ },
726
+ {
727
+ id: 3,
728
+ name: 'Beta Feature',
729
+ key: 'beta-feature2',
730
+ is_simple_flag: false,
731
+ active: true,
732
+ filters: {
733
+ groups: [
734
+ {
735
+ properties: [{ key: 'country', value: 'US' }],
736
+ rollout_percentage: 0,
737
+ },
738
+ ],
739
+ },
740
+ },
741
+ ],
742
+ }
743
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
744
+
745
+ mockedUndici.fetch.mockImplementation(
746
+ decideImplementation({ 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' })
747
+ )
748
+
749
+ posthog = new PostHog('TEST_API_KEY', {
750
+ host: 'http://example.com',
751
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
752
+ })
753
+
754
+ // # beta-feature2 has no value
755
+ expect(await posthog.getAllFlags('distinct-id', { onlyEvaluateLocally: true })).toEqual({
756
+ 'beta-feature': true,
757
+ 'disabled-feature': false,
758
+ })
759
+ expect(mockedUndici.fetch).not.toHaveBeenCalled()
760
+ })
761
+
762
+ it('get all flags with fallback, with no local flags', async () => {
763
+ const flags = {
764
+ flags: [],
765
+ }
766
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
767
+
768
+ mockedUndici.fetch.mockImplementation(
769
+ decideImplementation({ 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' })
770
+ )
771
+
772
+ posthog = new PostHog('TEST_API_KEY', {
773
+ host: 'http://example.com',
774
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
775
+ })
776
+
777
+ expect(await posthog.getAllFlags('distinct-id')).toEqual({
778
+ 'beta-feature': 'variant-1',
779
+ 'beta-feature2': 'variant-2',
780
+ })
781
+ expect(mockedUndici.fetch).toHaveBeenCalledTimes(1)
782
+ mockedUndici.fetch.mockClear()
783
+ })
784
+
785
+ it('get all flags with no fallback', async () => {
786
+ const flags = {
787
+ flags: [
788
+ {
789
+ id: 1,
790
+ name: 'Beta Feature',
791
+ key: 'beta-feature',
792
+ is_simple_flag: false,
793
+ active: true,
794
+ rollout_percentage: 100,
795
+ filters: {
796
+ groups: [
797
+ {
798
+ properties: [],
799
+ rollout_percentage: 100,
800
+ },
801
+ ],
802
+ },
803
+ },
804
+ {
805
+ id: 2,
806
+ name: 'Beta Feature',
807
+ key: 'disabled-feature',
808
+ is_simple_flag: false,
809
+ active: true,
810
+ filters: {
811
+ groups: [
812
+ {
813
+ properties: [],
814
+ rollout_percentage: 0,
815
+ },
816
+ ],
817
+ },
818
+ },
819
+ ],
820
+ }
821
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
822
+
823
+ mockedUndici.fetch.mockImplementation(
824
+ decideImplementation({ 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' })
825
+ )
826
+
827
+ posthog = new PostHog('TEST_API_KEY', {
828
+ host: 'http://example.com',
829
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
830
+ })
831
+
832
+ expect(await posthog.getAllFlags('distinct-id')).toEqual({ 'beta-feature': true, 'disabled-feature': false })
833
+ expect(mockedUndici.fetch).not.toHaveBeenCalled()
834
+ })
835
+
836
+ it('computes inactive flags locally as well', async () => {
837
+ const flags = {
838
+ flags: [
839
+ {
840
+ id: 1,
841
+ name: 'Beta Feature',
842
+ key: 'beta-feature',
843
+ is_simple_flag: false,
844
+ active: true,
845
+ rollout_percentage: 100,
846
+ filters: {
847
+ groups: [
848
+ {
849
+ properties: [],
850
+ rollout_percentage: 100,
851
+ },
852
+ ],
853
+ },
854
+ },
855
+ {
856
+ id: 2,
857
+ name: 'Beta Feature',
858
+ key: 'disabled-feature',
859
+ is_simple_flag: false,
860
+ active: true,
861
+ filters: {
862
+ groups: [
863
+ {
864
+ properties: [],
865
+ rollout_percentage: 0,
866
+ },
867
+ ],
868
+ },
869
+ },
870
+ ],
871
+ }
872
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
873
+
874
+ mockedUndici.fetch.mockImplementation(
875
+ decideImplementation({ 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' })
876
+ )
877
+
878
+ posthog = new PostHog('TEST_API_KEY', {
879
+ host: 'http://example.com',
880
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
881
+ })
882
+
883
+ expect(await posthog.getAllFlags('distinct-id')).toEqual({ 'beta-feature': true, 'disabled-feature': false })
884
+ expect(mockedUndici.fetch).not.toHaveBeenCalled()
885
+
886
+ // # Now, after a poll interval, flag 1 is inactive, and flag 2 rollout is set to 100%.
887
+ const flags2 = {
888
+ flags: [
889
+ {
890
+ id: 1,
891
+ name: 'Beta Feature',
892
+ key: 'beta-feature',
893
+ is_simple_flag: false,
894
+ active: false,
895
+ rollout_percentage: 100,
896
+ filters: {
897
+ groups: [
898
+ {
899
+ properties: [],
900
+ rollout_percentage: 100,
901
+ },
902
+ ],
903
+ },
904
+ },
905
+ {
906
+ id: 2,
907
+ name: 'Beta Feature',
908
+ key: 'disabled-feature',
909
+ is_simple_flag: false,
910
+ active: true,
911
+ filters: {
912
+ groups: [
913
+ {
914
+ properties: [],
915
+ rollout_percentage: 100,
916
+ },
917
+ ],
918
+ },
919
+ },
920
+ ],
921
+ }
922
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags2))
923
+
924
+ // # force reload to simulate poll interval
925
+ await posthog.reloadFeatureFlags()
926
+
927
+ expect(await posthog.getAllFlags('distinct-id')).toEqual({ 'beta-feature': false, 'disabled-feature': true })
928
+ expect(mockedUndici.fetch).not.toHaveBeenCalled()
929
+ })
930
+ })
931
+
932
+ describe('match properties', () => {
933
+ it('with operator exact', () => {
934
+ const property_a = { key: 'key', value: 'value' }
935
+
936
+ expect(matchProperty(property_a, { key: 'value' })).toBe(true)
937
+
938
+ expect(matchProperty(property_a, { key: 'value2' })).toBe(false)
939
+ expect(matchProperty(property_a, { key: '' })).toBe(false)
940
+ expect(matchProperty(property_a, { key: undefined })).toBe(false)
941
+
942
+ expect(() => matchProperty(property_a, { key2: 'value' })).toThrow(InconclusiveMatchError)
943
+ expect(() => matchProperty(property_a, {})).toThrow(InconclusiveMatchError)
944
+
945
+ const property_b = { key: 'key', value: 'value', operator: 'exact' }
946
+
947
+ expect(matchProperty(property_b, { key: 'value' })).toBe(true)
948
+ expect(matchProperty(property_b, { key: 'value2' })).toBe(false)
949
+
950
+ const property_c = { key: 'key', value: ['value1', 'value2', 'value3'], operator: 'exact' }
951
+ expect(matchProperty(property_c, { key: 'value1' })).toBe(true)
952
+ expect(matchProperty(property_c, { key: 'value2' })).toBe(true)
953
+ expect(matchProperty(property_c, { key: 'value3' })).toBe(true)
954
+
955
+ expect(matchProperty(property_c, { key: 'value4' })).toBe(false)
956
+
957
+ expect(() => matchProperty(property_c, { key2: 'value' })).toThrow(InconclusiveMatchError)
958
+ })
959
+
960
+ it('with operator is_not', () => {
961
+ const property_a = { key: 'key', value: 'value', operator: 'is_not' }
962
+
963
+ expect(matchProperty(property_a, { key: 'value' })).toBe(false)
964
+ expect(matchProperty(property_a, { key: 'value2' })).toBe(true)
965
+ expect(matchProperty(property_a, { key: '' })).toBe(true)
966
+ expect(matchProperty(property_a, { key: undefined })).toBe(true)
967
+
968
+ expect(() => matchProperty(property_a, { key2: 'value' })).toThrow(InconclusiveMatchError)
969
+ expect(() => matchProperty(property_a, {})).toThrow(InconclusiveMatchError)
970
+
971
+ const property_c = { key: 'key', value: ['value1', 'value2', 'value3'], operator: 'is_not' }
972
+ expect(matchProperty(property_c, { key: 'value1' })).toBe(false)
973
+ expect(matchProperty(property_c, { key: 'value2' })).toBe(false)
974
+ expect(matchProperty(property_c, { key: 'value3' })).toBe(false)
975
+
976
+ expect(matchProperty(property_c, { key: 'value4' })).toBe(true)
977
+ expect(matchProperty(property_c, { key: 'value5' })).toBe(true)
978
+ expect(matchProperty(property_c, { key: '' })).toBe(true)
979
+ expect(matchProperty(property_c, { key: undefined })).toBe(true)
980
+
981
+ expect(() => matchProperty(property_c, { key2: 'value' })).toThrow(InconclusiveMatchError)
982
+ })
983
+
984
+ it('with operator is_set', () => {
985
+ const property_a = { key: 'key', value: 'is_set', operator: 'is_set' }
986
+
987
+ expect(matchProperty(property_a, { key: 'value' })).toBe(true)
988
+ expect(matchProperty(property_a, { key: 'value2' })).toBe(true)
989
+ expect(matchProperty(property_a, { key: '' })).toBe(true)
990
+ expect(matchProperty(property_a, { key: undefined })).toBe(true)
991
+
992
+ expect(() => matchProperty(property_a, { key2: 'value' })).toThrow(InconclusiveMatchError)
993
+ expect(() => matchProperty(property_a, {})).toThrow(InconclusiveMatchError)
994
+ })
995
+
996
+ it('with operator icontains', () => {
997
+ const property_a = { key: 'key', value: 'vaLuE', operator: 'icontains' }
998
+
999
+ expect(matchProperty(property_a, { key: 'value' })).toBe(true)
1000
+ expect(matchProperty(property_a, { key: 'value2' })).toBe(true)
1001
+ expect(matchProperty(property_a, { key: 'vaLue3' })).toBe(true)
1002
+ expect(matchProperty(property_a, { key: '343tfvalUe5' })).toBe(true)
1003
+
1004
+ expect(matchProperty(property_a, { key: '' })).toBe(false)
1005
+ expect(matchProperty(property_a, { key: undefined })).toBe(false)
1006
+ expect(matchProperty(property_a, { key: 1234 })).toBe(false)
1007
+ expect(matchProperty(property_a, { key: '1234' })).toBe(false)
1008
+
1009
+ expect(() => matchProperty(property_a, { key2: 'value' })).toThrow(InconclusiveMatchError)
1010
+ expect(() => matchProperty(property_a, {})).toThrow(InconclusiveMatchError)
1011
+
1012
+ const property_b = { key: 'key', value: '3', operator: 'icontains' }
1013
+
1014
+ expect(matchProperty(property_b, { key: '3' })).toBe(true)
1015
+ expect(matchProperty(property_b, { key: 323 })).toBe(true)
1016
+ expect(matchProperty(property_b, { key: 'val3' })).toBe(true)
1017
+
1018
+ expect(matchProperty(property_b, { key: 'three' })).toBe(false)
1019
+ })
1020
+
1021
+ it('with operator regex', () => {
1022
+ const property_a = { key: 'key', value: '\\.com$', operator: 'regex' }
1023
+
1024
+ expect(matchProperty(property_a, { key: 'value.com' })).toBe(true)
1025
+ expect(matchProperty(property_a, { key: 'value2.com' })).toBe(true)
1026
+
1027
+ expect(matchProperty(property_a, { key: 'valuecom' })).toBe(false)
1028
+ expect(matchProperty(property_a, { key: 'valuecom' })).toBe(false)
1029
+ expect(matchProperty(property_a, { key: '.com343tfvalue5' })).toBe(false)
1030
+ expect(matchProperty(property_a, { key: undefined })).toBe(false)
1031
+ expect(matchProperty(property_a, { key: '' })).toBe(false)
1032
+
1033
+ expect(() => matchProperty(property_a, { key2: 'value' })).toThrow(InconclusiveMatchError)
1034
+ expect(() => matchProperty(property_a, {})).toThrow(InconclusiveMatchError)
1035
+
1036
+ const property_b = { key: 'key', value: '3', operator: 'regex' }
1037
+
1038
+ expect(matchProperty(property_b, { key: '3' })).toBe(true)
1039
+ expect(matchProperty(property_b, { key: 323 })).toBe(true)
1040
+ expect(matchProperty(property_b, { key: 'val3' })).toBe(true)
1041
+
1042
+ expect(matchProperty(property_b, { key: 'three' })).toBe(false)
1043
+
1044
+ // # invalid regex
1045
+ const property_c = { key: 'key', value: '?*', operator: 'regex' }
1046
+ expect(matchProperty(property_c, { key: 'value.com' })).toBe(false)
1047
+ expect(matchProperty(property_c, { key: 'value2' })).toBe(false)
1048
+
1049
+ // # non string value
1050
+ const property_d = { key: 'key', value: 4, operator: 'regex' }
1051
+ expect(matchProperty(property_d, { key: '4' })).toBe(true)
1052
+ expect(matchProperty(property_d, { key: 4 })).toBe(true)
1053
+
1054
+ expect(matchProperty(property_d, { key: 'value' })).toBe(false)
1055
+
1056
+ // # non string value - not_regex
1057
+ const property_e = { key: 'key', value: 4, operator: 'not_regex' }
1058
+ expect(matchProperty(property_e, { key: '4' })).toBe(false)
1059
+ expect(matchProperty(property_e, { key: 4 })).toBe(false)
1060
+
1061
+ expect(matchProperty(property_e, { key: 'value' })).toBe(true)
1062
+ })
1063
+
1064
+ it('with math operators', () => {
1065
+ const property_a = { key: 'key', value: 1, operator: 'gt' }
1066
+
1067
+ expect(matchProperty(property_a, { key: 2 })).toBe(true)
1068
+ expect(matchProperty(property_a, { key: 3 })).toBe(true)
1069
+
1070
+ expect(matchProperty(property_a, { key: 0 })).toBe(false)
1071
+ expect(matchProperty(property_a, { key: -1 })).toBe(false)
1072
+ expect(matchProperty(property_a, { key: '23' })).toBe(false)
1073
+
1074
+ const property_b = { key: 'key', value: 1, operator: 'lt' }
1075
+ expect(matchProperty(property_b, { key: 0 })).toBe(true)
1076
+ expect(matchProperty(property_b, { key: -1 })).toBe(true)
1077
+ expect(matchProperty(property_b, { key: -3 })).toBe(true)
1078
+
1079
+ expect(matchProperty(property_b, { key: '3' })).toBe(false)
1080
+ expect(matchProperty(property_b, { key: '1' })).toBe(false)
1081
+ expect(matchProperty(property_b, { key: 1 })).toBe(false)
1082
+
1083
+ const property_c = { key: 'key', value: 1, operator: 'gte' }
1084
+ expect(matchProperty(property_c, { key: 2 })).toBe(true)
1085
+ expect(matchProperty(property_c, { key: 1 })).toBe(true)
1086
+
1087
+ expect(matchProperty(property_c, { key: 0 })).toBe(false)
1088
+ expect(matchProperty(property_c, { key: -1 })).toBe(false)
1089
+ expect(matchProperty(property_c, { key: -3 })).toBe(false)
1090
+ expect(matchProperty(property_c, { key: '3' })).toBe(false)
1091
+
1092
+ const property_d = { key: 'key', value: '43', operator: 'lte' }
1093
+ expect(matchProperty(property_d, { key: '43' })).toBe(true)
1094
+ expect(matchProperty(property_d, { key: '42' })).toBe(true)
1095
+
1096
+ expect(matchProperty(property_d, { key: '44' })).toBe(false)
1097
+ expect(matchProperty(property_d, { key: 44 })).toBe(false)
1098
+ })
1099
+ })
1100
+
1101
+ describe('consistency tests', () => {
1102
+ // # These tests are the same across all libraries
1103
+ // # See https://github.com/PostHog/posthog/blob/master/posthog/test/test_feature_flag.py#L627
1104
+ // # where this test has directly been copied from.
1105
+ // # They ensure that the server and library hash calculations are in sync.
1106
+
1107
+ let posthog: PostHog
1108
+ jest.useFakeTimers()
1109
+
1110
+ afterEach(async () => {
1111
+ await posthog.shutdownAsync()
1112
+ })
1113
+
1114
+ it('is consistent for simple flags', () => {
1115
+ const flags = {
1116
+ flags: [
1117
+ {
1118
+ id: 1,
1119
+ name: '',
1120
+ key: 'simple-flag',
1121
+ active: true,
1122
+ is_simple_flag: false,
1123
+ filters: {
1124
+ groups: [{ properties: [], rollout_percentage: 45 }],
1125
+ },
1126
+ },
1127
+ ],
1128
+ }
1129
+
1130
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
1131
+
1132
+ mockedUndici.fetch.mockImplementation(decideImplementation({}, 400))
1133
+
1134
+ posthog = new PostHog('TEST_API_KEY', {
1135
+ host: 'http://example.com',
1136
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
1137
+ })
1138
+
1139
+ const results = [
1140
+ false,
1141
+ true,
1142
+ true,
1143
+ false,
1144
+ true,
1145
+ false,
1146
+ false,
1147
+ true,
1148
+ false,
1149
+ true,
1150
+ false,
1151
+ true,
1152
+ true,
1153
+ false,
1154
+ true,
1155
+ false,
1156
+ false,
1157
+ false,
1158
+ true,
1159
+ true,
1160
+ false,
1161
+ true,
1162
+ false,
1163
+ false,
1164
+ true,
1165
+ false,
1166
+ true,
1167
+ true,
1168
+ false,
1169
+ false,
1170
+ false,
1171
+ true,
1172
+ true,
1173
+ true,
1174
+ true,
1175
+ false,
1176
+ false,
1177
+ false,
1178
+ false,
1179
+ false,
1180
+ false,
1181
+ true,
1182
+ true,
1183
+ false,
1184
+ true,
1185
+ true,
1186
+ false,
1187
+ false,
1188
+ false,
1189
+ true,
1190
+ true,
1191
+ false,
1192
+ false,
1193
+ false,
1194
+ false,
1195
+ true,
1196
+ false,
1197
+ true,
1198
+ false,
1199
+ true,
1200
+ false,
1201
+ true,
1202
+ true,
1203
+ false,
1204
+ true,
1205
+ false,
1206
+ true,
1207
+ false,
1208
+ true,
1209
+ true,
1210
+ false,
1211
+ false,
1212
+ true,
1213
+ false,
1214
+ false,
1215
+ true,
1216
+ false,
1217
+ true,
1218
+ false,
1219
+ false,
1220
+ true,
1221
+ false,
1222
+ false,
1223
+ false,
1224
+ true,
1225
+ true,
1226
+ false,
1227
+ true,
1228
+ true,
1229
+ false,
1230
+ true,
1231
+ true,
1232
+ true,
1233
+ true,
1234
+ true,
1235
+ false,
1236
+ true,
1237
+ true,
1238
+ false,
1239
+ false,
1240
+ true,
1241
+ true,
1242
+ true,
1243
+ true,
1244
+ false,
1245
+ false,
1246
+ true,
1247
+ false,
1248
+ true,
1249
+ true,
1250
+ true,
1251
+ false,
1252
+ false,
1253
+ false,
1254
+ false,
1255
+ false,
1256
+ true,
1257
+ false,
1258
+ false,
1259
+ true,
1260
+ true,
1261
+ true,
1262
+ false,
1263
+ false,
1264
+ true,
1265
+ false,
1266
+ true,
1267
+ false,
1268
+ false,
1269
+ true,
1270
+ false,
1271
+ false,
1272
+ false,
1273
+ false,
1274
+ false,
1275
+ false,
1276
+ false,
1277
+ false,
1278
+ true,
1279
+ true,
1280
+ false,
1281
+ false,
1282
+ true,
1283
+ false,
1284
+ false,
1285
+ true,
1286
+ true,
1287
+ false,
1288
+ false,
1289
+ true,
1290
+ false,
1291
+ true,
1292
+ false,
1293
+ true,
1294
+ true,
1295
+ true,
1296
+ false,
1297
+ false,
1298
+ false,
1299
+ true,
1300
+ false,
1301
+ false,
1302
+ false,
1303
+ false,
1304
+ true,
1305
+ true,
1306
+ false,
1307
+ true,
1308
+ true,
1309
+ false,
1310
+ true,
1311
+ false,
1312
+ true,
1313
+ true,
1314
+ false,
1315
+ true,
1316
+ false,
1317
+ true,
1318
+ true,
1319
+ true,
1320
+ false,
1321
+ true,
1322
+ false,
1323
+ false,
1324
+ true,
1325
+ true,
1326
+ false,
1327
+ true,
1328
+ false,
1329
+ true,
1330
+ true,
1331
+ false,
1332
+ false,
1333
+ true,
1334
+ true,
1335
+ true,
1336
+ true,
1337
+ false,
1338
+ true,
1339
+ true,
1340
+ false,
1341
+ false,
1342
+ true,
1343
+ false,
1344
+ true,
1345
+ false,
1346
+ false,
1347
+ true,
1348
+ true,
1349
+ false,
1350
+ true,
1351
+ false,
1352
+ true,
1353
+ false,
1354
+ false,
1355
+ false,
1356
+ false,
1357
+ false,
1358
+ false,
1359
+ false,
1360
+ true,
1361
+ false,
1362
+ true,
1363
+ true,
1364
+ false,
1365
+ false,
1366
+ true,
1367
+ false,
1368
+ true,
1369
+ false,
1370
+ false,
1371
+ false,
1372
+ true,
1373
+ false,
1374
+ true,
1375
+ false,
1376
+ false,
1377
+ false,
1378
+ true,
1379
+ false,
1380
+ false,
1381
+ true,
1382
+ false,
1383
+ true,
1384
+ true,
1385
+ false,
1386
+ false,
1387
+ false,
1388
+ false,
1389
+ true,
1390
+ false,
1391
+ false,
1392
+ false,
1393
+ false,
1394
+ false,
1395
+ false,
1396
+ false,
1397
+ false,
1398
+ false,
1399
+ false,
1400
+ false,
1401
+ false,
1402
+ false,
1403
+ true,
1404
+ true,
1405
+ false,
1406
+ true,
1407
+ false,
1408
+ true,
1409
+ true,
1410
+ false,
1411
+ true,
1412
+ false,
1413
+ true,
1414
+ false,
1415
+ false,
1416
+ false,
1417
+ true,
1418
+ true,
1419
+ true,
1420
+ true,
1421
+ false,
1422
+ false,
1423
+ false,
1424
+ false,
1425
+ false,
1426
+ true,
1427
+ true,
1428
+ true,
1429
+ false,
1430
+ false,
1431
+ true,
1432
+ true,
1433
+ false,
1434
+ false,
1435
+ false,
1436
+ false,
1437
+ false,
1438
+ true,
1439
+ false,
1440
+ true,
1441
+ true,
1442
+ true,
1443
+ true,
1444
+ false,
1445
+ true,
1446
+ true,
1447
+ true,
1448
+ false,
1449
+ false,
1450
+ true,
1451
+ false,
1452
+ true,
1453
+ false,
1454
+ false,
1455
+ true,
1456
+ true,
1457
+ true,
1458
+ false,
1459
+ true,
1460
+ false,
1461
+ false,
1462
+ false,
1463
+ true,
1464
+ true,
1465
+ false,
1466
+ true,
1467
+ false,
1468
+ true,
1469
+ false,
1470
+ true,
1471
+ true,
1472
+ true,
1473
+ true,
1474
+ true,
1475
+ false,
1476
+ false,
1477
+ true,
1478
+ false,
1479
+ true,
1480
+ false,
1481
+ true,
1482
+ true,
1483
+ true,
1484
+ false,
1485
+ true,
1486
+ false,
1487
+ true,
1488
+ true,
1489
+ false,
1490
+ true,
1491
+ true,
1492
+ true,
1493
+ true,
1494
+ true,
1495
+ false,
1496
+ false,
1497
+ false,
1498
+ false,
1499
+ false,
1500
+ true,
1501
+ false,
1502
+ true,
1503
+ false,
1504
+ false,
1505
+ true,
1506
+ true,
1507
+ false,
1508
+ false,
1509
+ false,
1510
+ true,
1511
+ false,
1512
+ true,
1513
+ true,
1514
+ true,
1515
+ true,
1516
+ false,
1517
+ false,
1518
+ false,
1519
+ false,
1520
+ true,
1521
+ true,
1522
+ false,
1523
+ false,
1524
+ true,
1525
+ true,
1526
+ false,
1527
+ true,
1528
+ true,
1529
+ true,
1530
+ true,
1531
+ false,
1532
+ true,
1533
+ true,
1534
+ true,
1535
+ false,
1536
+ false,
1537
+ true,
1538
+ true,
1539
+ false,
1540
+ false,
1541
+ true,
1542
+ false,
1543
+ false,
1544
+ true,
1545
+ false,
1546
+ false,
1547
+ false,
1548
+ false,
1549
+ false,
1550
+ false,
1551
+ false,
1552
+ false,
1553
+ false,
1554
+ false,
1555
+ true,
1556
+ true,
1557
+ false,
1558
+ false,
1559
+ true,
1560
+ false,
1561
+ false,
1562
+ true,
1563
+ false,
1564
+ true,
1565
+ false,
1566
+ false,
1567
+ true,
1568
+ false,
1569
+ false,
1570
+ false,
1571
+ false,
1572
+ false,
1573
+ false,
1574
+ true,
1575
+ false,
1576
+ false,
1577
+ false,
1578
+ false,
1579
+ false,
1580
+ false,
1581
+ false,
1582
+ false,
1583
+ false,
1584
+ true,
1585
+ true,
1586
+ true,
1587
+ false,
1588
+ false,
1589
+ false,
1590
+ true,
1591
+ false,
1592
+ true,
1593
+ false,
1594
+ false,
1595
+ false,
1596
+ true,
1597
+ false,
1598
+ false,
1599
+ false,
1600
+ false,
1601
+ false,
1602
+ false,
1603
+ false,
1604
+ true,
1605
+ false,
1606
+ false,
1607
+ false,
1608
+ false,
1609
+ false,
1610
+ false,
1611
+ false,
1612
+ false,
1613
+ true,
1614
+ false,
1615
+ true,
1616
+ false,
1617
+ true,
1618
+ true,
1619
+ true,
1620
+ false,
1621
+ false,
1622
+ false,
1623
+ true,
1624
+ true,
1625
+ true,
1626
+ false,
1627
+ true,
1628
+ false,
1629
+ true,
1630
+ true,
1631
+ false,
1632
+ false,
1633
+ false,
1634
+ true,
1635
+ false,
1636
+ false,
1637
+ false,
1638
+ false,
1639
+ true,
1640
+ false,
1641
+ true,
1642
+ false,
1643
+ true,
1644
+ true,
1645
+ false,
1646
+ true,
1647
+ false,
1648
+ false,
1649
+ false,
1650
+ true,
1651
+ false,
1652
+ false,
1653
+ true,
1654
+ true,
1655
+ false,
1656
+ true,
1657
+ false,
1658
+ false,
1659
+ false,
1660
+ false,
1661
+ false,
1662
+ false,
1663
+ true,
1664
+ true,
1665
+ false,
1666
+ false,
1667
+ true,
1668
+ false,
1669
+ false,
1670
+ true,
1671
+ true,
1672
+ true,
1673
+ false,
1674
+ false,
1675
+ false,
1676
+ true,
1677
+ false,
1678
+ false,
1679
+ false,
1680
+ false,
1681
+ true,
1682
+ false,
1683
+ true,
1684
+ false,
1685
+ false,
1686
+ false,
1687
+ true,
1688
+ false,
1689
+ true,
1690
+ true,
1691
+ false,
1692
+ true,
1693
+ false,
1694
+ true,
1695
+ false,
1696
+ true,
1697
+ false,
1698
+ false,
1699
+ true,
1700
+ false,
1701
+ false,
1702
+ true,
1703
+ false,
1704
+ true,
1705
+ false,
1706
+ true,
1707
+ false,
1708
+ true,
1709
+ false,
1710
+ false,
1711
+ true,
1712
+ true,
1713
+ true,
1714
+ true,
1715
+ false,
1716
+ true,
1717
+ false,
1718
+ false,
1719
+ false,
1720
+ false,
1721
+ false,
1722
+ true,
1723
+ false,
1724
+ false,
1725
+ true,
1726
+ false,
1727
+ false,
1728
+ true,
1729
+ true,
1730
+ false,
1731
+ false,
1732
+ false,
1733
+ false,
1734
+ true,
1735
+ true,
1736
+ true,
1737
+ false,
1738
+ false,
1739
+ true,
1740
+ false,
1741
+ false,
1742
+ true,
1743
+ true,
1744
+ true,
1745
+ true,
1746
+ false,
1747
+ false,
1748
+ false,
1749
+ true,
1750
+ false,
1751
+ false,
1752
+ false,
1753
+ true,
1754
+ false,
1755
+ false,
1756
+ true,
1757
+ true,
1758
+ true,
1759
+ true,
1760
+ false,
1761
+ false,
1762
+ true,
1763
+ true,
1764
+ false,
1765
+ true,
1766
+ false,
1767
+ true,
1768
+ false,
1769
+ false,
1770
+ true,
1771
+ true,
1772
+ false,
1773
+ true,
1774
+ true,
1775
+ true,
1776
+ true,
1777
+ false,
1778
+ false,
1779
+ true,
1780
+ false,
1781
+ false,
1782
+ true,
1783
+ true,
1784
+ false,
1785
+ true,
1786
+ false,
1787
+ true,
1788
+ false,
1789
+ false,
1790
+ true,
1791
+ false,
1792
+ false,
1793
+ false,
1794
+ false,
1795
+ true,
1796
+ true,
1797
+ true,
1798
+ false,
1799
+ true,
1800
+ false,
1801
+ false,
1802
+ true,
1803
+ false,
1804
+ false,
1805
+ true,
1806
+ false,
1807
+ false,
1808
+ false,
1809
+ false,
1810
+ true,
1811
+ false,
1812
+ true,
1813
+ false,
1814
+ true,
1815
+ true,
1816
+ false,
1817
+ false,
1818
+ true,
1819
+ false,
1820
+ true,
1821
+ true,
1822
+ true,
1823
+ false,
1824
+ false,
1825
+ false,
1826
+ false,
1827
+ true,
1828
+ true,
1829
+ false,
1830
+ true,
1831
+ false,
1832
+ false,
1833
+ false,
1834
+ true,
1835
+ false,
1836
+ false,
1837
+ false,
1838
+ false,
1839
+ true,
1840
+ true,
1841
+ true,
1842
+ false,
1843
+ false,
1844
+ false,
1845
+ true,
1846
+ true,
1847
+ true,
1848
+ true,
1849
+ false,
1850
+ true,
1851
+ true,
1852
+ false,
1853
+ true,
1854
+ true,
1855
+ true,
1856
+ false,
1857
+ true,
1858
+ false,
1859
+ false,
1860
+ true,
1861
+ false,
1862
+ true,
1863
+ true,
1864
+ true,
1865
+ true,
1866
+ false,
1867
+ true,
1868
+ false,
1869
+ true,
1870
+ false,
1871
+ true,
1872
+ false,
1873
+ false,
1874
+ true,
1875
+ true,
1876
+ false,
1877
+ false,
1878
+ true,
1879
+ false,
1880
+ true,
1881
+ false,
1882
+ false,
1883
+ false,
1884
+ false,
1885
+ true,
1886
+ false,
1887
+ true,
1888
+ false,
1889
+ false,
1890
+ false,
1891
+ true,
1892
+ true,
1893
+ true,
1894
+ false,
1895
+ false,
1896
+ false,
1897
+ true,
1898
+ false,
1899
+ true,
1900
+ true,
1901
+ false,
1902
+ false,
1903
+ false,
1904
+ false,
1905
+ false,
1906
+ true,
1907
+ false,
1908
+ true,
1909
+ false,
1910
+ false,
1911
+ true,
1912
+ true,
1913
+ false,
1914
+ true,
1915
+ true,
1916
+ true,
1917
+ true,
1918
+ false,
1919
+ false,
1920
+ true,
1921
+ false,
1922
+ false,
1923
+ true,
1924
+ false,
1925
+ true,
1926
+ false,
1927
+ true,
1928
+ true,
1929
+ false,
1930
+ false,
1931
+ false,
1932
+ true,
1933
+ false,
1934
+ true,
1935
+ true,
1936
+ false,
1937
+ false,
1938
+ false,
1939
+ true,
1940
+ false,
1941
+ true,
1942
+ false,
1943
+ true,
1944
+ true,
1945
+ false,
1946
+ true,
1947
+ false,
1948
+ false,
1949
+ true,
1950
+ false,
1951
+ false,
1952
+ false,
1953
+ true,
1954
+ true,
1955
+ true,
1956
+ false,
1957
+ false,
1958
+ false,
1959
+ false,
1960
+ false,
1961
+ true,
1962
+ false,
1963
+ false,
1964
+ true,
1965
+ true,
1966
+ true,
1967
+ true,
1968
+ true,
1969
+ false,
1970
+ false,
1971
+ false,
1972
+ false,
1973
+ false,
1974
+ false,
1975
+ false,
1976
+ false,
1977
+ true,
1978
+ true,
1979
+ true,
1980
+ false,
1981
+ false,
1982
+ true,
1983
+ true,
1984
+ false,
1985
+ true,
1986
+ true,
1987
+ false,
1988
+ true,
1989
+ false,
1990
+ true,
1991
+ false,
1992
+ false,
1993
+ false,
1994
+ true,
1995
+ false,
1996
+ false,
1997
+ true,
1998
+ false,
1999
+ false,
2000
+ true,
2001
+ true,
2002
+ true,
2003
+ true,
2004
+ false,
2005
+ false,
2006
+ true,
2007
+ false,
2008
+ true,
2009
+ true,
2010
+ false,
2011
+ false,
2012
+ true,
2013
+ false,
2014
+ false,
2015
+ true,
2016
+ true,
2017
+ false,
2018
+ true,
2019
+ false,
2020
+ false,
2021
+ true,
2022
+ true,
2023
+ true,
2024
+ false,
2025
+ false,
2026
+ false,
2027
+ false,
2028
+ false,
2029
+ true,
2030
+ false,
2031
+ true,
2032
+ false,
2033
+ false,
2034
+ false,
2035
+ false,
2036
+ false,
2037
+ true,
2038
+ true,
2039
+ false,
2040
+ true,
2041
+ true,
2042
+ true,
2043
+ false,
2044
+ false,
2045
+ false,
2046
+ false,
2047
+ true,
2048
+ true,
2049
+ true,
2050
+ true,
2051
+ false,
2052
+ true,
2053
+ true,
2054
+ false,
2055
+ true,
2056
+ false,
2057
+ true,
2058
+ false,
2059
+ true,
2060
+ false,
2061
+ false,
2062
+ false,
2063
+ false,
2064
+ true,
2065
+ true,
2066
+ true,
2067
+ true,
2068
+ false,
2069
+ false,
2070
+ true,
2071
+ false,
2072
+ true,
2073
+ true,
2074
+ false,
2075
+ false,
2076
+ false,
2077
+ false,
2078
+ false,
2079
+ false,
2080
+ true,
2081
+ false,
2082
+ true,
2083
+ false,
2084
+ true,
2085
+ true,
2086
+ false,
2087
+ false,
2088
+ true,
2089
+ true,
2090
+ true,
2091
+ true,
2092
+ false,
2093
+ false,
2094
+ true,
2095
+ false,
2096
+ true,
2097
+ true,
2098
+ false,
2099
+ false,
2100
+ true,
2101
+ true,
2102
+ true,
2103
+ false,
2104
+ true,
2105
+ false,
2106
+ false,
2107
+ true,
2108
+ true,
2109
+ false,
2110
+ false,
2111
+ false,
2112
+ true,
2113
+ false,
2114
+ false,
2115
+ true,
2116
+ false,
2117
+ false,
2118
+ false,
2119
+ true,
2120
+ true,
2121
+ true,
2122
+ true,
2123
+ false,
2124
+ true,
2125
+ false,
2126
+ true,
2127
+ false,
2128
+ true,
2129
+ false,
2130
+ true,
2131
+ false,
2132
+ false,
2133
+ true,
2134
+ false,
2135
+ false,
2136
+ true,
2137
+ false,
2138
+ true,
2139
+ true,
2140
+ ]
2141
+
2142
+ results.forEach(async (result, index) => {
2143
+ const distinctId = `distinct_id_${index}`
2144
+ const value = await posthog.isFeatureEnabled('simple-flag', distinctId)
2145
+ expect(value).toBe(result)
2146
+ })
2147
+ })
2148
+
2149
+ it('is consistent for multivariate flags', async () => {
2150
+ const flags = {
2151
+ flags: [
2152
+ {
2153
+ id: 1,
2154
+ name: 'Beta Feature',
2155
+ key: 'multivariate-flag',
2156
+ is_simple_flag: false,
2157
+ active: true,
2158
+ filters: {
2159
+ groups: [{ properties: [], rollout_percentage: 55 }],
2160
+ multivariate: {
2161
+ variants: [
2162
+ { key: 'first-variant', name: 'First Variant', rollout_percentage: 50 },
2163
+ { key: 'second-variant', name: 'Second Variant', rollout_percentage: 20 },
2164
+ { key: 'third-variant', name: 'Third Variant', rollout_percentage: 20 },
2165
+ { key: 'fourth-variant', name: 'Fourth Variant', rollout_percentage: 5 },
2166
+ { key: 'fifth-variant', name: 'Fifth Variant', rollout_percentage: 5 },
2167
+ ],
2168
+ },
2169
+ },
2170
+ },
2171
+ ],
2172
+ }
2173
+
2174
+ mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
2175
+
2176
+ mockedUndici.fetch.mockImplementation(decideImplementation({}, 400))
2177
+
2178
+ posthog = new PostHog('TEST_API_KEY', {
2179
+ host: 'http://example.com',
2180
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
2181
+ })
2182
+
2183
+ const results = [
2184
+ 'second-variant',
2185
+ 'second-variant',
2186
+ 'first-variant',
2187
+ false,
2188
+ false,
2189
+ 'second-variant',
2190
+ 'first-variant',
2191
+ false,
2192
+ false,
2193
+ false,
2194
+ 'first-variant',
2195
+ 'third-variant',
2196
+ false,
2197
+ 'first-variant',
2198
+ 'second-variant',
2199
+ 'first-variant',
2200
+ false,
2201
+ false,
2202
+ 'fourth-variant',
2203
+ 'first-variant',
2204
+ false,
2205
+ 'third-variant',
2206
+ false,
2207
+ false,
2208
+ false,
2209
+ 'first-variant',
2210
+ 'first-variant',
2211
+ 'first-variant',
2212
+ 'first-variant',
2213
+ 'first-variant',
2214
+ 'first-variant',
2215
+ 'third-variant',
2216
+ false,
2217
+ 'third-variant',
2218
+ 'second-variant',
2219
+ 'first-variant',
2220
+ false,
2221
+ 'third-variant',
2222
+ false,
2223
+ false,
2224
+ 'first-variant',
2225
+ 'second-variant',
2226
+ false,
2227
+ 'first-variant',
2228
+ 'first-variant',
2229
+ 'second-variant',
2230
+ false,
2231
+ 'first-variant',
2232
+ false,
2233
+ false,
2234
+ 'first-variant',
2235
+ 'first-variant',
2236
+ 'first-variant',
2237
+ 'second-variant',
2238
+ 'first-variant',
2239
+ false,
2240
+ 'second-variant',
2241
+ 'second-variant',
2242
+ 'third-variant',
2243
+ 'second-variant',
2244
+ 'first-variant',
2245
+ false,
2246
+ 'first-variant',
2247
+ 'second-variant',
2248
+ 'fourth-variant',
2249
+ false,
2250
+ 'first-variant',
2251
+ 'first-variant',
2252
+ 'first-variant',
2253
+ false,
2254
+ 'first-variant',
2255
+ 'second-variant',
2256
+ false,
2257
+ 'third-variant',
2258
+ false,
2259
+ false,
2260
+ false,
2261
+ false,
2262
+ false,
2263
+ false,
2264
+ 'first-variant',
2265
+ 'fifth-variant',
2266
+ false,
2267
+ 'second-variant',
2268
+ 'first-variant',
2269
+ 'second-variant',
2270
+ false,
2271
+ 'third-variant',
2272
+ 'third-variant',
2273
+ false,
2274
+ false,
2275
+ false,
2276
+ false,
2277
+ 'third-variant',
2278
+ false,
2279
+ false,
2280
+ 'first-variant',
2281
+ 'first-variant',
2282
+ false,
2283
+ 'third-variant',
2284
+ 'third-variant',
2285
+ false,
2286
+ 'third-variant',
2287
+ 'second-variant',
2288
+ 'third-variant',
2289
+ false,
2290
+ false,
2291
+ 'second-variant',
2292
+ 'first-variant',
2293
+ false,
2294
+ false,
2295
+ 'first-variant',
2296
+ false,
2297
+ false,
2298
+ false,
2299
+ false,
2300
+ 'first-variant',
2301
+ 'first-variant',
2302
+ 'first-variant',
2303
+ false,
2304
+ false,
2305
+ false,
2306
+ 'first-variant',
2307
+ 'first-variant',
2308
+ false,
2309
+ 'first-variant',
2310
+ 'first-variant',
2311
+ false,
2312
+ false,
2313
+ false,
2314
+ false,
2315
+ false,
2316
+ false,
2317
+ false,
2318
+ false,
2319
+ false,
2320
+ 'first-variant',
2321
+ 'first-variant',
2322
+ 'first-variant',
2323
+ 'first-variant',
2324
+ 'second-variant',
2325
+ 'first-variant',
2326
+ 'first-variant',
2327
+ 'first-variant',
2328
+ 'second-variant',
2329
+ false,
2330
+ 'second-variant',
2331
+ 'first-variant',
2332
+ 'second-variant',
2333
+ 'first-variant',
2334
+ false,
2335
+ 'second-variant',
2336
+ 'second-variant',
2337
+ false,
2338
+ 'first-variant',
2339
+ false,
2340
+ false,
2341
+ false,
2342
+ 'third-variant',
2343
+ 'first-variant',
2344
+ false,
2345
+ false,
2346
+ 'first-variant',
2347
+ false,
2348
+ false,
2349
+ false,
2350
+ false,
2351
+ 'first-variant',
2352
+ false,
2353
+ false,
2354
+ false,
2355
+ false,
2356
+ false,
2357
+ false,
2358
+ false,
2359
+ 'first-variant',
2360
+ 'first-variant',
2361
+ 'third-variant',
2362
+ 'first-variant',
2363
+ 'first-variant',
2364
+ false,
2365
+ false,
2366
+ 'first-variant',
2367
+ false,
2368
+ false,
2369
+ 'fifth-variant',
2370
+ 'second-variant',
2371
+ false,
2372
+ 'second-variant',
2373
+ false,
2374
+ 'first-variant',
2375
+ 'third-variant',
2376
+ 'first-variant',
2377
+ 'fifth-variant',
2378
+ 'third-variant',
2379
+ false,
2380
+ false,
2381
+ 'fourth-variant',
2382
+ false,
2383
+ false,
2384
+ false,
2385
+ false,
2386
+ 'third-variant',
2387
+ false,
2388
+ false,
2389
+ 'third-variant',
2390
+ false,
2391
+ 'first-variant',
2392
+ 'second-variant',
2393
+ 'second-variant',
2394
+ 'second-variant',
2395
+ false,
2396
+ 'first-variant',
2397
+ 'third-variant',
2398
+ 'first-variant',
2399
+ 'first-variant',
2400
+ false,
2401
+ false,
2402
+ false,
2403
+ false,
2404
+ false,
2405
+ 'first-variant',
2406
+ 'first-variant',
2407
+ 'first-variant',
2408
+ 'second-variant',
2409
+ false,
2410
+ false,
2411
+ false,
2412
+ 'second-variant',
2413
+ false,
2414
+ false,
2415
+ 'first-variant',
2416
+ false,
2417
+ 'first-variant',
2418
+ false,
2419
+ false,
2420
+ 'first-variant',
2421
+ 'first-variant',
2422
+ 'first-variant',
2423
+ 'first-variant',
2424
+ 'third-variant',
2425
+ 'first-variant',
2426
+ 'third-variant',
2427
+ 'first-variant',
2428
+ 'first-variant',
2429
+ 'second-variant',
2430
+ 'third-variant',
2431
+ 'third-variant',
2432
+ false,
2433
+ 'second-variant',
2434
+ 'first-variant',
2435
+ false,
2436
+ 'second-variant',
2437
+ 'first-variant',
2438
+ false,
2439
+ 'first-variant',
2440
+ false,
2441
+ false,
2442
+ 'first-variant',
2443
+ 'fifth-variant',
2444
+ 'first-variant',
2445
+ false,
2446
+ false,
2447
+ false,
2448
+ false,
2449
+ 'first-variant',
2450
+ 'first-variant',
2451
+ 'second-variant',
2452
+ false,
2453
+ 'second-variant',
2454
+ 'third-variant',
2455
+ 'third-variant',
2456
+ false,
2457
+ 'first-variant',
2458
+ 'third-variant',
2459
+ false,
2460
+ false,
2461
+ 'first-variant',
2462
+ false,
2463
+ 'third-variant',
2464
+ 'first-variant',
2465
+ false,
2466
+ 'third-variant',
2467
+ 'first-variant',
2468
+ 'first-variant',
2469
+ false,
2470
+ 'first-variant',
2471
+ 'second-variant',
2472
+ 'second-variant',
2473
+ 'first-variant',
2474
+ false,
2475
+ false,
2476
+ false,
2477
+ 'second-variant',
2478
+ false,
2479
+ false,
2480
+ 'first-variant',
2481
+ 'first-variant',
2482
+ false,
2483
+ 'third-variant',
2484
+ false,
2485
+ 'first-variant',
2486
+ false,
2487
+ 'third-variant',
2488
+ false,
2489
+ 'third-variant',
2490
+ 'second-variant',
2491
+ 'first-variant',
2492
+ false,
2493
+ false,
2494
+ 'first-variant',
2495
+ 'third-variant',
2496
+ 'first-variant',
2497
+ 'second-variant',
2498
+ 'fifth-variant',
2499
+ false,
2500
+ false,
2501
+ 'first-variant',
2502
+ false,
2503
+ false,
2504
+ false,
2505
+ 'third-variant',
2506
+ false,
2507
+ 'second-variant',
2508
+ 'first-variant',
2509
+ false,
2510
+ false,
2511
+ false,
2512
+ false,
2513
+ 'third-variant',
2514
+ false,
2515
+ false,
2516
+ 'third-variant',
2517
+ false,
2518
+ false,
2519
+ 'first-variant',
2520
+ 'third-variant',
2521
+ false,
2522
+ false,
2523
+ 'first-variant',
2524
+ false,
2525
+ false,
2526
+ 'fourth-variant',
2527
+ 'fourth-variant',
2528
+ 'third-variant',
2529
+ 'second-variant',
2530
+ 'first-variant',
2531
+ 'third-variant',
2532
+ 'fifth-variant',
2533
+ false,
2534
+ 'first-variant',
2535
+ 'fifth-variant',
2536
+ false,
2537
+ 'first-variant',
2538
+ 'first-variant',
2539
+ 'first-variant',
2540
+ false,
2541
+ false,
2542
+ false,
2543
+ 'second-variant',
2544
+ 'fifth-variant',
2545
+ 'second-variant',
2546
+ 'first-variant',
2547
+ 'first-variant',
2548
+ 'second-variant',
2549
+ false,
2550
+ false,
2551
+ 'third-variant',
2552
+ false,
2553
+ 'second-variant',
2554
+ 'fifth-variant',
2555
+ false,
2556
+ 'third-variant',
2557
+ 'first-variant',
2558
+ false,
2559
+ false,
2560
+ 'fourth-variant',
2561
+ false,
2562
+ false,
2563
+ 'second-variant',
2564
+ false,
2565
+ false,
2566
+ 'first-variant',
2567
+ 'fourth-variant',
2568
+ 'first-variant',
2569
+ 'second-variant',
2570
+ false,
2571
+ false,
2572
+ false,
2573
+ 'first-variant',
2574
+ 'third-variant',
2575
+ 'third-variant',
2576
+ false,
2577
+ 'first-variant',
2578
+ 'first-variant',
2579
+ 'first-variant',
2580
+ false,
2581
+ 'first-variant',
2582
+ false,
2583
+ 'first-variant',
2584
+ 'third-variant',
2585
+ 'third-variant',
2586
+ false,
2587
+ false,
2588
+ 'first-variant',
2589
+ false,
2590
+ false,
2591
+ 'second-variant',
2592
+ 'second-variant',
2593
+ 'first-variant',
2594
+ 'first-variant',
2595
+ 'first-variant',
2596
+ false,
2597
+ 'fifth-variant',
2598
+ 'first-variant',
2599
+ false,
2600
+ false,
2601
+ false,
2602
+ 'second-variant',
2603
+ 'third-variant',
2604
+ 'first-variant',
2605
+ 'fourth-variant',
2606
+ 'first-variant',
2607
+ 'third-variant',
2608
+ false,
2609
+ 'first-variant',
2610
+ 'first-variant',
2611
+ false,
2612
+ 'third-variant',
2613
+ 'first-variant',
2614
+ 'first-variant',
2615
+ 'third-variant',
2616
+ false,
2617
+ 'fourth-variant',
2618
+ 'fifth-variant',
2619
+ 'first-variant',
2620
+ 'first-variant',
2621
+ false,
2622
+ false,
2623
+ false,
2624
+ 'first-variant',
2625
+ 'first-variant',
2626
+ 'first-variant',
2627
+ false,
2628
+ 'first-variant',
2629
+ 'first-variant',
2630
+ 'second-variant',
2631
+ 'first-variant',
2632
+ false,
2633
+ 'first-variant',
2634
+ 'second-variant',
2635
+ 'first-variant',
2636
+ false,
2637
+ 'first-variant',
2638
+ 'second-variant',
2639
+ false,
2640
+ 'first-variant',
2641
+ 'first-variant',
2642
+ false,
2643
+ 'first-variant',
2644
+ false,
2645
+ 'first-variant',
2646
+ false,
2647
+ 'first-variant',
2648
+ false,
2649
+ false,
2650
+ false,
2651
+ 'third-variant',
2652
+ 'third-variant',
2653
+ 'first-variant',
2654
+ false,
2655
+ false,
2656
+ 'second-variant',
2657
+ 'third-variant',
2658
+ 'first-variant',
2659
+ 'first-variant',
2660
+ false,
2661
+ false,
2662
+ false,
2663
+ 'second-variant',
2664
+ 'first-variant',
2665
+ false,
2666
+ 'first-variant',
2667
+ 'third-variant',
2668
+ false,
2669
+ 'first-variant',
2670
+ false,
2671
+ false,
2672
+ false,
2673
+ 'first-variant',
2674
+ 'third-variant',
2675
+ 'third-variant',
2676
+ false,
2677
+ false,
2678
+ false,
2679
+ false,
2680
+ 'third-variant',
2681
+ 'fourth-variant',
2682
+ 'fourth-variant',
2683
+ 'first-variant',
2684
+ 'second-variant',
2685
+ false,
2686
+ 'first-variant',
2687
+ false,
2688
+ 'second-variant',
2689
+ 'first-variant',
2690
+ 'third-variant',
2691
+ false,
2692
+ 'third-variant',
2693
+ false,
2694
+ 'first-variant',
2695
+ 'first-variant',
2696
+ 'third-variant',
2697
+ false,
2698
+ false,
2699
+ false,
2700
+ 'fourth-variant',
2701
+ 'second-variant',
2702
+ 'first-variant',
2703
+ false,
2704
+ false,
2705
+ 'first-variant',
2706
+ 'fourth-variant',
2707
+ false,
2708
+ 'first-variant',
2709
+ 'third-variant',
2710
+ 'first-variant',
2711
+ false,
2712
+ false,
2713
+ 'third-variant',
2714
+ false,
2715
+ 'first-variant',
2716
+ false,
2717
+ 'first-variant',
2718
+ 'first-variant',
2719
+ 'third-variant',
2720
+ 'second-variant',
2721
+ 'fourth-variant',
2722
+ false,
2723
+ 'first-variant',
2724
+ false,
2725
+ false,
2726
+ false,
2727
+ false,
2728
+ 'second-variant',
2729
+ 'first-variant',
2730
+ 'second-variant',
2731
+ false,
2732
+ 'first-variant',
2733
+ false,
2734
+ 'first-variant',
2735
+ 'first-variant',
2736
+ false,
2737
+ 'first-variant',
2738
+ 'first-variant',
2739
+ 'second-variant',
2740
+ 'third-variant',
2741
+ 'first-variant',
2742
+ 'first-variant',
2743
+ 'first-variant',
2744
+ false,
2745
+ false,
2746
+ false,
2747
+ 'third-variant',
2748
+ false,
2749
+ 'first-variant',
2750
+ 'first-variant',
2751
+ 'first-variant',
2752
+ 'third-variant',
2753
+ 'first-variant',
2754
+ 'first-variant',
2755
+ 'second-variant',
2756
+ 'first-variant',
2757
+ 'fifth-variant',
2758
+ 'fourth-variant',
2759
+ 'first-variant',
2760
+ 'second-variant',
2761
+ false,
2762
+ 'fourth-variant',
2763
+ false,
2764
+ false,
2765
+ false,
2766
+ 'fourth-variant',
2767
+ false,
2768
+ false,
2769
+ 'third-variant',
2770
+ false,
2771
+ false,
2772
+ false,
2773
+ 'first-variant',
2774
+ 'third-variant',
2775
+ 'third-variant',
2776
+ 'second-variant',
2777
+ 'first-variant',
2778
+ 'second-variant',
2779
+ 'first-variant',
2780
+ false,
2781
+ 'first-variant',
2782
+ false,
2783
+ false,
2784
+ false,
2785
+ false,
2786
+ false,
2787
+ 'first-variant',
2788
+ 'first-variant',
2789
+ false,
2790
+ 'second-variant',
2791
+ false,
2792
+ false,
2793
+ 'first-variant',
2794
+ false,
2795
+ 'second-variant',
2796
+ 'first-variant',
2797
+ 'first-variant',
2798
+ 'first-variant',
2799
+ 'third-variant',
2800
+ 'second-variant',
2801
+ false,
2802
+ false,
2803
+ 'fifth-variant',
2804
+ 'third-variant',
2805
+ false,
2806
+ false,
2807
+ 'first-variant',
2808
+ false,
2809
+ false,
2810
+ false,
2811
+ 'first-variant',
2812
+ 'second-variant',
2813
+ 'third-variant',
2814
+ 'third-variant',
2815
+ false,
2816
+ false,
2817
+ 'first-variant',
2818
+ false,
2819
+ 'third-variant',
2820
+ 'first-variant',
2821
+ false,
2822
+ false,
2823
+ false,
2824
+ false,
2825
+ 'fourth-variant',
2826
+ 'first-variant',
2827
+ false,
2828
+ false,
2829
+ false,
2830
+ 'third-variant',
2831
+ false,
2832
+ false,
2833
+ 'second-variant',
2834
+ 'first-variant',
2835
+ false,
2836
+ false,
2837
+ 'second-variant',
2838
+ 'third-variant',
2839
+ 'first-variant',
2840
+ 'first-variant',
2841
+ false,
2842
+ 'first-variant',
2843
+ 'first-variant',
2844
+ false,
2845
+ false,
2846
+ 'second-variant',
2847
+ 'third-variant',
2848
+ 'second-variant',
2849
+ 'third-variant',
2850
+ false,
2851
+ false,
2852
+ 'first-variant',
2853
+ false,
2854
+ false,
2855
+ 'first-variant',
2856
+ false,
2857
+ 'second-variant',
2858
+ false,
2859
+ false,
2860
+ false,
2861
+ false,
2862
+ 'first-variant',
2863
+ false,
2864
+ 'third-variant',
2865
+ false,
2866
+ 'first-variant',
2867
+ false,
2868
+ false,
2869
+ 'second-variant',
2870
+ 'third-variant',
2871
+ 'second-variant',
2872
+ 'fourth-variant',
2873
+ 'first-variant',
2874
+ 'first-variant',
2875
+ 'first-variant',
2876
+ false,
2877
+ 'first-variant',
2878
+ false,
2879
+ 'second-variant',
2880
+ false,
2881
+ false,
2882
+ false,
2883
+ false,
2884
+ false,
2885
+ 'first-variant',
2886
+ false,
2887
+ false,
2888
+ false,
2889
+ false,
2890
+ false,
2891
+ 'first-variant',
2892
+ false,
2893
+ 'second-variant',
2894
+ false,
2895
+ false,
2896
+ false,
2897
+ false,
2898
+ 'second-variant',
2899
+ false,
2900
+ 'first-variant',
2901
+ false,
2902
+ 'third-variant',
2903
+ false,
2904
+ false,
2905
+ 'first-variant',
2906
+ 'third-variant',
2907
+ false,
2908
+ 'third-variant',
2909
+ false,
2910
+ false,
2911
+ 'second-variant',
2912
+ false,
2913
+ 'first-variant',
2914
+ 'second-variant',
2915
+ 'first-variant',
2916
+ false,
2917
+ false,
2918
+ false,
2919
+ false,
2920
+ false,
2921
+ 'second-variant',
2922
+ false,
2923
+ false,
2924
+ 'first-variant',
2925
+ 'third-variant',
2926
+ false,
2927
+ 'first-variant',
2928
+ false,
2929
+ false,
2930
+ false,
2931
+ false,
2932
+ false,
2933
+ 'first-variant',
2934
+ 'second-variant',
2935
+ false,
2936
+ false,
2937
+ false,
2938
+ 'first-variant',
2939
+ 'first-variant',
2940
+ 'fifth-variant',
2941
+ false,
2942
+ false,
2943
+ false,
2944
+ 'first-variant',
2945
+ false,
2946
+ 'third-variant',
2947
+ false,
2948
+ false,
2949
+ 'second-variant',
2950
+ false,
2951
+ false,
2952
+ false,
2953
+ false,
2954
+ false,
2955
+ 'fourth-variant',
2956
+ 'second-variant',
2957
+ 'first-variant',
2958
+ 'second-variant',
2959
+ false,
2960
+ 'second-variant',
2961
+ false,
2962
+ 'second-variant',
2963
+ false,
2964
+ 'first-variant',
2965
+ false,
2966
+ 'first-variant',
2967
+ 'first-variant',
2968
+ false,
2969
+ 'second-variant',
2970
+ false,
2971
+ 'first-variant',
2972
+ false,
2973
+ 'fifth-variant',
2974
+ false,
2975
+ 'first-variant',
2976
+ 'first-variant',
2977
+ false,
2978
+ false,
2979
+ false,
2980
+ 'first-variant',
2981
+ false,
2982
+ 'first-variant',
2983
+ 'third-variant',
2984
+ false,
2985
+ false,
2986
+ 'first-variant',
2987
+ 'first-variant',
2988
+ false,
2989
+ false,
2990
+ 'fifth-variant',
2991
+ false,
2992
+ false,
2993
+ 'third-variant',
2994
+ false,
2995
+ 'third-variant',
2996
+ 'first-variant',
2997
+ 'first-variant',
2998
+ 'third-variant',
2999
+ 'third-variant',
3000
+ false,
3001
+ 'first-variant',
3002
+ false,
3003
+ false,
3004
+ false,
3005
+ false,
3006
+ false,
3007
+ 'first-variant',
3008
+ false,
3009
+ false,
3010
+ false,
3011
+ false,
3012
+ 'second-variant',
3013
+ 'first-variant',
3014
+ 'second-variant',
3015
+ 'first-variant',
3016
+ false,
3017
+ 'fifth-variant',
3018
+ 'first-variant',
3019
+ false,
3020
+ false,
3021
+ 'fourth-variant',
3022
+ 'first-variant',
3023
+ 'first-variant',
3024
+ false,
3025
+ false,
3026
+ 'fourth-variant',
3027
+ 'first-variant',
3028
+ false,
3029
+ 'second-variant',
3030
+ 'third-variant',
3031
+ 'third-variant',
3032
+ 'first-variant',
3033
+ 'first-variant',
3034
+ false,
3035
+ false,
3036
+ false,
3037
+ 'first-variant',
3038
+ 'first-variant',
3039
+ 'first-variant',
3040
+ false,
3041
+ 'third-variant',
3042
+ 'third-variant',
3043
+ 'third-variant',
3044
+ false,
3045
+ false,
3046
+ 'first-variant',
3047
+ 'first-variant',
3048
+ false,
3049
+ 'second-variant',
3050
+ false,
3051
+ false,
3052
+ 'second-variant',
3053
+ false,
3054
+ 'third-variant',
3055
+ 'first-variant',
3056
+ 'second-variant',
3057
+ 'fifth-variant',
3058
+ 'first-variant',
3059
+ 'first-variant',
3060
+ false,
3061
+ 'first-variant',
3062
+ 'fifth-variant',
3063
+ false,
3064
+ false,
3065
+ false,
3066
+ 'third-variant',
3067
+ 'first-variant',
3068
+ 'first-variant',
3069
+ 'second-variant',
3070
+ 'fourth-variant',
3071
+ 'first-variant',
3072
+ 'second-variant',
3073
+ 'first-variant',
3074
+ false,
3075
+ false,
3076
+ false,
3077
+ 'second-variant',
3078
+ 'third-variant',
3079
+ false,
3080
+ false,
3081
+ 'first-variant',
3082
+ false,
3083
+ false,
3084
+ false,
3085
+ false,
3086
+ false,
3087
+ false,
3088
+ 'first-variant',
3089
+ 'first-variant',
3090
+ false,
3091
+ 'third-variant',
3092
+ false,
3093
+ 'first-variant',
3094
+ false,
3095
+ 'third-variant',
3096
+ 'third-variant',
3097
+ 'first-variant',
3098
+ 'first-variant',
3099
+ false,
3100
+ 'second-variant',
3101
+ false,
3102
+ 'second-variant',
3103
+ 'first-variant',
3104
+ false,
3105
+ false,
3106
+ false,
3107
+ 'second-variant',
3108
+ false,
3109
+ 'third-variant',
3110
+ false,
3111
+ 'first-variant',
3112
+ 'fifth-variant',
3113
+ 'first-variant',
3114
+ 'first-variant',
3115
+ false,
3116
+ false,
3117
+ 'first-variant',
3118
+ false,
3119
+ false,
3120
+ false,
3121
+ 'first-variant',
3122
+ 'fourth-variant',
3123
+ 'first-variant',
3124
+ 'first-variant',
3125
+ 'first-variant',
3126
+ 'fifth-variant',
3127
+ false,
3128
+ false,
3129
+ false,
3130
+ 'second-variant',
3131
+ false,
3132
+ false,
3133
+ false,
3134
+ 'first-variant',
3135
+ 'first-variant',
3136
+ false,
3137
+ false,
3138
+ 'first-variant',
3139
+ 'first-variant',
3140
+ 'second-variant',
3141
+ 'first-variant',
3142
+ 'first-variant',
3143
+ 'first-variant',
3144
+ 'first-variant',
3145
+ 'first-variant',
3146
+ 'third-variant',
3147
+ 'first-variant',
3148
+ false,
3149
+ 'second-variant',
3150
+ false,
3151
+ false,
3152
+ 'third-variant',
3153
+ 'second-variant',
3154
+ 'third-variant',
3155
+ false,
3156
+ 'first-variant',
3157
+ 'third-variant',
3158
+ 'second-variant',
3159
+ 'first-variant',
3160
+ 'third-variant',
3161
+ false,
3162
+ false,
3163
+ 'first-variant',
3164
+ 'first-variant',
3165
+ false,
3166
+ false,
3167
+ false,
3168
+ 'first-variant',
3169
+ 'third-variant',
3170
+ 'second-variant',
3171
+ 'first-variant',
3172
+ 'first-variant',
3173
+ 'first-variant',
3174
+ false,
3175
+ 'third-variant',
3176
+ 'second-variant',
3177
+ 'third-variant',
3178
+ false,
3179
+ false,
3180
+ 'third-variant',
3181
+ 'first-variant',
3182
+ false,
3183
+ 'first-variant',
3184
+ ]
3185
+
3186
+ results.forEach(async (result, index) => {
3187
+ const distinctId = `distinct_id_${index}`
3188
+ const value = await posthog.getFeatureFlag('multivariate-flag', distinctId)
3189
+ expect(value).toBe(result)
3190
+ })
3191
+ })
3192
+ })