bedrock
Version:
A core foundation for rich Web applications.
652 lines (610 loc) • 23.4 kB
JavaScript
/*!
* Copyright (c) 2012-2019 Digital Bazaar, Inc. All rights reserved.
*/
;
const bedrock = require('bedrock');
const {util: {BedrockError}} = bedrock;
describe('bedrock', function() {
describe('util.extend()', function() {
it('should perform in-place default extension', function(done) {
const result = {};
bedrock.util.extend(result, {a: 1});
result.should.eql({a: 1});
done();
});
it('should perform in-place deep extension', function(done) {
const result = {a: {a0: 0}, b: 2};
bedrock.util.extend(true, result, {a: {a1: 1}});
result.should.eql({a: {a0: 0, a1: 1}, b: 2});
done();
});
it('should perform in-place shallow extension', function(done) {
const result = {a: {a0: 0}, b: 2};
bedrock.util.extend(false, result, {a: {a1: 1}});
result.should.eql({a: {a1: 1}, b: 2});
done();
});
it('should be able to return a new object', function(done) {
const result = bedrock.util.extend(true, {}, {a: 1});
result.should.eql({a: 1});
done();
});
it('should merge multiple objects into a new object', function(done) {
const result = {};
bedrock.util.extend(true, result, {a: 1}, {b: 2});
result.should.eql({a: 1, b: 2});
done();
});
});
describe('util.BedrockError', function() {
it('should have correct type', function(done) {
const err = new BedrockError('E', 'TYPE', null, null);
err.isType('BOGUS').should.be.false;
err.isType('TYPE').should.be.true;
err.hasType('BOGUS').should.be.false;
err.hasType('TYPE').should.be.true;
done();
});
it('should have correct cause', function(done) {
const err0 = new BedrockError('E0', 'E0TYPE', null, null);
const err1 = new BedrockError('E1', 'E1TYPE', null, err0);
err1.isType('BOGUS').should.be.false;
err1.isType('E1TYPE').should.be.true;
err1.hasType('BOGUS').should.be.false;
err1.hasType('E0TYPE').should.be.true;
err1.hasType('E1TYPE').should.be.true;
done();
});
});
describe('util.config', function() {
// simplify most tests with common config setup
let config;
let c;
let cc;
beforeEach('create config', function() {
config = {
// 'base' used for path testing
base: {}
};
const options = {
// set locals to our custom config root
locals: config
};
c = new bedrock.util.config.Config(config, options);
cc = c.computer();
});
describe('basic set functionality', function() {
it('should create', function() {
config.base.a = 'a';
config.base.b = 'b';
c.set('nopath', 'nopath');
config.nopath.should.equal('nopath');
c.set('path.path', 'path');
config.path.path.should.equal('path');
c.set(['array', 'path'], 'arraypath');
config.array.path.should.equal('arraypath');
});
it('should update', function() {
config.data = 'js';
config.data.should.equal('js');
c.set('data', 'set');
config.data.should.equal('set');
c.set('data', 'updated');
config.data.should.equal('updated');
config.data = 'js updated';
config.data.should.equal('js updated');
});
it('should multi-set', function() {
c.set({
'base.a': 'a',
'base.b': 'b'
});
config.base.a.should.equal('a');
config.base.b.should.equal('b');
});
});
describe('basic setDefault functionality', function() {
it('should setDefault when exists', function() {
(typeof config.p1).should.equal('undefined');
const object = c.setDefault('p1', {});
config.p1.should.equal(object);
Object.keys(object).length.should.equal(0);
});
it('should setDefault when does not exist', function() {
config.p1 = {};
config.p1.exists = true;
config.p1.exists.should.equal(true);
const object = c.setDefault('p1', {});
config.p1.should.equal(object);
config.p1.should.equal(config.p1);
config.p1.exists.should.equal(true);
});
it('should setDefault paths', function() {
(typeof config.p1).should.equal('undefined');
const object = c.setDefault('p1.p2', {});
config.p1.p2.should.equal(object);
Object.keys(config.p1).length.should.equal(1);
Object.keys(config.p1.p2).length.should.equal(0);
});
it('should setDefault array paths', function() {
(typeof config.p1).should.equal('undefined');
const object = c.setDefault(['p1', 'p2'], {});
config.p1.p2.should.equal(object);
Object.keys(config.p1).length.should.equal(1);
Object.keys(config.p1.p2).length.should.equal(0);
});
it('should multi-setDefault', function() {
config.p3 = {};
(typeof config.p1).should.equal('undefined');
(typeof config.p2).should.equal('undefined');
(typeof config.p3).should.equal('object');
Object.keys(config.p3).length.should.equal(0);
const object = c.setDefault({
p1: {},
'p2.c1': {},
'p3.c1': {}
});
config.p1.should.equal(object['p1']);
Object.keys(config.p1).length.should.equal(0);
config.p2.c1.should.equal(object['p2.c1']);
Object.keys(config.p2).length.should.equal(1);
Object.keys(config.p2.c1).length.should.equal(0);
config.p3.c1.should.equal(object['p3.c1']);
Object.keys(config.p3).length.should.equal(1);
Object.keys(config.p3.c1).length.should.equal(0);
});
});
describe('basic computed functionality', function() {
it('should create', function() {
config.base.a = 'a';
config.base.b = 'b';
cc('computed', () => config.base.a + config.base.b);
config.computed.should.equal('ab');
});
it('should update', function() {
config.base.a = 'a';
config.base.b = 'b';
cc('computed', () => config.base.a + config.base.b);
config.computed.should.equal('ab');
config.base.a = 'a2';
config.base.b = 'b2';
config.computed.should.equal('a2b2');
});
it('should multi-compute', function() {
cc({
'mod.name': 'NAME',
'mod.id': 1234,
'computed.f': () => config.mod.name + '-' + config.mod.id,
'computed.t': '${mod.name}-${mod.id}'
});
config.mod.name.should.equal('NAME');
config.mod.id.should.equal(1234);
config.computed.f.should.equal('NAME-1234');
config.computed.t.should.equal('NAME-1234');
});
it('should multi-compute options', function() {
// require the container exists so locals option works
c.setDefault('mod', {});
cc({
'mod.name': 'NAME',
'mod.id': 1234,
'computed.f': () => config.mod.name + '-' + config.mod.id,
'computed.t': '${name}-${id}'
}, {locals: config.mod});
config.mod.name.should.equal('NAME');
config.mod.id.should.equal(1234);
config.computed.f.should.equal('NAME-1234');
config.computed.t.should.equal('NAME-1234');
});
});
describe('basic array functionality', function() {
it('should create empty', function() {
c.set('a', []);
config.a.should.be.an('array');
config.a.length.should.equal(0);
});
it('should create with simple values', function() {
c.set('a', ['a', 'b']);
config.a.should.be.an('array');
config.a.length.should.equal(2);
config.a[0].should.equal('a');
config.a[1].should.equal('b');
});
it('should set computed values', function() {
config.a = ['a', 'b'];
c.setComputed('a[2]', '${a[0] + a[1]}');
config.a.should.be.an('array');
config.a.length.should.equal(3);
config.a[0].should.equal('a');
config.a[1].should.equal('b');
config.a[2].should.equal('ab');
});
it('should create with computed values', function() {
c.setComputed({
'a[0]': 'a',
'a[1]': 'b',
'a[2]': '${a[0] + a[1]}'
}, {parentDefault: []});
config.a.should.be.an('array');
config.a.length.should.equal(3);
config.a[0].should.equal('a');
config.a[1].should.equal('b');
config.a[2].should.equal('ab');
});
it('should update with computed values', function() {
config.a = [];
c.setComputed({
'a[0]': 'a',
'a[1]': 'b',
'a[2]': '${a[0] + a[1]}'
});
config.a.should.be.an('array');
config.a.length.should.equal(3);
config.a[0].should.equal('a');
config.a[1].should.equal('b');
config.a[2].should.equal('ab');
});
it('should push to array', function() {
c.pushComputed('a', 'a');
config.a.should.be.an('array');
config.a.length.should.equal(1);
config.a[0].should.equal('a');
});
it('should push computed values', function() {
config.a = [];
c.pushComputed('a', 'a');
c.pushComputed('a', 'b');
c.pushComputed('a', '${a[0] + a[1]}');
c.pushComputed('a', () => config.a[0] + config.a[1]);
config.a.should.be.an('array');
config.a.length.should.equal(4);
config.a[0].should.equal('a');
config.a[1].should.equal('b');
config.a[2].should.equal('ab');
config.a[3].should.equal('ab');
});
});
describe('default config', function() {
// computed config for main config
const _cc = bedrock.util.config.main.computer();
// ensure empty config container
beforeEach('create config', function() {
bedrock.config._mocha = {};
});
// clean up test config
afterEach('remove config', function() {
delete bedrock.config._mocha;
});
it('should use default config', function() {
const config = bedrock.config;
config._mocha.a = 'a';
config._mocha.b = 'b';
_cc('_mocha.computed', () => config._mocha.a + config._mocha.b);
bedrock.config._mocha.computed.should.equal('ab');
});
});
describe('templates', function() {
it('should create', function() {
const _options = {locals: config};
const _cc = new bedrock.util.config.Config(config, _options).computer();
config.base.a = 'a';
config.base.b = 'b';
_cc('computed', '${base.a + base.b}', _options);
config.computed.should.equal('ab');
});
it('should create with custom locals', function() {
const _options = {locals: config.base};
const _cc = new bedrock.util.config.Config(config, _options).computer();
config.base.a = 'a';
config.base.b = 'b';
_cc('computed', '${a + b}');
config.computed.should.equal('ab');
});
it('should create with per-call locals', function() {
const _options = {locals: config.base};
const _cc = new bedrock.util.config.Config(config).computer();
config.base.a = 'a';
config.base.b = 'b';
_cc('computed', '${a + b}', _options);
config.computed.should.equal('ab');
});
it('should update', function() {
const _options = {locals: config};
const _cc = new bedrock.util.config.Config(config, _options).computer();
config.base.a = 'a';
config.base.b = 'b';
_cc('computed', '${base.a + base.b}');
config.computed.should.equal('ab');
config.base.a = 'a2';
config.base.b = 'b2';
config.computed.should.equal('a2b2');
});
});
describe('ints', function() {
it('should support int function', function() {
config.a = 1;
config.b = 2;
cc('computed', () => config.a + config.b);
config.computed.should.equal(3);
});
it('should support int template', function() {
config.a = 1;
config.b = 2;
cc('computed', '${a + b}');
// will compute with ints but output string
config.computed.should.equal('3');
});
it('should support int computed values', function() {
cc('notcomputed', 123);
config.notcomputed.should.equal(123);
});
});
describe('double computed', function() {
it('should create two computed configs', function() {
config.a = 'a';
config.b = 'b';
cc('computed', () => config.a + config.b);
cc('computed2', () => config.computed + config.computed);
config.computed2.should.equal('abab');
});
it('should update two computed configs', function() {
config.a = 'a';
config.b = 'b';
cc('computed', () => config.a + config.b);
cc('computed2', () => config.computed + config.computed);
config.computed2.should.equal('abab');
config.a = 'a2';
config.b = 'b2';
config.computed2.should.equal('a2b2a2b2');
});
});
describe('override', function() {
it('should create twice', function() {
cc('computed', 'computed');
cc('computed', 'computed2');
config.computed.should.equal('computed2');
});
});
describe('js functionality', function() {
it('should support assignment', function() {
cc('computed', 'computed');
config.computed.should.equal('computed');
config.computed = 'computed2';
config.computed.should.equal('computed2');
});
it('should support "delete"', function() {
cc('computed', 'computed');
config.computed.should.equal('computed');
delete config.computed;
(typeof config.computed).should.equal('undefined');
});
it('should support "in"', function() {
cc('computed', 'computed');
('computed' in config).should.be.true;
});
it('should support JSON serialization', function() {
// clear config first for repeatable serialization
delete config.base;
cc('computed', 'computed');
config.computed.should.equal('computed');
JSON.stringify(config).should.equal('{"computed":"computed"}');
});
});
describe('paths', function() {
it('should support dotted paths', function() {
cc('hostname', 'www.example.com');
cc('computed.base', 'https://${hostname}');
cc('computed.endpoint', '${computed.base}/endpoint');
config.hostname.should.equal('www.example.com');
config.computed.base.should.equal('https://www.example.com');
config.computed.endpoint.should.equal(
'https://www.example.com/endpoint');
});
it('should support complex dotted paths', function() {
// [name] indexing
cc('a1[b1-c1].d1', 'e1');
config.a1['b1-c1'].d1.should.equal('e1');
// [dotted name] indexing
cc('a2[\'b2.c2\'].d2', 'e2');
config.a2['b2.c2'].d2.should.equal('e2');
cc('a3["b3.c3"].d3', 'e3');
config.a3['b3.c3'].d3.should.equal('e3');
// dotted names
cc('a4.b4.c4.d4', 'e4');
config.a4.b4.c4.d4.should.equal('e4');
// top level dotted name expands
cc('[a5.b5]', 'c5');
config.a5.b5.should.equal('c5');
// quoted top level dotted name
cc('[\'a6.b6\']', 'c6');
config['a6.b6'].should.equal('c6');
cc('["a7.b7"]', 'c7');
config['a7.b7'].should.equal('c7');
// result of confusing quoting
cc('\'a8.b8\'', 'c8');
config['\'a8']['b8\''].should.equal('c8');
// non dot names
cc('a9.b9-c9.d9', 'e9');
config.a9['b9-c9'].d9.should.equal('e9');
// array paths
cc(['a10', 'b10', 'c10'], 'd10');
config.a10.b10.c10.should.equal('d10');
});
});
describe('README examples', function() {
it('should support set', function() {
c.set('server.port', 8443);
config.server.port.should.equal(8443);
});
it('should support setDefault', function() {
c.setDefault('accounts.admin', {});
config.accounts.admin.name = 'Ima Admin';
c.set('accounts.admin.id', 1);
const account123 = c.setDefault('accounts.account123', {});
account123.id = 123;
account123.name = 'Account 123';
config.accounts.admin.name.should.equal('Ima Admin');
config.accounts.admin.id.should.equal(1);
config.accounts.account123.id.should.equal(123);
config.accounts.account123.name.should.equal('Account 123');
});
it('should support setComputed', function() {
c.set('server.port', 8443);
c.set('server.domain', 'bedrock.dev');
c.setComputed('server.host', () => {
return config.server.domain + ':' + config.server.port;
});
config.server.port.should.equal(8443);
config.server.domain.should.equal('bedrock.dev');
config.server.host.should.equal('bedrock.dev:8443');
});
it('should support setComputed2', function() {
c.set('server.port', 8443);
c.set('server.domain', 'bedrock.dev');
c.setComputed('server.host', () => {
if(config.server.port !== 443) {
return config.server.domain + ':' + config.server.port;
}
return config.server.domain;
});
config.server.port.should.equal(8443);
config.server.domain.should.equal('bedrock.dev');
config.server.host.should.equal('bedrock.dev:8443');
c.set('server.port', 443);
config.server.port.should.equal(443);
config.server.domain.should.equal('bedrock.dev');
config.server.host.should.equal('bedrock.dev');
});
it('should support computer', function() {
c.set('server.port', 8443);
c.set('server.domain', 'bedrock.dev');
cc('server.host', () => {
// only add the port if it's not the well known default
if(config.server.port !== 443) {
return config.server.domain + ':' + config.server.port;
}
return config.server.domain;
});
config.server.port.should.equal(8443);
config.server.domain.should.equal('bedrock.dev');
config.server.host.should.equal('bedrock.dev:8443');
c.set('server.port', 443);
config.server.port.should.equal(443);
config.server.domain.should.equal('bedrock.dev');
config.server.host.should.equal('bedrock.dev');
});
it('should support templates', function() {
c.set('server.port', 8443);
c.set('server.domain', 'bedrock.dev');
cc('server.host', () => {
// only add the port if it's not the well known default
if(config.server.port !== 443) {
return config.server.domain + ':' + config.server.port;
}
return config.server.domain;
});
cc('server.baseUri', 'https://${server.host}');
config.server.port.should.equal(8443);
config.server.domain.should.equal('bedrock.dev');
config.server.host.should.equal('bedrock.dev:8443');
config.server.baseUri.should.equal('https://bedrock.dev:8443');
// use locals option to simplify templates
c.set({
'base.a': 'a',
'base.b': 'b',
'base.c': 'c'
});
cc('base.computed1', '${base.a}:${base.b}:${base.c}');
cc('base.computed2', '${a}:${b}:${c}', {locals: config.base});
config.base.computed1.should.equal('a:b:c');
config.base.computed2.should.equal('a:b:c');
});
it('should support multi', function() {
c.set({
'server.port': 8443,
'server.domain': 'bedrock.dev',
'server.name': 'Bedrock Dev',
'users.admin.id': 1
});
cc({
'server.url': 'https://${server.domain}:${server.port}',
'users.admin.url': '${server.url}/users/${users.admin.id}'
});
config.server.port.should.equal(8443);
config.server.domain.should.equal('bedrock.dev');
config.server.name.should.equal('Bedrock Dev');
config.users.admin.id.should.equal(1);
config.server.url.should.equal('https://bedrock.dev:8443');
config.users.admin.url.should.equal('https://bedrock.dev:8443/users/1');
});
it('should support arrays', function() {
c.set('server.port', 8443);
c.set('server.domain', 'bedrock.dev');
cc('server.host', () => {
// only add the port if it's not the well known default
if(config.server.port !== 443) {
return config.server.domain + ':' + config.server.port;
}
return config.server.domain;
});
cc('server.baseUri', 'https://${server.host}');
c.setDefault('resources', []);
cc('resources[0]', '${server.baseUri}/r/0');
c.pushComputed('resources', '${server.baseUri}/r/1');
config.resources.should.be.an('array');
config.resources.length.should.equal(2);
config.resources[0].should.equal('https://bedrock.dev:8443/r/0');
config.resources[1].should.equal('https://bedrock.dev:8443/r/1');
});
});
describe('examples', function() {
it('should support an example', function() {
const config = {};
const options = {config, locals: config};
const c = new bedrock.util.config.Config(config, options);
const cc = c.computer();
c.set('server.port', 8443);
// direct access, 'server' container known to exist
config.server.domain = 'bedrock.dev';
cc('server.host', () => {
if(config.server.port !== 443) {
return config.server.domain + ':' + config.server.port;
}
return config.server.domain;
});
cc('server.baseUri', 'https://${server.host}');
// use Config.setComputed API
c.setComputed('server.baseUri1', 'https://${server.host}');
// bind setComputed
const cc2 = c.setComputed.bind(c);
cc2('server.baseUri2', 'https://${server.host}');
// use binding API
const cc3 = c.computer();
cc3('server.baseUri3', 'https://${server.host}');
// all should have the same value
config.server.baseUri1.should.equal('https://bedrock.dev:8443');
config.server.baseUri2.should.equal('https://bedrock.dev:8443');
config.server.baseUri3.should.equal('https://bedrock.dev:8443');
// check custom https port
config.server.baseUri.should.equal('https://bedrock.dev:8443');
// update and check well known https port
config.server.port = 443;
config.server.baseUri.should.equal('https://bedrock.dev');
});
});
});
});
// TODO: add test that adds logger early
/*
const winston = require('winston');
bedrock.events.on('bedrock-cli.init', function() {
// TODO: test w/custom logger that writes to string, not file
bedrock.loggers.addTransport('test', new winston.transports.File({
level: 'debug',
silent: false,
json: false,
timestamp: true,
filename: '/tmp/test.log'
}));
});
*/