@depup/launchdarkly-node-server-sdk 7.0.4-depup.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/.babelrc +16 -0
- package/.circleci/config.yml +89 -0
- package/.eslintignore +5 -0
- package/.eslintrc.yaml +114 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/pull_request_template.md +21 -0
- package/.github/workflows/stale.yml +8 -0
- package/.hound.yml +33 -0
- package/.ldrelease/config.yml +28 -0
- package/.prettierrc +6 -0
- package/CHANGELOG.md +603 -0
- package/CODEOWNERS +2 -0
- package/CONTRIBUTING.md +55 -0
- package/LICENSE.txt +13 -0
- package/README.md +36 -0
- package/SECURITY.md +5 -0
- package/attribute_reference.js +217 -0
- package/big_segments.js +117 -0
- package/caching_store_wrapper.js +240 -0
- package/changes.json +30 -0
- package/configuration.js +235 -0
- package/context.js +98 -0
- package/context_filter.js +137 -0
- package/contract-tests/README.md +7 -0
- package/contract-tests/index.js +109 -0
- package/contract-tests/log.js +23 -0
- package/contract-tests/package.json +15 -0
- package/contract-tests/sdkClientEntity.js +110 -0
- package/contract-tests/testharness-suppressions.txt +2 -0
- package/diagnostic_events.js +151 -0
- package/docs/typedoc.js +10 -0
- package/errors.js +26 -0
- package/evaluator.js +822 -0
- package/event_factory.js +121 -0
- package/event_processor.js +320 -0
- package/event_summarizer.js +101 -0
- package/feature_store.js +120 -0
- package/feature_store_event_wrapper.js +258 -0
- package/file_data_source.js +192 -0
- package/flags_state.js +46 -0
- package/index.d.ts +2426 -0
- package/index.js +452 -0
- package/integrations.js +7 -0
- package/interfaces.js +2 -0
- package/loggers.js +125 -0
- package/messages.js +31 -0
- package/operators.js +106 -0
- package/package.json +105 -0
- package/polling.js +70 -0
- package/requestor.js +62 -0
- package/scripts/better-audit.sh +76 -0
- package/sharedtest/big_segment_store_tests.js +86 -0
- package/sharedtest/feature_store_tests.js +177 -0
- package/sharedtest/persistent_feature_store_tests.js +183 -0
- package/sharedtest/store_tests.js +7 -0
- package/streaming.js +179 -0
- package/test/LDClient-big-segments-test.js +92 -0
- package/test/LDClient-end-to-end-test.js +218 -0
- package/test/LDClient-evaluation-all-flags-test.js +226 -0
- package/test/LDClient-evaluation-test.js +204 -0
- package/test/LDClient-events-test.js +502 -0
- package/test/LDClient-listeners-test.js +180 -0
- package/test/LDClient-test.js +96 -0
- package/test/LDClient-tls-test.js +110 -0
- package/test/attribute_reference-test.js +494 -0
- package/test/big_segments-test.js +182 -0
- package/test/caching_store_wrapper-test.js +434 -0
- package/test/configuration-test.js +249 -0
- package/test/context-test.js +93 -0
- package/test/context_filter-test.js +424 -0
- package/test/diagnostic_events-test.js +152 -0
- package/test/evaluator-big-segments-test.js +301 -0
- package/test/evaluator-bucketing-test.js +333 -0
- package/test/evaluator-clause-test.js +277 -0
- package/test/evaluator-flag-test.js +452 -0
- package/test/evaluator-pre-conditions-test.js +105 -0
- package/test/evaluator-rule-test.js +131 -0
- package/test/evaluator-segment-match-test.js +310 -0
- package/test/evaluator_helpers.js +106 -0
- package/test/event_processor-test.js +680 -0
- package/test/event_summarizer-test.js +146 -0
- package/test/feature_store-test.js +42 -0
- package/test/feature_store_event_wrapper-test.js +182 -0
- package/test/feature_store_test_base.js +60 -0
- package/test/file_data_source-test.js +255 -0
- package/test/loggers-test.js +126 -0
- package/test/operators-test.js +102 -0
- package/test/polling-test.js +158 -0
- package/test/requestor-test.js +60 -0
- package/test/store_tests_big_segments-test.js +61 -0
- package/test/streaming-test.js +323 -0
- package/test/stubs.js +107 -0
- package/test/test_data-test.js +341 -0
- package/test/update_queue-test.js +61 -0
- package/test-types.ts +210 -0
- package/test_data.js +323 -0
- package/tsconfig.json +14 -0
- package/update_queue.js +28 -0
- package/utils/__tests__/httpUtils-test.js +39 -0
- package/utils/__tests__/wrapPromiseCallback-test.js +33 -0
- package/utils/asyncUtils.js +32 -0
- package/utils/httpUtils.js +105 -0
- package/utils/stringifyAttrs.js +14 -0
- package/utils/wrapPromiseCallback.js +36 -0
- package/versioned_data_kind.js +34 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
const ContextFilter = require('../context_filter');
|
|
2
|
+
|
|
3
|
+
describe('when handling legacy user contexts', () => {
|
|
4
|
+
// users to serialize
|
|
5
|
+
const user = {
|
|
6
|
+
'key': 'abc',
|
|
7
|
+
'firstName': 'Sue',
|
|
8
|
+
'custom': { 'bizzle': 'def', 'dizzle': 'ghi' }
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const userSpecifyingOwnPrivateAttr = {
|
|
12
|
+
'key': 'abc',
|
|
13
|
+
'firstName': 'Sue',
|
|
14
|
+
'custom': { 'bizzle': 'def', 'dizzle': 'ghi' },
|
|
15
|
+
'privateAttributeNames': ['dizzle', 'unused']
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const userWithUnknownTopLevelAttrs = {
|
|
19
|
+
'key': 'abc',
|
|
20
|
+
'firstName': 'Sue',
|
|
21
|
+
'species': 'human',
|
|
22
|
+
'hatSize': 6,
|
|
23
|
+
'custom': { 'bizzle': 'def', 'dizzle': 'ghi' }
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const anonUser = {
|
|
27
|
+
'key': 'abc',
|
|
28
|
+
'anonymous': true,
|
|
29
|
+
'custom': { 'bizzle': 'def', 'dizzle': 'ghi' }
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const userWithNonStringsInStringRequiredFields = {
|
|
33
|
+
'key': -1,
|
|
34
|
+
'name': 0,
|
|
35
|
+
'ip': 1,
|
|
36
|
+
'firstName': 2,
|
|
37
|
+
'lastName': ['a', 99, null],
|
|
38
|
+
'email': 4,
|
|
39
|
+
'avatar': 5,
|
|
40
|
+
'country': 6,
|
|
41
|
+
'custom': {
|
|
42
|
+
'validNumericField': 7
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// expected results from serializing user
|
|
48
|
+
const userWithNothingHidden = {
|
|
49
|
+
'bizzle': 'def',
|
|
50
|
+
'dizzle': 'ghi',
|
|
51
|
+
'firstName': 'Sue',
|
|
52
|
+
'key': 'abc',
|
|
53
|
+
'kind': 'user'
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const userWithAllAttrsHidden = {
|
|
57
|
+
'kind': 'user',
|
|
58
|
+
'key': 'abc',
|
|
59
|
+
'_meta': {
|
|
60
|
+
'redactedAttributes': ['/bizzle', '/dizzle', '/firstName']
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const userWithSomeAttrsHidden = {
|
|
65
|
+
'kind': 'user',
|
|
66
|
+
'key': 'abc',
|
|
67
|
+
'dizzle': 'ghi',
|
|
68
|
+
'_meta': {
|
|
69
|
+
'redactedAttributes': ['/bizzle', '/firstName']
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const userWithOwnSpecifiedAttrHidden = {
|
|
74
|
+
'kind': 'user',
|
|
75
|
+
'key': 'abc',
|
|
76
|
+
'firstName': 'Sue',
|
|
77
|
+
'bizzle': 'def',
|
|
78
|
+
'_meta': {
|
|
79
|
+
'redactedAttributes': ['/dizzle']
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const anonUserWithAllAttrsHidden = {
|
|
84
|
+
'kind': 'user',
|
|
85
|
+
'key': 'abc',
|
|
86
|
+
'anonymous': true,
|
|
87
|
+
'_meta': {
|
|
88
|
+
'redactedAttributes': ['/bizzle', '/dizzle']
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const userWithStringFieldsConverted = {
|
|
93
|
+
'key': '-1',
|
|
94
|
+
'kind': 'user',
|
|
95
|
+
'name': '0',
|
|
96
|
+
'ip': '1',
|
|
97
|
+
'firstName': '2',
|
|
98
|
+
'lastName': 'a,99,',
|
|
99
|
+
'email': '4',
|
|
100
|
+
'avatar': '5',
|
|
101
|
+
'country': '6',
|
|
102
|
+
'validNumericField': 7
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const userWithPrivateFieldsWithAPrecedingSlash = {
|
|
106
|
+
'key': 'annoying',
|
|
107
|
+
'custom': {
|
|
108
|
+
'/why': 'not',
|
|
109
|
+
'why': 'because',
|
|
110
|
+
},
|
|
111
|
+
'privateAttributeNames': ['/why']
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
const userWithPrivateFieldsWithAPrecedingSlashFiltered = {
|
|
116
|
+
'kind': 'user',
|
|
117
|
+
'key': 'annoying',
|
|
118
|
+
'why': 'because',
|
|
119
|
+
'_meta': {
|
|
120
|
+
'redactedAttributes': ['/~1why']
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
it('includes all user attributes by default', () => {
|
|
126
|
+
const uf = ContextFilter({});
|
|
127
|
+
expect(uf.filter(user)).toEqual(userWithNothingHidden);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('hides all except key if allAttributesPrivate is true', () => {
|
|
131
|
+
const uf = ContextFilter({ allAttributesPrivate: true });
|
|
132
|
+
expect(uf.filter(user)).toEqual(userWithAllAttrsHidden);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('hides some attributes if privateAttributes is set', () => {
|
|
136
|
+
const uf = ContextFilter({ privateAttributes: ['firstName', 'bizzle'] });
|
|
137
|
+
expect(uf.filter(user)).toEqual(userWithSomeAttrsHidden);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('hides attributes specified in per-user redactedAttributes', () => {
|
|
141
|
+
const uf = ContextFilter({});
|
|
142
|
+
expect(uf.filter(userSpecifyingOwnPrivateAttr)).toEqual(userWithOwnSpecifiedAttrHidden);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('looks at both per-user redactedAttributes and global config', () => {
|
|
146
|
+
const uf = ContextFilter({ privateAttributes: ['firstName', 'bizzle'] });
|
|
147
|
+
expect(uf.filter(userSpecifyingOwnPrivateAttr)).toEqual(userWithAllAttrsHidden);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('strips unknown top-level attributes', () => {
|
|
151
|
+
const uf = ContextFilter({});
|
|
152
|
+
expect(uf.filter(userWithUnknownTopLevelAttrs)).toEqual(userWithNothingHidden);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('anonymous persists in the conversion to a single kind context', () => {
|
|
156
|
+
const uf = ContextFilter({ allAttributesPrivate: true });
|
|
157
|
+
expect(uf.filter(anonUser)).toEqual(anonUserWithAllAttrsHidden);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
it('converts non-boolean "anonymous" to boolean "anonymous"', () => {
|
|
162
|
+
const uf = ContextFilter({ allAttributesPrivate: true });
|
|
163
|
+
expect(uf.filter({ key: "user", anonymous: "yes" }))
|
|
164
|
+
.toEqual({ key: "user", kind: "user", anonymous: true });
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('converts fields to string types when needed', () => {
|
|
168
|
+
const uf = ContextFilter({});
|
|
169
|
+
expect(uf.filter(userWithNonStringsInStringRequiredFields)).toEqual(userWithStringFieldsConverted);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('it handles legacy names which had a preceding slash', () => {
|
|
173
|
+
const uf = ContextFilter({});
|
|
174
|
+
expect(uf.filter(userWithPrivateFieldsWithAPrecedingSlash)).toEqual(userWithPrivateFieldsWithAPrecedingSlashFiltered);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it.each([null, undefined])('handles null and undefined the same for built-in attributes', (value) => {
|
|
178
|
+
const cf = ContextFilter({});
|
|
179
|
+
const user = {
|
|
180
|
+
key: "userKey",
|
|
181
|
+
name: value,
|
|
182
|
+
ip: value,
|
|
183
|
+
firstName: value,
|
|
184
|
+
lastName: value,
|
|
185
|
+
email: value,
|
|
186
|
+
avatar: value,
|
|
187
|
+
country: value,
|
|
188
|
+
};
|
|
189
|
+
expect(cf.filter(user)).toEqual({key: 'userKey', kind: 'user'});
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe('when handling single kind contexts', () => {
|
|
194
|
+
// users to serialize
|
|
195
|
+
const context = {
|
|
196
|
+
'kind': 'organization',
|
|
197
|
+
'key': 'abc',
|
|
198
|
+
'firstName': 'Sue',
|
|
199
|
+
'bizzle': 'def',
|
|
200
|
+
'dizzle': 'ghi'
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const contextSpecifyingOwnPrivateAttr = {
|
|
204
|
+
'kind': 'organization',
|
|
205
|
+
'key': 'abc',
|
|
206
|
+
'firstName': 'Sue',
|
|
207
|
+
'bizzle': 'def',
|
|
208
|
+
'dizzle': 'ghi',
|
|
209
|
+
'_meta': {
|
|
210
|
+
'privateAttributes': ['dizzle', 'unused']
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const anonymousContext = {
|
|
215
|
+
'kind': 'organization',
|
|
216
|
+
'key': 'abc',
|
|
217
|
+
'anonymous': true,
|
|
218
|
+
'bizzle': 'def',
|
|
219
|
+
'dizzle': 'ghi'
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// expected results from serializing context
|
|
223
|
+
const userWithAllAttrsHidden = {
|
|
224
|
+
'kind': 'organization',
|
|
225
|
+
'key': 'abc',
|
|
226
|
+
'_meta': {
|
|
227
|
+
'redactedAttributes': ['/bizzle', '/dizzle', '/firstName']
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const contextWithSomeAttrsHidden = {
|
|
232
|
+
'kind': 'organization',
|
|
233
|
+
'key': 'abc',
|
|
234
|
+
'dizzle': 'ghi',
|
|
235
|
+
'_meta': {
|
|
236
|
+
'redactedAttributes': ['/bizzle', '/firstName']
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const contextWithOwnSpecifiedAttrHidden = {
|
|
241
|
+
'kind': 'organization',
|
|
242
|
+
'key': 'abc',
|
|
243
|
+
'firstName': 'Sue',
|
|
244
|
+
'bizzle': 'def',
|
|
245
|
+
'_meta': {
|
|
246
|
+
'redactedAttributes': ['/dizzle']
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const contextWithAllAttrsHidden = {
|
|
251
|
+
'kind': 'organization',
|
|
252
|
+
'key': 'abc',
|
|
253
|
+
'anonymous': true,
|
|
254
|
+
'_meta': {
|
|
255
|
+
'redactedAttributes': ['/bizzle', '/dizzle']
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
it('includes all attributes by default', () => {
|
|
260
|
+
const uf = ContextFilter({});
|
|
261
|
+
expect(uf.filter(context)).toEqual(context);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('hides all except key if allAttributesPrivate is true', () => {
|
|
265
|
+
const uf = ContextFilter({ allAttributesPrivate: true });
|
|
266
|
+
expect(uf.filter(context)).toEqual(userWithAllAttrsHidden);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('hides some attributes if privateAttributes is set', () => {
|
|
270
|
+
const uf = ContextFilter({ privateAttributes: ['firstName', 'bizzle'] });
|
|
271
|
+
expect(uf.filter(context)).toEqual(contextWithSomeAttrsHidden);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('hides attributes specified in per-context redactedAttributes', () => {
|
|
275
|
+
const uf = ContextFilter({});
|
|
276
|
+
expect(uf.filter(contextSpecifyingOwnPrivateAttr)).toEqual(contextWithOwnSpecifiedAttrHidden);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('looks at both per-context redactedAttributes and global config', () => {
|
|
280
|
+
const uf = ContextFilter({ privateAttributes: ['firstName', 'bizzle'] });
|
|
281
|
+
expect(uf.filter(contextSpecifyingOwnPrivateAttr)).toEqual(userWithAllAttrsHidden);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('context remains anonymous even when all attributes are hidden', () => {
|
|
285
|
+
var uf = ContextFilter({ allAttributesPrivate: true });
|
|
286
|
+
expect(uf.filter(anonymousContext)).toEqual(contextWithAllAttrsHidden);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('converts non-boolean anonymous to boolean.', () => {
|
|
290
|
+
var uf = ContextFilter({});
|
|
291
|
+
expect(uf.filter({ kind: 'user', key: 'user', anonymous: "string" }))
|
|
292
|
+
.toEqual({ kind: 'user', key: 'user', anonymous: true });
|
|
293
|
+
|
|
294
|
+
expect(uf.filter({ kind: 'user', key: 'user', anonymous: null }))
|
|
295
|
+
.toEqual({ kind: 'user', key: 'user', anonymous: false });
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
describe('when handling mult-kind contexts', () => {
|
|
300
|
+
const contextWithBadContexts = {
|
|
301
|
+
kind: 'multi',
|
|
302
|
+
string: 'string',
|
|
303
|
+
null: null,
|
|
304
|
+
number: 0,
|
|
305
|
+
real: {
|
|
306
|
+
key: "real"
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const contextWithBadContextsRemoved = {
|
|
311
|
+
kind: 'multi',
|
|
312
|
+
real: {
|
|
313
|
+
key: "real"
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const orgAndUserContext = {
|
|
318
|
+
kind: 'multi',
|
|
319
|
+
organization: {
|
|
320
|
+
key: 'LD',
|
|
321
|
+
rocks: true,
|
|
322
|
+
name: 'name',
|
|
323
|
+
department: {
|
|
324
|
+
name: 'sdk'
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
user: {
|
|
328
|
+
key: 'abc',
|
|
329
|
+
name: 'alphabet',
|
|
330
|
+
letters: ['a', 'b', 'c'],
|
|
331
|
+
order: 3,
|
|
332
|
+
object: {
|
|
333
|
+
a: 'a',
|
|
334
|
+
b: 'b'
|
|
335
|
+
},
|
|
336
|
+
_meta: {
|
|
337
|
+
privateAttributes: ['letters', '/object/b']
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const orgAndUserContextAllPrivate = {
|
|
343
|
+
kind: 'multi',
|
|
344
|
+
organization: {
|
|
345
|
+
key: 'LD',
|
|
346
|
+
_meta: {
|
|
347
|
+
redactedAttributes: ['/department', '/name', '/rocks']
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
user: {
|
|
351
|
+
key: 'abc',
|
|
352
|
+
_meta: {
|
|
353
|
+
redactedAttributes: ['/letters', '/name', '/object', '/order']
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const orgAndUserGlobalNamePrivate = {
|
|
359
|
+
kind: 'multi',
|
|
360
|
+
organization: {
|
|
361
|
+
key: 'LD',
|
|
362
|
+
rocks: true,
|
|
363
|
+
department: {
|
|
364
|
+
name: 'sdk'
|
|
365
|
+
},
|
|
366
|
+
_meta: {
|
|
367
|
+
redactedAttributes: ['/name']
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
user: {
|
|
371
|
+
key: 'abc',
|
|
372
|
+
order: 3,
|
|
373
|
+
object: {
|
|
374
|
+
a: 'a',
|
|
375
|
+
},
|
|
376
|
+
_meta: {
|
|
377
|
+
redactedAttributes: ['/letters', '/name', '/object/b']
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const orgAndUserContextIncludedPrivate = {
|
|
383
|
+
kind: 'multi',
|
|
384
|
+
organization: {
|
|
385
|
+
key: 'LD',
|
|
386
|
+
rocks: true,
|
|
387
|
+
name: 'name',
|
|
388
|
+
department: {
|
|
389
|
+
name: 'sdk'
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
user: {
|
|
393
|
+
key: 'abc',
|
|
394
|
+
name: 'alphabet',
|
|
395
|
+
order: 3,
|
|
396
|
+
object: {
|
|
397
|
+
a: 'a',
|
|
398
|
+
},
|
|
399
|
+
_meta: {
|
|
400
|
+
redactedAttributes: ['/letters', '/object/b']
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
it('it should not include invalid contexts', () => {
|
|
406
|
+
const uf = ContextFilter({});
|
|
407
|
+
expect(uf.filter(contextWithBadContexts)).toEqual(contextWithBadContextsRemoved);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('it should remove attributes from all contexts when all attributes are private.', () => {
|
|
411
|
+
const uf = ContextFilter({ allAttributesPrivate: true });
|
|
412
|
+
expect(uf.filter(orgAndUserContext)).toEqual(orgAndUserContextAllPrivate);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('it should apply private attributes from the context to the context.', () => {
|
|
416
|
+
const uf = ContextFilter({});
|
|
417
|
+
expect(uf.filter(orgAndUserContext)).toEqual(orgAndUserContextIncludedPrivate);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('it should apply global private attributes to all contexts.', () => {
|
|
421
|
+
const uf = ContextFilter({ privateAttributes: ['name'] });
|
|
422
|
+
expect(uf.filter(orgAndUserContext)).toEqual(orgAndUserGlobalNamePrivate);
|
|
423
|
+
});
|
|
424
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
const os = require('os');
|
|
2
|
+
const packageJson = require('../package.json');
|
|
3
|
+
const configuration = require('../configuration');
|
|
4
|
+
const { DiagnosticsManager, DiagnosticId } = require('../diagnostic_events');
|
|
5
|
+
|
|
6
|
+
describe('DiagnosticId', () => {
|
|
7
|
+
it('uses last 6 characters of SDK key', () => {
|
|
8
|
+
const id = DiagnosticId('my-sdk-key');
|
|
9
|
+
expect(id.sdkKeySuffix).toEqual('dk-key');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('creates random UUID', () => {
|
|
13
|
+
const id0 = DiagnosticId('my-sdk-key');
|
|
14
|
+
const id1 = DiagnosticId('my-sdk-key');
|
|
15
|
+
expect(id0.diagnosticId).toBeTruthy();
|
|
16
|
+
expect(id1.diagnosticId).toBeTruthy();
|
|
17
|
+
expect(id0.diagnosticId).not.toEqual(id1.diagnosticId);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('DiagnosticsManager', () => {
|
|
22
|
+
const id = DiagnosticId('my-sdk-key');
|
|
23
|
+
const defaultConfig = configuration.validate({});
|
|
24
|
+
|
|
25
|
+
it('copies DiagnosticId', () => {
|
|
26
|
+
const manager = DiagnosticsManager(defaultConfig, id, 100000);
|
|
27
|
+
const event = manager.createInitEvent();
|
|
28
|
+
expect(event.id).toEqual(id);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('copies start time', () => {
|
|
32
|
+
const manager = DiagnosticsManager(defaultConfig, id, 100000);
|
|
33
|
+
const event = manager.createInitEvent();
|
|
34
|
+
expect(event.creationDate).toEqual(100000);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('provides SDK data', () => {
|
|
38
|
+
const manager = DiagnosticsManager(defaultConfig, id, 100000);
|
|
39
|
+
const event = manager.createInitEvent();
|
|
40
|
+
expect(event.sdk).toEqual({
|
|
41
|
+
name: 'node-server-sdk',
|
|
42
|
+
version: packageJson.version
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('provides platform data', () => {
|
|
47
|
+
const manager = DiagnosticsManager(defaultConfig, id, 100000);
|
|
48
|
+
const event = manager.createInitEvent();
|
|
49
|
+
expect(event.platform).toEqual({
|
|
50
|
+
name: 'Node',
|
|
51
|
+
osArch: os.arch(),
|
|
52
|
+
osName: event.platform.osName, // this may have been transformed by normalizePlatformName
|
|
53
|
+
osVersion: os.release(),
|
|
54
|
+
nodeVersion: process.versions.node,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
function verifyConfig(configIn, configOut) {
|
|
59
|
+
const config = configuration.validate(configIn);
|
|
60
|
+
const manager = DiagnosticsManager(config, id, 100000);
|
|
61
|
+
const event = manager.createInitEvent();
|
|
62
|
+
expect(event.configuration).toMatchObject(configOut);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
it('translates default configuration', () => {
|
|
66
|
+
verifyConfig({}, {
|
|
67
|
+
allAttributesPrivate: false,
|
|
68
|
+
connectTimeoutMillis: 5000,
|
|
69
|
+
customBaseURI: false,
|
|
70
|
+
customEventsURI: false,
|
|
71
|
+
customStreamURI: false,
|
|
72
|
+
dataStoreType: 'memory',
|
|
73
|
+
diagnosticRecordingIntervalMillis: 900000,
|
|
74
|
+
eventsCapacity: 10000,
|
|
75
|
+
eventsFlushIntervalMillis: 5000,
|
|
76
|
+
offline: false,
|
|
77
|
+
pollingIntervalMillis: 30000,
|
|
78
|
+
reconnectTimeMillis: 1000,
|
|
79
|
+
socketTimeoutMillis: 5000,
|
|
80
|
+
streamingDisabled: false,
|
|
81
|
+
contextKeysCapacity: 1000,
|
|
82
|
+
contextKeysFlushIntervalMillis: 300000,
|
|
83
|
+
usingProxy: false,
|
|
84
|
+
usingProxyAuthenticator: false,
|
|
85
|
+
usingRelayDaemon: false,
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('translates custom configuration', () => {
|
|
90
|
+
verifyConfig({ baseUri: 'http://other' }, {
|
|
91
|
+
customBaseURI: true,
|
|
92
|
+
customEventsURI: false,
|
|
93
|
+
customStreamURI: false,
|
|
94
|
+
});
|
|
95
|
+
verifyConfig({ eventsUri: 'http://other' }, {
|
|
96
|
+
customBaseURI: false,
|
|
97
|
+
customEventsURI: true,
|
|
98
|
+
customStreamURI: false,
|
|
99
|
+
});
|
|
100
|
+
verifyConfig({ streamUri: 'http://other' }, {
|
|
101
|
+
customBaseURI: false,
|
|
102
|
+
customEventsURI: false,
|
|
103
|
+
customStreamURI: true,
|
|
104
|
+
});
|
|
105
|
+
verifyConfig({ allAttributesPrivate: true }, { allAttributesPrivate: true });
|
|
106
|
+
verifyConfig({ timeout: 6 }, { connectTimeoutMillis: 6000, socketTimeoutMillis: 6000 });
|
|
107
|
+
verifyConfig({ diagnosticRecordingInterval: 999 }, { diagnosticRecordingIntervalMillis: 999000 });
|
|
108
|
+
verifyConfig({ capacity: 999 }, { eventsCapacity: 999 });
|
|
109
|
+
verifyConfig({ flushInterval: 33 }, { eventsFlushIntervalMillis: 33000 });
|
|
110
|
+
verifyConfig({ stream: false }, { streamingDisabled: true });
|
|
111
|
+
verifyConfig({ streamInitialReconnectDelay: 33 }, { reconnectTimeMillis: 33000 });
|
|
112
|
+
verifyConfig({ contextKeysCapacity: 111 }, { contextKeysCapacity: 111 });
|
|
113
|
+
verifyConfig({ contextKeysFlushInterval: 33 }, { contextKeysFlushIntervalMillis: 33000 });
|
|
114
|
+
verifyConfig({ useLdd: true }, { usingRelayDaemon: true });
|
|
115
|
+
|
|
116
|
+
const fakeProxy = {};
|
|
117
|
+
verifyConfig({ proxyAgent: fakeProxy }, { usingProxy: true, usingProxyAuthenticator: false });
|
|
118
|
+
verifyConfig({ proxyHost: 'my-proxy' }, { usingProxy: true, usingProxyAuthenticator: false });
|
|
119
|
+
verifyConfig({ proxyAgent: fakeProxy, proxyAuth: 'basic' }, { usingProxy: true, usingProxyAuthenticator: true });
|
|
120
|
+
|
|
121
|
+
const fakeStore = { description: 'WeirdStore' };
|
|
122
|
+
verifyConfig({ featureStore: fakeStore }, { dataStoreType: fakeStore.description });
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('creates periodic event from stats, then resets', () => {
|
|
126
|
+
const manager = DiagnosticsManager(defaultConfig, id, 100000);
|
|
127
|
+
const timeBeforeReset = new Date().getTime();
|
|
128
|
+
const event1 = manager.createStatsEventAndReset(4, 5, 6);
|
|
129
|
+
|
|
130
|
+
expect(event1).toMatchObject({
|
|
131
|
+
kind: 'diagnostic',
|
|
132
|
+
dataSinceDate: 100000,
|
|
133
|
+
droppedEvents: 4,
|
|
134
|
+
deduplicatedUsers: 5,
|
|
135
|
+
eventsInLastBatch: 6,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
expect(event1.creationDate).toBeGreaterThanOrEqual(timeBeforeReset);
|
|
139
|
+
|
|
140
|
+
const event2 = manager.createStatsEventAndReset(1, 2, 3);
|
|
141
|
+
|
|
142
|
+
expect(event2).toMatchObject({
|
|
143
|
+
kind: 'diagnostic',
|
|
144
|
+
dataSinceDate: event1.creationDate,
|
|
145
|
+
droppedEvents: 1,
|
|
146
|
+
deduplicatedUsers: 2,
|
|
147
|
+
eventsInLastBatch: 3,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
expect(event2.creationDate).toBeGreaterThanOrEqual(event1.creationDate);
|
|
151
|
+
});
|
|
152
|
+
});
|