@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