UNPKG

15.8 kBJavaScriptView Raw
1// Load modules
2
3var Lab = require('lab');
4var Confidence = require('../');
5
6// Declare internals
7
8var internals = {};
9
10
11// Test shortcuts
12
13var lab = exports.lab = Lab.script();
14var expect = Lab.expect;
15var before = lab.before;
16var after = lab.after;
17var describe = lab.experiment;
18var it = lab.test;
19
20
21describe('Confidence', function () {
22
23 describe('Storage', function () {
24
25 var tree = {
26 // Fork
27 key1: 'abc', // Value
28 key2: {
29 // Filter
30 $filter: 'env',
31 production: {
32 // Fork
33 deeper: {
34 // Value
35 $value: 'value' // Value
36 }
37 },
38 $default: {
39 // Filter
40 $filter: 'platform',
41 ios: 1, // Value
42 android: false, // Value
43 $default: 2 // Value
44 }
45 },
46 key3: {
47 // Fork
48 sub1: {
49 $value: 0,
50 $meta: 'something'
51 },
52 sub2: {
53 // Filter
54 $filter: 'xfactor',
55 $id: 'x_factor',
56 yes: '' // Value
57 }
58 },
59 key4: [12, 13, { $filter: 'none', x: 10, $default: 14 }],
60 key5: {},
61 ab: {
62 // Range
63 $filter: 'random.1',
64 $id: 'random_ab_test',
65 $range: [
66 { limit: 1, value: [1, 2] },
67 { limit: 2, value: { $value: 2 } },
68 { limit: 3, value: { a: 5 }, id: '3' },
69 { limit: 10, value: 4 },
70 { limit: 20, value: 5 }
71 ],
72 $default: 6
73 },
74 $meta: {
75 something: 'else'
76 }
77 };
78
79 describe('#get', function () {
80
81 var store = new Confidence.Store();
82 store.load(tree);
83
84 var get = function (key, result, criteria, applied) {
85
86 it('gets value for ' + key + (criteria ? ' with criteria ' + JSON.stringify(criteria) : ''), function (done) {
87
88 var resultApplied = [];
89 var value = store.get(key, criteria, applied ? resultApplied : null);
90 expect(value).to.deep.equal(result);
91 if (applied) {
92 expect(resultApplied).to.deep.equal(applied);
93 }
94 done();
95 });
96 };
97
98 get('/key1', 'abc');
99 get('/key2', 2, null, [{ filter: 'env', valueId: '$default' }, { filter: 'platform', valueId: '$default' }]);
100 get('/key2', 1, { platform: 'ios' }, [{ filter: 'env', valueId: '$default' }, { filter: 'platform', valueId: 'ios' }]);
101 get('/key2', false, { platform: 'android' });
102 get('/key2', 2, { platform: 'else' });
103 get('/key2/deeper', 'value', { env: 'production' });
104 get('/key2/deeper', undefined, { env: 'qa' });
105 get('/key2/deeper', undefined);
106 get('/key5', {});
107 get('/', { key1: 'abc', key2: 2, key3: { sub1: 0 }, key4: [12, 13, 14], key5: {}, ab: 6 });
108 get('/', { key1: 'abc', key2: 2, key3: { sub1: 0, sub2: '' }, key4: [12, 13, 14], key5: {}, ab: 6 }, { xfactor: 'yes' });
109 get('/ab', 2, { random: { 1: 2 } }, [{ filter: 'random.1', valueId: '[object]', filterId: 'random_ab_test' }]);
110 get('/ab', { a: 5 }, { random: { 1: 3 } }, [{ filter: 'random.1', valueId: '3', filterId: 'random_ab_test' }]);
111 get('/ab', 4, { random: { 1: 9 } });
112 get('/ab', 4, { random: { 1: 10 } }, [{ filter: 'random.1', valueId: '4', filterId: 'random_ab_test' }]);
113 get('/ab', 5, { random: { 1: 11 } });
114 get('/ab', 5, { random: { 1: 19 } });
115 get('/ab', 6, { random: { 1: 29 } });
116
117 it('fails on invalid key', function (done) {
118
119 var value = store.get('key');
120 expect(value).to.equal(undefined);
121 done();
122 });
123 });
124
125 describe('#meta', function () {
126
127 it('returns root meta', function (done) {
128
129 var store = new Confidence.Store();
130 store.load(tree);
131 expect(store.meta('/')).to.deep.equal(tree.$meta);
132 done();
133 });
134
135 it('returns nested meta', function (done) {
136
137 var store = new Confidence.Store();
138 store.load(tree);
139 expect(store.meta('/key3/sub1')).to.equal('something');
140 done();
141 });
142
143 it('returns undefined for missing meta', function (done) {
144
145 var store = new Confidence.Store();
146 store.load(tree);
147 expect(store.meta('/key1')).to.equal(undefined);
148 done();
149 });
150 });
151
152 describe('#load', function () {
153
154 it('fails on invalid tree', function (done) {
155
156 var store = new Confidence.Store();
157 expect(function () {
158
159 var err = store.load({ $b: 3 });
160 }).to.throw('Unknown $ directive $b');
161
162 done();
163 });
164 });
165
166 describe('#validate', function () {
167
168 it('fails on Error node', function (done) {
169
170 var err = Confidence.Store.validate({ key: new Error() });
171 expect(err.message).to.equal('Invalid node object type');
172 expect(err.path).to.equal('/key');
173 done();
174 });
175
176 it('fails on empty filter', function (done) {
177
178 var err = Confidence.Store.validate({ key: { $filter: '' } });
179 expect(err.message).to.equal('Invalid empty filter value');
180 expect(err.path).to.equal('/key');
181 done();
182 });
183
184 it('fails on non-string filter', function (done) {
185
186 var err = Confidence.Store.validate({ key: { $filter: 3 } });
187 expect(err.message).to.equal('Filter value must be a string');
188 expect(err.path).to.equal('/key');
189 done();
190 });
191
192 it('fails on invalid filter', function (done) {
193
194 var err = Confidence.Store.validate({ key: { $filter: '4$' } });
195 expect(err.message).to.equal('Invalid filter value 4$');
196 expect(err.path).to.equal('/key');
197 done();
198 });
199
200 it('fails on invalid default', function (done) {
201
202 var err = Confidence.Store.validate({ key: { $default: { $b: 5 } } });
203 expect(err.message).to.equal('Unknown $ directive $b');
204 expect(err.path).to.equal('/key/$default');
205 done();
206 });
207
208 it('fails on unknown directive', function (done) {
209
210 var err = Confidence.Store.validate({ key: { $unknown: 'asd' } });
211 expect(err.message).to.equal('Unknown $ directive $unknown');
212 expect(err.path).to.equal('/key');
213 done();
214 });
215
216 it('fails on invalid child node', function (done) {
217
218 var err = Confidence.Store.validate({ key: { sub: { $b: 5 } } });
219 expect(err.message).to.equal('Unknown $ directive $b');
220 expect(err.path).to.equal('/key/sub');
221 done();
222 });
223
224 it('fails on invalid value node', function (done) {
225
226 var err = Confidence.Store.validate({ key: { $value: { $b: 5 } } });
227 expect(err.message).to.equal('Unknown $ directive $b');
228 expect(err.path).to.equal('/key/$value');
229 done();
230 });
231
232 it('fails on mix of value and other criteria', function (done) {
233
234 var values = {
235 $filter: 'a',
236 $default: '1',
237 $range: [{ limit: 10, value: 4 }],
238 a: 1
239 };
240 var node = {
241 key: {
242 $value: 1
243 }
244 };
245
246 var keys = Object.keys(values);
247
248 for (var i = 0, il = keys.length; i < il; ++i) {
249 var key = keys[i];
250 var value = values[key];
251
252 node.key[key] = value;
253
254 var err = Confidence.Store.validate(node);
255 expect(err.message).to.equal('Value directive can only be used with meta or nothing');
256 expect(err.path).to.equal('/key');
257
258 }
259
260 done();
261 });
262
263 it('fails on default value without a filter', function (done) {
264
265 var err = Confidence.Store.validate({ key: { $default: 1 } });
266 expect(err.message).to.equal('Default value without a filter');
267 expect(err.path).to.equal('/key');
268 done();
269 });
270
271 it('fails on filter without any value', function (done) {
272
273 var err = Confidence.Store.validate({ key: { $filter: '1' } });
274 expect(err.message).to.equal('Filter without any values');
275 expect(err.path).to.equal('/key');
276 done();
277 });
278
279 it('fails on filter with only default', function (done) {
280
281 var err = Confidence.Store.validate({ key: { $filter: 'a', $default: 1 } });
282 expect(err.message).to.equal('Filter with only a default');
283 expect(err.path).to.equal('/key');
284 done();
285 });
286
287 it('fails on non-array range', function (done) {
288
289 var err = Confidence.Store.validate({ key: { $filter: 'a', $range: {}, $default: 1 } });
290 expect(err.message).to.equal('Range value must be an array');
291 expect(err.path).to.equal('/key');
292 done();
293 });
294
295 it('fails on empty array range', function (done) {
296
297 var err = Confidence.Store.validate({ key: { $filter: 'a', $range: [], $default: 1 } });
298 expect(err.message).to.equal('Range must include at least one value');
299 expect(err.path).to.equal('/key');
300 done();
301 });
302
303 it('fails on non-object range array element', function (done) {
304
305 var err = Confidence.Store.validate({ key: { $filter: 'a', $range: [5], $default: 1 } });
306 expect(err.message).to.equal('Invalid range entry type');
307 expect(err.path).to.equal('/key');
308 done();
309 });
310
311 it('fails on range array element missing limit', function (done) {
312
313 var err = Confidence.Store.validate({ key: { $filter: 'a', $range: [{}], $default: 1 } });
314 expect(err.message).to.equal('Range entry missing limit');
315 expect(err.path).to.equal('/key');
316 done();
317 });
318
319 it('fails on range array element with non-number limit', function (done) {
320
321 var err = Confidence.Store.validate({ key: { $filter: 'a', $range: [{ limit: 'a' }], $default: 1 } });
322 expect(err.message).to.equal('Range limit must be a number');
323 expect(err.path).to.equal('/key');
324 done();
325 });
326
327 it('fails on out of order range array elements', function (done) {
328
329 var err = Confidence.Store.validate({ key: { $filter: 'a', $range: [{ limit: 11, value: 2 }, { limit: 10, value: 6 }], $default: 1 } });
330 expect(err.message).to.equal('Range entries not sorted in ascending order - 10 cannot come after 11');
331 expect(err.path).to.equal('/key');
332 done();
333 });
334
335 it('fails on range array element missing value', function (done) {
336
337 var err = Confidence.Store.validate({ key: { $filter: 'a', $range: [{ limit: 1 }], $default: 1 } });
338 expect(err.message).to.equal('Range entry missing value');
339 expect(err.path).to.equal('/key');
340 done();
341 });
342
343 it('fails on range array element with invalid value', function (done) {
344
345 var err = Confidence.Store.validate({ key: { $filter: 'a', $range: [{ limit: 1, value: { $b: 5 } }], $default: 1 } });
346 expect(err.message).to.equal('Unknown $ directive $b');
347 expect(err.path).to.equal('/key/$range[1]');
348 done();
349 });
350
351 it('fails on range without a filter', function (done) {
352
353 var err = Confidence.Store.validate({ key: { $range: [{ limit: 1, value: 1 }] } });
354 expect(err.message).to.equal('Range without a filter');
355 expect(err.path).to.equal('/key');
356 done();
357 });
358
359 it('fails on range with non-ranged values', function (done) {
360
361 var err = Confidence.Store.validate({ key: { $filter: 'a', $range: [{ limit: 1, value: 1 }], a: 1 } });
362 expect(err.message).to.equal('Range with non-ranged values');
363 expect(err.path).to.equal('/key');
364 done();
365 });
366
367 it('fails on invalid id', function (done) {
368
369 var err = Confidence.Store.validate({ key: 5, $id: 4 });
370 expect(err.message).to.equal('Id value must be a non-empty string');
371 expect(err.path).to.equal('/');
372 done();
373 });
374
375 it('fails on empty id', function (done) {
376
377 var err = Confidence.Store.validate({ key: 5, $id: null });
378 expect(err.message).to.equal('Id value must be a non-empty string');
379 expect(err.path).to.equal('/');
380 done();
381 });
382
383 it('returns null with null as the node', function (done) {
384
385 var err = Confidence.Store.validate(null);
386 expect(err).to.equal(null);
387 done();
388 });
389
390 it('returns null with undefined as the node', function (done) {
391
392 var err = Confidence.Store.validate(undefined);
393 expect(err).to.equal(null);
394 done();
395 });
396
397 it('fails on node that is a Date object', function (done) {
398
399 var err = Confidence.Store.validate(new Date);
400
401 expect(err.message).to.equal('Invalid node object type');
402 done();
403 });
404 });
405
406 describe('_logApplied', function () {
407
408 it('adds the filter to the list of applied filters if node or criteria is not defined ', function (done) {
409 var applied = [];
410
411 Confidence.Store._logApplied(applied, { filter: 'env', valueId: '$default'});
412 expect(applied.length).to.equal(1);
413 done();
414 });
415 });
416
417 it('accepts a document object in the constructor', function (done) {
418
419 var load = Confidence.Store.prototype.load;
420
421 Confidence.Store.prototype.load = function (document) {
422
423 expect(document).to.deep.equal(tree);
424 Confidence.Store.prototype.load = load;
425 done();
426 };
427
428 var store = new Confidence.Store(tree);
429
430 });
431 });
432});