85 lines
3.1 KiB
TypeScript
85 lines
3.1 KiB
TypeScript
import { describe, expect, it } from "@suaveplan/testing/runner";
|
|
import { cachedStorage } from "../../src/factory/factory.js";
|
|
import { KeysCapableFakeCache } from "../setup/fake-cache.js";
|
|
import { FakeStorage } from "../setup/fake-storage.js";
|
|
|
|
/**
|
|
* Integration: two `CachedStorage` wrappers sharing one
|
|
* `AbstractCache` instance with distinct namespaces. Neither wrapper
|
|
* observes the other's entries on `get`, and `wrapperA.clear()` MUST
|
|
* NOT evict `wrapperB`'s entries.
|
|
*
|
|
* Uses the `KeysCapableFakeCache` test helper because the canonical
|
|
* `MemoryCache` from `@suaveplan/cache` does not expose `keys()`. The
|
|
* test helper is a real `AbstractCache<Uint8Array>` subclass — not a
|
|
* mock — so the integration exercises real iteration over real
|
|
* cache entries.
|
|
*/
|
|
describe("integration: namespaced isolation on a shared cache", () => {
|
|
it("distinct namespaces isolate entries on a shared cache", {
|
|
timeout: 30000,
|
|
}, async () => {
|
|
const innerA = new FakeStorage();
|
|
const innerB = new FakeStorage();
|
|
const shared = new KeysCapableFakeCache();
|
|
|
|
const wrapperA = cachedStorage(innerA, {
|
|
cache: shared,
|
|
namespace: "a",
|
|
});
|
|
const wrapperB = cachedStorage(innerB, {
|
|
cache: shared,
|
|
namespace: "b",
|
|
});
|
|
|
|
innerA.seed("clip.mov", new Uint8Array([0xa]));
|
|
innerB.seed("clip.mov", new Uint8Array([0xb]));
|
|
|
|
// Wrapper A populates its entry under "a::clip.mov".
|
|
await wrapperA.get("clip.mov");
|
|
expect(shared._peek("a::clip.mov")).toEqual(new Uint8Array([0xa]));
|
|
expect(shared._peek("b::clip.mov")).toBeUndefined();
|
|
|
|
// Wrapper B observes a miss for the same logical key (different namespace).
|
|
const fromB = await wrapperB.get("clip.mov");
|
|
expect(fromB).toEqual(new Uint8Array([0xb]));
|
|
expect(wrapperB.getCacheStats().misses).toBe(1);
|
|
expect(shared._peek("b::clip.mov")).toEqual(new Uint8Array([0xb]));
|
|
});
|
|
|
|
it("clear on one wrapper does not evict the other's entries", {
|
|
timeout: 30000,
|
|
}, async () => {
|
|
const innerA = new FakeStorage();
|
|
const innerB = new FakeStorage();
|
|
const shared = new KeysCapableFakeCache();
|
|
const wrapperA = cachedStorage(innerA, {
|
|
cache: shared,
|
|
namespace: "a",
|
|
});
|
|
const wrapperB = cachedStorage(innerB, {
|
|
cache: shared,
|
|
namespace: "b",
|
|
});
|
|
|
|
// Populate both.
|
|
innerA.seed("x", new Uint8Array([0xa, 1]));
|
|
innerA.seed("y", new Uint8Array([0xa, 2]));
|
|
innerB.seed("z", new Uint8Array([0xb, 1]));
|
|
await wrapperA.get("x");
|
|
await wrapperA.get("y");
|
|
await wrapperB.get("z");
|
|
|
|
expect(shared._peek("a::x")).toBeDefined();
|
|
expect(shared._peek("a::y")).toBeDefined();
|
|
expect(shared._peek("b::z")).toBeDefined();
|
|
|
|
// Wrapper A clears.
|
|
await wrapperA.clear();
|
|
|
|
expect(shared._peek("a::x")).toBeUndefined();
|
|
expect(shared._peek("a::y")).toBeUndefined();
|
|
expect(shared._peek("b::z")).toEqual(new Uint8Array([0xb, 1]));
|
|
});
|
|
});
|