launchdarkly-js-sdk-common 5.7.1 → 5.8.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.
- package/.github/workflows/dependency-scan.yml +30 -0
- package/.github/workflows/lint-pr-title.yml +3 -0
- package/.github/workflows/release-please.yml +49 -7
- package/.github/workflows/stale.yml +14 -0
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +14 -0
- package/package.json +4 -5
- package/src/AnonymousContextProcessor.js +2 -2
- package/src/EventSender.js +2 -2
- package/src/FlagStore.js +144 -0
- package/src/__tests__/LDClient-debugOverride-test.js +342 -0
- package/src/__tests__/LDClient-plugins-test.js +63 -5
- package/src/__tests__/diagnosticEvents-test.js +6 -0
- package/src/diagnosticEvents.js +2 -5
- package/src/index.js +127 -31
- package/src/plugins.js +17 -0
- package/src/uuid.js +70 -0
- package/test-types.ts +1 -1
- package/typings.d.ts +55 -3
- package/.github/workflows/manual-publish.yml +0 -42
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
const { initialize } = require('../index');
|
|
2
|
+
const stubPlatform = require('./stubPlatform');
|
|
3
|
+
const { respondJson } = require('./mockHttp');
|
|
4
|
+
const { makeBootstrap } = require('./testUtils');
|
|
5
|
+
|
|
6
|
+
// Mock the logger functions
|
|
7
|
+
const mockLogger = () => ({
|
|
8
|
+
error: jest.fn(),
|
|
9
|
+
warn: jest.fn(),
|
|
10
|
+
info: jest.fn(),
|
|
11
|
+
debug: jest.fn(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// Define a basic Plugin structure for tests
|
|
15
|
+
const createTestPlugin = (name = 'Test Plugin') => {
|
|
16
|
+
const plugin = {
|
|
17
|
+
getMetadata: jest.fn().mockReturnValue({ name }),
|
|
18
|
+
register: jest.fn(),
|
|
19
|
+
getHooks: jest.fn().mockReturnValue([]),
|
|
20
|
+
registerDebug: jest.fn(),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return plugin;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Helper to initialize the client for tests
|
|
27
|
+
async function withClient(initialContext, configOverrides = {}, plugins = [], testFn) {
|
|
28
|
+
const platform = stubPlatform.defaults();
|
|
29
|
+
const server = platform.testing.http.newServer();
|
|
30
|
+
const logger = mockLogger();
|
|
31
|
+
|
|
32
|
+
// Disable streaming and event sending unless overridden
|
|
33
|
+
const defaults = {
|
|
34
|
+
baseUrl: server.url,
|
|
35
|
+
streaming: false,
|
|
36
|
+
sendEvents: false,
|
|
37
|
+
useLdd: false,
|
|
38
|
+
logger: logger,
|
|
39
|
+
plugins: plugins,
|
|
40
|
+
};
|
|
41
|
+
const config = { ...defaults, ...configOverrides };
|
|
42
|
+
const { client, start } = initialize('env', initialContext, config, platform);
|
|
43
|
+
|
|
44
|
+
server.byDefault(respondJson({}));
|
|
45
|
+
start();
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await client.waitForInitialization(10);
|
|
49
|
+
await testFn(client, logger, platform);
|
|
50
|
+
} finally {
|
|
51
|
+
await client.close();
|
|
52
|
+
server.close();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
describe('LDDebugOverride', () => {
|
|
57
|
+
describe('setOverride method', () => {
|
|
58
|
+
it('should set override value returned by variation method', async () => {
|
|
59
|
+
let debugOverrideInterface;
|
|
60
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
61
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
62
|
+
debugOverrideInterface = debugOverride;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async client => {
|
|
66
|
+
expect(client.variation('test-flag', 'default')).toBe('default');
|
|
67
|
+
|
|
68
|
+
debugOverrideInterface.setOverride('test-flag', 'override-value');
|
|
69
|
+
expect(client.variation('test-flag', 'default')).toBe('override-value');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should override values taking precedence over real flag values from bootstrap', async () => {
|
|
74
|
+
let debugOverrideInterface;
|
|
75
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
76
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
77
|
+
debugOverrideInterface = debugOverride;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const flags = makeBootstrap({ 'existing-flag': { value: 'real-value', version: 1 } });
|
|
81
|
+
|
|
82
|
+
await withClient({ key: 'user-key', kind: 'user' }, { bootstrap: flags }, [mockPlugin], async client => {
|
|
83
|
+
expect(client.variation('existing-flag', 'default')).toBe('real-value');
|
|
84
|
+
|
|
85
|
+
debugOverrideInterface.setOverride('existing-flag', 'override-value');
|
|
86
|
+
expect(client.variation('existing-flag', 'default')).toBe('override-value');
|
|
87
|
+
|
|
88
|
+
debugOverrideInterface.removeOverride('existing-flag');
|
|
89
|
+
expect(client.variation('existing-flag', 'default')).toBe('real-value');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('removeOverride method', () => {
|
|
95
|
+
it('should remove individual override and revert to default', async () => {
|
|
96
|
+
let debugOverrideInterface;
|
|
97
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
98
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
99
|
+
debugOverrideInterface = debugOverride;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async client => {
|
|
103
|
+
debugOverrideInterface.setOverride('test-flag', 'override-value');
|
|
104
|
+
expect(client.variation('test-flag', 'default')).toBe('override-value');
|
|
105
|
+
|
|
106
|
+
debugOverrideInterface.removeOverride('test-flag');
|
|
107
|
+
expect(client.variation('test-flag', 'default')).toBe('default');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should remove only the specified override leaving others intact', async () => {
|
|
112
|
+
let debugOverrideInterface;
|
|
113
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
114
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
115
|
+
debugOverrideInterface = debugOverride;
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async client => {
|
|
119
|
+
debugOverrideInterface.setOverride('flag1', 'value1');
|
|
120
|
+
debugOverrideInterface.setOverride('flag2', 'value2');
|
|
121
|
+
debugOverrideInterface.setOverride('flag3', 'value3');
|
|
122
|
+
|
|
123
|
+
debugOverrideInterface.removeOverride('flag2');
|
|
124
|
+
|
|
125
|
+
expect(client.variation('flag1', 'default')).toBe('value1');
|
|
126
|
+
expect(client.variation('flag2', 'default')).toBe('default');
|
|
127
|
+
expect(client.variation('flag3', 'default')).toBe('value3');
|
|
128
|
+
|
|
129
|
+
const allOverrides = debugOverrideInterface.getAllOverrides();
|
|
130
|
+
expect(allOverrides).toEqual({
|
|
131
|
+
flag1: 'value1',
|
|
132
|
+
flag3: 'value3',
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should handle removing non-existent override without throwing error', async () => {
|
|
138
|
+
let debugOverrideInterface;
|
|
139
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
140
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
141
|
+
debugOverrideInterface = debugOverride;
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async client => {
|
|
145
|
+
debugOverrideInterface.setOverride('existing-flag', 'value');
|
|
146
|
+
|
|
147
|
+
// Should not throw error
|
|
148
|
+
expect(() => {
|
|
149
|
+
debugOverrideInterface.removeOverride('non-existent-flag');
|
|
150
|
+
}).not.toThrow();
|
|
151
|
+
|
|
152
|
+
// Existing override should remain
|
|
153
|
+
expect(client.variation('existing-flag', 'default')).toBe('value');
|
|
154
|
+
const allOverrides = debugOverrideInterface.getAllOverrides();
|
|
155
|
+
expect(allOverrides).toEqual({ 'existing-flag': 'value' });
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should be callable multiple times on same flag key safely', async () => {
|
|
160
|
+
let debugOverrideInterface;
|
|
161
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
162
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
163
|
+
debugOverrideInterface = debugOverride;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async client => {
|
|
167
|
+
debugOverrideInterface.setOverride('test-flag', 'value');
|
|
168
|
+
|
|
169
|
+
debugOverrideInterface.removeOverride('test-flag');
|
|
170
|
+
expect(client.variation('test-flag', 'default')).toBe('default');
|
|
171
|
+
|
|
172
|
+
// Removing again should not cause issues
|
|
173
|
+
expect(() => {
|
|
174
|
+
debugOverrideInterface.removeOverride('test-flag');
|
|
175
|
+
}).not.toThrow();
|
|
176
|
+
|
|
177
|
+
expect(debugOverrideInterface.getAllOverrides()).toEqual({});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('clearAllOverrides method', () => {
|
|
183
|
+
it('should clear all overrides and revert all flags to their default values', async () => {
|
|
184
|
+
let debugOverrideInterface;
|
|
185
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
186
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
187
|
+
debugOverrideInterface = debugOverride;
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async client => {
|
|
191
|
+
debugOverrideInterface.setOverride('flag1', 'value1');
|
|
192
|
+
debugOverrideInterface.setOverride('flag2', 'value2');
|
|
193
|
+
|
|
194
|
+
debugOverrideInterface.clearAllOverrides();
|
|
195
|
+
expect(client.variation('flag1', 'default')).toBe('default');
|
|
196
|
+
expect(client.variation('flag2', 'default')).toBe('default');
|
|
197
|
+
expect(debugOverrideInterface.getAllOverrides()).toEqual({});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('should operate safely when no overrides exist', async () => {
|
|
202
|
+
let debugOverrideInterface;
|
|
203
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
204
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
205
|
+
debugOverrideInterface = debugOverride;
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async () => {
|
|
209
|
+
// Should not throw error when no overrides exist
|
|
210
|
+
expect(() => {
|
|
211
|
+
debugOverrideInterface.clearAllOverrides();
|
|
212
|
+
}).not.toThrow();
|
|
213
|
+
|
|
214
|
+
expect(debugOverrideInterface.getAllOverrides()).toEqual({});
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('getAllOverrides method', () => {
|
|
220
|
+
it('should return all current overrides', async () => {
|
|
221
|
+
let debugOverrideInterface;
|
|
222
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
223
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
224
|
+
debugOverrideInterface = debugOverride;
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async () => {
|
|
228
|
+
debugOverrideInterface.setOverride('test-flag', 'override-value');
|
|
229
|
+
|
|
230
|
+
const allOverrides = debugOverrideInterface.getAllOverrides();
|
|
231
|
+
expect(allOverrides).toEqual({ 'test-flag': 'override-value' });
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should return empty object when no overrides have been set', async () => {
|
|
236
|
+
let debugOverrideInterface;
|
|
237
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
238
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
239
|
+
debugOverrideInterface = debugOverride;
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async () => {
|
|
243
|
+
const allOverrides = debugOverrideInterface.getAllOverrides();
|
|
244
|
+
expect(allOverrides).toEqual({});
|
|
245
|
+
expect(typeof allOverrides).toBe('object');
|
|
246
|
+
expect(Array.isArray(allOverrides)).toBe(false);
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should return immutable copy not reference to internal state', async () => {
|
|
251
|
+
let debugOverrideInterface;
|
|
252
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
253
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
254
|
+
debugOverrideInterface = debugOverride;
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async client => {
|
|
258
|
+
debugOverrideInterface.setOverride('test-flag', 'original-value');
|
|
259
|
+
|
|
260
|
+
const overrides1 = debugOverrideInterface.getAllOverrides();
|
|
261
|
+
const overrides2 = debugOverrideInterface.getAllOverrides();
|
|
262
|
+
|
|
263
|
+
// Should be different objects
|
|
264
|
+
expect(overrides1).not.toBe(overrides2);
|
|
265
|
+
|
|
266
|
+
// Modifying returned object should not affect internal state
|
|
267
|
+
overrides1['new-flag'] = 'new-value';
|
|
268
|
+
delete overrides1['test-flag'];
|
|
269
|
+
|
|
270
|
+
expect(client.variation('test-flag', 'default')).toBe('original-value');
|
|
271
|
+
expect(client.variation('new-flag', 'default')).toBe('default');
|
|
272
|
+
|
|
273
|
+
const overrides3 = debugOverrideInterface.getAllOverrides();
|
|
274
|
+
expect(overrides3).toEqual({ 'test-flag': 'original-value' });
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should maintain consistency across different operations', async () => {
|
|
279
|
+
let debugOverrideInterface;
|
|
280
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
281
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
282
|
+
debugOverrideInterface = debugOverride;
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async () => {
|
|
286
|
+
// Test consistency through various operations
|
|
287
|
+
expect(debugOverrideInterface.getAllOverrides()).toEqual({});
|
|
288
|
+
|
|
289
|
+
debugOverrideInterface.setOverride('flag1', 'value1');
|
|
290
|
+
expect(debugOverrideInterface.getAllOverrides()).toEqual({ flag1: 'value1' });
|
|
291
|
+
|
|
292
|
+
debugOverrideInterface.setOverride('flag2', 'value2');
|
|
293
|
+
expect(debugOverrideInterface.getAllOverrides()).toEqual({ flag1: 'value1', flag2: 'value2' });
|
|
294
|
+
|
|
295
|
+
debugOverrideInterface.removeOverride('flag1');
|
|
296
|
+
expect(debugOverrideInterface.getAllOverrides()).toEqual({ flag2: 'value2' });
|
|
297
|
+
|
|
298
|
+
debugOverrideInterface.setOverride('flag2', 'updated-value2');
|
|
299
|
+
expect(debugOverrideInterface.getAllOverrides()).toEqual({ flag2: 'updated-value2' });
|
|
300
|
+
|
|
301
|
+
debugOverrideInterface.clearAllOverrides();
|
|
302
|
+
expect(debugOverrideInterface.getAllOverrides()).toEqual({});
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe('integration with client methods', () => {
|
|
308
|
+
it('should work correctly with variationDetail method', async () => {
|
|
309
|
+
let debugOverrideInterface;
|
|
310
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
311
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
312
|
+
debugOverrideInterface = debugOverride;
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async client => {
|
|
316
|
+
debugOverrideInterface.setOverride('test-flag', 'override-value');
|
|
317
|
+
|
|
318
|
+
const detail = client.variationDetail('test-flag', 'default');
|
|
319
|
+
expect(detail.value).toBe('override-value');
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('should include overrides in allFlags method output', async () => {
|
|
324
|
+
let debugOverrideInterface;
|
|
325
|
+
const mockPlugin = createTestPlugin('test-plugin');
|
|
326
|
+
mockPlugin.registerDebug.mockImplementation(debugOverride => {
|
|
327
|
+
debugOverrideInterface = debugOverride;
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const flags = makeBootstrap({ 'real-flag': { value: 'real-value', version: 1 } });
|
|
331
|
+
|
|
332
|
+
await withClient({ key: 'user-key', kind: 'user' }, { bootstrap: flags }, [mockPlugin], async client => {
|
|
333
|
+
debugOverrideInterface.setOverride('override-flag', 'override-value');
|
|
334
|
+
debugOverrideInterface.setOverride('real-flag', 'overridden-real-value');
|
|
335
|
+
|
|
336
|
+
const allFlags = client.allFlags();
|
|
337
|
+
expect(allFlags['real-flag']).toBe('overridden-real-value');
|
|
338
|
+
expect(allFlags['override-flag']).toBe('override-value');
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
});
|
|
@@ -21,11 +21,19 @@ const createTestHook = (name = 'Test Hook') => ({
|
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
// Define a basic Plugin structure for tests
|
|
24
|
-
const createTestPlugin = (name = 'Test Plugin', hooks = []) =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
const createTestPlugin = (name = 'Test Plugin', hooks = [], includeDebug = false) => {
|
|
25
|
+
const plugin = {
|
|
26
|
+
getMetadata: jest.fn().mockReturnValue({ name }),
|
|
27
|
+
register: jest.fn(),
|
|
28
|
+
getHooks: jest.fn().mockReturnValue(hooks),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
if (includeDebug) {
|
|
32
|
+
plugin.registerDebug = jest.fn();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return plugin;
|
|
36
|
+
};
|
|
29
37
|
|
|
30
38
|
// Helper to initialize the client for tests
|
|
31
39
|
async function withClient(initialContext, configOverrides = {}, plugins = [], testFn) {
|
|
@@ -212,3 +220,53 @@ it('passes correct environmentMetadata without optional fields', async () => {
|
|
|
212
220
|
}
|
|
213
221
|
);
|
|
214
222
|
});
|
|
223
|
+
|
|
224
|
+
it('registers plugins and calls registerDebug when a plugin implements it', async () => {
|
|
225
|
+
const mockPlugin = createTestPlugin('test-plugin', [], true);
|
|
226
|
+
|
|
227
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async () => {
|
|
228
|
+
expect(mockPlugin.register).toHaveBeenCalled();
|
|
229
|
+
|
|
230
|
+
// Verify that registerDebug was called
|
|
231
|
+
expect(mockPlugin.registerDebug).toHaveBeenCalledTimes(1);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('registers plugins but does not call registerDebug when a plugin does not implement it', async () => {
|
|
236
|
+
const mockPlugin = createTestPlugin('test-plugin', [], false);
|
|
237
|
+
|
|
238
|
+
await withClient({ key: 'user-key', kind: 'user' }, {}, [mockPlugin], async () => {
|
|
239
|
+
expect(mockPlugin.register).toHaveBeenCalled();
|
|
240
|
+
|
|
241
|
+
// Verify that registerDebug was not called
|
|
242
|
+
expect(mockPlugin.registerDebug).toBeUndefined();
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('registers multiple plugins and calls registerDebug selectively', async () => {
|
|
247
|
+
const mockPluginWithDebug1 = createTestPlugin('test-plugin-with-debug-1', [], true);
|
|
248
|
+
const mockPluginWithDebug2 = createTestPlugin('test-plugin-with-debug-2', [], true);
|
|
249
|
+
const mockPluginWithoutDebug1 = createTestPlugin('test-plugin-without-debug-1', [], false);
|
|
250
|
+
const mockPluginWithoutDebug2 = createTestPlugin('test-plugin-without-debug-2', [], false);
|
|
251
|
+
|
|
252
|
+
await withClient(
|
|
253
|
+
{ key: 'user-key', kind: 'user' },
|
|
254
|
+
{},
|
|
255
|
+
[mockPluginWithDebug1, mockPluginWithoutDebug1, mockPluginWithDebug2, mockPluginWithoutDebug2],
|
|
256
|
+
async () => {
|
|
257
|
+
// Verify all plugins were registered
|
|
258
|
+
expect(mockPluginWithDebug1.register).toHaveBeenCalled();
|
|
259
|
+
expect(mockPluginWithDebug2.register).toHaveBeenCalled();
|
|
260
|
+
expect(mockPluginWithoutDebug1.register).toHaveBeenCalled();
|
|
261
|
+
expect(mockPluginWithoutDebug2.register).toHaveBeenCalled();
|
|
262
|
+
|
|
263
|
+
// Verify that registerDebug was called only on plugins that implement it
|
|
264
|
+
expect(mockPluginWithDebug1.registerDebug).toHaveBeenCalledTimes(1);
|
|
265
|
+
expect(mockPluginWithDebug2.registerDebug).toHaveBeenCalledTimes(1);
|
|
266
|
+
|
|
267
|
+
// Verify that registerDebug was not called on plugins that don't implement it
|
|
268
|
+
expect(mockPluginWithoutDebug1.registerDebug).toBeUndefined();
|
|
269
|
+
expect(mockPluginWithoutDebug2.registerDebug).toBeUndefined();
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
});
|
|
@@ -16,6 +16,12 @@ describe('DiagnosticId', () => {
|
|
|
16
16
|
expect(id1.diagnosticId).not.toEqual(id2.diagnosticId);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
+
it('generates valid UUID v4 format', () => {
|
|
20
|
+
const id = DiagnosticId('key');
|
|
21
|
+
const uuidV4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
22
|
+
expect(id.diagnosticId).toMatch(uuidV4Regex);
|
|
23
|
+
});
|
|
24
|
+
|
|
19
25
|
it('uses only last 6 characters of key', () => {
|
|
20
26
|
const id = DiagnosticId('0123456789abcdef');
|
|
21
27
|
expect(id.sdkKeySuffix).toEqual('abcdef');
|
package/src/diagnosticEvents.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
const {
|
|
2
|
-
// Note that in the diagnostic events spec, these IDs are to be generated with UUID v4. However,
|
|
3
|
-
// in JS we were already using v1 for unique context keys, so to avoid bringing in two packages we
|
|
4
|
-
// will use v1 here as well.
|
|
1
|
+
const { randomUuidV4 } = require('./uuid');
|
|
5
2
|
|
|
6
3
|
const { baseOptionDefs } = require('./configuration');
|
|
7
4
|
const messages = require('./messages');
|
|
@@ -9,7 +6,7 @@ const { appendUrlPath } = require('./utils');
|
|
|
9
6
|
|
|
10
7
|
function DiagnosticId(sdkKey) {
|
|
11
8
|
const ret = {
|
|
12
|
-
diagnosticId:
|
|
9
|
+
diagnosticId: randomUuidV4(),
|
|
13
10
|
};
|
|
14
11
|
if (sdkKey) {
|
|
15
12
|
ret.sdkKeySuffix = sdkKey.length > 6 ? sdkKey.substring(sdkKey.length - 6) : sdkKey;
|