octonom
Version:
Object Document Mapper for any database
145 lines (128 loc) • 5.77 kB
text/typescript
import { ArrayCollection } from '../array-collection';
import { SanitizationError, ValidationError } from '../errors';
import { Model } from '../model';
import { ModelSchema } from './model';
import { ReferenceSchema } from './reference';
import { StringSchema } from './string';
describe('ModelSchema', () => {
class ReferencedModel extends Model {
public id: string;
}
ReferencedModel.setSchema('id', new StringSchema());
class TestModel extends Model {
public foo: string;
public reference: string | ReferencedModel;
}
TestModel.setSchema('foo', new StringSchema());
TestModel.setSchema('reference', new ReferenceSchema({collection: () => collection}));
const collection = new ArrayCollection<ReferencedModel>(ReferencedModel, {modelIdField: 'id'});
const referencedInstance = new ReferencedModel({id: '0xACAB'});
collection.insert(referencedInstance);
describe('constructor()', () => {
it('should be instantiatable with options', () => {
const schema = new ModelSchema({model: TestModel});
expect(schema).to.have.property('options').that.eql({model: TestModel});
});
});
describe('sanitize()', () => {
it('should throw a SanitizationError if value is not an object and not a model instance', () => {
const schema = new ModelSchema({model: TestModel});
expect(() => schema.create('foo'))
.to.throw(SanitizationError, 'Value is not an object.');
});
it('should throw a SanitizationError if model sanitization fails', () => {
const schema = new ModelSchema({model: TestModel});
expect(() => schema.create({foo: 42}))
.to.throw(SanitizationError, 'Value is not a string.');
});
it('should return undefined', () => {
const schema = new ModelSchema({model: TestModel});
expect(schema.create(undefined)).to.eql(undefined);
});
it('should return an empty model instance if undefined and required and defaults are enabled', () => {
const schema = new ModelSchema({required: true, model: TestModel});
expect(schema.create(undefined, {defaults: true}))
.to.have.property('value').that.is.an.instanceOf(TestModel).and.to.eql({});
});
it('should return a model instance', () => {
const schema = new ModelSchema({model: TestModel});
const instance = new TestModel();
expect(schema.create(instance))
.to.have.property('value').that.eql(instance).and.not.equals(instance);
});
it('should return a model instance when an object was provided', () => {
const schema = new ModelSchema({model: TestModel});
expect(schema.create({foo: 'bar'}))
.to.have.property('value').that.is.instanceOf(TestModel).and.to.eql({foo: 'bar'});
});
});
describe('populate()', () => {
it('should throw if populateReference is not an object', async () => {
const schema = new ModelSchema({model: TestModel});
await expect(schema.populate(schema.create(new TestModel()), true))
.to.be.rejectedWith(Error, 'populateReference must be an object.');
});
it('should populate a reference', async () => {
const schema = new ModelSchema<TestModel>({model: TestModel});
const instance = schema.create({reference: '0xACAB'});
const result = await schema.populate(instance, {reference: true});
expect(result).to.equal(instance.value);
expect(result).to.eql({reference: {id: '0xACAB'}});
expect(result.reference).to.be.an.instanceOf(ReferencedModel);
});
});
describe('toObject()', () => {
it('should return an object', () => {
const schema = new ModelSchema({model: TestModel});
const instance = schema.create(new TestModel({foo: 'bar'}));
const obj = schema.toObject(instance);
expect(obj).to.not.equal(instance);
expect(obj).to.eql({foo: 'bar'});
});
});
describe('validate()', () => {
it('should throw if nested validator fails', async () => {
class FailModel extends Model {
public foo: string;
}
FailModel.setSchema('foo', new StringSchema({
validate: async instance => {
if (instance.value === 'baz') {
throw new ValidationError('baz is not allowed.');
}
if (instance.value === 'err') {
throw new Error('Critical error.');
}
},
}));
const schema = new ModelSchema({model: FailModel});
await schema.validate(schema.create(new FailModel({foo: 'bar'})));
await expect(schema.validate(schema.create(new FailModel({foo: 'baz'}))))
.to.be.rejectedWith(ValidationError, 'baz is not allowed.');
await expect(schema.validate(schema.create(new FailModel({foo: 'err'}))))
.to.be.rejectedWith(Error, 'Critical error.');
});
it('should run custom validator', async () => {
const schema = new ModelSchema<TestModel>({
model: TestModel,
validate: async instance => {
if (instance.value.foo === 'baz') {
throw new ValidationError('baz is not allowed.');
}
if (instance.value.foo === 'err') {
throw new Error('Critical error.');
}
},
});
await schema.validate(schema.create(new TestModel({foo: 'bar'})));
await expect(schema.validate(schema.create(new TestModel({foo: 'baz'}))))
.to.be.rejectedWith(ValidationError, 'baz is not allowed.');
await expect(schema.validate(schema.create(new TestModel({foo: 'err'}))))
.to.be.rejectedWith(Error, 'Critical error.');
});
it('should validate a model instance', async () => {
const schema = new ModelSchema({model: TestModel});
await schema.validate(schema.create(new TestModel({foo: 'bar'})));
});
});
});