@e22m4u/js-repository
Version:
Реализация репозитория для работы с базами данных в Node.js
1,422 lines (1,381 loc) • 75.9 kB
JavaScript
import {expect} from 'chai';
import {format} from '@e22m4u/js-format';
import {DataType} from '../definition/index.js';
import {RelationType} from '../definition/index.js';
import {HasOneResolver} from './has-one-resolver.js';
import {DatabaseSchema} from '../database-schema.js';
import {DEFAULT_PRIMARY_KEY_PROPERTY_NAME as DEF_PK} from '../definition/index.js';
describe('HasOneResolver', function () {
describe('includeTo', function () {
it('requires the "entities" parameter to be an array', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "entities" of HasOneResolver.includeTo requires ' +
'an Array of Object, but %s was given.',
v,
);
const throwable = v =>
R.includeTo(
v,
'sourceName',
'targetName',
'relationName',
'foreignKey',
);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires elements of the "entities" parameter to be an Object', async function () {
const dbs = new DatabaseSchema();
dbs.defineModel({name: 'source'});
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "entities" of HasOneResolver.includeTo requires ' +
'an Array of Object, but %s was given.',
v,
);
const throwable = v =>
R.includeTo([v], 'source', 'target', 'relationName', 'foreignKey');
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the "sourceName" parameter to be a non-empty string', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "sourceName" of HasOneResolver.includeTo requires ' +
'a non-empty String, but %s was given.',
v,
);
const throwable = v =>
R.includeTo([], v, 'targetName', 'relationName', 'foreignKey');
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the "targetName" parameter to be a non-empty string', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "targetName" of HasOneResolver.includeTo requires ' +
'a non-empty String, but %s was given.',
v,
);
const throwable = v =>
R.includeTo([], 'sourceName', v, 'relationName', 'foreignKey');
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the "relationName" parameter to be a non-empty string', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "relationName" of HasOneResolver.includeTo requires ' +
'a non-empty String, but %s was given.',
v,
);
const throwable = v =>
R.includeTo([], 'sourceName', 'targetName', v, 'foreignKey');
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the "foreignKey" parameter to be a non-empty string', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "foreignKey" of HasOneResolver.includeTo requires ' +
'a non-empty String, but %s was given.',
v,
);
const throwable = v =>
R.includeTo([], 'sourceName', 'targetName', 'relationName', v);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the provided parameter "scope" to be an object', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The provided parameter "scope" of HasOneResolver.includeTo ' +
'should be an Object, but %s was given.',
v,
);
const throwable = v =>
R.includeTo(
[],
'sourceName',
'targetName',
'relationName',
'foreignKey',
v,
);
await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
});
it('throws an error if a target model is not found', async function () {
const dbs = new DatabaseSchema();
dbs.defineModel({name: 'source'});
const R = dbs.getService(HasOneResolver);
const promise = R.includeTo(
[],
'source',
'target',
'relationName',
'foreignKey',
);
await expect(promise).to.be.rejectedWith(
'The model "target" is not defined',
);
});
it('throws an error if a target model does not have datasource', async function () {
const dbs = new DatabaseSchema();
dbs.defineModel({name: 'source'});
dbs.defineModel({name: 'target'});
const R = dbs.getService(HasOneResolver);
const promise = R.includeTo(
[],
'source',
'target',
'relationName',
'foreignKey',
);
await expect(promise).to.be.rejectedWith(
'The model "target" does not have a specified datasource.',
);
});
it('does not throw an error if a relation target is not exist', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRel = dbs.getRepository('source');
const source = await sourceRel.create({});
const R = dbs.getService(HasOneResolver);
await R.includeTo([source], 'source', 'target', 'child', 'parentId');
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
});
});
it('includes if a primary key is not defined in the source model', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRep = dbs.getRepository('source');
const targetRep = dbs.getRepository('target');
const source = await sourceRep.create({});
expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
const target = await targetRep.create({parentId: source[DEF_PK]});
expect(target).to.be.eql({
[DEF_PK]: target[DEF_PK],
parentId: source[DEF_PK],
});
const R = dbs.getService(HasOneResolver);
await R.includeTo([source], 'source', 'target', 'child', 'parentId');
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
id: target[DEF_PK],
parentId: source[DEF_PK],
},
});
});
it('includes if the source model has a custom primary key', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({
name: 'source',
datasource: 'datasource',
properties: {
myId: {
type: DataType.NUMBER,
primaryKey: true,
},
},
});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRep = dbs.getRepository('source');
const targetRep = dbs.getRepository('target');
const source = await sourceRep.create({});
expect(source).to.be.eql({myId: source.myId});
const target = await targetRep.create({parentId: source.myId});
expect(target).to.be.eql({
[DEF_PK]: target[DEF_PK],
parentId: source.myId,
});
const R = dbs.getService(HasOneResolver);
await R.includeTo([source], 'source', 'target', 'child', 'parentId');
expect(source).to.be.eql({
myId: source.myId,
child: {
[DEF_PK]: target[DEF_PK],
parentId: source.myId,
},
});
});
it('includes if the target model has a custom primary key', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({
name: 'target',
datasource: 'datasource',
properties: {
myId: {
type: DataType.NUMBER,
primaryKey: true,
},
},
});
const sourceRep = dbs.getRepository('source');
const targetRep = dbs.getRepository('target');
const source = await sourceRep.create({});
expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
const target = await targetRep.create({parentId: source[DEF_PK]});
expect(target).to.be.eql({
myId: target.myId,
parentId: source[DEF_PK],
});
const R = dbs.getService(HasOneResolver);
await R.includeTo([source], 'source', 'target', 'child', 'parentId');
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
myId: target.myId,
parentId: source[DEF_PK],
},
});
});
it('uses a where clause of the given scope to filter the relation target', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRep = dbs.getRepository('source');
const targetRep = dbs.getRepository('target');
const source = await sourceRep.create({});
expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
const target1 = await targetRep.create({
featured: false,
parentId: source[DEF_PK],
});
expect(target1).to.be.eql({
[DEF_PK]: target1[DEF_PK],
featured: false,
parentId: source[DEF_PK],
});
const target2 = await targetRep.create({
featured: true,
parentId: source[DEF_PK],
});
expect(target2).to.be.eql({
[DEF_PK]: target2[DEF_PK],
featured: true,
parentId: source[DEF_PK],
});
const R = dbs.getService(HasOneResolver);
await R.includeTo([source], 'source', 'target', 'child', 'parentId', {
where: {featured: false},
});
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
[DEF_PK]: target1[DEF_PK],
featured: false,
parentId: source[DEF_PK],
},
});
await R.includeTo([source], 'source', 'target', 'child', 'parentId', {
where: {featured: true},
});
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
[DEF_PK]: target2[DEF_PK],
featured: true,
parentId: source[DEF_PK],
},
});
});
it('uses a fields clause of the given scope to filter the relation target', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRep = dbs.getRepository('source');
const targetRep = dbs.getRepository('target');
const source = await sourceRep.create({});
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
});
const target = await targetRep.create({
foo: 'fooVal',
bar: 'barVal',
parentId: source[DEF_PK],
});
expect(target).to.be.eql({
[DEF_PK]: target[DEF_PK],
foo: target.foo,
bar: target.bar,
parentId: source[DEF_PK],
});
const R = dbs.getService(HasOneResolver);
await R.includeTo([source], 'source', 'target', 'child', 'parentId', {
fields: [DEF_PK, 'bar'],
});
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
[DEF_PK]: target[DEF_PK],
bar: target.bar,
},
});
});
it('uses an include clause of the given scope to resolve target relations', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({
name: 'datasource',
adapter: 'memory',
});
dbs.defineModel({
name: 'modelA',
datasource: 'datasource',
properties: {
id: {
type: DataType.NUMBER,
primaryKey: true,
},
source: {
type: DataType.STRING,
default: 'modelA',
},
},
relations: {
child: {
type: RelationType.HAS_ONE,
model: 'modelB',
foreignKey: 'parentId',
},
},
});
dbs.defineModel({
name: 'modelB',
datasource: 'datasource',
properties: {
id: {
type: DataType.NUMBER,
primaryKey: true,
},
source: {
type: DataType.STRING,
default: 'modelB',
},
},
relations: {
child: {
type: RelationType.HAS_ONE,
model: 'modelC',
foreignKey: 'parentId',
},
},
});
dbs.defineModel({
name: 'modelC',
datasource: 'datasource',
properties: {
id: {
type: DataType.NUMBER,
primaryKey: true,
},
source: {
type: DataType.STRING,
default: 'modelC',
},
},
});
const aRep = dbs.getRepository('modelA');
const bRep = dbs.getRepository('modelB');
const cRep = dbs.getRepository('modelC');
const a = await aRep.create({});
const b = await bRep.create({parentId: a.id});
const c = await cRep.create({parentId: b.id});
expect(a).to.be.eql({
id: a.id,
source: 'modelA',
});
expect(b).to.be.eql({
id: b.id,
source: 'modelB',
parentId: a.id,
});
expect(c).to.be.eql({
id: c.id,
source: 'modelC',
parentId: b.id,
});
const R = dbs.getService(HasOneResolver);
await R.includeTo([a], 'modelA', 'modelB', 'child', 'parentId', {
include: 'child',
});
expect(a).to.be.eql({
id: a.id,
source: 'modelA',
child: {
id: b.id,
source: 'modelB',
parentId: a.id,
child: {
id: c.id,
source: 'modelC',
parentId: b.id,
},
},
});
});
it('does not break the "and" operator of the given "where" clause', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRep = dbs.getRepository('source');
const targetRep = dbs.getRepository('target');
const source = await sourceRep.create({});
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
});
const target1 = await targetRep.create({
featured: false,
parentId: source[DEF_PK],
});
expect(target1).to.be.eql({
[DEF_PK]: target1[DEF_PK],
featured: false,
parentId: source[DEF_PK],
});
const target2 = await targetRep.create({
featured: true,
parentId: source[DEF_PK],
});
expect(target2).to.be.eql({
[DEF_PK]: target2[DEF_PK],
featured: true,
parentId: source[DEF_PK],
});
const R = dbs.getService(HasOneResolver);
await R.includeTo([source], 'source', 'target', 'child', 'parentId', {
where: {and: [{featured: false}]},
});
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
[DEF_PK]: target1[DEF_PK],
featured: false,
parentId: source[DEF_PK],
},
});
delete source.child;
await R.includeTo([source], 'source', 'target', 'child', 'parentId', {
where: {and: [{featured: true}]},
});
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
[DEF_PK]: target2[DEF_PK],
featured: true,
parentId: source[DEF_PK],
},
});
});
});
describe('includePolymorphicTo', function () {
it('requires the "entities" parameter to be an array', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
'an Array of Object, but %s was given.',
v,
);
const throwable = v =>
R.includePolymorphicTo(
v,
'sourceName',
'targetName',
'relationName',
'foreignKey',
'discriminator',
);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires elements of the "entities" parameter to be an Object', async function () {
const dbs = new DatabaseSchema();
dbs.defineModel({name: 'source'});
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
'an Array of Object, but %s was given.',
v,
);
const throwable = v =>
R.includePolymorphicTo(
[v],
'source',
'target',
'relationName',
'foreignKey',
'discriminator',
);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the "sourceName" parameter to be a non-empty string', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "sourceName" of HasOneResolver.includePolymorphicTo requires ' +
'a non-empty String, but %s was given.',
v,
);
const throwable = v =>
R.includePolymorphicTo(
[],
v,
'targetName',
'relationName',
'foreignKey',
'discriminator',
);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the "targetName" parameter to be a non-empty string', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "targetName" of HasOneResolver.includePolymorphicTo requires ' +
'a non-empty String, but %s was given.',
v,
);
const throwable = v =>
R.includePolymorphicTo(
[],
'sourceName',
v,
'relationName',
'foreignKey',
'discriminator',
);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the "relationName" parameter to be a non-empty string', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "relationName" of HasOneResolver.includePolymorphicTo requires ' +
'a non-empty String, but %s was given.',
v,
);
const throwable = v =>
R.includePolymorphicTo(
[],
'sourceName',
'targetName',
v,
'foreignKey',
'discriminator',
);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the "foreignKey" parameter to be a non-empty string', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "foreignKey" of HasOneResolver.includePolymorphicTo requires ' +
'a non-empty String, but %s was given.',
v,
);
const throwable = v =>
R.includePolymorphicTo(
[],
'sourceName',
'targetName',
'relationName',
v,
'discriminator',
);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the "discriminator" parameter to be a non-empty string', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "discriminator" of HasOneResolver.includePolymorphicTo requires ' +
'a non-empty String, but %s was given.',
v,
);
const throwable = v =>
R.includePolymorphicTo(
[],
'sourceName',
'targetName',
'relationName',
'foreignKey',
v,
);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the provided parameter "scope" to be an object', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The provided parameter "scope" of HasOneResolver.includePolymorphicTo ' +
'should be an Object, but %s was given.',
v,
);
const throwable = v =>
R.includePolymorphicTo(
[],
'sourceName',
'targetName',
'relationName',
'foreignKey',
'discriminator',
v,
);
await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
});
it('throws an error if the given target model is not found', async function () {
const dbs = new DatabaseSchema();
dbs.defineModel({name: 'source'});
const R = dbs.getService(HasOneResolver);
const entity = {[DEF_PK]: 1};
const promise = R.includePolymorphicTo(
[entity],
'source',
'target',
'child',
'parentId',
'parentType',
);
await expect(promise).to.be.rejectedWith(
'The model "target" is not defined',
);
});
it('throws an error if the given target model does not have a datasource', async function () {
const dbs = new DatabaseSchema();
dbs.defineModel({name: 'source'});
dbs.defineModel({name: 'target'});
const R = dbs.getService(HasOneResolver);
const entity = {[DEF_PK]: 1};
const promise = R.includePolymorphicTo(
[entity],
'source',
'target',
'child',
'parentId',
'parentType',
);
await expect(promise).to.be.rejectedWith(
'The model "target" does not have a specified datasource.',
);
});
it('does not throw an error if a relation target is not found', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRel = dbs.getRepository('source');
const source = await sourceRel.create({});
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
});
const R = dbs.getService(HasOneResolver);
await R.includePolymorphicTo(
[source],
'source',
'target',
'child',
'parentId',
'parentType',
);
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
});
});
it('does not include an entity with a not matched discriminator value', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRel = dbs.getRepository('source');
const targetRel = dbs.getRepository('target');
const source = await sourceRel.create({});
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
});
const target = await targetRel.create({
parentId: source[DEF_PK],
parentType: 'unknown',
});
expect(target).to.be.eql({
[DEF_PK]: target[DEF_PK],
parentId: source[DEF_PK],
parentType: 'unknown',
});
const R = dbs.getService(HasOneResolver);
await R.includePolymorphicTo(
[source],
'source',
'target',
'child',
'parentId',
'parentType',
);
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
});
});
it('includes if a primary key is not defined in the source model', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRep = dbs.getRepository('source');
const targetRep = dbs.getRepository('target');
const source = await sourceRep.create({});
expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
const target = await targetRep.create({
parentId: source[DEF_PK],
parentType: 'source',
});
expect(target).to.be.eql({
[DEF_PK]: target[DEF_PK],
parentId: source[DEF_PK],
parentType: target.parentType,
});
const R = dbs.getService(HasOneResolver);
await R.includePolymorphicTo(
[source],
'source',
'target',
'child',
'parentId',
'parentType',
);
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
id: target[DEF_PK],
parentId: source[DEF_PK],
parentType: target.parentType,
},
});
});
it('includes if the source model has a custom primary key', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({
name: 'source',
datasource: 'datasource',
properties: {
myId: {
type: DataType.NUMBER,
primaryKey: true,
},
},
});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRep = dbs.getRepository('source');
const targetRep = dbs.getRepository('target');
const source = await sourceRep.create({});
expect(source).to.be.eql({myId: source.myId});
const target = await targetRep.create({
parentId: source.myId,
parentType: 'source',
});
expect(target).to.be.eql({
[DEF_PK]: target[DEF_PK],
parentId: source.myId,
parentType: target.parentType,
});
const R = dbs.getService(HasOneResolver);
await R.includePolymorphicTo(
[source],
'source',
'target',
'child',
'parentId',
'parentType',
);
expect(source).to.be.eql({
myId: source.myId,
child: {
[DEF_PK]: target[DEF_PK],
parentId: source.myId,
parentType: target.parentType,
},
});
});
it('includes if the target model has a custom primary key', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({
name: 'target',
datasource: 'datasource',
properties: {
myId: {
type: DataType.NUMBER,
primaryKey: true,
},
},
});
const sourceRep = dbs.getRepository('source');
const targetRep = dbs.getRepository('target');
const source = await sourceRep.create({});
expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
const target = await targetRep.create({
parentId: source[DEF_PK],
parentType: 'source',
});
expect(target).to.be.eql({
myId: target.myId,
parentId: source[DEF_PK],
parentType: target.parentType,
});
const R = dbs.getService(HasOneResolver);
await R.includePolymorphicTo(
[source],
'source',
'target',
'child',
'parentId',
'parentType',
);
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
myId: target.myId,
parentId: source[DEF_PK],
parentType: target.parentType,
},
});
});
it('uses a where clause of the given scope to filter the relation target', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRep = dbs.getRepository('source');
const targetRep = dbs.getRepository('target');
const source = await sourceRep.create({});
expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
const target1 = await targetRep.create({
featured: false,
parentId: source[DEF_PK],
parentType: 'source',
});
expect(target1).to.be.eql({
[DEF_PK]: target1[DEF_PK],
featured: false,
parentId: source[DEF_PK],
parentType: target1.parentType,
});
const target2 = await targetRep.create({
featured: true,
parentId: source[DEF_PK],
parentType: 'source',
});
expect(target2).to.be.eql({
[DEF_PK]: target2[DEF_PK],
featured: true,
parentId: source[DEF_PK],
parentType: target2.parentType,
});
const R = dbs.getService(HasOneResolver);
await R.includePolymorphicTo(
[source],
'source',
'target',
'child',
'parentId',
'parentType',
{where: {featured: false}},
);
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
[DEF_PK]: target1[DEF_PK],
featured: false,
parentId: source[DEF_PK],
parentType: target1.parentType,
},
});
await R.includePolymorphicTo(
[source],
'source',
'target',
'child',
'parentId',
'parentType',
{where: {featured: true}},
);
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
[DEF_PK]: target2[DEF_PK],
featured: true,
parentId: source[DEF_PK],
parentType: target2.parentType,
},
});
});
it('uses a fields clause of the given scope to filter the relation target', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRep = dbs.getRepository('source');
const targetRep = dbs.getRepository('target');
const source = await sourceRep.create({});
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
});
const target = await targetRep.create({
foo: 'fooVal',
bar: 'barVal',
parentId: source[DEF_PK],
parentType: 'source',
});
expect(target).to.be.eql({
[DEF_PK]: target[DEF_PK],
foo: target.foo,
bar: target.bar,
parentId: source[DEF_PK],
parentType: target.parentType,
});
const R = dbs.getService(HasOneResolver);
await R.includePolymorphicTo(
[source],
'source',
'target',
'child',
'parentId',
'parentType',
{fields: [DEF_PK, 'bar']},
);
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
[DEF_PK]: target[DEF_PK],
bar: target.bar,
},
});
});
it('uses an include clause of the given scope to resolve target relations', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({
name: 'datasource',
adapter: 'memory',
});
dbs.defineModel({
name: 'modelA',
datasource: 'datasource',
properties: {
id: {
type: DataType.NUMBER,
primaryKey: true,
},
source: {
type: DataType.STRING,
default: 'modelA',
},
},
relations: {
child: {
type: RelationType.HAS_ONE,
model: 'modelB',
polymorphic: true,
foreignKey: 'parentId',
discriminator: 'parentType',
},
},
});
dbs.defineModel({
name: 'modelB',
datasource: 'datasource',
properties: {
id: {
type: DataType.NUMBER,
primaryKey: true,
},
source: {
type: DataType.STRING,
default: 'modelB',
},
},
relations: {
child: {
type: RelationType.HAS_ONE,
model: 'modelC',
polymorphic: true,
foreignKey: 'parentId',
discriminator: 'parentType',
},
},
});
dbs.defineModel({
name: 'modelC',
datasource: 'datasource',
properties: {
id: {
type: DataType.NUMBER,
primaryKey: true,
},
source: {
type: DataType.STRING,
default: 'modelC',
},
},
});
const aRep = dbs.getRepository('modelA');
const bRep = dbs.getRepository('modelB');
const cRep = dbs.getRepository('modelC');
const a = await aRep.create({});
const b = await bRep.create({parentId: a.id, parentType: 'modelA'});
const c = await cRep.create({parentId: b.id, parentType: 'modelB'});
expect(a).to.be.eql({
id: a.id,
source: 'modelA',
});
expect(b).to.be.eql({
id: b.id,
source: 'modelB',
parentId: a.id,
parentType: 'modelA',
});
expect(c).to.be.eql({
id: c.id,
source: 'modelC',
parentId: b.id,
parentType: 'modelB',
});
const R = dbs.getService(HasOneResolver);
await R.includePolymorphicTo(
[a],
'modelA',
'modelB',
'child',
'parentId',
'parentType',
{include: 'child'},
);
expect(a).to.be.eql({
id: a.id,
source: 'modelA',
child: {
id: b.id,
source: 'modelB',
parentId: a.id,
parentType: 'modelA',
child: {
id: c.id,
source: 'modelC',
parentId: b.id,
parentType: 'modelB',
},
},
});
});
it('does not break the "and" operator of the given "where" clause', async function () {
const dbs = new DatabaseSchema();
dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
dbs.defineModel({name: 'source', datasource: 'datasource'});
dbs.defineModel({name: 'target', datasource: 'datasource'});
const sourceRep = dbs.getRepository('source');
const targetRep = dbs.getRepository('target');
const source = await sourceRep.create({});
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
});
const target1 = await targetRep.create({
featured: false,
parentId: source[DEF_PK],
parentType: 'source',
});
expect(target1).to.be.eql({
[DEF_PK]: target1[DEF_PK],
featured: false,
parentId: source[DEF_PK],
parentType: target1.parentType,
});
const target2 = await targetRep.create({
featured: true,
parentId: source[DEF_PK],
parentType: 'source',
});
expect(target2).to.be.eql({
[DEF_PK]: target2[DEF_PK],
featured: true,
parentId: source[DEF_PK],
parentType: target2.parentType,
});
const R = dbs.getService(HasOneResolver);
await R.includePolymorphicTo(
[source],
'source',
'target',
'child',
'parentId',
'parentType',
{where: {and: [{featured: false}]}},
);
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
[DEF_PK]: target1[DEF_PK],
featured: false,
parentId: source[DEF_PK],
parentType: target1.parentType,
},
});
delete source.child;
await R.includePolymorphicTo(
[source],
'source',
'target',
'child',
'parentId',
'parentType',
{where: {and: [{featured: true}]}},
);
expect(source).to.be.eql({
[DEF_PK]: source[DEF_PK],
child: {
[DEF_PK]: target2[DEF_PK],
featured: true,
parentId: source[DEF_PK],
parentType: target2.parentType,
},
});
});
});
describe('includePolymorphicByRelationName', function () {
it('requires the "entities" parameter to be an array', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "entities" of HasOneResolver.includePolymorphicByRelationName requires ' +
'an Array of Object, but %s was given.',
v,
);
const throwable = v =>
R.includePolymorphicByRelationName(
v,
'sourceName',
'targetName',
'relationName',
'targetRelationName',
);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires elements of the "entities" parameter to be an Object', async function () {
const dbs = new DatabaseSchema();
dbs.defineModel({name: 'source'});
dbs.defineModel({
name: 'target',
relations: {
parent: {
type: RelationType.BELONGS_TO,
polymorphic: true,
},
},
});
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
'an Array of Object, but %s was given.',
v,
);
const throwable = v =>
R.includePolymorphicByRelationName(
[v],
'source',
'target',
'child',
'parent',
);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the "sourceName" parameter to be a non-empty string', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "sourceName" of HasOneResolver.includePolymorphicByRelationName requires ' +
'a non-empty String, but %s was given.',
v,
);
const throwable = v =>
R.includePolymorphicByRelationName(
[],
v,
'targetName',
'relationName',
'targetRelationName',
);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires the "targetName" parameter to be a non-empty string', async function () {
const dbs = new DatabaseSchema();
const R = dbs.getService(HasOneResolver);
const error = v =>
format(
'The parameter "targetName" of HasOneResolver.includePolymorphicByRelationName requires ' +
'a non-empty String, but %s was given.',
v,
);
const throwable = v =>
R.includePolymorphicByRelationName(
[],
'sourceName',
v,
'relationName',
'targetRelationName',
);
await expect(throwable('')).to.be.rejectedWith(error('""'));
await expect(throwable(10)).to.be.rejectedWith(error('10'));
await expect(throwable(true)).to.be.rejectedWith(error('true'));
await expect(throwable(false)).to.be.rejectedWith(error('false'));
await expect(throwable([])).to.be.rejectedWith(error('Array'));
await expect(throwable({})).to.be.rejectedWith(error('Object'));
await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
await expect(throwable(null)).to.be.rejectedWith(error('null'));
});
it('requires