Skip to content

Commit 9126060

Browse files
authored
Merge branch 'main' into ta/sdk-198/ping-stream-support
2 parents c22fd0e + 8c84e01 commit 9126060

34 files changed

+1199
-38
lines changed

contract-tests/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ app.get('/', (req, res) => {
3838
'anonymous-redaction',
3939
'evaluation-hooks',
4040
'wrapper',
41+
'client-prereq-events',
4142
],
4243
});
4344
});

packages/sdk/browser/__tests__/BrowserDataManager.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ describe('given a BrowserDataManager with mocked dependencies', () => {
9494
userAgentHeaderName: 'user-agent',
9595
trackEventModifier: (event) => event,
9696
hooks: [],
97+
inspectors: [],
9798
};
9899
const mockedFetch = mockFetch('{"flagA": true}', 200);
99100
platform = {

packages/sdk/browser/contract-tests/entity/src/TestHarnessWebSocket.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export default class TestHarnessWebSocket {
4141
'inline-context',
4242
'anonymous-redaction',
4343
'strongly-typed',
44+
'client-prereq-events',
4445
];
4546

4647
break;

packages/sdk/browser/rollup.config.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ const terserOpts = {
2828
regex: /^_/,
2929
// Do not mangle '_meta', because this is part of our JSON
3030
// data model.
31-
reserved: ['_meta']
31+
reserved: ['_meta'],
3232
},
33-
}
33+
},
3434
};
3535

3636
export default [
@@ -53,12 +53,6 @@ export default [
5353
},
5454
{
5555
...getSharedConfig('cjs', 'dist/index.cjs.js'),
56-
plugins: [
57-
typescript(),
58-
common(),
59-
resolve(),
60-
terser(terserOpts),
61-
json(),
62-
],
56+
plugins: [typescript(), common(), resolve(), terser(terserOpts), json()],
6357
},
6458
];

packages/sdk/react-native/__tests__/MobileDataManager.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ describe('given a MobileDataManager with mocked dependencies', () => {
8282
userAgentHeaderName: 'user-agent',
8383
trackEventModifier: (event) => event,
8484
hooks: [],
85+
inspectors: [],
8586
};
8687
const mockedFetch = mockFetch('{"flagA": true}', 200);
8788
platform = {
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import { AsyncQueue } from 'launchdarkly-js-test-helpers';
2+
3+
import { AutoEnvAttributes, clone } from '@launchdarkly/js-sdk-common';
4+
5+
import { LDInspection } from '../src/api/LDInspection';
6+
import LDClientImpl from '../src/LDClientImpl';
7+
import { Flags, PatchFlag } from '../src/types';
8+
import { createBasicPlatform } from './createBasicPlatform';
9+
import * as mockResponseJson from './evaluation/mockResponse.json';
10+
import { MockEventSource } from './streaming/LDClientImpl.mocks';
11+
import { makeTestDataManagerFactory } from './TestDataManager';
12+
13+
it('calls flag-used inspectors', async () => {
14+
const flagUsedInspector: LDInspection = {
15+
type: 'flag-used',
16+
name: 'test flag used inspector',
17+
method: jest.fn(),
18+
};
19+
const platform = createBasicPlatform();
20+
const factory = makeTestDataManagerFactory('sdk-key', platform, {
21+
disableNetwork: true,
22+
});
23+
const client = new LDClientImpl(
24+
'sdk-key',
25+
AutoEnvAttributes.Disabled,
26+
platform,
27+
{
28+
sendEvents: false,
29+
inspectors: [flagUsedInspector],
30+
logger: {
31+
debug: jest.fn(),
32+
info: jest.fn(),
33+
warn: jest.fn(),
34+
error: jest.fn(),
35+
},
36+
},
37+
factory,
38+
);
39+
40+
await client.identify({ key: 'user-key' });
41+
await client.variation('flag-key', false);
42+
43+
expect(flagUsedInspector.method).toHaveBeenCalledWith(
44+
'flag-key',
45+
{
46+
value: false,
47+
variationIndex: null,
48+
reason: {
49+
kind: 'ERROR',
50+
errorKind: 'FLAG_NOT_FOUND',
51+
},
52+
},
53+
{ key: 'user-key' },
54+
);
55+
});
56+
57+
it('calls client-identity-changed inspectors', async () => {
58+
const identifyInspector: LDInspection = {
59+
type: 'client-identity-changed',
60+
name: 'test client identity inspector',
61+
method: jest.fn(),
62+
};
63+
64+
const platform = createBasicPlatform();
65+
const factory = makeTestDataManagerFactory('sdk-key', platform, {
66+
disableNetwork: true,
67+
});
68+
const client = new LDClientImpl(
69+
'sdk-key',
70+
AutoEnvAttributes.Disabled,
71+
platform,
72+
{
73+
sendEvents: false,
74+
inspectors: [identifyInspector],
75+
logger: {
76+
debug: jest.fn(),
77+
info: jest.fn(),
78+
warn: jest.fn(),
79+
error: jest.fn(),
80+
},
81+
},
82+
factory,
83+
);
84+
85+
await client.identify({ key: 'user-key' });
86+
87+
expect(identifyInspector.method).toHaveBeenCalledWith({ key: 'user-key' });
88+
});
89+
90+
it('calls flag-detail-changed inspector for individial flag changes on patch', async () => {
91+
const eventQueue = new AsyncQueue();
92+
const flagDetailChangedInspector: LDInspection = {
93+
type: 'flag-detail-changed',
94+
name: 'test flag detail changed inspector',
95+
method: jest.fn(() => eventQueue.add({})),
96+
};
97+
const platform = createBasicPlatform();
98+
const factory = makeTestDataManagerFactory('sdk-key', platform);
99+
const client = new LDClientImpl(
100+
'sdk-key',
101+
AutoEnvAttributes.Disabled,
102+
platform,
103+
{
104+
sendEvents: false,
105+
inspectors: [flagDetailChangedInspector],
106+
logger: {
107+
debug: jest.fn(),
108+
info: jest.fn(),
109+
warn: jest.fn(),
110+
error: jest.fn(),
111+
},
112+
},
113+
factory,
114+
);
115+
let mockEventSource: MockEventSource;
116+
117+
const putResponse = clone<Flags>(mockResponseJson);
118+
const putEvents = [{ data: JSON.stringify(putResponse) }];
119+
platform.requests.createEventSource.mockImplementation(
120+
(streamUri: string = '', options: any = {}) => {
121+
mockEventSource = new MockEventSource(streamUri, options);
122+
const patchResponse = clone<PatchFlag>(putResponse['dev-test-flag']);
123+
patchResponse.key = 'dev-test-flag';
124+
patchResponse.value = false;
125+
patchResponse.version += 1;
126+
const patchEvents = [{ data: JSON.stringify(patchResponse) }];
127+
128+
// @ts-ignore
129+
mockEventSource.simulateEvents('patch', patchEvents);
130+
mockEventSource.simulateEvents('put', putEvents);
131+
return mockEventSource;
132+
},
133+
);
134+
135+
await client.identify({ key: 'user-key' }, { waitForNetworkResults: true });
136+
137+
await eventQueue.take();
138+
expect(flagDetailChangedInspector.method).toHaveBeenCalledWith('dev-test-flag', {
139+
reason: null,
140+
value: false,
141+
variationIndex: 0,
142+
});
143+
});
144+
145+
it('calls flag-details-changed inspectors when all flag values change', async () => {
146+
const flagDetailsChangedInspector: LDInspection = {
147+
type: 'flag-details-changed',
148+
name: 'test flag details changed inspector',
149+
method: jest.fn(),
150+
};
151+
const platform = createBasicPlatform();
152+
const factory = makeTestDataManagerFactory('sdk-key', platform);
153+
const client = new LDClientImpl(
154+
'sdk-key',
155+
AutoEnvAttributes.Disabled,
156+
platform,
157+
{
158+
sendEvents: false,
159+
inspectors: [flagDetailsChangedInspector],
160+
logger: {
161+
debug: jest.fn(),
162+
info: jest.fn(),
163+
warn: jest.fn(),
164+
error: jest.fn(),
165+
},
166+
},
167+
factory,
168+
);
169+
let mockEventSource: MockEventSource;
170+
171+
platform.requests.createEventSource.mockImplementation(
172+
(streamUri: string = '', options: any = {}) => {
173+
mockEventSource = new MockEventSource(streamUri, options);
174+
const simulatedEvents = [{ data: JSON.stringify(mockResponseJson) }];
175+
mockEventSource.simulateEvents('put', simulatedEvents);
176+
return mockEventSource;
177+
},
178+
);
179+
180+
await client.identify({ key: 'user-key' }, { waitForNetworkResults: true });
181+
expect(flagDetailsChangedInspector.method).toHaveBeenCalledWith({
182+
'dev-test-flag': { reason: null, value: true, variationIndex: 0 },
183+
'easter-i-tunes-special': { reason: null, value: false, variationIndex: 1 },
184+
'easter-specials': { reason: null, value: 'no specials', variationIndex: 3 },
185+
fdsafdsafdsafdsa: { reason: null, value: true, variationIndex: 0 },
186+
'log-level': { reason: null, value: 'warn', variationIndex: 3 },
187+
'moonshot-demo': { reason: null, value: true, variationIndex: 0 },
188+
test1: { reason: null, value: 's1', variationIndex: 0 },
189+
'this-is-a-test': { reason: null, value: true, variationIndex: 0 },
190+
'has-prereq-depth-1': { reason: { kind: 'FALLTHROUGH' }, value: true, variationIndex: 0 },
191+
'is-prereq': { reason: { kind: 'FALLTHROUGH' }, value: true, variationIndex: 0 },
192+
});
193+
});

packages/shared/sdk-client/__tests__/LDClientImpl.events.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,45 @@ describe('sdk-client object', () => {
199199
expect.stringMatching(/was called with a non-numeric/),
200200
);
201201
});
202+
203+
it('sends events for prerequisite flags', async () => {
204+
await ldc.identify({ kind: 'user', key: 'bob' });
205+
ldc.variation('has-prereq-depth-1', false);
206+
ldc.flush();
207+
208+
// Prerequisite evaluation event should be emitted before the evaluation event for the flag
209+
// being evaluated.
210+
expect(mockedSendEvent).toHaveBeenNthCalledWith(
211+
2,
212+
expect.objectContaining({
213+
context: expect.anything(),
214+
creationDate: expect.any(Number),
215+
default: undefined,
216+
key: 'is-prereq',
217+
kind: 'feature',
218+
samplingRatio: 1,
219+
trackEvents: true,
220+
value: true,
221+
variation: 0,
222+
version: 1,
223+
withReasons: false,
224+
}),
225+
);
226+
expect(mockedSendEvent).toHaveBeenNthCalledWith(
227+
3,
228+
expect.objectContaining({
229+
context: expect.anything(),
230+
creationDate: expect.any(Number),
231+
default: false,
232+
key: 'has-prereq-depth-1',
233+
kind: 'feature',
234+
samplingRatio: 1,
235+
trackEvents: true,
236+
value: true,
237+
variation: 0,
238+
version: 4,
239+
withReasons: false,
240+
}),
241+
);
242+
});
202243
});

packages/shared/sdk-client/__tests__/LDClientImpl.storage.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ describe('sdk-client storage', () => {
104104
'easter-i-tunes-special': false,
105105
'easter-specials': 'no specials',
106106
fdsafdsafdsafdsa: true,
107+
'has-prereq-depth-1': true,
108+
'is-prereq': true,
107109
'log-level': 'warn',
108110
'moonshot-demo': true,
109111
test1: 's1',
@@ -156,6 +158,8 @@ describe('sdk-client storage', () => {
156158
'easter-i-tunes-special': false,
157159
'easter-specials': 'no specials',
158160
fdsafdsafdsafdsa: true,
161+
'has-prereq-depth-1': true,
162+
'is-prereq': true,
159163
'log-level': 'warn',
160164
'moonshot-demo': true,
161165
test1: 's1',
@@ -218,6 +222,8 @@ describe('sdk-client storage', () => {
218222
'easter-i-tunes-special': false,
219223
'easter-specials': 'no specials',
220224
fdsafdsafdsafdsa: true,
225+
'has-prereq-depth-1': true,
226+
'is-prereq': true,
221227
'log-level': 'warn',
222228
'moonshot-demo': true,
223229
test1: 's1',
@@ -388,6 +394,8 @@ describe('sdk-client storage', () => {
388394
'easter-i-tunes-special': false,
389395
'easter-specials': 'no specials',
390396
fdsafdsafdsafdsa: true,
397+
'has-prereq-depth-1': true,
398+
'is-prereq': true,
391399
'log-level': 'warn',
392400
'moonshot-demo': true,
393401
test1: 's1',
@@ -517,6 +525,8 @@ describe('sdk-client storage', () => {
517525
'easter-i-tunes-special': false,
518526
'easter-specials': 'no specials',
519527
fdsafdsafdsafdsa: true,
528+
'has-prereq-depth-1': true,
529+
'is-prereq': true,
520530
'log-level': 'warn',
521531
'moonshot-demo': true,
522532
test1: 's1',

packages/shared/sdk-client/__tests__/LDClientImpl.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ describe('sdk-client object', () => {
103103
'easter-i-tunes-special': false,
104104
'easter-specials': 'no specials',
105105
fdsafdsafdsafdsa: true,
106+
'has-prereq-depth-1': true,
107+
'is-prereq': true,
106108
'log-level': 'warn',
107109
'moonshot-demo': true,
108110
test1: 's1',

packages/shared/sdk-client/__tests__/LDClientImpl.timeout.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ describe('sdk-client identify timeout', () => {
8989
'easter-i-tunes-special': false,
9090
'easter-specials': 'no specials',
9191
fdsafdsafdsafdsa: true,
92+
'has-prereq-depth-1': true,
93+
'is-prereq': true,
9294
'log-level': 'warn',
9395
'moonshot-demo': true,
9496
test1: 's1',
@@ -112,6 +114,8 @@ describe('sdk-client identify timeout', () => {
112114
'easter-i-tunes-special': false,
113115
'easter-specials': 'no specials',
114116
fdsafdsafdsafdsa: true,
117+
'has-prereq-depth-1': true,
118+
'is-prereq': true,
115119
'log-level': 'warn',
116120
'moonshot-demo': true,
117121
test1: 's1',

0 commit comments

Comments
 (0)