UNPKG

domain-objects

Version:

A simple, convenient way to represent domain objects, leverage domain knowledge, and add runtime validation in your code base.

381 lines 16.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const uni_time_1 = require("@ehmpathy/uni-time"); const joi_1 = __importDefault(require("joi")); const DomainEntity_1 = require("../../instantiation/DomainEntity"); const DomainLiteral_1 = require("../../instantiation/DomainLiteral"); const deserialize_1 = require("./deserialize"); /* eslint-disable no-useless-escape */ const serialize_1 = require("./serialize"); describe('deserialize', () => { describe('basic types', () => { it('should deserialize strings', () => { const original = 'hello!'; const serial = (0, serialize_1.serialize)(original); const undone = (0, deserialize_1.deserialize)(serial); expect(undone).toEqual(original); }); it('should deserialize numbers', () => { const original = 821; const serial = (0, serialize_1.serialize)(original); const undone = (0, deserialize_1.deserialize)(serial); expect(undone).toEqual(original); }); it.todo('should deserialize dates'); it.todo('should deserialize undefined'); it('should deserialize nulls', () => { const original = null; const serial = (0, serialize_1.serialize)(original); const undone = (0, deserialize_1.deserialize)(serial); expect(undone).toEqual(original); }); it.todo('should deserialize buffers'); }); describe('arrays', () => { it('should be able to deserialize arrays', () => { const original = ['2', 'four', 1, 3]; const serial = (0, serialize_1.serialize)(original); const undone = (0, deserialize_1.deserialize)(serial); expect(undone).toEqual(original); // sorted }); it('should deserialize arrays even if they have objects', () => { const original = [ 'banana', 821, { id: 0, meaning: null }, { id: 1, meaning: 42, value: 821 }, ]; const serial = (0, serialize_1.serialize)(original); const undone = (0, deserialize_1.deserialize)(serial); expect(undone).toEqual(original); }); }); describe('objects', () => { it('should be able to serialize an object with all sorts of types', () => { const original = { color: 'blue', cost: 821, orders: [ { id: 0, meaning: null }, { id: 1, meaning: 42, value: 821 }, ], application: { type: 'PAINTING', }, }; const serial = (0, serialize_1.serialize)(original); const undone = (0, deserialize_1.deserialize)(serial); expect(undone).toEqual(original); }); }); describe('domain objects', () => { class Spaceship extends DomainEntity_1.DomainEntity { } Spaceship.unique = ['serialNumber']; Spaceship.updatable = ['serialNumber']; Spaceship.schema = joi_1.default.object().keys({ _dobj: joi_1.default.string().optional(), serialNumber: joi_1.default.string().required(), fuelQuantity: joi_1.default.number().required(), passengers: joi_1.default.number().required(), }); class Address extends DomainLiteral_1.DomainLiteral { } class Spaceport extends DomainEntity_1.DomainEntity { } Spaceport.unique = ['uuid']; Spaceport.updatable = ['spaceships']; Spaceport.nested = { address: Address, spaceships: Spaceship }; class Human extends DomainEntity_1.DomainEntity { } Human.unique = ['birthCode']; Human.updatable = ['name']; class Robot extends DomainEntity_1.DomainEntity { } Robot.unique = ['serialNumber']; Robot.updatable = ['name']; class Captain extends DomainEntity_1.DomainEntity { } Captain.unique = ['ship', 'agent']; Captain.nested = { ship: Spaceship, agent: [Robot, Human], }; // run the tests it('should deserialize domain objects', () => { const ship = new Spaceship({ serialNumber: '__UUID__', fuelQuantity: 9001, passengers: 21, }); const original = ship; const serial = (0, serialize_1.serialize)(original); const undone = (0, deserialize_1.deserialize)(serial, { with: [Spaceship] }); expect(undone).toEqual(original); expect(undone).toBeInstanceOf(Spaceship); }); it('should throw a helpful error if attempted to deserialize a domain object without its constructor being provided in the context', () => { const ship = new Spaceship({ serialNumber: '__UUID__', fuelQuantity: 9001, passengers: 21, }); const original = ship; const serial = (0, serialize_1.serialize)(original); try { (0, deserialize_1.deserialize)(serial, { with: [] }); throw new Error('should not reach here'); } catch (error) { if (!(error instanceof Error)) throw error; expect(error.message).toContain(`DomainObject 'Spaceship' was referenced in the string being deserialized but was missing from the context given to the deserialize method`); expect(error.message).toMatchSnapshot(); // save an example of the message to snapshot } }); it('recursively deserialize domain objects', () => { const shipA = new Spaceship({ serialNumber: '__SHIP_A__', fuelQuantity: 7000, passengers: 42, }); const shipB = new Spaceship({ serialNumber: '__SHIP_B__', fuelQuantity: 9001, passengers: 21, }); const spaceport = new Spaceport({ uuid: '__SPACEPORT_UUID__', address: new Address({ galaxy: 'Milky Way', solarSystem: 'Sun', planet: 'Earth', continent: 'North America', }), spaceships: [shipA, shipB], }); const original = spaceport; const serial = (0, serialize_1.serialize)(original, { lossless: true }); const undone = (0, deserialize_1.deserialize)(serial, { with: [Spaceport, Spaceship, Address], }); expect(undone).toEqual(original); expect(undone).toBeInstanceOf(Spaceport); expect(undone.address).toBeInstanceOf(Address); expect(undone.spaceships[0]).toBeInstanceOf(Spaceship); }); it('recursively deserialize an array of domain objects', () => { const shipA = new Spaceship({ serialNumber: '__SHIP_A__', fuelQuantity: 7000, passengers: 42, }); const shipB = new Spaceship({ serialNumber: '__SHIP_B__', fuelQuantity: 9001, passengers: 21, }); const original = [shipA, shipB]; const serial = (0, serialize_1.serialize)(original, { lossless: true }); const undone = (0, deserialize_1.deserialize)(serial, { with: [Spaceport, Spaceship, Address], }); expect(undone).toEqual(original); expect(undone[0]).toBeInstanceOf(Spaceship); }); it('recursively deserialize a domain object which has a nested domain-object property instantiable with several options of domain objects', () => { const ship = new Spaceship({ serialNumber: '__SHIP_A__', fuelQuantity: 7000, passengers: 42, }); const agent = new Robot({ serialNumber: '821', name: 'Bender', }); const captain = new Captain({ ship, agent, }); const original = captain; const serial = (0, serialize_1.serialize)(original, { lossless: true }); const undone = (0, deserialize_1.deserialize)(serial, { with: [Spaceship, Human, Robot, Captain], }); expect(undone).toEqual(original); expect(undone).toBeInstanceOf(Captain); expect(undone.agent).toBeInstanceOf(Robot); }); describe('speed', () => { it.skip('should be faster if schema is skipped', () => __awaiter(void 0, void 0, void 0, function* () { // define the choices const shipA = new Spaceship({ serialNumber: '__SHIP_A__', fuelQuantity: 7000, passengers: 42, }); const shipB = new Spaceship({ serialNumber: '__SHIP_B__', fuelQuantity: 9001, passengers: 21, }); const spaceport = new Spaceport({ uuid: '__SPACEPORT_UUID__', address: new Address({ galaxy: 'Milky Way', solarSystem: 'Sun', planet: 'Earth', continent: 'North America', }), spaceships: [shipA, shipB], }); const agent = new Robot({ serialNumber: '821', name: 'Bender', }); const captainA = new Captain({ ship: shipA, agent, }); const captainB = new Captain({ ship: shipB, agent, }); // define many instances of domain objects const setSingle = [ shipA, shipB, shipA, shipB, shipA, shipB, spaceport, agent, captainA, captainB, ]; const setMany = Array(100).fill(setSingle).flat(); // 100 copies of set single // serialize the dobjs into a document const document = (0, serialize_1.serialize)(setMany, { lossless: true }); // check the deserialize duration with schema const stopwatchWithSchema = (0, uni_time_1.startDurationStopwatch)({ for: 'deserialize with schema', log: { level: 'info', threshold: { milliseconds: 1 }, }, }, { log: console }); yield (0, deserialize_1.deserialize)(document, { with: [Spaceship, Spaceport, Robot, Captain], }); const { duration: durationWithSchema } = stopwatchWithSchema.stop(); // check the deserialize duration without schema const stopwatchWithoutSchema = (0, uni_time_1.startDurationStopwatch)({ for: 'deserialize without schema', log: { level: 'info', threshold: { milliseconds: 1 }, }, }, { log: console }); yield (0, deserialize_1.deserialize)(document, { with: [Spaceship, Spaceport, Robot, Captain], skip: { schema: true, }, }); const { duration: durationWithoutSchema } = stopwatchWithoutSchema.stop(); expect(durationWithoutSchema.milliseconds).toBeLessThan(durationWithSchema.milliseconds); })); it.skip('should be instant on repeat attempts, due to in memory cache', () => __awaiter(void 0, void 0, void 0, function* () { // define the choices const shipA = new Spaceship({ serialNumber: '__SHIP_A__', fuelQuantity: 7000, passengers: 42, }); const shipB = new Spaceship({ serialNumber: '__SHIP_B__', fuelQuantity: 9001, passengers: 21, }); const spaceport = new Spaceport({ uuid: '__SPACEPORT_UUID__', address: new Address({ galaxy: 'Milky Way', solarSystem: 'Sun', planet: 'Earth', continent: 'North America', }), spaceships: [shipA, shipB], }); const agent = new Robot({ serialNumber: '821', name: 'Bender', }); const captainA = new Captain({ ship: shipA, agent, }); const captainB = new Captain({ ship: shipB, agent, }); // define many instances of domain objects const setSingle = [ shipA, shipB, shipA, shipB, shipA, shipB, spaceport, agent, captainA, captainB, ]; const setMany = Array(300).fill(setSingle).flat(); // 100 copies of set single // serialize the dobjs into a document const document = (0, serialize_1.serialize)(setMany, { lossless: true }); // check the deserialize duration with schema const stopwatchFirst = (0, uni_time_1.startDurationStopwatch)({ for: 'deserialize with schema', log: { level: 'info', threshold: { milliseconds: 1 }, }, }, { log: console }); yield (0, deserialize_1.deserialize)(document, { with: [Spaceship, Spaceport, Robot, Captain], }); const { duration: durationFirst } = stopwatchFirst.stop(); expect(durationFirst.milliseconds).toBeGreaterThan(20); // check the deserialize duration without schema const stopwatchSecond = (0, uni_time_1.startDurationStopwatch)({ for: 'deserialize with schema', log: { level: 'info', threshold: { milliseconds: 1 }, }, }, { log: console }); yield (0, deserialize_1.deserialize)(document, { with: [Spaceship, Spaceport, Robot, Captain], }); const { duration: durationSecond } = stopwatchSecond.stop(); expect(durationSecond.milliseconds).toBeLessThan(5); // instant })); }); }); }); //# sourceMappingURL=deserialize.test.js.map