@daiso-tech/core
Version:
The library offers flexible, framework-agnostic solutions for modern web applications, built on adaptable components that integrate seamlessly with popular frameworks like Next Js.
301 lines • 13.3 kB
JavaScript
/**
* @module Cache
*/
import {} from "vitest";
import { TypeCacheError, } from "../../../cache/contracts/_module-exports.js";
import {} from "../../../utilities/_module-exports.js";
import { TimeSpan } from "../../../utilities/_module-exports.js";
import { LazyPromise } from "../../../async/_module-exports.js";
/**
* The `cacheAdapterTestSuite` function simplifies the process of testing your custom implementation of {@link ICacheAdapter | `ICacheAdapter`} with `vitest`.
*
* IMPORT_PATH: `"@daiso-tech/core/cache/test-utilities"`
* @group TestUtilities
* @example
* ```ts
* import { afterEach, beforeEach, describe, expect, test } from "vitest";
* import { Redis } from "ioredis";
* import {
* RedisContainer,
* type StartedRedisContainer,
* } from "@testcontainers/redis";
* import { cacheAdapterTestSuite } from "@daiso-tech/core/cache/test-utilities";
* import { RedisCacheAdapter } from "@daiso-tech/core/cache/adapters";
* import { TimeSpan } from "@daiso-tech/core/utilities";
* import { SuperJsonSerdeAdapter } from "@daiso-tech/core/serde/adapters";
* import { Serde } from "@daiso-tech/core/serde";
*
* const timeout = TimeSpan.fromMinutes(2);
* describe("class: RedisCacheAdapter", () => {
* let client: Redis;
* let startedContainer: StartedRedisContainer;
* beforeEach(async () => {
* startedContainer = await new RedisContainer("redis:7.4.2").start();
* client = new Redis(startedContainer.getConnectionUrl());
* }, timeout.toMilliseconds());
* afterEach(async () => {
* await client.quit();
* await startedContainer.stop();
* }, timeout.toMilliseconds());
* cacheAdapterTestSuite({
* createAdapter: () =>
* new RedisCacheAdapter({
* database: client,
* serde: new Serde(new SuperJsonSerdeAdapter()),
* }),
* test,
* beforeEach,
* expect,
* describe,
* });
* });
* ```
*/
export function cacheAdapterTestSuite(settings) {
const { expect, test, createAdapter, describe, beforeEach } = settings;
let adapter;
beforeEach(async () => {
adapter = await createAdapter();
});
const TTL = TimeSpan.fromMilliseconds(50);
describe("method: get", () => {
test("Should return the value when key exists", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.get("a")).toBe(1);
});
test("Should return null when keys doesnt exists", async () => {
expect(await adapter.get("a")).toBeNull();
});
test("Should return null when key is experied", async () => {
await adapter.add("a", 1, TTL);
await LazyPromise.delay(TTL.addTimeSpan(TTL.divide(4)));
expect(await adapter.get("a")).toBeNull();
});
});
describe("method: getAndRemove", () => {
test("Should return value when key exists", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.getAndRemove("a")).toBe(1);
});
test("Should return null when key doesnt exists", async () => {
expect(await adapter.getAndRemove("a")).toBeNull();
});
test("Should return null when key is expired", async () => {
await adapter.add("a", 1, TTL);
await LazyPromise.delay(TTL.addTimeSpan(TTL.divide(4)));
expect(await adapter.getAndRemove("a")).toBeNull();
});
test("Should persist removal when key exists", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
await adapter.getAndRemove("a");
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.get("a")).toBeNull();
});
});
describe("method: add", () => {
test("Should return true when key doesnt exists", async () => {
const result = await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
expect(result).toBe(true);
});
test("Should return true when key is expired", async () => {
await adapter.add("a", 1, TTL);
await LazyPromise.delay(TTL.addTimeSpan(TTL.divide(4)));
expect(await adapter.add("a", 1, null)).toBe(true);
});
test("Should persist values when key doesnt exist", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.get("a")).toBe(1);
});
test("Should persist values when key is expired", async () => {
await adapter.add("a", -1, TTL);
await LazyPromise.delay(TTL.addTimeSpan(TTL.divide(4)));
await adapter.add("a", 1, null);
expect(await adapter.get("a")).toBe(1);
});
test("Should return false when key exists", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.add("a", 1, null)).toBe(false);
});
test("Should not persist value when key exist", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
await adapter.add("a", 2, null);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.get("a")).toBe(1);
});
});
describe("method: put", () => {
test("Should return true when key exists", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.put("a", -1, null)).toBe(true);
});
test("Should persist value when key exist", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
await adapter.put("a", -1, null);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.get("a")).toBe(-1);
});
test("Should return false when key doesnt exists", async () => {
expect(await adapter.put("a", -1, null)).toBe(false);
});
test("Should return false when key is expired", async () => {
await adapter.add("a", 1, TTL);
await LazyPromise.delay(TTL.addTimeSpan(TTL.divide(4)));
expect(await adapter.put("a", -1, null)).toBe(false);
});
test("Should persist values when key doesnt exist", async () => {
await adapter.put("a", -1, null);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.get("a")).toBe(-1);
});
test("Should persist values when key is expired", async () => {
await adapter.add("a", 1, TTL);
await LazyPromise.delay(TTL.addTimeSpan(TTL.divide(4)));
await adapter.put("a", -1, null);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.get("a")).toBe(-1);
});
test("Should replace the ttl value", async () => {
const ttlA = TimeSpan.fromMilliseconds(100);
await adapter.add("a", 1, ttlA);
await LazyPromise.delay(TTL.divide(4));
const ttlB = TimeSpan.fromMilliseconds(50);
await adapter.put("a", -1, ttlB);
await LazyPromise.delay(ttlB);
expect(await adapter.get("a")).toBeNull();
});
});
describe("method: update", () => {
test("Should return true when key exists", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.update("a", -1)).toBe(true);
});
test("Should persist value when key exist", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
await adapter.update("a", -1);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.get("a")).toBe(-1);
});
test("Should return false when key doesnt exists", async () => {
expect(await adapter.update("a", -1)).toBe(false);
});
test("Should return false when key is expired", async () => {
await adapter.add("a", 1, TTL);
await LazyPromise.delay(TTL.addTimeSpan(TTL.divide(4)));
expect(await adapter.update("a", -1)).toBe(false);
});
test("Should not persist value when key doesnt exist", async () => {
await adapter.update("a", -1);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.get("a")).toBeNull();
});
test("Should not persist value when key is expired", async () => {
await adapter.add("a", 1, TTL);
await LazyPromise.delay(TTL.addTimeSpan(TTL.divide(4)));
await adapter.update("a", -1);
expect(await adapter.get("a")).toBeNull();
});
});
describe("method: increment", () => {
test("Should return true when key exists", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.increment("a", 1)).toBe(true);
});
test("Should persist increment when key exists", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
await adapter.increment("a", 1);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.get("a")).toBe(2);
});
test("Should return false when key doesnt exists", async () => {
expect(await adapter.increment("a", 1)).toBe(false);
});
test("Should return false when key is expired", async () => {
await adapter.add("a", 1, TTL);
await LazyPromise.delay(TTL.addTimeSpan(TTL.divide(4)));
expect(await adapter.increment("a", 1)).toBe(false);
});
test("Should not persist increment when key doesnt exists", async () => {
await adapter.increment("a", 1);
await LazyPromise.delay(TTL.divide(4));
expect(await adapter.get("a")).toBeNull();
});
test("Should not persist increment when key is expired", async () => {
await adapter.add("a", 1, TTL);
await LazyPromise.delay(TTL.addTimeSpan(TTL.divide(4)));
await adapter.increment("a", 1);
expect(await adapter.get("a")).toBeNull();
});
test("Should throw TypeCacheError key value is not number type", async () => {
await adapter.add("a", "str", null);
await LazyPromise.delay(TTL.divide(4));
await expect(adapter.increment("a", 1)).rejects.toBeInstanceOf(TypeCacheError);
});
});
describe("method: removeMany", () => {
test("Should return true when one key exists", async () => {
await adapter.add("a", 1, null);
await LazyPromise.delay(TTL.divide(4));
const result = await adapter.removeMany(["a", "b", "c"]);
expect(result).toBe(true);
});
test("Should persist removal of the keys that exists", async () => {
await adapter.add("a", 1, null);
await adapter.add("b", 2, null);
await adapter.add("c", 3, null);
await LazyPromise.delay(TTL.divide(4));
await adapter.removeMany(["a", "b"]);
await LazyPromise.delay(TTL.divide(4));
const result = [
await adapter.get("a"),
await adapter.get("b"),
await adapter.get("c"),
];
expect(result).toEqual([null, null, 3]);
});
});
describe("method: removeAll", () => {
test("Should remove all keys", async () => {
await adapter.add("cache/a", 1, null);
await adapter.add("cache/b", 2, null);
await adapter.add("c", 3, null);
await LazyPromise.delay(TTL.divide(4));
await adapter.removeAll();
await LazyPromise.delay(TTL.divide(4));
expect([
await adapter.get("cache/a"),
await adapter.get("cache/b"),
await adapter.get("c"),
]).toEqual([null, null, null]);
});
});
describe("method: removeByKeyPrefix", () => {
test(`Should remove all keys that start with prefix "cache"`, async () => {
await adapter.add("cache/a", 1, null);
await adapter.add("cache/b", 2, null);
await adapter.add("c", 3, null);
await LazyPromise.delay(TTL.divide(4));
await adapter.removeByKeyPrefix("cache");
await LazyPromise.delay(TTL.divide(4));
const result = [
await adapter.get("cache/a"),
await adapter.get("cache/b"),
await adapter.get("c"),
];
expect(result).toEqual([null, null, 3]);
});
});
}
//# sourceMappingURL=cache-adapter.test-suite.js.map