Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: plugins system #12

Merged
merged 12 commits into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,9 @@
"url": "git://github.com/JointlyTech/cache-candidate.git"
},
"license": "MIT",
"author": "Jointly <dev@jointly.pro>"
"author": "Jointly <dev@jointly.pro>",
"dependencies": {
"@jointly/cache-candidate-plugin-base": "^1.0.0",
"hook-fn": "^1.0.3"
}
}
1 change: 1 addition & 0 deletions src/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const CacheCandidateOptionsDefault: CacheCandidateOptions = {
requestsThreshold: 3,
cache: makeAsyncMap(),
keepAlive: false,
plugins: [],
events: {
onCacheHit: (_) => {
return _;
Expand Down
243 changes: 96 additions & 147 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { cacheCandidate } from './lib';
import { cacheCandidateDependencyManager } from './manager';
import { MockClass } from './test/MockClass';
import { MockClass as MockClass2 } from './test/MockClass2';
import { MockClass as MockClass3 } from './test/MockClass3';
import { MockClass as MockClass4 } from './test/MockClass4';

import {
step,
Expand All @@ -22,159 +19,111 @@ beforeEach(async () => {
flushMaps();
});

it('should verify cache is empty', async () => {
expect(eventHits.get('onCacheSet')).toBe(0);
expect(eventHits.get('onCacheHit')).toBe(0);
expect(eventHits.get('onCacheDelete')).toBe(0);
});

it('should separate the cache entries for MockClass and MockClass2 even if original names are equal', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
const mock2 = new MockClass2(step, step, step, step);
mock.mockFunction(step);
await sleep(TTL + EXECUTION_MARGIN);
mock2.mockFunction(step);
await sleep(TTL + EXECUTION_MARGIN);
expect(eventHits.get('onCacheSet')).toBe(2);
expect(eventHits.get('onCacheHit')).toBe(0);
});

it('should call onCacheDelete for sync method', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
mock.mockFunction(step);
await sleep(TTL + EXECUTION_MARGIN);
expect(eventHits.get('onCacheDelete')).toBe(1);
});

it('should call onCacheDelete for async method', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
await mock.mockAsyncFunction(step);
await sleep(TTL + EXECUTION_MARGIN);
expect(eventHits.get('onCacheDelete')).toBe(1);
});

it('should call onCacheSet for sync method', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
expect(eventHits.get('onCacheSet')).toBe(1);
});
describe('CacheCandidate - Simple', () => {
it('should verify cache is empty', async () => {
expect(eventHits.get('onCacheSet')).toBe(0);
expect(eventHits.get('onCacheHit')).toBe(0);
expect(eventHits.get('onCacheDelete')).toBe(0);
});

it('should call onCacheHit for sync method', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
expect(eventHits.get('onCacheHit')).toBe(1);
});
it('should separate the cache entries for MockClass and MockClass2 even if original names are equal', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
const mock2 = new MockClass2(step, step, step, step);
mock.mockFunction(step);
await sleep(TTL + EXECUTION_MARGIN);
mock2.mockFunction(step);
await sleep(TTL + EXECUTION_MARGIN);
expect(eventHits.get('onCacheSet')).toBe(2);
expect(eventHits.get('onCacheHit')).toBe(0);
});

it('should call onCacheSet for async method', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
await mock.mockAsyncFunction(step);
await sleep(EXECUTION_MARGIN);
expect(eventHits.get('onCacheSet')).toBe(1);
});
it('should call onCacheDelete for sync method', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
mock.mockFunction(step);
await sleep(TTL + EXECUTION_MARGIN);
expect(eventHits.get('onCacheDelete')).toBe(1);
});

it('should call onCacheHit for async method', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
await mock.mockAsyncFunction(step);
await sleep(EXECUTION_MARGIN);
await mock.mockAsyncFunction(step);
await sleep(EXECUTION_MARGIN);
expect(eventHits.get('onCacheHit')).toBe(1);
});
it('should call onCacheDelete for async method', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
await mock.mockAsyncFunction(step);
await sleep(TTL + EXECUTION_MARGIN);
expect(eventHits.get('onCacheDelete')).toBe(1);
});

it('should make an item expire after TTL', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
expect(eventHits.get('onCacheSet')).toBe(1);
expect(eventHits.get('onCacheHit')).toBe(1);
await sleep(TTL + EXECUTION_MARGIN);
expect(eventHits.get('onCacheDelete')).toBe(1);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
expect(eventHits.get('onCacheSet')).toBe(2);
expect(eventHits.get('onCacheHit')).toBe(1);
});
it('should call onCacheSet for sync method', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
expect(eventHits.get('onCacheSet')).toBe(1);
});

it('should expose manager', async () => {
const step = stepper();
new MockClass(step, step, step, step);
expect(cacheCandidateDependencyManager).toBeDefined();
});
it('should call onCacheHit for sync method', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
expect(eventHits.get('onCacheHit')).toBe(1);
});

it('should fill manager map if dependencyKeys is defined as array', async () => {
const step = stepper();
const mock = new MockClass3(step, step, step, step);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
expect(cacheCandidateDependencyManager.instances.size).toBe(2);
mock.mockAsyncFunction(step);
await sleep(EXECUTION_MARGIN);
expect(cacheCandidateDependencyManager.instances.size).toBe(2);
});
it('should call onCacheSet for async method', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
await mock.mockAsyncFunction(step);
await sleep(EXECUTION_MARGIN);
expect(eventHits.get('onCacheSet')).toBe(1);
});

it('should fill manager map if dependencyKeys is defined as function', async () => {
const step = stepper();
const mock = new MockClass4(step, step, step, step);
/*mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
expect(manager.instances.size).toBe(3);*/
await mock.mockAsyncFunction(step);
await sleep(EXECUTION_MARGIN);
expect(cacheCandidateDependencyManager.instances.size).toBe(3);
});
it('should call onCacheHit for async method', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
await mock.mockAsyncFunction(step);
await sleep(EXECUTION_MARGIN);
await mock.mockAsyncFunction(step);
await sleep(EXECUTION_MARGIN);
expect(eventHits.get('onCacheHit')).toBe(1);
});

it('should delete a record if invalidated', async () => {
const step = stepper();
const mock = new MockClass3(step, step, step, step);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
expect(cacheCandidateDependencyManager.instances.size).toBe(2);
cacheCandidateDependencyManager.invalidate('a');
expect(cacheCandidateDependencyManager.instances.size).toBe(2);
});
it('should make an item expire after TTL', async () => {
const step = stepper();
const mock = new MockClass(step, step, step, step);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
expect(eventHits.get('onCacheSet')).toBe(1);
expect(eventHits.get('onCacheHit')).toBe(1);
await sleep(TTL + EXECUTION_MARGIN);
expect(eventHits.get('onCacheDelete')).toBe(1);
mock.mockFunction(step);
await sleep(EXECUTION_MARGIN);
expect(eventHits.get('onCacheSet')).toBe(2);
expect(eventHits.get('onCacheHit')).toBe(1);
});

it('should behave in the same way as a decorator if the higher-order function is used', async () => {
let counter = 0;
const mockFn = (step: number) =>
new Promise((resolve) => {
counter += step;
resolve(counter);
it('should behave in the same way as a decorator if the higher-order function is used', async () => {
let counter = 0;
const mockFn = (step: number) =>
new Promise((resolve) => {
counter += step;
resolve(counter);
});
const wrappedMockFn = cacheCandidate(mockFn, {
requestsThreshold: 1,
ttl: 800
});
const wrappedMockFn = cacheCandidate(mockFn, {
requestsThreshold: 1,
dependencyKeys(result) {
return result;
}
let result: unknown;
result = await wrappedMockFn(1);
await sleep(EXECUTION_MARGIN);
expect(result).toBe(1);
result = await wrappedMockFn(1);
await sleep(EXECUTION_MARGIN);
expect(result).toBe(1);
});
let result: unknown;
result = await wrappedMockFn(1);
await sleep(EXECUTION_MARGIN);
expect(result).toBe(1);
result = await wrappedMockFn(1);
await sleep(EXECUTION_MARGIN);
expect(result).toBe(1);
result = await wrappedMockFn(1);
await sleep(EXECUTION_MARGIN);
expect(result).toBe(1);
cacheCandidateDependencyManager.invalidate(0);
await sleep(EXECUTION_MARGIN);
expect(cacheCandidateDependencyManager.instances.size).toBe(1);
cacheCandidateDependencyManager.invalidate(1);
await sleep(EXECUTION_MARGIN);
expect(cacheCandidateDependencyManager.instances.size).toBe(1);
await sleep(EXECUTION_MARGIN);
});
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './default';
export * from './models';
export * from './lib';
export * from './manager';
export * from './plugins/poc/dependency-keys';
export * from './plugins/poc/manager';
Loading