Skip to content

Commit

Permalink
feat: done, all tests passing
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-pr-p committed Jan 17, 2020
1 parent 052438d commit 99fd888
Show file tree
Hide file tree
Showing 13 changed files with 5,866 additions and 155 deletions.
8 changes: 8 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
clearMocks: true,
coverageDirectory: 'coverage',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[t]s?(x)'],
verbose: true,
preset: 'ts-jest'
};
13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
"fix": "run-s fix:*",
"fix:prettier": "prettier \"src/**/*.ts\" --write",
"fix:tslint": "tslint --fix --project .",
"test": "run-s build test:*",
"test": "run-s test:*",
"test:lint": "tslint --project . && prettier \"src/**/*.ts\" --list-different",
"test:unit": "nyc --silent ava",
"test:unit": "jest",
"watch": "run-s clean build:main && run-p \"build:main -- -w\" \"test:unit -- --watch\"",
"cov": "run-s build test:unit cov:html && open-cli coverage/index.html",
"cov:html": "nyc report --reporter=html",
Expand Down Expand Up @@ -51,22 +51,27 @@
"node": ">=8.9"
},
"dependencies": {
"redis": "^2.8.0",
"redis-lock": "^0.1.4",
"runtypes": "^4.0.3",
"sha.js": "^2.4.11"
},
"devDependencies": {
"@bitjson/npm-scripts-info": "^1.0.0",
"@bitjson/typedoc": "^0.15.0-0",
"@istanbuljs/nyc-config-typescript": "^0.1.3",
"ava": "2.2.0",
"@types/jest": "^24.9.0",
"@types/redis": "^2.8.14",
"codecov": "^3.5.0",
"cz-conventional-changelog": "^2.1.0",
"gh-pages": "^2.0.1",
"jest": "^24.9.0",
"npm-run-all": "^4.1.5",
"nyc": "^14.1.1",
"open-cli": "^5.0.0",
"prettier": "^1.18.2",
"standard-version": "^6.0.1",
"trash-cli": "^3.0.0",
"ts-jest": "^24.3.0",
"tslint": "^5.18.0",
"tslint-config-prettier": "^1.18.0",
"tslint-immutable": "^6.0.1",
Expand Down
3 changes: 0 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
export * from './lib/async';
export * from './lib/hash';
export * from './lib/number';
7 changes: 0 additions & 7 deletions src/lib/async.spec.ts

This file was deleted.

32 changes: 0 additions & 32 deletions src/lib/async.ts

This file was deleted.

13 changes: 0 additions & 13 deletions src/lib/hash.spec.ts

This file was deleted.

38 changes: 0 additions & 38 deletions src/lib/hash.ts

This file was deleted.

177 changes: 177 additions & 0 deletions src/lib/memoizer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import redis from 'redis';
import { createMemoizer, produceKeyWithArgs } from './memoizer';

const flushRedis = () =>
new Promise((resolve, reject) => {
const client = redis.createClient();
client.flushall('ASYNC', err => (err ? reject(err) : resolve(true)));
});

const sleep = n => new Promise(resolve => setTimeout(resolve, n * 1000));

describe('produceKeyWithArgs', () => {
test('produceKeyWithArgs should produce the same key for differently ordered objects', () => {
expect(produceKeyWithArgs('prefix', 'key', { a: 1, b: 2 })).toEqual(
produceKeyWithArgs('prefix', 'key', { b: 2, a: 1 })
);
});
});

describe('basic functionality', () => {
beforeAll(async () => {
await flushRedis();
});

afterAll(async () => {
await flushRedis();
});

test('should only call function once for same args', async () => {
const memoizer = createMemoizer({});

const mock = jest.fn();

const memoizableFunction = async () => {
mock();
return 4;
};

const memoizedFunction = memoizer.memoize(memoizableFunction, {
key: 'basic'
});

const args = { a: 4 };
await memoizedFunction(args);
await memoizedFunction(args);
expect(mock).toHaveBeenCalledTimes(1);
});

test('should distinguish different args', async () => {
const memoizer = createMemoizer({});

const mock = jest.fn();

const memoizableFunction = async () => {
mock();
return 4;
};

const memoizedFunction = memoizer.memoize(memoizableFunction, {
key: 'different'
});

await memoizedFunction({ a: 1 });
await memoizedFunction({ a: 2 });
expect(mock).toHaveBeenCalledTimes(2);
});

test('should lock duplicate calls', async () => {
const memoizer = createMemoizer({});

const mock = jest.fn();

const memoizableSlowFunction = async () => {
await sleep(1);
mock();
return 4;
};

const memoizedFunction = memoizer.memoize(memoizableSlowFunction, {
key: 'locking'
});

await Promise.all([memoizedFunction({ a: 1 }), memoizedFunction({ a: 1 })]);
expect(mock).toHaveBeenCalledTimes(1);
});
});

describe('invalidation', () => {
beforeAll(async () => {
await flushRedis();
});

afterAll(async () => {
await flushRedis();
});

test('should call twice if invalidated', async () => {
const memoizer = createMemoizer({});

const mock = jest.fn();

const memoizableFunction = async () => {
mock();
return 4;
};

const memoizedFunction = memoizer.memoize(memoizableFunction, {
key: 'basic-invalidate'
});

const args = { a: 4 };
await memoizedFunction(args);
await memoizer.invalidate('basic-invalidate', args);
await memoizedFunction(args);
expect(mock).toHaveBeenCalledTimes(2);
});

test('should call once if different arg is invalidated', async () => {
const memoizer = createMemoizer({});

const mock = jest.fn();

const memoizableFunction = async () => {
mock();
return 4;
};

const memoizedFunction = memoizer.memoize(memoizableFunction, {
key: 'missed-invalidate'
});

await memoizedFunction({ a: 4 });
await memoizer.invalidate('basic-invalidate', { a: 5 });
await memoizedFunction({ a: 4 });
expect(mock).toHaveBeenCalledTimes(1);
});

test('should call twice if different compound covered invalidate', async () => {
const memoizer = createMemoizer({});

const mock = jest.fn();

const memoizableFunction = async () => {
mock();
return 4;
};

const memoizedFunction = memoizer.memoize(memoizableFunction, {
key: 'compound-covered-invalidate'
});

await memoizedFunction({ a: 1, b: 2 });
await memoizer.invalidate('compound-covered-invalidate', { a: 1 });
await memoizedFunction({ a: 1, b: 2 });
expect(mock).toHaveBeenCalledTimes(2);
});

test('should call once if invalidation is partially wrong', async () => {
const memoizer = createMemoizer({});

const mock = jest.fn();

const memoizableFunction = async () => {
mock();
return 4;
};

const memoizedFunction = memoizer.memoize(memoizableFunction, {
key: 'compound-missed-invalidate'
});

await memoizedFunction({ a: 1, b: 2 });
await memoizer.invalidate('compound-missed-invalidate', { a: 1, b: 3 });
await memoizedFunction({ a: 1, b: 2 });
expect(mock).toHaveBeenCalledTimes(1);
});
});
Loading

0 comments on commit 99fd888

Please sign in to comment.