@furystack/repository
Version:
Repository implementation for FuryStack
474 lines • 25.8 kB
JavaScript
import { InMemoryStore, addStore } from '@furystack/core';
import { Injector } from '@furystack/inject';
import { usingAsync } from '@furystack/utils';
import { describe, expect, it, vi } from 'vitest';
import { getDataSetFor, getRepository } from './helpers.js';
import { Repository } from './repository.js';
class TestClass {
id = 1;
value = '';
}
describe('DataSet', () => {
describe('Construction', () => {
it('can be retrieved from an extension method with class', async () => {
await usingAsync(new Injector(), async (i) => {
addStore(i, new InMemoryStore({
model: TestClass,
primaryKey: 'id',
}));
getRepository(i).createDataSet(TestClass, 'id');
const dataSet = getDataSetFor(i, TestClass, 'id');
expect(dataSet.settings.physicalStore.model).toBe(TestClass);
});
});
it('can be retrieved from an extension method with string', async () => {
await usingAsync(new Injector(), async (i) => {
addStore(i, new InMemoryStore({
model: TestClass,
primaryKey: 'id',
}));
getRepository(i).createDataSet(TestClass, 'id');
const dataSet = getDataSetFor(i, TestClass, 'id');
expect(dataSet.settings.physicalStore.model).toBe(TestClass);
});
});
it('Should throw if dataset is not registered through extension', async () => {
await usingAsync(new Injector(), async (i) => {
expect(() => getDataSetFor(i, TestClass, 'id')).toThrowError(`No DataSet found for 'class TestClass {
id = 1;
value = "";
}'`);
});
});
it('Should throw if dataset is not registered through service', async () => {
await usingAsync(new Injector(), async (i) => {
expect(() => i.getInstance(Repository).getDataSetFor(TestClass, 'id'))
.toThrowError(`No DataSet found for 'class TestClass {
id = 1;
value = "";
}'`);
});
});
it('Should throw error on mismatched primary key', async () => {
await usingAsync(new Injector(), async (i) => {
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', {});
expect(() => getDataSetFor(i, TestClass, 'name')).toThrowError();
});
});
});
describe('Authorizers', () => {
describe('Add', () => {
it('should add an entity if no settings are provided', async () => {
await usingAsync(new Injector(), async (i) => {
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id');
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
const result = await dataSet.get(i, 1);
expect(result && result.value).toBe('asd');
});
});
it('should call the add async authorizer and add the entity on pass', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeAdd = vi.fn(async () => ({ isAllowed: true }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeAdd });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
expect(authorizeAdd).toBeCalled();
const added = await dataSet.get(i, 1);
expect(added && added.value).toBe('asd');
});
});
it('should throw if the add authorizer returns a non-valid result and should not add a value to the store', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeAdd = vi.fn(async () => ({ isAllowed: false, message: '...' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeAdd });
const dataSet = getDataSetFor(i, TestClass, 'id');
try {
await dataSet.add(i, { id: 1, value: 'asd' });
throw Error('Should throw');
}
catch (error) {
/** */
expect(authorizeAdd).toBeCalled();
const added = await dataSet.get(i, 1);
expect(added).toBeUndefined();
}
});
});
it('should modify an entity on add, if modifyOnAdd is provided', async () => {
await usingAsync(new Injector(), async (i) => {
const modifyOnAdd = vi.fn(async (options) => ({
...options.entity,
value: options.entity.value.toUpperCase(),
}));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { modifyOnAdd });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
const result = await dataSet.get(i, 1);
expect(modifyOnAdd).toBeCalled();
expect(result && result.value).toBe('ASD');
});
});
it('should call the onEntityAdded callback if an entity has been added', async () => {
await usingAsync(new Injector(), async (i) => {
expect.assertions(1);
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', {});
getRepository(i)
.getDataSetFor(TestClass, 'id')
.subscribe('onEntityAdded', ({ entity }) => {
expect(entity.value).toBe('asd');
});
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
});
});
});
describe('Update', () => {
it('should update an entity if no settings are provided', async () => {
await usingAsync(new Injector(), async (i) => {
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id');
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
await dataSet.update(i, 1, { id: 1, value: 'asd2' });
const result = await dataSet.get(i, 1);
expect(result && result.value).toBe('asd2');
});
});
it('should call the authorizeUpdate authorizer and add the entity on pass', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeUpdate = vi.fn(async () => ({ isAllowed: true }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeUpdate });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
await dataSet.update(i, 1, { id: 1, value: 'asd2' });
expect(authorizeUpdate).toBeCalled();
const added = await dataSet.get(i, 1);
expect(added && added.value).toBe('asd2');
});
});
it('should throw if the authorizeUpdateEntity returns a non-valid result and should not update a value to the store', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeUpdateEntity = vi.fn(async () => ({ isAllowed: false, message: '...' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeUpdateEntity });
const dataSet = getDataSetFor(i, TestClass, 'id');
try {
await dataSet.add(i, { id: 1, value: 'asd' });
await dataSet.update(i, 1, { id: 1, value: 'asd2' });
throw Error('Should throw');
}
catch (error) {
/** */
expect(authorizeUpdateEntity).toBeCalled();
const added = await dataSet.get(i, 1);
expect(added && added.value).toBe('asd');
}
});
});
it('should call the authorizeUpdateEntity authorizer and add the entity on pass', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeUpdateEntity = vi.fn(async () => ({ isAllowed: true }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeUpdateEntity });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
await dataSet.update(i, 1, { id: 1, value: 'asd2' });
expect(authorizeUpdateEntity).toBeCalled();
const added = await dataSet.get(i, 1);
expect(added && added.value).toBe('asd2');
});
});
it('should throw if the authorizeUpdate returns a non-valid result and should not update a value to the store', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeUpdate = vi.fn(async () => ({ isAllowed: false, message: '...' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeUpdate });
const dataSet = getDataSetFor(i, TestClass, 'id');
try {
await dataSet.add(i, { id: 1, value: 'asd' });
await dataSet.update(i, 1, { id: 1, value: 'asd2' });
throw Error('Should throw');
}
catch (error) {
/** */
expect(authorizeUpdate).toBeCalled();
const added = await dataSet.get(i, 1);
expect(added && added.value).toBe('asd');
}
});
});
it('should modify an entity on update, if modifyOnAdd is provided', async () => {
await usingAsync(new Injector(), async (i) => {
const modifyOnUpdate = vi.fn(async (options) => ({
...options.entity,
value: options.entity.value?.toUpperCase(),
}));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { modifyOnUpdate });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
await dataSet.update(i, 1, { id: 1, value: 'asd2' });
const result = await dataSet.get(i, 1);
expect(modifyOnUpdate).toBeCalled();
expect(result && result.value).toBe('ASD2');
});
});
it('should publish to the onEntityUpdated observable if an entity has been updated', async () => {
await usingAsync(new Injector(), async (i) => {
expect.assertions(1);
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id');
getRepository(i)
.getDataSetFor(TestClass, 'id')
.subscribe('onEntityUpdated', ({ change }) => {
expect(change).toEqual({ id: 1, value: 'asd2' });
});
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
await dataSet.update(i, 1, { id: 1, value: 'asd2' });
});
});
});
describe('Count', () => {
it('should return the count if no settings are provided', async () => {
await usingAsync(new Injector(), async (i) => {
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id');
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
const result = await dataSet.count(i);
expect(result).toBe(1);
});
});
it('should return the count if authorizeGet returns valid result', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeGet = vi.fn(async () => ({ isAllowed: true, message: '' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeGet });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
const result = await dataSet.count(i);
expect(result).toBe(1);
});
});
it('should throw if authorizeGet returns invalid result', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeGet = vi.fn(async () => ({ isAllowed: false, message: ':(' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeGet });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
try {
await dataSet.count(i);
throw Error('Should throw');
}
catch (error) {
/** */
}
});
});
});
});
describe('filter', () => {
it('should return the unfiltered result if no settings are provided', async () => {
await usingAsync(new Injector(), async (i) => {
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id');
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
const result = await dataSet.find(i, {});
expect(result.length).toBe(1);
});
});
it('should return the unfiltered result if authorizeGet returns valid result', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeGet = vi.fn(async () => ({ isAllowed: true, message: '' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeGet });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
const result = await dataSet.find(i, {});
expect(result.length).toBe(1);
});
});
it('should throw if authorizeGet returns invalid result', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeGet = vi.fn(async () => ({ isAllowed: false, message: ':(' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeGet });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
try {
await dataSet.find(i, {});
throw Error('Should throw');
}
catch (error) {
/** */
}
});
});
});
describe('get', () => {
it('should return the entity if no settings are provided', async () => {
await usingAsync(new Injector(), async (i) => {
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id');
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
const result = await dataSet.get(i, 1);
expect(result && result.id).toBe(1);
});
});
it('should return the entity if authorizeGet returns valid result', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeGet = vi.fn(async () => ({ isAllowed: true, message: '' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeGet });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
const result = await dataSet.get(i, 1);
expect(result && result.id).toBe(1);
});
});
it('should throw if authorizeGet returns invalid result', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeGet = vi.fn(async () => ({ isAllowed: false, message: ':(' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeGet });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
try {
await dataSet.get(i, 1);
throw Error('Should throw');
}
catch (error) {
/** */
}
});
});
it('should return the entity if authorizeGetEntity returns valid result', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeGetEntity = vi.fn(async () => ({ isAllowed: true, message: '' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeGetEntity });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
const result = await dataSet.get(i, 1);
expect(result && result.id).toBe(1);
});
});
it('should throw if authorizeGetEntity returns invalid result', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeGetEntity = vi.fn(async () => ({ isAllowed: false, message: ':(' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeGetEntity });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
try {
await dataSet.get(i, 1);
throw Error('Should throw');
}
catch (error) {
/** */
}
});
});
});
describe('remove', () => {
it('should remove the entity if no settings are provided', async () => {
await usingAsync(new Injector(), async (i) => {
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id');
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
await dataSet.remove(i, 1);
const countValue = await dataSet.count(i);
expect(countValue).toBe(0);
});
});
it('should remove the entity if authorizeRemove returns valid result', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeRemove = vi.fn(async () => ({ isAllowed: true, message: '' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeRemove });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
await dataSet.remove(i, 1);
const count = await dataSet.count(i);
expect(count).toBe(0);
});
});
it('should throw if authorizeRemove returns invalid result', async () => {
await usingAsync(new Injector(), async (i) => {
const authorizeRemove = vi.fn(async () => ({ isAllowed: false, message: ':(' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authorizeRemove });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
try {
await dataSet.remove(i, 1);
throw Error('Should throw');
}
catch (error) {
/** */
}
const count = await dataSet.count(i);
expect(count).toBe(1);
});
});
it('should remove the entity if authroizeRemoveEntity returns valid result', async () => {
await usingAsync(new Injector(), async (i) => {
const authroizeRemoveEntity = vi.fn(async () => ({ isAllowed: true, message: '' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authroizeRemoveEntity });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
await dataSet.remove(i, 1);
const count = await dataSet.count(i);
expect(count).toBe(0);
});
});
it('should throw if authroizeRemoveEntity returns invalid result', async () => {
await usingAsync(new Injector(), async (i) => {
const authroizeRemoveEntity = vi.fn(async () => ({ isAllowed: false, message: ':(' }));
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id', { authroizeRemoveEntity });
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
try {
await dataSet.remove(i, 1);
throw Error('Should throw');
}
catch (error) {
/** */
}
const count = await dataSet.count(i);
expect(count).toBe(1);
});
});
it('should publish to the onEntityRemoved observable if an entity has been removed', async () => {
await usingAsync(new Injector(), async (i) => {
expect.assertions(1);
addStore(i, new InMemoryStore({ model: TestClass, primaryKey: 'id' }));
getRepository(i).createDataSet(TestClass, 'id');
getRepository(i)
.getDataSetFor(TestClass, 'id')
.subscribe('onEntityRemoved', ({ key }) => {
expect(key).toEqual(1);
});
const dataSet = getDataSetFor(i, TestClass, 'id');
await dataSet.add(i, { id: 1, value: 'asd' });
await dataSet.remove(i, 1);
});
});
});
});
//# sourceMappingURL=dataset.spec.js.map