diff --git a/karma.conf.js b/karma.conf.js index 43ad307a3c..07be7852f0 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -132,6 +132,10 @@ module.exports = function(config) { } }, + mochaReporter: { + ignoreSkipped: true + } + /* * Continuous Integration mode * if true, Karma captures browsers, runs the tests and exits diff --git a/src/app/core/cache/object-cache.reducer.spec.ts b/src/app/core/cache/object-cache.reducer.spec.ts new file mode 100644 index 0000000000..dbc0c20eed --- /dev/null +++ b/src/app/core/cache/object-cache.reducer.spec.ts @@ -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); + }); + +}); diff --git a/src/app/core/cache/object-cache.reducer.ts b/src/app/core/cache/object-cache.reducer.ts index 93e9f6ff05..5e0eacdc74 100644 --- a/src/app/core/cache/object-cache.reducer.ts +++ b/src/app/core/cache/object-cache.reducer.ts @@ -2,16 +2,30 @@ import { ObjectCacheAction, ObjectCacheActionTypes, AddToObjectCacheAction, Remo import { hasValue } from "../../shared/empty.util"; import { CacheEntry } from "./cache-entry"; +/** + * An interface to represent objects that can be cached + * + * A cacheable object should have a uuid + */ export interface CacheableObject { uuid: string; } +/** + * An entry in the ObjectCache + */ export class ObjectCacheEntry implements CacheEntry { data: CacheableObject; timeAdded: number; msToLive: number; } +/** + * The ObjectCache State + * + * Consists of a map with UUIDs as keys, + * and ObjectCacheEntries as values + */ export interface ObjectCacheState { [uuid: string]: ObjectCacheEntry } @@ -19,6 +33,16 @@ export interface ObjectCacheState { // Object.create(null) ensures the object has no default js properties (e.g. `__proto__`) 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 => { 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 { return Object.assign({}, state, { [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 { if (hasValue(state[action.payload])) { let newObjectCache = Object.assign({}, state);