UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

289 lines (226 loc) 8.96 kB
import { Alepha } from "alepha"; import { DateTimeProvider } from "alepha/datetime"; import { beforeEach, describe, expect, it } from "vitest"; import { MemoryCacheProvider } from "../providers/MemoryCacheProvider.ts"; describe("MemoryCacheProvider", () => { let alepha: Alepha; let cache: MemoryCacheProvider; let time: DateTimeProvider; const encode = (value: string) => new TextEncoder().encode(value); beforeEach(async () => { alepha = Alepha.create(); cache = alepha.inject(MemoryCacheProvider); time = alepha.inject(DateTimeProvider); await alepha.start(); }); describe("get/set", () => { it("should store and retrieve values", async () => { const data = encode("hello"); await cache.set("ns", "key1", data); const result = await cache.get("ns", "key1"); expect(result).toEqual(data); }); it("should return undefined for missing key", async () => { const result = await cache.get("ns", "missing"); expect(result).toBeUndefined(); }); it("should overwrite existing values", async () => { await cache.set("ns", "key", encode("first")); await cache.set("ns", "key", encode("second")); const result = await cache.get("ns", "key"); expect(new TextDecoder().decode(result)).toBe("second"); }); it("should isolate cache names", async () => { await cache.set("a", "key", encode("A")); await cache.set("b", "key", encode("B")); expect(new TextDecoder().decode(await cache.get("a", "key"))).toBe("A"); expect(new TextDecoder().decode(await cache.get("b", "key"))).toBe("B"); }); }); describe("TTL", () => { it("should expire entries after TTL", async () => { await cache.set("ns", "key", encode("value"), 5000); expect(await cache.get("ns", "key")).toBeDefined(); await time.travel([6, "seconds"]); expect(await cache.get("ns", "key")).toBeUndefined(); }); it("should not expire entries without TTL", async () => { await cache.set("ns", "key", encode("value")); await time.travel([1, "day"]); expect(await cache.get("ns", "key")).toBeDefined(); }); it("should reset TTL when overwriting", async () => { await cache.set("ns", "key", encode("v1"), 3000); await cache.set("ns", "key", encode("v2"), 10000); await time.travel([5, "seconds"]); // should still be alive (second TTL of 10s) expect(new TextDecoder().decode(await cache.get("ns", "key"))).toBe("v2"); }); }); describe("del", () => { it("should delete specific keys", async () => { await cache.set("ns", "a", encode("1")); await cache.set("ns", "b", encode("2")); await cache.set("ns", "c", encode("3")); await cache.del("ns", "a", "b"); expect(await cache.get("ns", "a")).toBeUndefined(); expect(await cache.get("ns", "b")).toBeUndefined(); expect(await cache.get("ns", "c")).toBeDefined(); }); it("should delete all keys for a name when no keys specified", async () => { await cache.set("ns", "a", encode("1")); await cache.set("ns", "b", encode("2")); await cache.del("ns"); expect(await cache.get("ns", "a")).toBeUndefined(); expect(await cache.get("ns", "b")).toBeUndefined(); }); it("should clean up name when last key is deleted", async () => { await cache.set("ns", "only", encode("val")); await cache.del("ns", "only"); expect(cache.names()).not.toContain("ns"); }); }); describe("has", () => { it("should return true for existing keys", async () => { await cache.set("ns", "key", encode("val")); expect(await cache.has("ns", "key")).toBe(true); }); it("should return false for missing keys", async () => { expect(await cache.has("ns", "missing")).toBe(false); }); }); describe("keys", () => { it("should return all keys for a name", async () => { await cache.set("ns", "a", encode("1")); await cache.set("ns", "b", encode("2")); await cache.set("ns", "c", encode("3")); const keys = await cache.keys("ns"); expect(keys).toEqual(["a", "b", "c"]); }); it("should filter keys by prefix", async () => { await cache.set("ns", "user:1", encode("a")); await cache.set("ns", "user:2", encode("b")); await cache.set("ns", "post:1", encode("c")); const keys = await cache.keys("ns", "user:"); expect(keys).toEqual(["user:1", "user:2"]); }); it("should return empty array for missing name", async () => { expect(await cache.keys("missing")).toEqual([]); }); }); describe("clear", () => { it("should remove all entries", async () => { await cache.set("a", "k1", encode("v1")); await cache.set("b", "k2", encode("v2")); await cache.clear(); expect(await cache.get("a", "k1")).toBeUndefined(); expect(await cache.get("b", "k2")).toBeUndefined(); expect(cache.names()).toEqual([]); }); }); describe("incr", () => { it("should start from zero", async () => { const val = await cache.incr("ns", "counter", 1); expect(val).toBe(1); }); it("should increment existing value", async () => { await cache.incr("ns", "counter", 1); const val = await cache.incr("ns", "counter", 5); expect(val).toBe(6); }); it("should handle negative amounts", async () => { await cache.incr("ns", "counter", 10); const val = await cache.incr("ns", "counter", -3); expect(val).toBe(7); }); }); describe("stats", () => { it("should track hits and misses", async () => { await cache.set("ns", "key", encode("val")); await cache.get("ns", "key"); // hit await cache.get("ns", "key"); // hit await cache.get("ns", "miss"); // miss const stats = cache.stats(); expect(stats.hits).toBe(2); expect(stats.misses).toBe(1); }); it("should track sets and deletes", async () => { await cache.set("ns", "a", encode("1")); await cache.set("ns", "b", encode("2")); await cache.del("ns", "a"); const stats = cache.stats(); expect(stats.sets).toBe(2); expect(stats.deletes).toBe(1); }); }); describe("test utilities", () => { it("wasSet should check by name and key", async () => { await cache.set("ns", "key", encode("val")); expect(cache.wasSet("ns")).toBe(true); expect(cache.wasSet("ns", "key")).toBe(true); expect(cache.wasSet("ns", "other")).toBe(false); expect(cache.wasSet("other")).toBe(false); }); it("wasGet should check by name and key", async () => { await cache.set("ns", "key", encode("val")); await cache.get("ns", "key"); expect(cache.wasGet("ns")).toBe(true); expect(cache.wasGet("ns", "key")).toBe(true); expect(cache.wasGet("ns", "other")).toBe(false); }); it("wasDeleted should check by name and key", async () => { await cache.set("ns", "key", encode("val")); await cache.del("ns", "key"); expect(cache.wasDeleted("ns")).toBe(true); expect(cache.wasDeleted("ns", "key")).toBe(true); expect(cache.wasDeleted("ns", "other")).toBe(false); }); it("size should count entries", async () => { await cache.set("a", "k1", encode("v")); await cache.set("a", "k2", encode("v")); await cache.set("b", "k3", encode("v")); expect(cache.size("a")).toBe(2); expect(cache.size("b")).toBe(1); expect(cache.size()).toBe(3); }); it("names should list cache names", async () => { await cache.set("alpha", "k", encode("v")); await cache.set("beta", "k", encode("v")); expect(cache.names()).toEqual(["alpha", "beta"]); }); }); describe("reset", () => { it("should clear all state", async () => { await cache.set("ns", "key", encode("val")); await cache.get("ns", "key"); await cache.del("ns", "key"); cache.reset(); expect(cache.size()).toBe(0); expect(cache.getCalls).toHaveLength(0); expect(cache.setCalls).toHaveLength(0); expect(cache.delCalls).toHaveLength(0); expect(cache.stats()).toEqual({ hits: 0, misses: 0, sets: 0, deletes: 0, }); }); }); describe("error injection", () => { it("should throw getError when set", async () => { cache.getError = new Error("get failed"); await expect(cache.get("ns", "key")).rejects.toThrow("get failed"); }); it("should throw setError when set", async () => { cache.setError = new Error("set failed"); await expect(cache.set("ns", "key", encode("val"))).rejects.toThrow( "set failed", ); }); it("should throw delError when set", async () => { cache.delError = new Error("del failed"); await expect(cache.del("ns", "key")).rejects.toThrow("del failed"); }); }); });