posthog-node 3.3.0 → 3.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.
- package/CHANGELOG.md +5 -0
- package/lib/index.cjs.js +105 -14
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.esm.js +105 -14
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-node/src/feature-flags.d.ts +2 -1
- package/package.json +1 -1
- package/src/feature-flags.ts +106 -13
- package/test/feature-flags.spec.ts +256 -3
|
@@ -55,4 +55,5 @@ declare class FeatureFlagsPoller {
|
|
|
55
55
|
stopPoller(): void;
|
|
56
56
|
}
|
|
57
57
|
declare function matchProperty(property: FeatureFlagCondition['properties'][number], propertyValues: Record<string, any>): boolean;
|
|
58
|
-
|
|
58
|
+
declare function relativeDateParseForFeatureFlagMatching(value: string): Date | null;
|
|
59
|
+
export { FeatureFlagsPoller, matchProperty, relativeDateParseForFeatureFlagMatching, InconclusiveMatchError, ClientError, };
|
package/package.json
CHANGED
package/src/feature-flags.ts
CHANGED
|
@@ -464,11 +464,32 @@ function matchProperty(
|
|
|
464
464
|
|
|
465
465
|
const overrideValue = propertyValues[key]
|
|
466
466
|
|
|
467
|
+
function computeExactMatch(value: any, overrideValue: any): boolean {
|
|
468
|
+
if (Array.isArray(value)) {
|
|
469
|
+
return value.map((val) => String(val).toLowerCase()).includes(String(overrideValue).toLowerCase())
|
|
470
|
+
}
|
|
471
|
+
return String(value).toLowerCase() === String(overrideValue).toLowerCase()
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function compare(lhs: any, rhs: any, operator: string): boolean {
|
|
475
|
+
if (operator === 'gt') {
|
|
476
|
+
return lhs > rhs
|
|
477
|
+
} else if (operator === 'gte') {
|
|
478
|
+
return lhs >= rhs
|
|
479
|
+
} else if (operator === 'lt') {
|
|
480
|
+
return lhs < rhs
|
|
481
|
+
} else if (operator === 'lte') {
|
|
482
|
+
return lhs <= rhs
|
|
483
|
+
} else {
|
|
484
|
+
throw new Error(`Invalid operator: ${operator}`)
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
467
488
|
switch (operator) {
|
|
468
489
|
case 'exact':
|
|
469
|
-
return
|
|
490
|
+
return computeExactMatch(value, overrideValue)
|
|
470
491
|
case 'is_not':
|
|
471
|
-
return
|
|
492
|
+
return !computeExactMatch(value, overrideValue)
|
|
472
493
|
case 'is_set':
|
|
473
494
|
return key in propertyValues
|
|
474
495
|
case 'icontains':
|
|
@@ -480,25 +501,54 @@ function matchProperty(
|
|
|
480
501
|
case 'not_regex':
|
|
481
502
|
return isValidRegex(String(value)) && String(overrideValue).match(String(value)) === null
|
|
482
503
|
case 'gt':
|
|
483
|
-
return typeof overrideValue == typeof value && overrideValue > value
|
|
484
504
|
case 'gte':
|
|
485
|
-
return typeof overrideValue == typeof value && overrideValue >= value
|
|
486
505
|
case 'lt':
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
506
|
+
case 'lte': {
|
|
507
|
+
// :TRICKY: We adjust comparison based on the override value passed in,
|
|
508
|
+
// to make sure we handle both numeric and string comparisons appropriately.
|
|
509
|
+
let parsedValue = typeof value === 'number' ? value : null
|
|
510
|
+
|
|
511
|
+
if (typeof value === 'string') {
|
|
512
|
+
try {
|
|
513
|
+
parsedValue = parseFloat(value)
|
|
514
|
+
} catch (err) {
|
|
515
|
+
// pass
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (parsedValue != null && overrideValue != null) {
|
|
520
|
+
// check both null and undefined
|
|
521
|
+
if (typeof overrideValue === 'string') {
|
|
522
|
+
return compare(overrideValue, String(value), operator)
|
|
523
|
+
} else {
|
|
524
|
+
return compare(overrideValue, parsedValue, operator)
|
|
525
|
+
}
|
|
526
|
+
} else {
|
|
527
|
+
return compare(String(overrideValue), String(value), operator)
|
|
528
|
+
}
|
|
529
|
+
}
|
|
490
530
|
case 'is_date_after':
|
|
491
|
-
case 'is_date_before':
|
|
492
|
-
|
|
531
|
+
case 'is_date_before':
|
|
532
|
+
case 'is_relative_date_before':
|
|
533
|
+
case 'is_relative_date_after': {
|
|
534
|
+
let parsedDate = null
|
|
535
|
+
if (['is_relative_date_before', 'is_relative_date_after'].includes(operator)) {
|
|
536
|
+
parsedDate = relativeDateParseForFeatureFlagMatching(String(value))
|
|
537
|
+
} else {
|
|
538
|
+
parsedDate = convertToDateTime(value)
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (parsedDate == null) {
|
|
542
|
+
throw new InconclusiveMatchError(`Invalid date: ${value}`)
|
|
543
|
+
}
|
|
493
544
|
const overrideDate = convertToDateTime(overrideValue)
|
|
494
|
-
if (
|
|
545
|
+
if (['is_date_before', 'is_relative_date_before'].includes(operator)) {
|
|
495
546
|
return overrideDate < parsedDate
|
|
496
547
|
}
|
|
497
548
|
return overrideDate > parsedDate
|
|
498
549
|
}
|
|
499
550
|
default:
|
|
500
|
-
|
|
501
|
-
return false
|
|
551
|
+
throw new InconclusiveMatchError(`Unknown operator: ${operator}`)
|
|
502
552
|
}
|
|
503
553
|
}
|
|
504
554
|
|
|
@@ -636,4 +686,47 @@ function convertToDateTime(value: string | number | (string | number)[] | Date):
|
|
|
636
686
|
}
|
|
637
687
|
}
|
|
638
688
|
|
|
639
|
-
|
|
689
|
+
function relativeDateParseForFeatureFlagMatching(value: string): Date | null {
|
|
690
|
+
const regex = /^(?<number>[0-9]+)(?<interval>[a-z])$/
|
|
691
|
+
const match = value.match(regex)
|
|
692
|
+
const parsedDt = new Date(new Date().toISOString())
|
|
693
|
+
|
|
694
|
+
if (match) {
|
|
695
|
+
if (!match.groups) {
|
|
696
|
+
return null
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const number = parseInt(match.groups['number'])
|
|
700
|
+
|
|
701
|
+
if (number >= 10000) {
|
|
702
|
+
// Guard against overflow, disallow numbers greater than 10_000
|
|
703
|
+
return null
|
|
704
|
+
}
|
|
705
|
+
const interval = match.groups['interval']
|
|
706
|
+
if (interval == 'h') {
|
|
707
|
+
parsedDt.setUTCHours(parsedDt.getUTCHours() - number)
|
|
708
|
+
} else if (interval == 'd') {
|
|
709
|
+
parsedDt.setUTCDate(parsedDt.getUTCDate() - number)
|
|
710
|
+
} else if (interval == 'w') {
|
|
711
|
+
parsedDt.setUTCDate(parsedDt.getUTCDate() - number * 7)
|
|
712
|
+
} else if (interval == 'm') {
|
|
713
|
+
parsedDt.setUTCMonth(parsedDt.getUTCMonth() - number)
|
|
714
|
+
} else if (interval == 'y') {
|
|
715
|
+
parsedDt.setUTCFullYear(parsedDt.getUTCFullYear() - number)
|
|
716
|
+
} else {
|
|
717
|
+
return null
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
return parsedDt
|
|
721
|
+
} else {
|
|
722
|
+
return null
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
export {
|
|
727
|
+
FeatureFlagsPoller,
|
|
728
|
+
matchProperty,
|
|
729
|
+
relativeDateParseForFeatureFlagMatching,
|
|
730
|
+
InconclusiveMatchError,
|
|
731
|
+
ClientError,
|
|
732
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// import { PostHog, PostHogOptions } from '../'
|
|
2
2
|
// Uncomment below line while developing to not compile code everytime
|
|
3
3
|
import { PostHog as PostHog, PostHogOptions } from '../src/posthog-node'
|
|
4
|
-
import { matchProperty, InconclusiveMatchError } from '../src/feature-flags'
|
|
4
|
+
import { matchProperty, InconclusiveMatchError, relativeDateParseForFeatureFlagMatching } from '../src/feature-flags'
|
|
5
5
|
jest.mock('../src/fetch')
|
|
6
6
|
import fetch from '../src/fetch'
|
|
7
7
|
|
|
@@ -1814,6 +1814,8 @@ describe('local evaluation', () => {
|
|
|
1814
1814
|
})
|
|
1815
1815
|
|
|
1816
1816
|
describe('match properties', () => {
|
|
1817
|
+
jest.useFakeTimers()
|
|
1818
|
+
|
|
1817
1819
|
it('with operator exact', () => {
|
|
1818
1820
|
const property_a = { key: 'key', value: 'value' }
|
|
1819
1821
|
|
|
@@ -1953,7 +1955,8 @@ describe('match properties', () => {
|
|
|
1953
1955
|
|
|
1954
1956
|
expect(matchProperty(property_a, { key: 0 })).toBe(false)
|
|
1955
1957
|
expect(matchProperty(property_a, { key: -1 })).toBe(false)
|
|
1956
|
-
|
|
1958
|
+
// # now we handle type mismatches so this should be true
|
|
1959
|
+
expect(matchProperty(property_a, { key: '23' })).toBe(true)
|
|
1957
1960
|
|
|
1958
1961
|
const property_b = { key: 'key', value: 1, operator: 'lt' }
|
|
1959
1962
|
expect(matchProperty(property_b, { key: 0 })).toBe(true)
|
|
@@ -1971,7 +1974,8 @@ describe('match properties', () => {
|
|
|
1971
1974
|
expect(matchProperty(property_c, { key: 0 })).toBe(false)
|
|
1972
1975
|
expect(matchProperty(property_c, { key: -1 })).toBe(false)
|
|
1973
1976
|
expect(matchProperty(property_c, { key: -3 })).toBe(false)
|
|
1974
|
-
|
|
1977
|
+
// # now we handle type mismatches so this should be true
|
|
1978
|
+
expect(matchProperty(property_c, { key: '3' })).toBe(true)
|
|
1975
1979
|
|
|
1976
1980
|
const property_d = { key: 'key', value: '43', operator: 'lte' }
|
|
1977
1981
|
expect(matchProperty(property_d, { key: '43' })).toBe(true)
|
|
@@ -1979,6 +1983,21 @@ describe('match properties', () => {
|
|
|
1979
1983
|
|
|
1980
1984
|
expect(matchProperty(property_d, { key: '44' })).toBe(false)
|
|
1981
1985
|
expect(matchProperty(property_d, { key: 44 })).toBe(false)
|
|
1986
|
+
expect(matchProperty(property_d, { key: 42 })).toBe(true)
|
|
1987
|
+
|
|
1988
|
+
const property_e = { key: 'key', value: '30', operator: 'lt' }
|
|
1989
|
+
expect(matchProperty(property_e, { key: '29' })).toBe(true)
|
|
1990
|
+
|
|
1991
|
+
// # depending on the type of override, we adjust type comparison
|
|
1992
|
+
expect(matchProperty(property_e, { key: '100' })).toBe(true)
|
|
1993
|
+
expect(matchProperty(property_e, { key: 100 })).toBe(false)
|
|
1994
|
+
|
|
1995
|
+
const property_f = { key: 'key', value: '123aloha', operator: 'gt' }
|
|
1996
|
+
expect(matchProperty(property_f, { key: '123' })).toBe(false)
|
|
1997
|
+
expect(matchProperty(property_f, { key: 122 })).toBe(false)
|
|
1998
|
+
|
|
1999
|
+
// # this turns into a string comparison
|
|
2000
|
+
expect(matchProperty(property_f, { key: 129 })).toBe(true)
|
|
1982
2001
|
})
|
|
1983
2002
|
|
|
1984
2003
|
it('with date operators', () => {
|
|
@@ -2016,6 +2035,240 @@ describe('match properties', () => {
|
|
|
2016
2035
|
expect(matchProperty(property_d, { key: '2022-04-05 11:34:11 +00:00' })).toBe(true)
|
|
2017
2036
|
expect(matchProperty(property_d, { key: '2022-04-05 11:34:13 +00:00' })).toBe(false)
|
|
2018
2037
|
})
|
|
2038
|
+
|
|
2039
|
+
it('with relative date operators', () => {
|
|
2040
|
+
jest.setSystemTime(new Date('2022-05-01'))
|
|
2041
|
+
|
|
2042
|
+
const property_a = { key: 'key', value: '6h', operator: 'is_relative_date_before' }
|
|
2043
|
+
expect(matchProperty(property_a, { key: '2022-03-01' })).toBe(true)
|
|
2044
|
+
expect(matchProperty(property_a, { key: '2022-04-30' })).toBe(true)
|
|
2045
|
+
|
|
2046
|
+
// :TRICKY: MonthIndex is 0 indexed, so 3 is actually the 4th month, April.
|
|
2047
|
+
expect(matchProperty(property_a, { key: new Date(Date.UTC(2022, 3, 30, 1, 2, 3)) })).toBe(true)
|
|
2048
|
+
// false because date comparison, instead of datetime, so reduces to same date
|
|
2049
|
+
expect(matchProperty(property_a, { key: new Date(2022, 3, 30, 19, 2, 3) })).toBe(false)
|
|
2050
|
+
expect(matchProperty(property_a, { key: new Date('2022-04-30T01:02:03+02:00') })).toBe(true) // europe/madrid
|
|
2051
|
+
expect(matchProperty(property_a, { key: new Date('2022-04-30T20:02:03+02:00') })).toBe(false) // europe/madrid
|
|
2052
|
+
expect(matchProperty(property_a, { key: new Date('2022-04-30T19:59:03+02:00') })).toBe(true) // europe/madrid
|
|
2053
|
+
expect(matchProperty(property_a, { key: new Date('2022-04-30') })).toBe(true)
|
|
2054
|
+
expect(matchProperty(property_a, { key: '2022-05-30' })).toBe(false)
|
|
2055
|
+
|
|
2056
|
+
// # can't be an invalid string
|
|
2057
|
+
expect(() => matchProperty(property_a, { key: 'abcdef' })).toThrow(InconclusiveMatchError)
|
|
2058
|
+
// however js understands numbers as date offsets from utc epoch
|
|
2059
|
+
expect(() => matchProperty(property_a, { key: 1 })).not.toThrow(InconclusiveMatchError)
|
|
2060
|
+
|
|
2061
|
+
const property_b = { key: 'key', value: '1h', operator: 'is_relative_date_after' }
|
|
2062
|
+
expect(matchProperty(property_b, { key: '2022-05-02' })).toBe(true)
|
|
2063
|
+
expect(matchProperty(property_b, { key: '2022-05-30' })).toBe(true)
|
|
2064
|
+
expect(matchProperty(property_b, { key: new Date(2022, 4, 30) })).toBe(true)
|
|
2065
|
+
expect(matchProperty(property_b, { key: new Date('2022-05-30') })).toBe(true)
|
|
2066
|
+
expect(matchProperty(property_b, { key: '2022-04-30' })).toBe(false)
|
|
2067
|
+
|
|
2068
|
+
// # Invalid flag property
|
|
2069
|
+
const property_c = { key: 'key', value: 1234, operator: 'is_relative_date_after' }
|
|
2070
|
+
expect(() => matchProperty(property_c, { key: '2022-05-30' })).toThrow(InconclusiveMatchError)
|
|
2071
|
+
expect(() => matchProperty(property_c, { key: 1 })).toThrow(InconclusiveMatchError)
|
|
2072
|
+
|
|
2073
|
+
// # Try all possible relative dates
|
|
2074
|
+
const property_e = { key: 'key', value: '1h', operator: 'is_relative_date_before' }
|
|
2075
|
+
expect(matchProperty(property_e, { key: '2022-05-01 00:00:00' })).toBe(false)
|
|
2076
|
+
expect(matchProperty(property_e, { key: '2022-04-30 22:00:00' })).toBe(true)
|
|
2077
|
+
|
|
2078
|
+
const property_f = { key: 'key', value: '1d', operator: 'is_relative_date_before' }
|
|
2079
|
+
expect(matchProperty(property_f, { key: '2022-04-29 23:59:00 GMT' })).toBe(true)
|
|
2080
|
+
expect(matchProperty(property_f, { key: '2022-04-30 00:00:01 GMT' })).toBe(false)
|
|
2081
|
+
|
|
2082
|
+
const property_g = { key: 'key', value: '1w', operator: 'is_relative_date_before' }
|
|
2083
|
+
expect(matchProperty(property_g, { key: '2022-04-23 00:00:00 GMT' })).toBe(true)
|
|
2084
|
+
expect(matchProperty(property_g, { key: '2022-04-24 00:00:00 GMT' })).toBe(false)
|
|
2085
|
+
expect(matchProperty(property_g, { key: '2022-04-24 00:00:01 GMT' })).toBe(false)
|
|
2086
|
+
|
|
2087
|
+
const property_h = { key: 'key', value: '1m', operator: 'is_relative_date_before' }
|
|
2088
|
+
expect(matchProperty(property_h, { key: '2022-03-01 00:00:00 GMT' })).toBe(true)
|
|
2089
|
+
expect(matchProperty(property_h, { key: '2022-04-05 00:00:00 GMT' })).toBe(false)
|
|
2090
|
+
|
|
2091
|
+
const property_i = { key: 'key', value: '1y', operator: 'is_relative_date_before' }
|
|
2092
|
+
expect(matchProperty(property_i, { key: '2021-04-28 00:00:00 GMT' })).toBe(true)
|
|
2093
|
+
expect(matchProperty(property_i, { key: '2021-05-01 00:00:01 GMT' })).toBe(false)
|
|
2094
|
+
|
|
2095
|
+
const property_j = { key: 'key', value: '122h', operator: 'is_relative_date_after' }
|
|
2096
|
+
expect(matchProperty(property_j, { key: '2022-05-01 00:00:00 GMT' })).toBe(true)
|
|
2097
|
+
expect(matchProperty(property_j, { key: '2022-04-23 01:00:00 GMT' })).toBe(false)
|
|
2098
|
+
|
|
2099
|
+
const property_k = { key: 'key', value: '2d', operator: 'is_relative_date_after' }
|
|
2100
|
+
expect(matchProperty(property_k, { key: '2022-05-01 00:00:00 GMT' })).toBe(true)
|
|
2101
|
+
expect(matchProperty(property_k, { key: '2022-04-29 00:00:01 GMT' })).toBe(true)
|
|
2102
|
+
expect(matchProperty(property_k, { key: '2022-04-29 00:00:00 GMT' })).toBe(false)
|
|
2103
|
+
|
|
2104
|
+
const property_l = { key: 'key', value: '02w', operator: 'is_relative_date_after' }
|
|
2105
|
+
expect(matchProperty(property_l, { key: '2022-05-01 00:00:00 GMT' })).toBe(true)
|
|
2106
|
+
expect(matchProperty(property_l, { key: '2022-04-16 00:00:00 GMT' })).toBe(false)
|
|
2107
|
+
|
|
2108
|
+
const property_m = { key: 'key', value: '1m', operator: 'is_relative_date_after' }
|
|
2109
|
+
expect(matchProperty(property_m, { key: '2022-04-01 00:00:01 GMT' })).toBe(true)
|
|
2110
|
+
expect(matchProperty(property_m, { key: '2022-04-01 00:00:00 GMT' })).toBe(false)
|
|
2111
|
+
|
|
2112
|
+
const property_n = { key: 'key', value: '1y', operator: 'is_relative_date_after' }
|
|
2113
|
+
expect(matchProperty(property_n, { key: '2022-05-01 00:00:00 GMT' })).toBe(true)
|
|
2114
|
+
expect(matchProperty(property_n, { key: '2021-05-01 00:00:01 GMT' })).toBe(true)
|
|
2115
|
+
expect(matchProperty(property_n, { key: '2021-05-01 00:00:00 GMT' })).toBe(false)
|
|
2116
|
+
expect(matchProperty(property_n, { key: '2021-04-30 00:00:00 GMT' })).toBe(false)
|
|
2117
|
+
expect(matchProperty(property_n, { key: '2021-03-01 12:13:00 GMT' })).toBe(false)
|
|
2118
|
+
})
|
|
2119
|
+
|
|
2120
|
+
it('null or undefined property value', () => {
|
|
2121
|
+
const property_a = { key: 'key', value: 'null', operator: 'is_not' }
|
|
2122
|
+
expect(matchProperty(property_a, { key: null })).toBe(false)
|
|
2123
|
+
expect(matchProperty(property_a, { key: undefined })).toBe(true)
|
|
2124
|
+
expect(matchProperty(property_a, { key: 'null' })).toBe(false)
|
|
2125
|
+
expect(matchProperty(property_a, { key: 'nul' })).toBe(true)
|
|
2126
|
+
|
|
2127
|
+
const property_b = { key: 'key', value: 'null', operator: 'is_set' }
|
|
2128
|
+
expect(matchProperty(property_b, { key: null })).toBe(true)
|
|
2129
|
+
expect(matchProperty(property_b, { key: undefined })).toBe(true)
|
|
2130
|
+
expect(matchProperty(property_b, { key: 'null' })).toBe(true)
|
|
2131
|
+
|
|
2132
|
+
const property_c = { key: 'key', value: 'undefined', operator: 'icontains' }
|
|
2133
|
+
expect(matchProperty(property_c, { key: null })).toBe(false)
|
|
2134
|
+
expect(matchProperty(property_c, { key: undefined })).toBe(true)
|
|
2135
|
+
expect(matchProperty(property_c, { key: 'lol' })).toBe(false)
|
|
2136
|
+
|
|
2137
|
+
const property_d = { key: 'key', value: 'undefined', operator: 'regex' }
|
|
2138
|
+
expect(matchProperty(property_d, { key: null })).toBe(false)
|
|
2139
|
+
expect(matchProperty(property_d, { key: undefined })).toBe(true)
|
|
2140
|
+
|
|
2141
|
+
const property_e = { key: 'key', value: 1, operator: 'gt' }
|
|
2142
|
+
expect(matchProperty(property_e, { key: null })).toBe(true)
|
|
2143
|
+
expect(matchProperty(property_e, { key: undefined })).toBe(true)
|
|
2144
|
+
|
|
2145
|
+
const property_f = { key: 'key', value: 1, operator: 'lt' }
|
|
2146
|
+
expect(matchProperty(property_f, { key: null })).toBe(false)
|
|
2147
|
+
expect(matchProperty(property_f, { key: undefined })).toBe(false)
|
|
2148
|
+
|
|
2149
|
+
const property_g = { key: 'key', value: 'xyz', operator: 'gte' }
|
|
2150
|
+
expect(matchProperty(property_g, { key: null })).toBe(false)
|
|
2151
|
+
expect(matchProperty(property_g, { key: undefined })).toBe(false)
|
|
2152
|
+
|
|
2153
|
+
const property_h = { key: 'key', value: 'Oo', operator: 'lte' }
|
|
2154
|
+
expect(matchProperty(property_h, { key: null })).toBe(false)
|
|
2155
|
+
expect(matchProperty(property_h, { key: undefined })).toBe(false)
|
|
2156
|
+
|
|
2157
|
+
const property_h_lower = { key: 'key', value: 'oo', operator: 'lte' }
|
|
2158
|
+
expect(matchProperty(property_h_lower, { key: null })).toBe(true)
|
|
2159
|
+
expect(matchProperty(property_h_lower, { key: undefined })).toBe(false)
|
|
2160
|
+
|
|
2161
|
+
const property_i = { key: 'key', value: '2022-05-01', operator: 'is_date_before' }
|
|
2162
|
+
expect(() => matchProperty(property_i, { key: null })).toThrow(InconclusiveMatchError)
|
|
2163
|
+
expect(() => matchProperty(property_i, { key: undefined })).toThrow(InconclusiveMatchError)
|
|
2164
|
+
|
|
2165
|
+
const property_j = { key: 'key', value: '2022-05-01', operator: 'is_date_after' }
|
|
2166
|
+
expect(() => matchProperty(property_j, { key: null })).toThrow(InconclusiveMatchError)
|
|
2167
|
+
|
|
2168
|
+
const property_k = { key: 'key', value: '2022-05-01', operator: 'is_date_before' }
|
|
2169
|
+
expect(() => matchProperty(property_k, { key: null })).toThrow(InconclusiveMatchError)
|
|
2170
|
+
})
|
|
2171
|
+
|
|
2172
|
+
it('with invalid operator', () => {
|
|
2173
|
+
const property_a = { key: 'key', value: '2022-05-01', operator: 'is_unknown' }
|
|
2174
|
+
|
|
2175
|
+
expect(() => matchProperty(property_a, { key: 'random' })).toThrow(
|
|
2176
|
+
new InconclusiveMatchError('Unknown operator: is_unknown')
|
|
2177
|
+
)
|
|
2178
|
+
})
|
|
2179
|
+
})
|
|
2180
|
+
|
|
2181
|
+
describe('relative date parsing', () => {
|
|
2182
|
+
jest.useFakeTimers()
|
|
2183
|
+
beforeEach(() => {
|
|
2184
|
+
jest.setSystemTime(new Date('2020-01-01T12:01:20.134Z'))
|
|
2185
|
+
})
|
|
2186
|
+
|
|
2187
|
+
it('invalid input', () => {
|
|
2188
|
+
expect(relativeDateParseForFeatureFlagMatching('1')).toBe(null)
|
|
2189
|
+
expect(relativeDateParseForFeatureFlagMatching('1x')).toBe(null)
|
|
2190
|
+
expect(relativeDateParseForFeatureFlagMatching('1.2y')).toBe(null)
|
|
2191
|
+
expect(relativeDateParseForFeatureFlagMatching('1z')).toBe(null)
|
|
2192
|
+
expect(relativeDateParseForFeatureFlagMatching('1s')).toBe(null)
|
|
2193
|
+
expect(relativeDateParseForFeatureFlagMatching('123344000.134m')).toBe(null)
|
|
2194
|
+
expect(relativeDateParseForFeatureFlagMatching('bazinga')).toBe(null)
|
|
2195
|
+
expect(relativeDateParseForFeatureFlagMatching('000bello')).toBe(null)
|
|
2196
|
+
expect(relativeDateParseForFeatureFlagMatching('000hello')).toBe(null)
|
|
2197
|
+
|
|
2198
|
+
expect(relativeDateParseForFeatureFlagMatching('000h')).not.toBe(null)
|
|
2199
|
+
expect(relativeDateParseForFeatureFlagMatching('1000h')).not.toBe(null)
|
|
2200
|
+
})
|
|
2201
|
+
|
|
2202
|
+
it('overflow', () => {
|
|
2203
|
+
expect(relativeDateParseForFeatureFlagMatching('1000000h')).toBe(null)
|
|
2204
|
+
expect(relativeDateParseForFeatureFlagMatching('100000000000000000y')).toBe(null)
|
|
2205
|
+
})
|
|
2206
|
+
|
|
2207
|
+
it('hour parsing', () => {
|
|
2208
|
+
expect(relativeDateParseForFeatureFlagMatching('1h')).toEqual(new Date('2020-01-01T11:01:20.134Z'))
|
|
2209
|
+
expect(relativeDateParseForFeatureFlagMatching('2h')).toEqual(new Date('2020-01-01T10:01:20.134Z'))
|
|
2210
|
+
expect(relativeDateParseForFeatureFlagMatching('24h')).toEqual(new Date('2019-12-31T12:01:20.134Z'))
|
|
2211
|
+
expect(relativeDateParseForFeatureFlagMatching('30h')).toEqual(new Date('2019-12-31T06:01:20.134Z'))
|
|
2212
|
+
expect(relativeDateParseForFeatureFlagMatching('48h')).toEqual(new Date('2019-12-30T12:01:20.134Z'))
|
|
2213
|
+
|
|
2214
|
+
expect(relativeDateParseForFeatureFlagMatching('24h')).toEqual(relativeDateParseForFeatureFlagMatching('1d'))
|
|
2215
|
+
expect(relativeDateParseForFeatureFlagMatching('48h')).toEqual(relativeDateParseForFeatureFlagMatching('2d'))
|
|
2216
|
+
})
|
|
2217
|
+
|
|
2218
|
+
it('day parsing', () => {
|
|
2219
|
+
expect(relativeDateParseForFeatureFlagMatching('1d')).toEqual(new Date('2019-12-31T12:01:20.134Z'))
|
|
2220
|
+
expect(relativeDateParseForFeatureFlagMatching('2d')).toEqual(new Date('2019-12-30T12:01:20.134Z'))
|
|
2221
|
+
expect(relativeDateParseForFeatureFlagMatching('7d')).toEqual(new Date('2019-12-25T12:01:20.134Z'))
|
|
2222
|
+
expect(relativeDateParseForFeatureFlagMatching('14d')).toEqual(new Date('2019-12-18T12:01:20.134Z'))
|
|
2223
|
+
expect(relativeDateParseForFeatureFlagMatching('30d')).toEqual(new Date('2019-12-02T12:01:20.134Z'))
|
|
2224
|
+
|
|
2225
|
+
expect(relativeDateParseForFeatureFlagMatching('7d')).toEqual(relativeDateParseForFeatureFlagMatching('1w'))
|
|
2226
|
+
})
|
|
2227
|
+
|
|
2228
|
+
it('week parsing', () => {
|
|
2229
|
+
expect(relativeDateParseForFeatureFlagMatching('1w')).toEqual(new Date('2019-12-25T12:01:20.134Z'))
|
|
2230
|
+
expect(relativeDateParseForFeatureFlagMatching('2w')).toEqual(new Date('2019-12-18T12:01:20.134Z'))
|
|
2231
|
+
expect(relativeDateParseForFeatureFlagMatching('4w')).toEqual(new Date('2019-12-04T12:01:20.134Z'))
|
|
2232
|
+
expect(relativeDateParseForFeatureFlagMatching('8w')).toEqual(new Date('2019-11-06T12:01:20.134Z'))
|
|
2233
|
+
|
|
2234
|
+
expect(relativeDateParseForFeatureFlagMatching('1m')).toEqual(new Date('2019-12-01T12:01:20.134Z'))
|
|
2235
|
+
expect(relativeDateParseForFeatureFlagMatching('4w')).not.toEqual(relativeDateParseForFeatureFlagMatching('1m'))
|
|
2236
|
+
})
|
|
2237
|
+
|
|
2238
|
+
it('month parsing', () => {
|
|
2239
|
+
expect(relativeDateParseForFeatureFlagMatching('1m')).toEqual(new Date('2019-12-01T12:01:20.134Z'))
|
|
2240
|
+
expect(relativeDateParseForFeatureFlagMatching('2m')).toEqual(new Date('2019-11-01T12:01:20.134Z'))
|
|
2241
|
+
|
|
2242
|
+
expect(relativeDateParseForFeatureFlagMatching('4m')).toEqual(new Date('2019-09-01T12:01:20.134Z'))
|
|
2243
|
+
expect(relativeDateParseForFeatureFlagMatching('5m')).toEqual(new Date('2019-08-01T12:01:20.134Z'))
|
|
2244
|
+
expect(relativeDateParseForFeatureFlagMatching('6m')).toEqual(new Date('2019-07-01T12:01:20.134Z'))
|
|
2245
|
+
expect(relativeDateParseForFeatureFlagMatching('8m')).toEqual(new Date('2019-05-01T12:01:20.134Z'))
|
|
2246
|
+
expect(relativeDateParseForFeatureFlagMatching('10m')).toEqual(new Date('2019-03-01T12:01:20.134Z'))
|
|
2247
|
+
|
|
2248
|
+
expect(relativeDateParseForFeatureFlagMatching('24m')).toEqual(new Date('2018-01-01T12:01:20.134Z'))
|
|
2249
|
+
|
|
2250
|
+
expect(relativeDateParseForFeatureFlagMatching('1y')).toEqual(new Date('2019-01-01T12:01:20.134Z'))
|
|
2251
|
+
expect(relativeDateParseForFeatureFlagMatching('12m')).toEqual(relativeDateParseForFeatureFlagMatching('1y'))
|
|
2252
|
+
|
|
2253
|
+
jest.setSystemTime(new Date('2020-04-03T00:00:00Z'))
|
|
2254
|
+
expect(relativeDateParseForFeatureFlagMatching('1m')).toEqual(new Date('2020-03-03T00:00:00Z'))
|
|
2255
|
+
expect(relativeDateParseForFeatureFlagMatching('2m')).toEqual(new Date('2020-02-03T00:00:00Z'))
|
|
2256
|
+
expect(relativeDateParseForFeatureFlagMatching('4m')).toEqual(new Date('2019-12-03T00:00:00Z'))
|
|
2257
|
+
expect(relativeDateParseForFeatureFlagMatching('8m')).toEqual(new Date('2019-08-03T00:00:00Z'))
|
|
2258
|
+
|
|
2259
|
+
expect(relativeDateParseForFeatureFlagMatching('1y')).toEqual(new Date('2019-04-03T00:00:00Z'))
|
|
2260
|
+
expect(relativeDateParseForFeatureFlagMatching('12m')).toEqual(relativeDateParseForFeatureFlagMatching('1y'))
|
|
2261
|
+
})
|
|
2262
|
+
|
|
2263
|
+
it('year parsing', () => {
|
|
2264
|
+
expect(relativeDateParseForFeatureFlagMatching('1y')).toEqual(new Date('2019-01-01T12:01:20.134Z'))
|
|
2265
|
+
expect(relativeDateParseForFeatureFlagMatching('2y')).toEqual(new Date('2018-01-01T12:01:20.134Z'))
|
|
2266
|
+
expect(relativeDateParseForFeatureFlagMatching('4y')).toEqual(new Date('2016-01-01T12:01:20.134Z'))
|
|
2267
|
+
expect(relativeDateParseForFeatureFlagMatching('8y')).toEqual(new Date('2012-01-01T12:01:20.134Z'))
|
|
2268
|
+
|
|
2269
|
+
expect(relativeDateParseForFeatureFlagMatching('1y')).toEqual(new Date('2019-01-01T12:01:20.134Z'))
|
|
2270
|
+
expect(relativeDateParseForFeatureFlagMatching('12m')).toEqual(relativeDateParseForFeatureFlagMatching('1y'))
|
|
2271
|
+
})
|
|
2019
2272
|
})
|
|
2020
2273
|
|
|
2021
2274
|
describe('consistency tests', () => {
|