Ctrl K
ComunidadGuías…

shared/utils/LRUCache.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { LRUCache } from "./LRUCache";

describe("LRUCache", () => {
  it("stores and retrieves values", () => {
    const cache = new LRUCache<number>({ max: 3, storage: "memory" });
    cache.set("a", 1);

    expect(cache.get("a")).toBe(1);
    expect(cache.get("missing")).toBeUndefined();
    expect(cache.size).toBe(1);
  });

  it("evicts the least recently used entry when over max", () => {
    const cache = new LRUCache<number>({ max: 2, storage: "memory" });
    cache.set("a", 1);
    cache.set("b", 2);
    cache.set("c", 3);

    expect(cache.size).toBe(2);
    expect(cache.get("a")).toBeUndefined();
    expect(cache.get("b")).toBe(2);
    expect(cache.get("c")).toBe(3);
  });

  it("marks an entry as recently used when read", () => {
    const cache = new LRUCache<number>({ max: 2, storage: "memory" });
    cache.set("a", 1);
    cache.set("b", 2);
    cache.get("a");
    cache.set("c", 3);

    expect(cache.get("a")).toBe(1);
    expect(cache.get("b")).toBeUndefined();
  });

  it("overwrites without growing", () => {
    const cache = new LRUCache<number>({ max: 2, storage: "memory" });
    cache.set("a", 1);
    cache.set("a", 2);

    expect(cache.size).toBe(1);
    expect(cache.get("a")).toBe(2);
  });

  it("deletes and clears entries", () => {
    const cache = new LRUCache<number>({ max: 3, storage: "memory" });
    cache.set("a", 1);
    cache.set("b", 2);

    expect(cache.delete("a")).toBe(true);
    expect(cache.delete("a")).toBe(false);
    expect(cache.has("a")).toBe(false);
    expect(cache.has("b")).toBe(true);

    cache.clear();
    expect(cache.size).toBe(0);
  });

  // Shared tests run in both node and jsdom; web storage only exists in jsdom.
  const hasStorage = typeof window !== "undefined";

  describe.runIf(!hasStorage)("without web storage", () => {
    it("falls back to an in-memory cache", () => {
      const cache = new LRUCache<string>({
        max: 1,
        namespace: "test",
        storage: "sessionStorage",
      });
      cache.set("a", "one");
      cache.set("b", "two");

      expect(cache.get("a")).toBeUndefined();
      expect(cache.get("b")).toBe("two");
    });
  });

  describe.runIf(hasStorage)("with persistence", () => {
    beforeEach(() => {
      sessionStorage.clear();
      localStorage.clear();
    });

    it("restores entries from storage", () => {
      const cache = new LRUCache<string>({
        max: 3,
        namespace: "test",
        storage: "sessionStorage",
      });
      cache.set("a", "one");
      cache.set("b", "two");

      const restored = new LRUCache<string>({
        max: 3,
        namespace: "test",
        storage: "sessionStorage",
      });
      expect(restored.get("a")).toBe("one");
      expect(restored.get("b")).toBe("two");
    });

    it("restores entries from local storage", () => {
      const cache = new LRUCache<string>({
        max: 3,
        namespace: "test-local",
        storage: "localStorage",
      });
      cache.set("a", "one");
      cache.set("b", "two");

      const restored = new LRUCache<string>({
        max: 3,
        namespace: "test-local",
        storage: "localStorage",
      });
      expect(restored.get("a")).toBe("one");
      expect(restored.get("b")).toBe("two");
    });

    it("reads persisted values only when they are accessed", () => {
      const cache = new LRUCache<string>({
        max: 3,
        namespace: "test",
        storage: "sessionStorage",
      });
      cache.set("a", "one");
      cache.set("b", "two");

      const restored = new LRUCache<string>({
        max: 3,
        namespace: "test",
        storage: "sessionStorage",
      });
      // Hydrating reads the index alone, so a value replaced afterwards is
      // still the one returned on first access.
      expect(restored.size).toBe(2);
      sessionStorage.setItem("test:a", JSON.stringify("updated"));
      expect(restored.get("a")).toBe("updated");

      // Once in memory the value is not read from storage again.
      sessionStorage.setItem("test:a", JSON.stringify("ignored"));
      expect(restored.get("a")).toBe("updated");
    });

    it("drops an indexed key whose value is missing", () => {
      const cache = new LRUCache<string>({
        max: 3,
        namespace: "test",
        storage: "sessionStorage",
      });
      cache.set("a", "one");
      sessionStorage.removeItem("test:a");

      const restored = new LRUCache<string>({
        max: 3,
        namespace: "test",
        storage: "sessionStorage",
      });
      expect(restored.get("a")).toBeUndefined();
      expect(restored.size).toBe(0);
    });

    it("does not restore entries from another namespace", () => {
      const cache = new LRUCache<string>({
        max: 3,
        namespace: "test",
        storage: "sessionStorage",
      });
      cache.set("a", "one");

      const other = new LRUCache<string>({
        max: 3,
        namespace: "other",
        storage: "sessionStorage",
      });
      expect(other.get("a")).toBeUndefined();
    });

    it("removes evicted entries from storage", () => {
      const cache = new LRUCache<string>({
        max: 1,
        namespace: "test",
        storage: "sessionStorage",
      });
      cache.set("a", "one");
      cache.set("b", "two");

      expect(sessionStorage.getItem("test:a")).toBeNull();

      const restored = new LRUCache<string>({
        max: 1,
        namespace: "test",
        storage: "sessionStorage",
      });
      expect(restored.get("a")).toBeUndefined();
      expect(restored.get("b")).toBe("two");
    });

    it("removes persisted entries on clear", () => {
      const cache = new LRUCache<string>({
        max: 3,
        namespace: "test",
        storage: "sessionStorage",
      });
      cache.set("a", "one");
      cache.clear();

      const restored = new LRUCache<string>({
        max: 3,
        namespace: "test",
        storage: "sessionStorage",
      });
      expect(restored.size).toBe(0);
    });

    it("starts empty when the persisted index is corrupt", () => {
      sessionStorage.setItem("test:keys", "not json");

      const cache = new LRUCache<string>({
        max: 3,
        namespace: "test",
        storage: "sessionStorage",
      });
      expect(cache.size).toBe(0);

      cache.set("a", "one");
      expect(cache.get("a")).toBe("one");
    });
  });
});