UNPKG

fashion-model

Version:

JavaScript library for defining types and their properties with support for wrapping/unwrapping and serialization/deserialization.

299 lines (266 loc) 5.98 kB
const test = require('ava'); const Model = require('../Model'); const Enum = require('../Enum'); const Entity = Model.extend({ properties: { id: String } }); const Gender = Enum.create({ title: 'Gender', description: 'A person\'s gender', values: ['M', 'F'] }); const Species = Enum.create({ title: 'Species', description: 'A species', values: ['dog', 'cat'] }); const Pet = Model.extend({ properties: { name: String, species: Species } }); const Person = Entity.extend({ title: 'Person', description: 'A person', properties: { name: String, dateOfBirth: Date, gender: Gender, age: 'integer', pets: [Pet], favoriteNumbers: ['integer'], arrayOfAnything: [], anything: {}, blob: Object } }); const models = { Entity: Entity, Gender: Gender, Species: Species, Pet: Pet, Person: Person }; function _dateToString (date) { const str = JSON.stringify(date); const len = str.length; return str.substring(1, len - 1); } const jsonSchema = require('../json-schema-draft4'); const schemasByNameMap = {}; const schemas = []; // create z-schema validator const ZSchema = require('z-schema'); const zschemaValidator = new ZSchema(); // create JaySchema validator const JaySchema = require('jayschema'); const jschemaValidator = new JaySchema(function loader (ref, callback) { const schema = schemasByNameMap[ref]; if (schema) { callback(null, schema); } else { callback(new Error('Invalid ref: ' + ref)); } }); function _validateObject (data, Type) { return new Promise((resolve, reject) => { const schema = schemasByNameMap[Type.typeName]; const valid = zschemaValidator.validate(data, schema); if (!valid) { const err = zschemaValidator.getLastError(); const betterErr = new Error(err.name + ' - ' + err.message + ': ' + err.details .toString()); betterErr.stack = err.stack; return reject(betterErr); } jschemaValidator.validate(data, schema, (err, result) => { if (err) { return reject(err); } resolve(result); }); }); } test.before(function () { Object.keys(models).forEach(function (typeName) { const Type = models[typeName]; Type.typeName = typeName; const schema = schemasByNameMap[typeName] = jsonSchema.fromModel(Type); schemas.push(schema); }); }); test.before(function () { const allSchemasValid = zschemaValidator.validateSchema(schemas); if (!allSchemasValid) { throw zschemaValidator.getLastError(); } }); test('should generate JSON schemas', function (t) { t.deepEqual(schemasByNameMap.Entity, { id: 'Entity', type: 'object', properties: { id: { type: 'string' } } }); t.deepEqual(schemasByNameMap.Gender, { id: 'Gender', title: 'Gender', description: 'A person\'s gender', type: 'string', enum: ['M', 'F'] }); t.deepEqual(schemasByNameMap.Species, { id: 'Species', title: 'Species', description: 'A species', type: 'string', enum: ['dog', 'cat'] }); t.deepEqual(schemasByNameMap.Pet, { id: 'Pet', type: 'object', properties: { name: { type: 'string' }, species: { '$ref': 'Species' } } }); t.deepEqual(schemasByNameMap.Person, { id: 'Person', title: 'Person', description: 'A person', allOf: [{ '$ref': 'Entity' }], type: 'object', properties: { name: { type: 'string' }, dateOfBirth: { type: 'string', format: 'date-time' }, gender: { '$ref': 'Gender' }, age: { type: 'integer' }, pets: { type: 'array', items: { $ref: 'Pet' } }, favoriteNumbers: { type: 'array', items: { type: 'integer' } }, anything: {}, arrayOfAnything: { type: 'array', items: {} }, blob: { type: 'object' } } }); }); test('should handle converting model type to json schema without using composition', function (t) { const jsonSchema = require('../json-schema-draft4'); const Response = Model.extend({ properties: { result: { type: Object, key: 'res' } } }); const PaginationResponse = Response.extend({ properties: { limit: require('../Integer'), offset: require('../Integer'), count: require('../Integer') } }); const schema = jsonSchema.fromModel(PaginationResponse, { useAllOf: false }); const properties = {}; PaginationResponse.forEachProperty(function (property) { properties[property.getKey()] = true; }); t.deepEqual(properties, { res: true, limit: true, offset: true, count: true }); t.deepEqual(schema, { properties: { count: { type: 'integer' }, limit: { type: 'integer' }, offset: { type: 'integer' }, res: { type: 'object' } }, type: 'object' }); }); test('should validate complex data', async function (t) { const result = await _validateObject({ id: 'john.doe', name: 'John Doe', dateOfBirth: _dateToString(new Date(2015, 10, 10)), gender: 'M', age: 5, pets: [ { name: 'max', species: 'dog' } ], favoriteNumbers: [1, 2, 3], arrayOfAnything: ['a', 'b'], blob: { test: 123 } }, Person); t.is(result, undefined); }); test('should handle copying extra property metadata', function (t) { const MyType = Model.extend({ properties: { something: { type: Object, extra: 'abc' } } }); const schema = jsonSchema.fromModel(MyType, { handleProperty: function (propertyName, src, dest) { dest.extra = src.extra; } }); t.is(schema.properties.something.extra, 'abc'); });