2
2
* @jest -environment jsdom
3
3
*/
4
4
5
+ import * as td from 'testdouble' ;
5
6
import mock from 'xhr-mock' ;
6
7
7
8
import {
@@ -10,18 +11,70 @@ import {
10
11
readMockRacResponse ,
11
12
} from '../test/testHelpers' ;
12
13
13
- import { EppoJSClient , init } from './index' ;
14
+ import { EppoLocalStorage } from './local-storage' ;
15
+
16
+ import { EppoJSClient , IAssignmentLogger , IEppoClient , init } from './index' ;
14
17
15
18
describe ( 'EppoJSClient E2E test' , ( ) => {
16
- let client : EppoJSClient ;
19
+ let globalClient : IEppoClient ;
20
+
21
+ const flagKey = 'mock-experiment' ;
22
+
23
+ const mockExperimentConfig = {
24
+ name : flagKey ,
25
+ enabled : true ,
26
+ subjectShards : 100 ,
27
+ overrides : { } ,
28
+ typedOverrides : { } ,
29
+ rules : [
30
+ {
31
+ allocationKey : 'allocation1' ,
32
+ conditions : [ ] ,
33
+ } ,
34
+ ] ,
35
+ allocations : {
36
+ allocation1 : {
37
+ percentExposure : 1 ,
38
+ variations : [
39
+ {
40
+ name : 'control' ,
41
+ value : 'control' ,
42
+ typedValue : 'control' ,
43
+ shardRange : {
44
+ start : 0 ,
45
+ end : 34 ,
46
+ } ,
47
+ } ,
48
+ {
49
+ name : 'variant-1' ,
50
+ value : 'variant-1' ,
51
+ typedValue : 'variant-1' ,
52
+ shardRange : {
53
+ start : 34 ,
54
+ end : 67 ,
55
+ } ,
56
+ } ,
57
+ {
58
+ name : 'variant-2' ,
59
+ value : 'variant-2' ,
60
+ typedValue : 'variant-2' ,
61
+ shardRange : {
62
+ start : 67 ,
63
+ end : 100 ,
64
+ } ,
65
+ } ,
66
+ ] ,
67
+ } ,
68
+ } ,
69
+ } ;
17
70
18
71
beforeAll ( async ( ) => {
19
72
mock . setup ( ) ;
20
73
mock . get ( / r a n d o m i z e d _ a s s i g n m e n t \/ v 3 \/ c o n f i g * / , ( _req , res ) => {
21
74
const rac = readMockRacResponse ( ) ;
22
75
return res . status ( 200 ) . body ( JSON . stringify ( rac ) ) ;
23
76
} ) ;
24
- client = await init ( {
77
+ globalClient = await init ( {
25
78
apiKey : 'dummy' ,
26
79
baseUrl : 'http://127.0.0.1:4000' ,
27
80
assignmentLogger : {
@@ -37,6 +90,116 @@ describe('EppoJSClient E2E test', () => {
37
90
mock . teardown ( ) ;
38
91
} ) ;
39
92
93
+ it ( 'assigns subject from overrides when experiment is enabled' , ( ) => {
94
+ const mockConfigStore = td . object < EppoLocalStorage > ( ) ;
95
+ const mockLogger = td . object < IAssignmentLogger > ( ) ;
96
+ td . when ( mockConfigStore . get ( flagKey ) ) . thenReturn ( {
97
+ ...mockExperimentConfig ,
98
+ overrides : {
99
+ '1b50f33aef8f681a13f623963da967ed' : 'variant-2' ,
100
+ } ,
101
+ typedOverrides : {
102
+ '1b50f33aef8f681a13f623963da967ed' : 'variant-2' ,
103
+ } ,
104
+ } ) ;
105
+ const client = new EppoJSClient ( mockConfigStore ) ;
106
+ client . setLogger ( mockLogger ) ;
107
+ const assignment = client . getAssignment ( 'subject-10' , flagKey ) ;
108
+ expect ( assignment ) . toEqual ( 'variant-2' ) ;
109
+ } ) ;
110
+
111
+ it ( 'assigns subject from overrides when experiment is not enabled' , ( ) => {
112
+ const mockConfigStore = td . object < EppoLocalStorage > ( ) ;
113
+ const mockLogger = td . object < IAssignmentLogger > ( ) ;
114
+ td . when ( mockConfigStore . get ( flagKey ) ) . thenReturn ( {
115
+ ...mockExperimentConfig ,
116
+ overrides : {
117
+ '1b50f33aef8f681a13f623963da967ed' : 'variant-2' ,
118
+ } ,
119
+ typedOverrides : {
120
+ '1b50f33aef8f681a13f623963da967ed' : 'variant-2' ,
121
+ } ,
122
+ } ) ;
123
+ const client = new EppoJSClient ( mockConfigStore ) ;
124
+ client . setLogger ( mockLogger ) ;
125
+ const assignment = client . getAssignment ( 'subject-10' , flagKey ) ;
126
+ expect ( assignment ) . toEqual ( 'variant-2' ) ;
127
+ } ) ;
128
+
129
+ it ( 'returns null when experiment config is absent' , ( ) => {
130
+ const mockConfigStore = td . object < EppoLocalStorage > ( ) ;
131
+ const mockLogger = td . object < IAssignmentLogger > ( ) ;
132
+ td . when ( mockConfigStore . get ( flagKey ) ) . thenReturn ( null ) ;
133
+ const client = new EppoJSClient ( mockConfigStore ) ;
134
+ client . setLogger ( mockLogger ) ;
135
+ const assignment = client . getAssignment ( 'subject-10' , flagKey ) ;
136
+ expect ( assignment ) . toEqual ( null ) ;
137
+ } ) ;
138
+
139
+ it ( 'logs variation assignment and experiment key' , ( ) => {
140
+ const mockConfigStore = td . object < EppoLocalStorage > ( ) ;
141
+ const mockLogger = td . object < IAssignmentLogger > ( ) ;
142
+ td . when ( mockConfigStore . get ( flagKey ) ) . thenReturn ( mockExperimentConfig ) ;
143
+ const subjectAttributes = { foo : 3 } ;
144
+ const client = new EppoJSClient ( mockConfigStore ) ;
145
+ client . setLogger ( mockLogger ) ;
146
+ const assignment = client . getAssignment ( 'subject-10' , flagKey , subjectAttributes ) ;
147
+ expect ( assignment ) . toEqual ( 'control' ) ;
148
+ expect ( td . explain ( mockLogger . logAssignment ) . callCount ) . toEqual ( 1 ) ;
149
+ expect ( td . explain ( mockLogger ?. logAssignment ) . calls [ 0 ] ?. args [ 0 ] . subject ) . toEqual ( 'subject-10' ) ;
150
+ expect ( td . explain ( mockLogger ?. logAssignment ) . calls [ 0 ] ?. args [ 0 ] . featureFlag ) . toEqual ( flagKey ) ;
151
+ expect ( td . explain ( mockLogger ?. logAssignment ) . calls [ 0 ] ?. args [ 0 ] . experiment ) . toEqual (
152
+ `${ flagKey } -${ mockExperimentConfig ?. rules [ 0 ] ?. allocationKey } ` ,
153
+ ) ;
154
+ expect ( td . explain ( mockLogger ?. logAssignment ) . calls [ 0 ] ?. args [ 0 ] . allocation ) . toEqual (
155
+ `${ mockExperimentConfig ?. rules [ 0 ] ?. allocationKey } ` ,
156
+ ) ;
157
+ } ) ;
158
+
159
+ it ( 'handles logging exception' , ( ) => {
160
+ const mockConfigStore = td . object < EppoLocalStorage > ( ) ;
161
+ const mockLogger = td . object < IAssignmentLogger > ( ) ;
162
+ td . when ( mockLogger . logAssignment ( td . matchers . anything ( ) ) ) . thenThrow ( new Error ( 'logging error' ) ) ;
163
+ td . when ( mockConfigStore . get ( flagKey ) ) . thenReturn ( mockExperimentConfig ) ;
164
+ const subjectAttributes = { foo : 3 } ;
165
+ const client = new EppoJSClient ( mockConfigStore ) ;
166
+ client . setLogger ( mockLogger ) ;
167
+ const assignment = client . getAssignment ( 'subject-10' , flagKey , subjectAttributes ) ;
168
+ expect ( assignment ) . toEqual ( 'control' ) ;
169
+ } ) ;
170
+
171
+ it ( 'only returns variation if subject matches rules' , ( ) => {
172
+ const mockConfigStore = td . object < EppoLocalStorage > ( ) ;
173
+ const mockLogger = td . object < IAssignmentLogger > ( ) ;
174
+ td . when ( mockConfigStore . get ( flagKey ) ) . thenReturn ( {
175
+ ...mockExperimentConfig ,
176
+ rules : [
177
+ {
178
+ allocationKey : 'allocation1' ,
179
+ conditions : [
180
+ {
181
+ operator : 'GT' ,
182
+ attribute : 'appVersion' ,
183
+ value : 10 ,
184
+ } ,
185
+ ] ,
186
+ } ,
187
+ ] ,
188
+ } ) ;
189
+ const client = new EppoJSClient ( mockConfigStore ) ;
190
+ client . setLogger ( mockLogger ) ;
191
+ let assignment = client . getAssignment ( 'subject-10' , flagKey , {
192
+ appVersion : 9 ,
193
+ } ) ;
194
+ expect ( assignment ) . toEqual ( null ) ;
195
+ assignment = client . getAssignment ( 'subject-10' , flagKey ) ;
196
+ expect ( assignment ) . toEqual ( null ) ;
197
+ assignment = client . getAssignment ( 'subject-10' , flagKey , {
198
+ appVersion : 11 ,
199
+ } ) ;
200
+ expect ( assignment ) . toEqual ( 'control' ) ;
201
+ } ) ;
202
+
40
203
describe ( 'getAssignment' , ( ) => {
41
204
const testData = readAssignmentTestData ( ) ;
42
205
@@ -69,9 +232,9 @@ describe('EppoJSClient E2E test', () => {
69
232
} ) ;
70
233
} ) ;
71
234
72
- function getAssignments ( subjects : string [ ] , experiment : string ) : string [ ] {
235
+ function getAssignments ( subjects : string [ ] , experiment : string ) : ( string | null ) [ ] {
73
236
return subjects . map ( ( subjectKey ) => {
74
- return client . getAssignment ( subjectKey , experiment ) ;
237
+ return globalClient . getAssignment ( subjectKey , experiment ) ;
75
238
} ) ;
76
239
}
77
240
@@ -82,9 +245,9 @@ describe('EppoJSClient E2E test', () => {
82
245
subjectAttributes : Record < string , any > ;
83
246
} [ ] ,
84
247
experiment : string ,
85
- ) : string [ ] {
248
+ ) : ( string | null ) [ ] {
86
249
return subjectsWithAttributes . map ( ( subject ) => {
87
- return client . getAssignment ( subject . subjectKey , experiment , subject . subjectAttributes ) ;
250
+ return globalClient . getAssignment ( subject . subjectKey , experiment , subject . subjectAttributes ) ;
88
251
} ) ;
89
252
}
90
253
} ) ;
0 commit comments