import { Lock } from '../../../src/storage/Lock'; import { LockingResourceStore } from '../../../src/storage/LockingResourceStore'; import { ResourceLocker } from '../../../src/storage/ResourceLocker'; import { ResourceStore } from '../../../src/storage/ResourceStore'; describe('A LockingResourceStore', (): void => { let store: LockingResourceStore; let locker: ResourceLocker; let lock: Lock; let release: () => Promise; let source: ResourceStore; let order: string[]; beforeEach(async(): Promise => { order = []; const delayedResolve = (resolve: () => void, name: string): void => { // `setImmediate` is introduced to make sure the promise doesn't execute immediately setImmediate((): void => { order.push(name); resolve(); }); }; source = { getRepresentation: jest.fn(async(): Promise => new Promise((resolve): any => delayedResolve(resolve, 'getRepresentation'))), addResource: jest.fn(async(): Promise => new Promise((resolve): any => delayedResolve(resolve, 'addResource'))), setRepresentation: jest.fn(async(): Promise => new Promise((resolve): any => delayedResolve(resolve, 'setRepresentation'))), deleteResource: jest.fn(async(): Promise => new Promise((resolve): any => delayedResolve(resolve, 'deleteResource'))), modifyResource: jest.fn(async(): Promise => new Promise((resolve): any => delayedResolve(resolve, 'modifyResource'))), }; release = jest.fn(async(): Promise => order.push('release')); locker = { acquire: jest.fn(async(): Promise => { order.push('acquire'); lock = { release }; return lock; }), }; store = new LockingResourceStore(source, locker); }); it('acquires a lock on the resource when getting it.', async(): Promise => { await store.getRepresentation({ path: 'path' }, null); expect(locker.acquire).toHaveBeenCalledTimes(1); expect(locker.acquire).toHaveBeenLastCalledWith({ path: 'path' }); expect(source.getRepresentation).toHaveBeenCalledTimes(1); expect(lock.release).toHaveBeenCalledTimes(1); expect(order).toEqual([ 'acquire', 'getRepresentation', 'release' ]); }); it('acquires a lock on the container when adding a representation.', async(): Promise => { await store.addResource({ path: 'path' }, null); expect(locker.acquire).toHaveBeenCalledTimes(1); expect(locker.acquire).toHaveBeenLastCalledWith({ path: 'path' }); expect(source.addResource).toHaveBeenCalledTimes(1); expect(lock.release).toHaveBeenCalledTimes(1); expect(order).toEqual([ 'acquire', 'addResource', 'release' ]); }); it('acquires a lock on the resource when setting its representation.', async(): Promise => { await store.setRepresentation({ path: 'path' }, null); expect(locker.acquire).toHaveBeenCalledTimes(1); expect(locker.acquire).toHaveBeenLastCalledWith({ path: 'path' }); expect(source.setRepresentation).toHaveBeenCalledTimes(1); expect(lock.release).toHaveBeenCalledTimes(1); expect(order).toEqual([ 'acquire', 'setRepresentation', 'release' ]); }); it('acquires a lock on the resource when deleting it.', async(): Promise => { await store.deleteResource({ path: 'path' }); expect(locker.acquire).toHaveBeenCalledTimes(1); expect(locker.acquire).toHaveBeenLastCalledWith({ path: 'path' }); expect(source.deleteResource).toHaveBeenCalledTimes(1); expect(lock.release).toHaveBeenCalledTimes(1); expect(order).toEqual([ 'acquire', 'deleteResource', 'release' ]); }); it('acquires a lock on the resource when modifying its representation.', async(): Promise => { await store.modifyResource({ path: 'path' }, null); expect(locker.acquire).toHaveBeenCalledTimes(1); expect(locker.acquire).toHaveBeenLastCalledWith({ path: 'path' }); expect(source.modifyResource).toHaveBeenCalledTimes(1); expect(lock.release).toHaveBeenCalledTimes(1); expect(order).toEqual([ 'acquire', 'modifyResource', 'release' ]); }); it('releases the lock if an error was thrown.', async(): Promise => { source.getRepresentation = async(): Promise => { throw new Error('dummy'); }; await expect(store.getRepresentation({ path: 'path' }, null)).rejects.toThrow('dummy'); expect(locker.acquire).toHaveBeenCalledTimes(1); expect(locker.acquire).toHaveBeenLastCalledWith({ path: 'path' }); expect(lock.release).toHaveBeenCalledTimes(1); expect(order).toEqual([ 'acquire', 'release' ]); }); });