mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-03 20:08:11 +08:00
Bkmartinjr/state mgr tests (#298)
Jest tests for part of util/stateManager * reorganize test directories * lint * add Jest config to eslint * add Jest config to eslint * lint * fix babel configuration for tests * start at kvCache tests * reorganize test directories * lint * add Jest config to eslint * add Jest config to eslint * lint * fix babel configuration for tests * start at kvCache tests * fix cache flush bug manifest by new test cases * new kvCache tests * skeletal universe test * fix race in test case * more universe test cases
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
import _ from "lodash";
|
||||
import * as kvCache from "../../../src/util/stateManager/keyvalcache";
|
||||
|
||||
/*
|
||||
This is PRIVATE to keyvalcache and must be kept in sync with
|
||||
any changs ot that module. Need to Know - to enable error handling test
|
||||
*/
|
||||
const cachePrivateKey = "__kvcachekey__";
|
||||
|
||||
/*
|
||||
helper function - promisify setTimeout()
|
||||
*/
|
||||
function timeout(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
describe("kvcache API", () => {
|
||||
/*
|
||||
test the happy path create/set/get API
|
||||
*/
|
||||
|
||||
test("simple create", () => {
|
||||
/* with defaults */
|
||||
const kvc = kvCache.create();
|
||||
expect(kvc).toBeDefined();
|
||||
expect(kvc).toEqual(expect.objectContaining({}));
|
||||
expect(kvCache.get(kvc, "test")).toBeUndefined();
|
||||
|
||||
/* with params */
|
||||
const kvc1 = kvCache.create(/* lowWatermark */ 99, /* minTTL */ 0);
|
||||
expect(kvc1).toBeDefined();
|
||||
expect(kvc1).toEqual(expect.objectContaining({}));
|
||||
});
|
||||
|
||||
test("set/get", () => {
|
||||
/*
|
||||
- check basic get/set functionality
|
||||
- check set does not mutate source cache
|
||||
*/
|
||||
const keyName = "foo";
|
||||
const kvc1 = kvCache.create();
|
||||
expect(kvc1).toBeDefined();
|
||||
expect(kvCache.get(kvc1, keyName)).toBeUndefined();
|
||||
|
||||
const val2 = [2];
|
||||
const kvc2 = kvCache.set(kvc1, keyName, val2);
|
||||
expect(kvc2).toBeDefined();
|
||||
expect(kvc2).not.toBe(kvc1);
|
||||
expect(kvCache.get(kvc1, keyName)).toBeUndefined();
|
||||
expect(kvCache.get(kvc2, keyName)).toBe(val2);
|
||||
|
||||
const val3 = [3];
|
||||
const kvc3 = kvCache.set(kvc2, keyName, val3);
|
||||
expect(kvc3).toBeDefined();
|
||||
expect(kvc3).not.toBe(kvc1);
|
||||
expect(kvc3).not.toBe(kvc2);
|
||||
expect(kvCache.get(kvc1, keyName)).toBeUndefined();
|
||||
expect(kvCache.get(kvc2, keyName)).toBe(val2);
|
||||
expect(kvCache.get(kvc3, keyName)).toBe(val3);
|
||||
});
|
||||
});
|
||||
|
||||
describe("common error handling", () => {
|
||||
/*
|
||||
Test common error handlers
|
||||
*/
|
||||
|
||||
test("set() protection from namespace pollution", () => {
|
||||
/*
|
||||
Test that set() will not allow use of the private cache key
|
||||
*/
|
||||
const kvc = kvCache.create();
|
||||
expect(() => {
|
||||
kvCache.set(kvc, cachePrivateKey, {});
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test("create() does not accept bogus config", () => {
|
||||
expect(() => {
|
||||
kvCache.create([], {});
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
kvCache.create(-99, 0);
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
kvCache.create(100, -1);
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
kvCache.create(1000, "foobar");
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
kvCache.create(null, 8);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("map", () => {
|
||||
/*
|
||||
Test kvCache.map() - create new cache that is a transformation of an
|
||||
existing cache
|
||||
*/
|
||||
test("map of empty cache", () => {
|
||||
const kvc = kvCache.create();
|
||||
const callback = jest.fn();
|
||||
const kvcMapped = kvCache.map(kvc, callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
expect(kvcMapped).toBeDefined();
|
||||
expect(kvcMapped).not.toBe(kvc); // immutable operation
|
||||
expect(kvcMapped).toEqual(kvc);
|
||||
});
|
||||
|
||||
test("map of non-empty cache", () => {
|
||||
const key = "aKey";
|
||||
const val = [0, 1, 2];
|
||||
let kvc = kvCache.create();
|
||||
kvc = kvCache.set(kvc, key, val);
|
||||
const mockCB = jest.fn().mockImplementation(v => [...v]);
|
||||
const kvcMapped = kvCache.map(kvc, mockCB);
|
||||
|
||||
expect(kvcMapped).toBeDefined();
|
||||
expect(kvcMapped).not.toBe(kvc); // immutable operation
|
||||
expect(_.isEqual(kvc, kvcMapped)).toBe(true);
|
||||
|
||||
expect(mockCB).toHaveBeenCalledTimes(1);
|
||||
expect(mockCB).toHaveBeenLastCalledWith(val, key);
|
||||
});
|
||||
});
|
||||
|
||||
describe("flush", () => {
|
||||
/*
|
||||
test various cache flush behavior
|
||||
*/
|
||||
test("flush - lowWatermark, disable minTTL", () => {
|
||||
/*
|
||||
verify lowWatermark functions correctly
|
||||
*/
|
||||
|
||||
// set lowWatermark to 2, set three times - only the final two
|
||||
// should remain.
|
||||
let kvc = kvCache.create(2, 0);
|
||||
["a", "b", "c"].forEach(k => {
|
||||
kvc = kvCache.set(kvc, k, []);
|
||||
});
|
||||
|
||||
expect(kvc).toEqual(
|
||||
expect.objectContaining({
|
||||
b: expect.arrayContaining([]),
|
||||
c: expect.arrayContaining([])
|
||||
})
|
||||
);
|
||||
expect(kvc).toEqual(
|
||||
expect.not.objectContaining({
|
||||
a: expect.arrayContaining([])
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("flush - minTTL, disable lowWatermark", async () => {
|
||||
/*
|
||||
verify minTTL functions correctly
|
||||
*/
|
||||
|
||||
// set minTTL to 1 ms
|
||||
let kvc = kvCache.create(0, 10);
|
||||
kvc = kvCache.set(kvc, "a", []);
|
||||
await timeout(20);
|
||||
["b", "c"].forEach(k => {
|
||||
kvc = kvCache.set(kvc, k, []);
|
||||
});
|
||||
|
||||
expect(kvc).toEqual(
|
||||
expect.objectContaining({
|
||||
b: expect.arrayContaining([]),
|
||||
c: expect.arrayContaining([])
|
||||
})
|
||||
);
|
||||
expect(kvc).toEqual(
|
||||
expect.not.objectContaining({
|
||||
a: expect.arrayContaining([])
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("flush - minTTL and lowWatermark", async () => {
|
||||
/*
|
||||
verify minTTL functions correctly
|
||||
*/
|
||||
|
||||
// set lowwatermark to 3, minTTL to 1 ms
|
||||
let kvc = kvCache.create(3, 10);
|
||||
kvc = kvCache.set(kvc, "a", []);
|
||||
// delay
|
||||
await timeout(20);
|
||||
["b", "c"].forEach(k => {
|
||||
kvc = kvCache.set(kvc, k, []);
|
||||
});
|
||||
|
||||
expect(kvc).toEqual(
|
||||
expect.objectContaining({
|
||||
a: expect.arrayContaining([]),
|
||||
b: expect.arrayContaining([]),
|
||||
c: expect.arrayContaining([])
|
||||
})
|
||||
);
|
||||
|
||||
kvc = kvCache.set(kvc, "d", []);
|
||||
expect(kvc).toEqual(
|
||||
expect.objectContaining({
|
||||
b: expect.arrayContaining([]),
|
||||
c: expect.arrayContaining([]),
|
||||
d: expect.arrayContaining([])
|
||||
})
|
||||
);
|
||||
expect(kvc).toEqual(
|
||||
expect.not.objectContaining({
|
||||
a: expect.arrayContaining([])
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("manual flush", async () => {
|
||||
let kvc = kvCache.create(1, 10);
|
||||
["a", "b", "c", "d"].forEach(k => {
|
||||
kvc = kvCache.set(kvc, k, []);
|
||||
});
|
||||
|
||||
// Before TTL has expired, should have all values in cache.
|
||||
expect(kvc).toEqual(
|
||||
expect.objectContaining({
|
||||
a: expect.arrayContaining([]),
|
||||
b: expect.arrayContaining([]),
|
||||
c: expect.arrayContaining([])
|
||||
})
|
||||
);
|
||||
|
||||
// let TTL expire
|
||||
await timeout(10);
|
||||
|
||||
// manually flush
|
||||
const postFlushKvc = kvCache.flush(kvc);
|
||||
expect(postFlushKvc).toBeDefined();
|
||||
expect(postFlushKvc).not.toBe(kvc);
|
||||
expect(postFlushKvc).toEqual(
|
||||
expect.objectContaining({
|
||||
d: expect.arrayContaining([])
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user