UNPKG

@e22m4u/js-repository

Version:

Реализация репозитория для работы с базами данных в Node.js

1,491 lines (1,439 loc) 147 kB
import {expect} from 'chai'; import {format} from '@e22m4u/js-format'; import {MemoryAdapter} from './memory-adapter.js'; import {DataType} from '../../definition/index.js'; import {DatabaseSchema} from '../../database-schema.js'; import {DEFAULT_PRIMARY_KEY_PROPERTY_NAME as DEF_PK} from '../../definition/index.js'; describe('MemoryAdapter', function () { describe('_getTableOrCreate', function () { it('returns an existing table or creates a new', function () { const dbs = new DatabaseSchema(); dbs.defineModel({name: 'model'}); const A = dbs.getService(MemoryAdapter); const table = A._getTableOrCreate('model'); expect(table).to.be.instanceof(Map); const sameTable = A._getTableOrCreate('model'); expect(table).to.be.eq(sameTable); }); it('uses a model name to find a table, even a table name is specified', function () { const dbs = new DatabaseSchema(); dbs.defineModel({ name: 'myModel', tableName: 'myTable', }); const A = dbs.getService(MemoryAdapter); const table = A._getTableOrCreate('myModel'); expect(table).to.be.instanceof(Map); const sameTable = A._getTableOrCreate('myModel'); expect(table).to.be.eq(sameTable); }); it('stores a table by specified table name', function () { const dbs = new DatabaseSchema(); dbs.defineModel({ name: 'myModel', tableName: 'myTable', }); const A = dbs.getService(MemoryAdapter); const table = A._getTableOrCreate('myModel'); expect(table).to.be.instanceof(Map); const sameTable = A._tables.get('myTable'); expect(table).to.be.eq(sameTable); }); }); describe('_genNextIdValue', function () { it('returns an unique number identifier', function () { const dbs = new DatabaseSchema(); dbs.defineModel({name: 'model'}); const A = dbs.getService(MemoryAdapter); const id1 = A._genNextIdValue('model', DEF_PK); const id2 = A._genNextIdValue('model', DEF_PK); const id3 = A._genNextIdValue('model', DEF_PK); expect(id1).to.be.eq(1); expect(id2).to.be.eq(2); expect(id3).to.be.eq(3); }); }); describe('create', function () { it('skips existing values when generating a new identifier for a default primary key', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', }); const adapter = new MemoryAdapter(dbs.container, {}); const result1 = await adapter.create('model', {}); const result2 = await adapter.create('model', {}); const result3 = await adapter.create('model', {[DEF_PK]: 3}); const result4 = await adapter.create('model', {}); expect(result1).to.be.eql({[DEF_PK]: 1}); expect(result2).to.be.eql({[DEF_PK]: 2}); expect(result3).to.be.eql({[DEF_PK]: 3}); expect(result4).to.be.eql({[DEF_PK]: 4}); }); it('skips existing values when generating a new identifier for a specified primary key', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.NUMBER, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const result1 = await adapter.create('model', {}); const result2 = await adapter.create('model', {}); const result3 = await adapter.create('model', {myId: 3}); const result4 = await adapter.create('model', {}); expect(result1).to.be.eql({myId: 1}); expect(result2).to.be.eql({myId: 2}); expect(result3).to.be.eql({myId: 3}); expect(result4).to.be.eql({myId: 4}); }); it('generates a new identifier when a value of a primary key has not provided', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = {foo: 'string', bar: 10}; const created = await adapter.create('model', input); const idValue = created[DEF_PK]; expect(created).to.be.eql({...input, [DEF_PK]: idValue}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({...input, [DEF_PK]: idValue}); }); it('generates a new identifier when a value of a primary key is undefined', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = { [DEF_PK]: undefined, foo: 'string', bar: 10, }; const created = await adapter.create('model', input); const idValue = created[DEF_PK]; expect(created).to.be.eql({...input, [DEF_PK]: idValue}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({...input, [DEF_PK]: idValue}); }); it('generates a new identifier when a value of a primary key is null', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = { [DEF_PK]: null, foo: 'string', bar: 10, }; const created = await adapter.create('model', input); const idValue = created[DEF_PK]; expect(created).to.be.eql({...input, [DEF_PK]: idValue}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({...input, [DEF_PK]: idValue}); }); it('generates a new identifier when a value of a primary key is an empty string', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = { [DEF_PK]: '', foo: 'string', bar: 10, }; const created = await adapter.create('model', input); const idValue = created[DEF_PK]; expect(idValue).to.be.not.eq(''); expect(created).to.be.eql({...input, [DEF_PK]: idValue}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({...input, [DEF_PK]: idValue}); }); it('generates a new identifier when a value of a primary key is zero', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = { [DEF_PK]: 0, foo: 'string', bar: 10, }; const created = await adapter.create('model', input); const idValue = created[DEF_PK]; expect(idValue).to.be.not.eq(0); expect(created).to.be.eql({...input, [DEF_PK]: idValue}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({...input, [DEF_PK]: idValue}); }); it('generates a new identifier for a primary key of a "number" type', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.NUMBER, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const result1 = await adapter.create('model', {}); const result2 = await adapter.create('model', {}); const result3 = await adapter.create('model', {}); expect(result1).to.be.eql({myId: 1}); expect(result2).to.be.eql({myId: 2}); expect(result3).to.be.eql({myId: 3}); }); it('generates a new identifier for a primary key of an "any" type', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.ANY, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const result1 = await adapter.create('model', {}); const result2 = await adapter.create('model', {}); const result3 = await adapter.create('model', {}); expect(result1).to.be.eql({myId: 1}); expect(result2).to.be.eql({myId: 2}); expect(result3).to.be.eql({myId: 3}); }); it('throws an error when generating a new value for a primary key of a "string" type', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.STRING, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const promise = adapter.create('model', {foo: 'string', bar: 10}); await expect(promise).to.be.rejectedWith( 'The memory adapter able to generate only Number identifiers, ' + 'but the primary key "myId" of the model "model" is defined as String. ' + 'Do provide your own value for the "myId" property, or change the type ' + 'in the primary key definition to a Number that will be ' + 'generated automatically.', ); }); it('throws an error when generating a new value for a primary key of a "boolean" type', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.BOOLEAN, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const promise = adapter.create('model', {foo: 'string', bar: 10}); await expect(promise).to.be.rejectedWith( 'The memory adapter able to generate only Number identifiers, ' + 'but the primary key "myId" of the model "model" is defined as Boolean. ' + 'Do provide your own value for the "myId" property, or change the type ' + 'in the primary key definition to a Number that will be ' + 'generated automatically.', ); }); it('throws an error when generating a new value for a primary key of an "array" type', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.ARRAY, itemType: DataType.NUMBER, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const promise = adapter.create('model', {}); await expect(promise).to.be.rejectedWith( 'The memory adapter able to generate only Number identifiers, ' + 'but the primary key "myId" of the model "model" is defined as Array. ' + 'Do provide your own value for the "myId" property, or change the type ' + 'in the primary key definition to a Number that will be ' + 'generated automatically.', ); }); it('throws an error when generating a new value for a primary key of an "object" type', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.OBJECT, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const promise = adapter.create('model', {}); await expect(promise).to.be.rejectedWith( 'The memory adapter able to generate only Number identifiers, ' + 'but the primary key "myId" of the model "model" is defined as Object. ' + 'Do provide your own value for the "myId" property, or change the type ' + 'in the primary key definition to a Number that will be ' + 'generated automatically.', ); }); it('allows to specify an identifier value for a new item', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const idValue = 5; const input = {foo: 'string', bar: 10}; const created = await adapter.create('model', { [DEF_PK]: idValue, ...input, }); expect(created).to.be.eql({[DEF_PK]: idValue, ...input}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({[DEF_PK]: idValue, ...input}); }); it('throws an error if a given identifier value already exists', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const created = await adapter.create('model', {foo: 'string'}); const promise = adapter.create('model', created); await expect(promise).to.be.rejectedWith( format( 'The value 1 of the primary key %v already exists in the model "model".', DEF_PK, ), ); }); it('sets default values if they are not provided for a new item', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.NUMBER, default: 10, }, bar: { type: DataType.STRING, default: 'string', }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const created = await adapter.create('model', {}); const idValue = created[DEF_PK]; const defaults = {foo: 10, bar: 'string'}; expect(created).to.be.eql({[DEF_PK]: idValue, ...defaults}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({[DEF_PK]: idValue, ...defaults}); }); it('sets default values for properties provided with an undefined value', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.NUMBER, default: 1, }, bar: { type: DataType.NUMBER, default: 2, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const created = await adapter.create('model', {foo: undefined}); const idValue = created[DEF_PK]; const defaults = {foo: 1, bar: 2}; expect(created).to.be.eql({[DEF_PK]: idValue, ...defaults}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({[DEF_PK]: idValue, ...defaults}); }); it('sets default values for properties provided with a null value', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.NUMBER, default: 1, }, bar: { type: DataType.NUMBER, default: 2, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const created = await adapter.create('model', {foo: null}); const idValue = created[DEF_PK]; const defaults = {foo: 1, bar: 2}; expect(created).to.be.eql({[DEF_PK]: idValue, ...defaults}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({[DEF_PK]: idValue, ...defaults}); }); it('uses a specified column name for a primary key', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.NUMBER, primaryKey: true, columnName: 'bar', }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const created = await adapter.create('model', {}); expect(created).to.be.eql({foo: created.foo}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(created.foo); expect(tableData).to.be.eql({bar: created.foo}); }); it('uses a specified column name for a regular property', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.NUMBER, columnName: 'bar', }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const created = await adapter.create('model', {foo: 10}); const idValue = created[DEF_PK]; expect(created).to.be.eql({[DEF_PK]: idValue, foo: 10}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({[DEF_PK]: idValue, bar: 10}); }); it('uses a specified column name for a regular property with a default value', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.NUMBER, columnName: 'bar', default: 10, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const created = await adapter.create('model', {}); const idValue = created[DEF_PK]; expect(created).to.be.eql({[DEF_PK]: idValue, foo: 10}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({[DEF_PK]: idValue, bar: 10}); }); it('uses a short form of a fields clause to filter a return value', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = {foo: 'string', bar: 10}; const filter = {fields: 'foo'}; const result = await adapter.create('model', input, filter); expect(result).to.be.eql({[DEF_PK]: result[DEF_PK], foo: input.foo}); }); it('uses a full form of a fields clause to filter a return value', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, baz: DataType.BOOLEAN, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = {foo: 'string', bar: 10, baz: true}; const filter = {fields: ['foo', 'bar']}; const result = await adapter.create('model', input, filter); expect(result).to.be.eql({ [DEF_PK]: result[DEF_PK], foo: input.foo, bar: input.bar, }); }); it('a fields clause uses property names instead of column names', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.STRING, columnName: 'fooCol', }, bar: { type: DataType.NUMBER, columnName: 'barCol', }, baz: { type: DataType.BOOLEAN, columnName: 'bazCol', }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = {foo: 'string', bar: 10, baz: true}; const filter = {fields: ['foo', 'bar']}; const result = await adapter.create('model', input, filter); expect(result).to.be.eql({ [DEF_PK]: result[DEF_PK], foo: input.foo, bar: input.bar, }); }); }); describe('replaceById', function () { it('removes properties when replacing an item by a given identifier', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.NUMBER, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = {foo: 1, bar: 2}; const created = await adapter.create('model', input); const idValue = created[DEF_PK]; expect(created).to.be.eql({[DEF_PK]: idValue, ...input}); const replaced = await adapter.replaceById('model', idValue, {foo: 2}); expect(replaced).to.be.eql({[DEF_PK]: idValue, foo: 2}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({[DEF_PK]: idValue, foo: 2}); }); it('ignores identifier value in a given data in case of a default primary key', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', }); const adapter = new MemoryAdapter(dbs.container, {}); const createdModelData = await adapter.create('model', {[DEF_PK]: 10}); expect(createdModelData).to.be.eql({[DEF_PK]: 10}); const table = adapter._getTableOrCreate('model'); const createdTableData = table.get(10); expect(createdTableData).to.be.eql({[DEF_PK]: 10}); const replacedModelData = await adapter.replaceById('model', 10, { [DEF_PK]: 20, }); expect(replacedModelData).to.be.eql({[DEF_PK]: 10}); const replacedTableData = table.get(10); expect(replacedTableData).to.be.eql({[DEF_PK]: 10}); }); it('ignores identifier value in a given data in case of a specified primary key', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.NUMBER, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const createdModelData = await adapter.create('model', {myId: 10}); expect(createdModelData).to.be.eql({myId: 10}); const table = adapter._getTableOrCreate('model'); const createdTableData = table.get(10); expect(createdTableData).to.be.eql({myId: 10}); const replacedModelData = await adapter.replaceById('model', 10, { myId: 20, }); expect(replacedModelData).to.be.eql({myId: 10}); const replacedTableData = table.get(10); expect(replacedTableData).to.be.eql({myId: 10}); }); it('sets a default values for removed properties when replacing an item', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.NUMBER, default: 1, }, bar: { type: DataType.NUMBER, default: 2, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const created = await adapter.create('model', {}); const idValue = created[DEF_PK]; const defaults = {foo: 1, bar: 2}; expect(created).to.be.eql({[DEF_PK]: idValue, ...defaults}); const replacing = {foo: 2}; const replaced = await adapter.replaceById('model', idValue, replacing); expect(replaced).to.be.eql({ [DEF_PK]: idValue, ...defaults, ...replacing, }); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({ [DEF_PK]: idValue, ...defaults, ...replacing, }); }); it('sets a default values for replaced properties with an undefined value', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.NUMBER, default: 1, }, bar: { type: DataType.NUMBER, default: 2, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const created = await adapter.create('model', {}); const idValue = created[DEF_PK]; const defaults = {foo: 1, bar: 2}; expect(created).to.be.eql({[DEF_PK]: idValue, ...defaults}); const replaced = await adapter.replaceById('model', idValue, { foo: undefined, }); expect(replaced).to.be.eql({[DEF_PK]: idValue, ...defaults}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({[DEF_PK]: idValue, ...defaults}); }); it('sets a default values for replaced properties with a null value', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.NUMBER, default: 1, }, bar: { type: DataType.NUMBER, default: 2, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const created = await adapter.create('model', {}); const idValue = created[DEF_PK]; const defaults = {foo: 1, bar: 2}; expect(created).to.be.eql({[DEF_PK]: idValue, ...defaults}); const replaced = await adapter.replaceById('model', idValue, { foo: null, }); expect(replaced).to.be.eql({[DEF_PK]: idValue, ...defaults}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({[DEF_PK]: idValue, ...defaults}); }); it('throws an error if a given identifier does not exist', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.NUMBER, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const promise = adapter.replaceById('model', 1, {foo: 2}); await expect(promise).to.be.rejectedWith( format( 'The value 1 of the primary key %v does not exist in the model "model".', DEF_PK, ), ); }); it('uses a specified column name for a primary key', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.NUMBER, primaryKey: true, columnName: 'qux', }, bar: DataType.NUMBER, baz: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = {bar: 1, baz: 2}; const createdModelData = await adapter.create('model', input); expect(createdModelData).to.be.eql({ foo: createdModelData.foo, ...input, }); const table = adapter._getTableOrCreate('model'); const createdTableData = table.get(createdModelData.foo); expect(createdTableData).to.be.eql({ qux: createdModelData.foo, ...input, }); const replacing = {bar: 2}; const replacedModelData = await adapter.replaceById( 'model', createdModelData.foo, replacing, ); expect(replacedModelData).to.be.eql({ foo: createdModelData.foo, ...replacing, }); const replacedTableData = table.get(createdModelData.foo); expect(replacedTableData).to.be.eql({ qux: createdModelData.foo, ...replacing, }); }); it('uses a specified column name for a regular property', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.NUMBER, columnName: 'baz', }, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = {foo: 1, bar: 2}; const createdModelData = await adapter.create('model', input); const idValue = createdModelData[DEF_PK]; expect(createdModelData).to.be.eql({[DEF_PK]: idValue, ...input}); const table = adapter._getTableOrCreate('model'); const createdTableData = table.get(idValue); expect(createdTableData).to.be.eql({ [DEF_PK]: idValue, baz: input.foo, bar: input.bar, }); const replacing = {foo: 2}; const replacedModelData = await adapter.replaceById( 'model', idValue, replacing, ); expect(replacedModelData).to.be.eql({[DEF_PK]: idValue, ...replacing}); const replacedTableData = table.get(idValue); expect(replacedTableData).to.be.eql({ [DEF_PK]: idValue, baz: replacing.foo, }); }); it('uses a specified column name for a regular property with a default value', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.NUMBER, columnName: 'baz', default: 1, }, bar: { type: DataType.NUMBER, default: 2, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const createdModelData = await adapter.create('model', {}); const idValue = createdModelData[DEF_PK]; const defaults = {foo: 1, bar: 2}; expect(createdModelData).to.be.eql({[DEF_PK]: idValue, ...defaults}); const table = adapter._getTableOrCreate('model'); const createdTableData = table.get(idValue); expect(createdTableData).to.be.eql({ [DEF_PK]: idValue, baz: defaults.foo, bar: defaults.bar, }); const replacing = {foo: 2}; const replacedModelData = await adapter.replaceById( 'model', idValue, replacing, ); expect(replacedModelData).to.be.eql({ [DEF_PK]: idValue, foo: replacing.foo, bar: defaults.bar, }); const replacedTableData = table.get(idValue); expect(replacedTableData).to.be.eql({ [DEF_PK]: idValue, baz: replacing.foo, bar: defaults.bar, }); }); it('allows to specify a short form of a fields clause to filter a return value', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = {foo: 'string', bar: 10}; const createdModelData = await adapter.create('model', input); const idValue = createdModelData[DEF_PK]; expect(createdModelData).to.be.eql({[DEF_PK]: idValue, ...input}); const table = adapter._getTableOrCreate('model'); const createdTableData = table.get(idValue); expect(createdTableData).to.be.eql({[DEF_PK]: idValue, ...input}); const replacedModelData = await adapter.replaceById( 'model', idValue, input, {fields: 'foo'}, ); expect(replacedModelData).to.be.eql({[DEF_PK]: idValue, foo: input.foo}); const replacedTableData = table.get(idValue); expect(replacedTableData).to.be.eql({[DEF_PK]: idValue, ...input}); }); it('allows to specify a full form of a fields clause to filter a return value', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, baz: DataType.BOOLEAN, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = {foo: 'string', bar: 10, baz: true}; const createdModelData = await adapter.create('model', input); const idValue = createdModelData[DEF_PK]; expect(createdModelData).to.be.eql({[DEF_PK]: idValue, ...input}); const table = adapter._getTableOrCreate('model'); const createdTableData = table.get(idValue); expect(createdTableData).to.be.eql({[DEF_PK]: idValue, ...input}); const replacedModelData = await adapter.replaceById( 'model', idValue, input, {fields: ['foo', 'bar']}, ); expect(replacedModelData).to.be.eql({ [DEF_PK]: idValue, foo: input.foo, bar: input.bar, }); const replacedTableData = table.get(idValue); expect(replacedTableData).to.be.eql({[DEF_PK]: idValue, ...input}); }); it('a fields clause uses property names instead of column names', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: { type: DataType.STRING, columnName: 'fooCol', }, bar: { type: DataType.NUMBER, columnName: 'barCol', }, baz: { type: DataType.BOOLEAN, columnName: 'bazCol', }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = {foo: 'string', bar: 10, baz: true}; const createdModelData = await adapter.create('model', input); const idValue = createdModelData[DEF_PK]; expect(createdModelData).to.be.eql({[DEF_PK]: idValue, ...input}); const table = adapter._getTableOrCreate('model'); const createdTableData = table.get(idValue); expect(createdTableData).to.be.eql({ [DEF_PK]: idValue, fooCol: input.foo, barCol: input.bar, bazCol: input.baz, }); const replacedModelData = await adapter.replaceById( 'model', idValue, input, {fields: ['foo', 'bar']}, ); expect(replacedModelData).to.be.eql({ [DEF_PK]: idValue, foo: input.foo, bar: input.bar, }); const replacedTableData = table.get(idValue); expect(replacedTableData).to.be.eql({ [DEF_PK]: idValue, fooCol: input.foo, barCol: input.bar, bazCol: input.baz, }); }); }); describe('replaceOrCreate', function () { it('generates a new identifier when a value of a primary key has not provided', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = {foo: 'string', bar: 10}; const created = await adapter.replaceOrCreate('model', input); const idValue = created[DEF_PK]; expect(created).to.be.eql({...input, [DEF_PK]: idValue}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({...input, [DEF_PK]: idValue}); }); it('generates a new identifier when a value of a primary key is undefined', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = { [DEF_PK]: undefined, foo: 'string', bar: 10, }; const created = await adapter.replaceOrCreate('model', input); const idValue = created[DEF_PK]; expect(created).to.be.eql({...input, [DEF_PK]: idValue}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({...input, [DEF_PK]: idValue}); }); it('generates a new identifier when a value of a primary key is null', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = { [DEF_PK]: null, foo: 'string', bar: 10, }; const created = await adapter.replaceOrCreate('model', input); const idValue = created[DEF_PK]; expect(created).to.be.eql({...input, [DEF_PK]: idValue}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({...input, [DEF_PK]: idValue}); }); it('generates a new identifier when a value of a primary key is an empty string', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = { [DEF_PK]: '', foo: 'string', bar: 10, }; const created = await adapter.replaceOrCreate('model', input); const idValue = created[DEF_PK]; expect(idValue).to.be.not.eq(''); expect(created).to.be.eql({...input, [DEF_PK]: idValue}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({...input, [DEF_PK]: idValue}); }); it('generates a new identifier when a value of a primary key is zero', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { foo: DataType.STRING, bar: DataType.NUMBER, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const input = { [DEF_PK]: 0, foo: 'string', bar: 10, }; const created = await adapter.replaceOrCreate('model', input); const idValue = created[DEF_PK]; expect(idValue).to.be.not.eq(0); expect(created).to.be.eql({...input, [DEF_PK]: idValue}); const table = adapter._getTableOrCreate('model'); const tableData = table.get(idValue); expect(tableData).to.be.eql({...input, [DEF_PK]: idValue}); }); it('generates a new identifier for a primary key of a "number" type', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.NUMBER, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const result1 = await adapter.replaceOrCreate('model', {}); const result2 = await adapter.replaceOrCreate('model', {}); const result3 = await adapter.replaceOrCreate('model', {}); expect(result1).to.be.eql({myId: 1}); expect(result2).to.be.eql({myId: 2}); expect(result3).to.be.eql({myId: 3}); }); it('generates a new identifier for a primary key of an "any" type', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.ANY, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const result1 = await adapter.replaceOrCreate('model', {}); const result2 = await adapter.replaceOrCreate('model', {}); const result3 = await adapter.replaceOrCreate('model', {}); expect(result1).to.be.eql({myId: 1}); expect(result2).to.be.eql({myId: 2}); expect(result3).to.be.eql({myId: 3}); }); it('throws an error when generating a new value for a primary key of a "string" type', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.STRING, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const promise = adapter.replaceOrCreate('model', { foo: 'string', bar: 10, }); await expect(promise).to.be.rejectedWith( 'The memory adapter able to generate only Number identifiers, ' + 'but the primary key "myId" of the model "model" is defined as String. ' + 'Do provide your own value for the "myId" property, or change the type ' + 'in the primary key definition to a Number that will be ' + 'generated automatically.', ); }); it('throws an error when generating a new value for a primary key of a "boolean" type', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.BOOLEAN, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const promise = adapter.replaceOrCreate('model', { foo: 'string', bar: 10, }); await expect(promise).to.be.rejectedWith( 'The memory adapter able to generate only Number identifiers, ' + 'but the primary key "myId" of the model "model" is defined as Boolean. ' + 'Do provide your own value for the "myId" property, or change the type ' + 'in the primary key definition to a Number that will be ' + 'generated automatically.', ); }); it('throws an error when generating a new value for a primary key of an "array" type', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.ARRAY, itemType: DataType.NUMBER, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const promise = adapter.replaceOrCreate('model', {}); await expect(promise).to.be.rejectedWith( 'The memory adapter able to generate only Number identifiers, ' + 'but the primary key "myId" of the model "model" is defined as Array. ' + 'Do provide your own value for the "myId" property, or change the type ' + 'in the primary key definition to a Number that will be ' + 'generated automatically.', ); }); it('throws an error when generating a new value for a primary key of an "object" type', async function () { const dbs = new DatabaseSchema(); dbs.defineDatasource({ name: 'memory', adapter: 'memory', }); dbs.defineModel({ name: 'model', datasource: 'memory', properties: { myId: { type: DataType.OBJECT, primaryKey: true, }, }, }); const adapter = new MemoryAdapter(dbs.container, {}); const promise = adapter.replaceOrCreate('model', {}); await expect(promise).to.be.rejectedWith( 'The memory adapter able to generate only Number identifiers, ' + 'but the primary key