Skip to content

Commit

Permalink
cache
Browse files Browse the repository at this point in the history
improve unit tests
  • Loading branch information
JohnDoePlusPlus committed Nov 18, 2019
1 parent 20437a4 commit 5c66cec
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 92 deletions.
4 changes: 2 additions & 2 deletions lib/cache/cacheProvider/InstanceCacheProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export class InstanceCacheProvider<K = any> implements CacheProvider<K> {
constructor(private readonly cacheFactory: CacheFactory) { }

public get(instance: ClassType): Cache<K> {
const hasCache = !this.instanceCaches.has(instance);
if (hasCache) {
const hasCache = this.instanceCaches.has(instance);
if (!hasCache) {
const cache = this.cacheFactory.create();
this.instanceCaches.set(instance, cache);
}
Expand Down
17 changes: 11 additions & 6 deletions lib/cache/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { StorageFactory } from './storages/factory';
import { CacheFactory } from './caches/factory';
import { HashService } from '../utils/hash';
import { CacheProviderFactory } from './cacheProvider/factory';
import { CacheProvider } from './cacheProvider/CacheProvider';

export { CacheOptions };

Expand All @@ -23,12 +24,7 @@ export function cache(
): MethodDecorator {

const { timeout, options } = parseParameters(timeoutOrOptions, optionsOrVoid);

const hashService = new HashService();
const expirationFactory = new ExpirationFactory(timeout, options.expiration);
const storageFactory = new StorageFactory(options.size, options.storage);
const cacheFactory = new CacheFactory(hashService, expirationFactory, storageFactory);
const cacheProvider = new CacheProviderFactory(options.scope, cacheFactory).create();
const cacheProvider = createCacheProvider(timeout, options);

return function (_: any, __: any, descriptor: PropertyDescriptor) {
const method = descriptor.value;
Expand Down Expand Up @@ -67,3 +63,12 @@ function parseParameters(
options: { ...DEFAULT_OPTIONS, ...timeoutOrOptions },
};
}

function createCacheProvider(timeout: number, options: CacheOptions): CacheProvider {
const hashService = new HashService();
const expirationFactory = new ExpirationFactory(timeout, options.expiration);
const storageFactory = new StorageFactory(options.size, options.storage);
const cacheFactory = new CacheFactory(hashService, expirationFactory, storageFactory);

return new CacheProviderFactory(options.scope, cacheFactory).create();
}
6 changes: 3 additions & 3 deletions test/cache/cache.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { delay, executionTime } from '../utils';
describe('@cache', () => {

const delayTime = 8;
const timePrecision = 2;
const timePrecision = 3;

const factory = (timeout: number, options?: CacheOptions) => {
class Test {
Expand Down Expand Up @@ -123,7 +123,7 @@ describe('@cache', () => {

it('should return same value as without decorator', async () => {
const instance = new (factory(1000));
expect(await instance.method(42)).to.be.equals(43);
expect(await instance.method(42)).to.equals(43);
});

describe('result should be same at multiple calls', () => {
Expand All @@ -133,7 +133,7 @@ describe('@cache', () => {
const promises = Array.from({ length: 10 }, () => instance.method(42));
const values = await Promise.all(promises);

expect(new Set(values).size).to.be.equals(1);
expect(new Set(values).size).to.equals(1);
});

});
Expand Down
22 changes: 17 additions & 5 deletions test/cache/cacheProvider/ClassCacheProvider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ describe('@cache ClassCacheProvider', () => {

it('should create', () => expect(service).to.be.instanceOf(ClassCacheProvider));

it('should init cache property to null', () => expect(service['cache']).to.be.null);

});

describe('get', () => {
Expand All @@ -29,16 +27,30 @@ describe('@cache ClassCacheProvider', () => {
const cacheInstance = {} as any;
cacheFactoryStub.create.returns(cacheInstance);

expect(service.get()).to.be.equals(cacheInstance);
service.get();

expect(cacheFactoryStub.create.calledOnce).to.be.true;
expect(service['cache']).to.be.equals(cacheInstance);
});

it('should return instance create from cahceFactory', () => {
const cacheInstance = {} as any;
cacheFactoryStub.create.returns(cacheInstance);

expect(service.get()).to.equals(cacheInstance);
});

it('should return existent instance of cache if is not first call', () => {
const response = service['cache'] = {} as any;

expect(service.get()).to.equals(response);
});

it('should not call CacheFactory.create if instance of cache service exists', () => {
service['cache'] = {} as any;

service.get();

expect(cacheFactoryStub.create.called).to.be.false;
expect(service.get()).to.be.equals(response);
});

});
Expand Down
6 changes: 3 additions & 3 deletions test/cache/cacheProvider/InstanceCacheProvider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ describe('@cache InstanceCacheProvider', () => {
const instance = {} as any;
cacheFactoryStub.create.returns(result);

expect(service.get(instance)).to.be.equals(result);
expect(service['instanceCaches'].get(instance)).to.be.equals(result);
expect(service.get(instance)).to.equals(result);
expect(service['instanceCaches'].get(instance)).to.equals(result);
expect(cacheFactoryStub.create.calledOnce).to.be.true;
});

Expand All @@ -42,7 +42,7 @@ describe('@cache InstanceCacheProvider', () => {
const instance = {} as any;
service['instanceCaches'].set(instance, result);

expect(service.get(instance)).to.be.equals(result);
expect(service.get(instance)).to.equals(result);
expect(cacheFactoryStub.create.called).to.be.false;
});

Expand Down
69 changes: 67 additions & 2 deletions test/cache/caches/Cache.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ describe('@cache Cache', () => {
await service.set(['key'], 'value');

expect(storageStub.set.calledOnce).to.be.true;
});

it('should call storage.set to store given hashed key with correct parameters', async () => {
const key = 'key';
hashStub.calculate.returns(key);

await service.set(['key'], 'value');

expect(storageStub.set.calledWithExactly(key, 'value')).to.be.true;
});

Expand All @@ -54,6 +62,14 @@ describe('@cache Cache', () => {
await service.set(['key'], 'value');

expect(expirationStub.add.calledOnce).to.be.true;
});

it('should call expiration.add with correct parameters', async () => {
const key = 'key';
hashStub.calculate.returns(key);

await service.set(['key'], 'value');

expect(expirationStub.add.calledWith(key, sinon.match.func)).to.be.true;
});

Expand All @@ -70,6 +86,18 @@ describe('@cache Cache', () => {
await callback(key);

expect(storageStub.delete.calledOnce).to.be.true;
});

it('should call storage.delte with correct parameters', async () => {
const key = 'key';
hashStub.calculate.returns(key);

await service.set(['key'], 'value');

const callback = expirationStub.add.firstCall.args[1];

await callback(key);

expect(storageStub.delete.calledWith(key)).to.be.true;
});

Expand All @@ -91,7 +119,15 @@ describe('@cache Cache', () => {

await service.has(['key']);

expect(storageStub.has.calledOnce).to.be.true;
expect(storageStub.has.calledWith(key)).to.be.true;
});

it('should call storage has with correct parameters', async () => {
const key = 'key';
hashStub.calculate.returns(key);

await service.has(['key']);

expect(storageStub.has.calledWith(key)).to.be.true;
});

Expand All @@ -111,6 +147,15 @@ describe('@cache Cache', () => {

await service.get(['key']);

expect(expirationStub.add.calledOnce).to.be.true;
});

it('should call expiration.add with correct parameters', async () => {
const key = 'key';
hashStub.calculate.returns(key);

await service.get(['key']);

expect(expirationStub.add.calledOnce).to.be.true;
expect(expirationStub.add.calledWith(key, sinon.match.func)).to.be.true;
});
Expand All @@ -122,12 +167,20 @@ describe('@cache Cache', () => {
await service.get(['key']);

expect(storageStub.get.calledOnce).to.be.true;
});

it('should call storage.get with correct parameters', async () => {
const key = 'key';
hashStub.calculate.returns(key);

await service.get(['key']);

expect(storageStub.get.calledWith(key)).to.be.true;
});

describe('function passed to expiration', () => {

it('should call storage.delete', async () => {
it('should call storage.delete once', async () => {
const key = 'key';
hashStub.calculate.returns(key);

Expand All @@ -138,6 +191,18 @@ describe('@cache Cache', () => {
await callback(key);

expect(storageStub.delete.calledOnce).to.be.true;
});

it('should call storage.delete with correct parameters', async () => {
const key = 'key';
hashStub.calculate.returns(key);

await service.get(['key']);

const callback = expirationStub.add.firstCall.args[1];

await callback(key);

expect(storageStub.delete.calledWith(key)).to.be.true;
});

Expand Down
25 changes: 0 additions & 25 deletions test/cache/expirations/AbsoluteExpiration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,12 @@ describe('@cache AbsoluteExpiration', () => {

it('should create', () => expect(service).to.be.instanceOf(AbsoluteExpiration));

it('should init expirations', () => {
expect(service['expirations']).to.be.instanceOf(Set);
});

});

describe('add', () => {

describe('new key', () => {

it('should add key to expirations', () => {
const size = service['expirations'].size;

service.add('key', () => { });

expect(service['expirations'].size).to.be.equals(size + 1);
expect(service['expirations'].has('key')).to.be.true;
});

it('should remove key from expirations after timeout ms', async () => {
const size = service['expirations'].size;

service.add('key', () => { });

await delay(timeout);

expect(service['expirations'].size).to.be.equals(size);
expect(service['expirations'].has('key')).to.be.false;
});

it('should call clearCallback after timeout ms', async () => {
const spy = sinon.spy();

Expand Down Expand Up @@ -83,7 +59,6 @@ describe('@cache AbsoluteExpiration', () => {
await delay(timeout / 2);

expect(firstSpy.calledOnce).to.be.true;
expect(secondSpy.called).to.be.false;
});

});
Expand Down
40 changes: 1 addition & 39 deletions test/cache/expirations/SlidingExpiration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,12 @@ describe('@cache SlidingExpiration', () => {

it('should create', () => expect(service).to.be.instanceOf(SlidingExpiration));

it('should init expirations storage', () => {
expect(service['expirations']).to.be.instanceOf(Map);
});

});

describe('add', () => {

describe('new key', () => {

it('should add key in expirations', () => {
const size = service['expirations'].size;

service.add('key', () => { });

expect(service['expirations'].size).to.be.equals(size + 1);
expect(service['expirations'].has('key')).to.be.true;
});

it('should remove key from expirations after timeout ms', async () => {
const size = service['expirations'].size;

service.add('key', () => { });
await delay(timeout);

expect(service['expirations'].size).to.be.equals(size);
expect(service['expirations'].has('key')).to.be.false;
});

it('should call clearCallback after timeout ms', async () => {
const spy = sinon.spy();

Expand All @@ -59,14 +36,6 @@ describe('@cache SlidingExpiration', () => {

beforeEach(() => service['expirations'].set('key', setTimeout(() => { }, timeout) as any));

it('should update timer for key in expirations', () => {
const timer = service['expirations'].get('key');

service.add('key', () => { });

expect(service['expirations'].get('key')).not.to.be.equals(timer);
});

it('should remove existing expiration', async () => {
const expirationSpy = sinon.spy();
service['expirations'].set('key', setTimeout(() => expirationSpy(), timeout) as any);
Expand All @@ -78,18 +47,11 @@ describe('@cache SlidingExpiration', () => {
expect(expirationSpy.called).to.be.false;
});

it('should clear key in expirations after timeout ms', async () => {
service.add('key', () => { });

await delay(timeout);

expect(service['expirations'].get('key')).to.be.undefined;
});

it('should call clearCallback after timeout ms', async () => {
const spy = sinon.spy();

service.add('key', spy);

await delay(timeout);

expect(spy.calledOnce).to.be.true;
Expand Down
Loading

0 comments on commit 5c66cec

Please sign in to comment.