1
0
Fork 0
arangodb/js/node/node_modules/joi/test/object.js

910 lines
26 KiB
JavaScript
Executable File

// Load modules
var Lab = require('lab');
var Joi = require('../lib');
var Helper = require('./helper');
// Declare internals
var internals = {};
// Test shortcuts
var expect = Lab.expect;
var before = Lab.before;
var after = Lab.after;
var describe = Lab.experiment;
var it = Lab.test;
describe('object', function () {
it('converts a json string to an object', function (done) {
Joi.object().validate('{"hi":true}', function (err, value) {
expect(err).to.not.exist;
expect(value.hi).to.equal(true);
done();
});
});
it('errors on non-object string', function (done) {
Joi.object().validate('a string', function (err, value) {
expect(err).to.exist;
expect(value).to.equal('a string');
done();
});
});
it('should validate an object', function (done) {
var schema = Joi.object().required();
Helper.validate(schema, [
[{}, true],
[{ hi: true }, true],
['', false]
], done);
});
it('retains ignored values', function (done) {
var schema = Joi.object();
schema.validate({ a: 5 }, function (err, value) {
expect(value.a).to.equal(5);
done();
});
});
it('retains skipped values', function (done) {
var schema = Joi.object({ b: 5 }).unknown(true);
schema.validate({ b: '5', a: 5 }, function (err, value) {
expect(value.a).to.equal(5);
expect(value.b).to.equal(5);
done();
});
});
it('allows any key when schema is undefined', function (done) {
Joi.object().validate({ a: 4 }, function (err, value) {
expect(err).to.not.exist;
Joi.object(undefined).validate({ a: 4 }, function (err, value) {
expect(err).to.not.exist;
done();
});
});
});
it('allows any key when schema is null', function (done) {
Joi.object(null).validate({ a: 4 }, function (err, value) {
expect(err).to.not.exist;
done();
});
});
it('throws on invalid object schema', function (done) {
expect(function () {
Joi.object(4);
}).to.throw('Object schema must be a valid object');
done();
});
it('throws on joi object schema', function (done) {
expect(function () {
Joi.object(Joi.object());
}).to.throw('Object schema cannot be a joi schema');
done();
});
it('skips conversion when value is undefined', function (done) {
Joi.object({ a: Joi.object() }).validate(undefined, function (err, value) {
expect(err).to.not.exist;
expect(value).to.not.exist;
done();
});
});
it('errors on array', function (done) {
Joi.object().validate([1, 2, 3], function (err, value) {
expect(err).to.exist;
done();
});
});
it('should prevent extra keys from existing by default', function (done) {
var schema = Joi.object({ item: Joi.string().required() }).required();
Helper.validate(schema, [
[{ item: 'something' }, true],
[{ item: 'something', item2: 'something else' }, false],
['', false]
], done);
});
it('should validate count when min is set', function (done) {
var schema = Joi.object().min(3);
Helper.validate(schema, [
[{ item: 'something' }, false],
[{ item: 'something', item2: 'something else' }, false],
[{ item: 'something', item2: 'something else', item3: 'something something else' }, true],
['', false]
], done);
});
it('should validate count when max is set', function (done) {
var schema = Joi.object().max(2);
Helper.validate(schema, [
[{ item: 'something' }, true],
[{ item: 'something', item2: 'something else' }, true],
[{ item: 'something', item2: 'something else', item3: 'something something else' }, false],
['', false]
], done);
});
it('should validate count when min and max is set', function (done) {
var schema = Joi.object().max(3).min(2);
Helper.validate(schema, [
[{ item: 'something' }, false],
[{ item: 'something', item2: 'something else' }, true],
[{ item: 'something', item2: 'something else', item3: 'something something else' }, true],
[{ item: 'something', item2: 'something else', item3: 'something something else', item4: 'item4' }, false],
['', false]
], done);
});
it('should validate count when length is set', function (done) {
var schema = Joi.object().length(2);
Helper.validate(schema, [
[{ item: 'something' }, false],
[{ item: 'something', item2: 'something else' }, true],
[{ item: 'something', item2: 'something else', item3: 'something something else' }, false],
['', false]
], done);
});
it('should traverse an object and validate all properties in the top level', function (done) {
var schema = Joi.object({
num: Joi.number()
});
Helper.validate(schema, [
[{ num: 1 }, true],
[{ num: [1, 2, 3] }, false]
], done);
});
it('should traverse an object and child objects and validate all properties', function (done) {
var schema = Joi.object({
num: Joi.number(),
obj: Joi.object({
item: Joi.string()
})
});
Helper.validate(schema, [
[{ num: 1 }, true],
[{ num: [1, 2, 3] }, false],
[{ num: 1, obj: { item: 'something' } }, true],
[{ num: 1, obj: { item: 123 } }, false]
], done);
});
it('should traverse an object several levels', function (done) {
var schema = Joi.object({
obj: Joi.object({
obj: Joi.object({
obj: Joi.object({
item: Joi.boolean()
})
})
})
});
Helper.validate(schema, [
[{ num: 1 }, false],
[{ obj: {} }, true],
[{ obj: { obj: {} } }, true],
[{ obj: { obj: { obj: {} } } }, true],
[{ obj: { obj: { obj: { item: true } } } }, true],
[{ obj: { obj: { obj: { item: 10 } } } }, false]
], done);
});
it('should traverse an object several levels with required levels', function (done) {
var schema = Joi.object({
obj: Joi.object({
obj: Joi.object({
obj: Joi.object({
item: Joi.boolean()
})
}).required()
})
});
Helper.validate(schema, [
[null, false],
[undefined, true],
[{}, true],
[{ obj: {} }, false],
[{ obj: { obj: {} } }, true],
[{ obj: { obj: { obj: {} } } }, true],
[{ obj: { obj: { obj: { item: true } } } }, true],
[{ obj: { obj: { obj: { item: 10 } } } }, false]
], done);
});
it('should traverse an object several levels with required levels (without Joi.obj())', function (done) {
var schema = {
obj: {
obj: {
obj: {
item: Joi.boolean().required()
}
}
}
};
Helper.validate(schema, [
[null, false],
[undefined, true],
[{}, true],
[{ obj: {} }, true],
[{ obj: { obj: {} } }, true],
[{ obj: { obj: { obj: {} } } }, false],
[{ obj: { obj: { obj: { item: true } } } }, true],
[{ obj: { obj: { obj: { item: 10 } } } }, false]
], done);
});
it('errors on unknown keys when functions allows', function (done) {
var schema = Joi.object({ a: Joi.number() }).options({ skipFunctions: true });
var obj = { a: 5, b: 'value' };
schema.validate(obj, function (err, value) {
expect(err).to.exist;
done();
});
});
it('validates both valid() and with()', function (done) {
var schema = Joi.object({
first: Joi.valid('value'),
second: Joi.any()
}).with('first', 'second');
Helper.validate(schema, [
[{ first: 'value' }, false]
], done);
});
describe('#keys', function () {
it('allows any key', function (done) {
var a = Joi.object({ a: 4 });
var b = a.keys();
a.validate({ b: 3 }, function (err, value) {
expect(err).to.exist;
b.validate({ b: 3 }, function (err, value) {
expect(err).to.not.exist;
done();
});
});
});
it('forbids all keys', function (done) {
var a = Joi.object();
var b = a.keys({});
a.validate({ b: 3 }, function (err, value) {
expect(err).to.not.exist;
b.validate({ b: 3 }, function (err, value) {
expect(err).to.exist;
done();
});
});
});
it('adds to existing keys', function (done) {
var a = Joi.object({ a: 1 });
var b = a.keys({ b: 2 });
a.validate({ a: 1, b: 2 }, function (err, value) {
expect(err).to.exist;
b.validate({ a: 1, b: 2 }, function (err, value) {
expect(err).to.not.exist;
done();
});
});
});
});
describe('#unknown', function () {
it('allows local unknown without applying to children', function (done) {
var schema = Joi.object({
a: {
b: Joi.number()
}
}).unknown();
Helper.validate(schema, [
[{ a: { b: 5 } }, true],
[{ a: { b: 'x' } }, false],
[{ a: { b: 5 }, c: 'ignore' }, true],
[{ a: { b: 5, c: 'ignore' } }, false]
], done);
});
it('forbids local unknown without applying to children', function (done) {
var schema = Joi.object({
a: Joi.object({
b: Joi.number()
}).unknown()
}).options({ allowUnknown: false });
Helper.validate(schema, [
[{ a: { b: 5 } }, true],
[{ a: { b: 'x' } }, false],
[{ a: { b: 5 }, c: 'ignore' }, false],
[{ a: { b: 5, c: 'ignore' } }, true]
], done);
});
});
describe('#rename', function () {
it('allows renaming multiple times with multiple enabled', function (done) {
var schema = Joi.object({
test: Joi.string()
}).rename('test1', 'test').rename('test2', 'test', { multiple: true });
Joi.compile(schema).validate({ test1: 'a', test2: 'b' }, function (err, value) {
expect(err).to.not.exist;
done();
});
});
it('errors renaming multiple times with multiple disabled', function (done) {
var schema = Joi.object({
test: Joi.string()
}).rename('test1', 'test').rename('test2', 'test');
Joi.compile(schema).validate({ test1: 'a', test2: 'b' }, function (err, value) {
expect(err.message).to.equal('value cannot rename child test2 because multiple renames are disabled and another key was already renamed to test');
done();
});
});
it('errors multiple times when abortEarly is false', function (done) {
Joi.object().rename('a', 'b').rename('c', 'b').rename('d', 'b').options({ abortEarly: false }).validate({ a: 1, c: 1, d: 1 }, function (err, value) {
expect(err).to.exist;
expect(err.message).to.equal('value cannot rename child c because multiple renames are disabled and another key was already renamed to b. value cannot rename child d because multiple renames are disabled and another key was already renamed to b');
done();
});
});
it('aliases a key', function (done) {
var schema = Joi.object({
a: Joi.number(),
b: Joi.number()
}).rename('a', 'b', { alias: true });
var obj = { a: 10 };
Joi.compile(schema).validate(obj, function (err, value) {
expect(err).to.not.exist;
expect(value.a).to.equal(10);
expect(value.b).to.equal(10);
done();
});
});
it('with override disabled should not allow overwriting existing value', function (done) {
var schema = Joi.object({
test1: Joi.string()
}).rename('test', 'test1');
schema.validate({ test: 'b', test1: 'a' }, function (err, value) {
expect(err.message).to.equal('value cannot rename child test because override is disabled and target test1 exists');
done();
});
});
it('with override enabled should allow overwriting existing value', function (done) {
var schema = Joi.object({
test1: Joi.string()
}).rename('test', 'test1', { override: true });
schema.validate({ test: 'b', test1: 'a' }, function (err, value) {
expect(err).to.not.exist;
done();
});
});
it('renames when data is nested in an array via includes', function (done) {
var schema = {
arr: Joi.array().includes(Joi.object({
one: Joi.string(),
two: Joi.string()
}).rename('uno', 'one').rename('dos', 'two'))
};
var data = { arr: [{ uno: '1', dos: '2' }] };
Joi.object(schema).validate(data, function (err, value) {
expect(err).to.not.exist;
expect(value.arr[0].one).to.equal('1');
expect(value.arr[0].two).to.equal('2');
done();
});
});
it('applies rename and validation in the correct order regardless of key order', function (done) {
var schema1 = Joi.object({
a: Joi.number()
}).rename('b', 'a');
var input1 = { b: '5' };
schema1.validate(input1, function (err1, value1) {
expect(err1).to.not.exist;
expect(value1.b).to.not.exist;
expect(value1.a).to.equal(5);
var schema2 = Joi.object({ a: Joi.number(), b: Joi.any() }).rename('b', 'a');
var input2 = { b: '5' };
schema2.validate(input2, function (err2, value2) {
expect(err2).to.not.exist;
expect(value2.b).to.not.exist;
expect(value2.a).to.equal(5);
done();
});
});
});
it('sets the default value after key is renamed', function (done) {
var schema = Joi.object({
foo2: Joi.string().default('test')
}).rename('foo', 'foo2');
var input = {};
Joi.validate(input, schema, function (err, value) {
expect(err).to.not.exist;
expect(value.foo2).to.equal('test');
done();
});
});
it('should be able to rename keys that are empty strings', function (done) {
var schema = Joi.object().rename('', 'notEmpty');
var input = {
'': 'something'
};
schema.validate(input, function (err, value) {
expect(err).to.not.exist;
expect(value['']).to.not.exist;
expect(value.notEmpty).to.equal('something');
done();
});
});
it('should not create new keys when they key in question does not exist', function (done) {
var schema = Joi.object().rename('b', '_b');
var input = {
a: 'something'
};
schema.validate(input, function (err, value) {
expect(err).to.not.exist;
expect(value).to.have.keys(['a']);
expect(value.a).to.equal('something');
done();
});
});
});
describe('#describe', function () {
it('return empty description when no schema defined', function (done) {
var schema = Joi.object();
var desc = schema.describe();
expect(desc).to.deep.equal({
type: 'object'
});
done();
});
it('respects the shallow parameter', function (done) {
var schema = Joi.object({
name: Joi.string(),
child: Joi.object({
name: Joi.string()
})
});
expect(schema.describe(true)).to.not.contain.key('children');
expect(schema.describe()).to.contain.key('children');
done();
});
});
describe('#length', function () {
it('throws when length is not a number', function (done) {
expect(function () {
Joi.object().length('a');
}).to.throw('limit must be a positive integer');
done();
});
});
describe('#min', function () {
it('throws when limit is not a number', function (done) {
expect(function () {
Joi.object().min('a');
}).to.throw('limit must be a positive integer');
done();
});
});
describe('#max', function () {
it('throws when limit is not a number', function (done) {
expect(function () {
Joi.object().max('a');
}).to.throw('limit must be a positive integer');
done();
});
});
describe('#pattern', function () {
it('validates unknown keys using a pattern', function (done) {
var schema = Joi.object({
a: Joi.number()
}).pattern(/\d+/, Joi.boolean()).pattern(/\w\w+/, 'x');
Joi.validate({ bb: 'y', 5: 'x' }, schema, { abortEarly: false }, function (err, value) {
expect(err).to.exist;
expect(err.message).to.equal('5 must be a boolean. bb must be one of x');
Helper.validate(schema, [
[{ a: 5 }, true],
[{ a: 'x' }, false],
[{ b: 'x' }, false],
[{ bb: 'x' }, true],
[{ 5: 'x' }, false],
[{ 5: false }, true],
[{ 5: undefined }, true]
], done)
});
});
it('validates unknown keys using a pattern (nested)', function (done) {
var schema = {
x: Joi.object({
a: Joi.number()
}).pattern(/\d+/, Joi.boolean()).pattern(/\w\w+/, 'x')
};
Joi.validate({ x: { bb: 'y', 5: 'x' } }, schema, { abortEarly: false }, function (err, value) {
expect(err).to.exist;
expect(err.message).to.equal('5 must be a boolean. bb must be one of x');
done();
});
});
it('errors when using a pattern on empty schema with unknown(false) and pattern mismatch', function (done) {
var schema = Joi.object().pattern(/\d/, Joi.number()).unknown(false);
Joi.validate({ a: 5 }, schema, { abortEarly: false }, function (err, value) {
expect(err).to.exist;
expect(err.message).to.equal('a is not allowed');
done();
});
});
});
describe('#with', function () {
it('should throw an error when a parameter is not a string', function (done) {
try {
Joi.object().with({});
var error = false;
}
catch (e) {
error = true;
}
expect(error).to.equal(true);
try {
Joi.object().with(123);
error = false;
}
catch (e) {
error = true;
}
expect(error).to.equal(true);
done();
});
it('should validate correctly when key is an empty string', function (done) {
var schema = Joi.object().with('', 'b');
Helper.validate(schema, [
[{ c: 'hi', d: 'there' }, true],
]);
done();
});
});
describe('#without', function () {
it('should throw an error when a parameter is not a string', function (done) {
try {
Joi.object().without({});
var error = false;
}
catch (e) {
error = true;
}
expect(error).to.equal(true);
try {
Joi.object().without(123);
error = false;
}
catch (e) {
error = true;
}
expect(error).to.equal(true);
done();
});
it('should validate correctly when key is an empty string', function (done) {
var schema = Joi.object().without('', 'b');
Helper.validate(schema, [
[{ a: 'hi', b: 'there' }, true]
]);
done();
});
});
describe('#xor', function () {
it('should throw an error when a parameter is not a string', function (done) {
try {
Joi.object().xor({});
var error = false;
}
catch (e) {
error = true;
}
expect(error).to.equal(true);
try {
Joi.object().xor(123);
error = false;
}
catch (e) {
error = true;
}
expect(error).to.equal(true);
done();
});
});
describe('#or', function () {
it('should throw an error when a parameter is not a string', function (done) {
try {
Joi.object().or({});
var error = false;
}
catch (e) {
error = true;
}
expect(error).to.equal(true);
try {
Joi.object().or(123);
error = false;
}
catch (e) {
error = true;
}
expect(error).to.equal(true);
done();
});
it('errors multiple levels deep', function (done) {
Joi.object({
a: {
b: Joi.object().or('x', 'y')
}
}).validate({ a: { b: { c: 1 } } }, function (err, value) {
expect(err).to.exist;
expect(err.message).to.equal('value must contain at least one of x, y');
done();
});
});
});
describe('#assert', function () {
it('validates upwards reference', function (done) {
var schema = Joi.object({
a: {
b: Joi.string(),
c: Joi.number()
},
d: {
e: Joi.any()
}
}).assert(Joi.ref('d/e', { separator: '/' }), Joi.ref('a.c'), 'equal to a.c');
schema.validate({ a: { b: 'x', c: 5 }, d: { e: 6 } }, function (err, value) {
expect(err).to.exist;
expect(err.message).to.equal('value validation failed because d.e failed to equal to a.c');
Helper.validate(schema, [
[{ a: { b: 'x', c: 5 }, d: { e: 5 } }, true]
], done);
});
});
it('validates upwards reference with implicit context', function (done) {
var schema = Joi.object({
a: {
b: Joi.string(),
c: Joi.number()
},
d: {
e: Joi.any()
}
}).assert('d.e', Joi.ref('a.c'), 'equal to a.c');
schema.validate({ a: { b: 'x', c: 5 }, d: { e: 6 } }, function (err, value) {
expect(err).to.exist;
expect(err.message).to.equal('value validation failed because d.e failed to equal to a.c');
Helper.validate(schema, [
[{ a: { b: 'x', c: 5 }, d: { e: 5 } }, true]
], done);
});
});
it('throws when context is at root level', function (done) {
expect(function () {
var schema = Joi.object({
a: {
b: Joi.string(),
c: Joi.number()
},
d: {
e: Joi.any()
}
}).assert('a', Joi.ref('d.e'), 'equal to d.e');
}).to.throw('Cannot use assertions for root level references - use direct key rules instead');
done();
});
it('allows root level context ref', function (done) {
expect(function () {
var schema = Joi.object({
a: {
b: Joi.string(),
c: Joi.number()
},
d: {
e: Joi.any()
}
}).assert('$a', Joi.ref('d.e'), 'equal to d.e');
}).to.not.throw();
done();
});
});
});