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.
@@ -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
- export { FeatureFlagsPoller, matchProperty, InconclusiveMatchError, ClientError };
58
+ declare function relativeDateParseForFeatureFlagMatching(value: string): Date | null;
59
+ export { FeatureFlagsPoller, matchProperty, relativeDateParseForFeatureFlagMatching, InconclusiveMatchError, ClientError, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "posthog-node",
3
- "version": "3.3.0",
3
+ "version": "3.4.0",
4
4
  "description": "PostHog Node.js integration",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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 Array.isArray(value) ? value.indexOf(overrideValue) !== -1 : value === overrideValue
490
+ return computeExactMatch(value, overrideValue)
470
491
  case 'is_not':
471
- return Array.isArray(value) ? value.indexOf(overrideValue) === -1 : value !== overrideValue
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
- return typeof overrideValue == typeof value && overrideValue < value
488
- case 'lte':
489
- return typeof overrideValue == typeof value && overrideValue <= value
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
- const parsedDate = convertToDateTime(value)
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 (operator === 'is_date_before') {
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
- console.error(`Unknown operator: ${operator}`)
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
- export { FeatureFlagsPoller, matchProperty, InconclusiveMatchError, ClientError }
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
- expect(matchProperty(property_a, { key: '23' })).toBe(false)
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
- expect(matchProperty(property_c, { key: '3' })).toBe(false)
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', () => {