ObjectCache reducer tests & docs

This commit is contained in:
Art Lowel
2017-03-06 14:42:19 +01:00
parent 07150d3d6a
commit f1bf3e4062
3 changed files with 149 additions and 0 deletions

View File

@@ -132,6 +132,10 @@ module.exports = function(config) {
} }
}, },
mochaReporter: {
ignoreSkipped: true
}
/* /*
* Continuous Integration mode * Continuous Integration mode
* if true, Karma captures browsers, runs the tests and exits * if true, Karma captures browsers, runs the tests and exits

View File

@@ -0,0 +1,101 @@
import * as deepFreeze from "deep-freeze";
import { objectCacheReducer } from "./object-cache.reducer";
import {
AddToObjectCacheAction,
RemoveFromObjectCacheAction
} from "./object-cache.actions";
class NullAction extends RemoveFromObjectCacheAction {
type = null;
payload = null;
constructor() {
super(null);
}
}
describe("objectCacheReducer", () => {
const uuid = '1698f1d3-be98-4c51-9fd8-6bfedcbd59b7';
const testState = {
[uuid]: {
data: {
uuid: uuid,
foo: "bar"
},
timeAdded: new Date().getTime(),
msToLive: 900000
}
};
deepFreeze(testState);
it("should return the current state when no valid actions have been made", () => {
const action = new NullAction();
const newState = objectCacheReducer(testState, action);
expect(newState).toEqual(testState);
});
it("should start with an empty cache", () => {
const action = new NullAction();
const initialState = objectCacheReducer(undefined, action);
expect(initialState).toEqual(Object.create(null));
});
it("should add the payload to the cache in response to an ADD action", () => {
const state = Object.create(null);
const objectToCache = {uuid: uuid};
const timeAdded = new Date().getTime();
const msToLive = 900000;
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive);
const newState = objectCacheReducer(state, action);
expect(newState[uuid].data).toEqual(objectToCache);
expect(newState[uuid].timeAdded).toEqual(timeAdded);
expect(newState[uuid].msToLive).toEqual(msToLive);
});
it("should overwrite an object in the cache in response to an ADD action if it already exists", () => {
const objectToCache = {uuid: uuid, foo: "baz", somethingElse: true};
const timeAdded = new Date().getTime();
const msToLive = 900000;
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive);
const newState = objectCacheReducer(testState, action);
expect(newState[uuid].data['foo']).toBe("baz");
expect(newState[uuid].data['somethingElse']).toBe(true);
});
it("should perform the ADD action without affecting the previous state", () => {
const state = Object.create(null);
const objectToCache = {uuid: uuid};
const timeAdded = new Date().getTime();
const msToLive = 900000;
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive);
deepFreeze(state);
objectCacheReducer(state, action);
});
it("should remove the specified object from the cache in response to the REMOVE action", () => {
const action = new RemoveFromObjectCacheAction(uuid);
const newState = objectCacheReducer(testState, action);
expect(testState[uuid]).not.toBeUndefined();
expect(newState[uuid]).toBeUndefined();
});
it("shouldn't do anything in response to the REMOVE action for an object that isn't cached", () => {
const action = new RemoveFromObjectCacheAction("this isn't cached");
const newState = objectCacheReducer(testState, action);
expect(newState).toEqual(testState);
});
it("should perform the REMOVE action without affecting the previous state", () => {
const action = new RemoveFromObjectCacheAction(uuid);
//testState has already been frozen above
objectCacheReducer(testState, action);
});
});

View File

@@ -2,16 +2,30 @@ import { ObjectCacheAction, ObjectCacheActionTypes, AddToObjectCacheAction, Remo
import { hasValue } from "../../shared/empty.util"; import { hasValue } from "../../shared/empty.util";
import { CacheEntry } from "./cache-entry"; import { CacheEntry } from "./cache-entry";
/**
* An interface to represent objects that can be cached
*
* A cacheable object should have a uuid
*/
export interface CacheableObject { export interface CacheableObject {
uuid: string; uuid: string;
} }
/**
* An entry in the ObjectCache
*/
export class ObjectCacheEntry implements CacheEntry { export class ObjectCacheEntry implements CacheEntry {
data: CacheableObject; data: CacheableObject;
timeAdded: number; timeAdded: number;
msToLive: number; msToLive: number;
} }
/**
* The ObjectCache State
*
* Consists of a map with UUIDs as keys,
* and ObjectCacheEntries as values
*/
export interface ObjectCacheState { export interface ObjectCacheState {
[uuid: string]: ObjectCacheEntry [uuid: string]: ObjectCacheEntry
} }
@@ -19,6 +33,16 @@ export interface ObjectCacheState {
// Object.create(null) ensures the object has no default js properties (e.g. `__proto__`) // Object.create(null) ensures the object has no default js properties (e.g. `__proto__`)
const initialState: ObjectCacheState = Object.create(null); const initialState: ObjectCacheState = Object.create(null);
/**
* The ObjectCache Reducer
*
* @param state
* the current state
* @param action
* the action to perform on the state
* @return ObjectCacheState
* the new state
*/
export const objectCacheReducer = (state = initialState, action: ObjectCacheAction): ObjectCacheState => { export const objectCacheReducer = (state = initialState, action: ObjectCacheAction): ObjectCacheState => {
switch (action.type) { switch (action.type) {
@@ -36,6 +60,16 @@ export const objectCacheReducer = (state = initialState, action: ObjectCacheActi
} }
}; };
/**
* Add an object to the cache
*
* @param state
* the current state
* @param action
* an AddToObjectCacheAction
* @return ObjectCacheState
* the new state, with the object added, or overwritten.
*/
function addToObjectCache(state: ObjectCacheState, action: AddToObjectCacheAction): ObjectCacheState { function addToObjectCache(state: ObjectCacheState, action: AddToObjectCacheAction): ObjectCacheState {
return Object.assign({}, state, { return Object.assign({}, state, {
[action.payload.objectToCache.uuid]: { [action.payload.objectToCache.uuid]: {
@@ -46,6 +80,16 @@ function addToObjectCache(state: ObjectCacheState, action: AddToObjectCacheActio
}); });
} }
/**
* Remove an object from the cache
*
* @param state
* the current state
* @param action
* an RemoveFromObjectCacheAction
* @return ObjectCacheState
* the new state, with the object removed if it existed.
*/
function removeFromObjectCache(state: ObjectCacheState, action: RemoveFromObjectCacheAction): ObjectCacheState { function removeFromObjectCache(state: ObjectCacheState, action: RemoveFromObjectCacheAction): ObjectCacheState {
if (hasValue(state[action.payload])) { if (hasValue(state[action.payload])) {
let newObjectCache = Object.assign({}, state); let newObjectCache = Object.assign({}, state);