📄 src/downloader/test/cache-mock.ts
D-OPEN SOVEREIGN

Documentation & Insights

Installs a fresh in-memory `caches` global and returns it.
Installs a `navigator.storage.estimate()` mock returning the given usage/quota.
1// Minimal in-memory CacheStorage/Cache polyfill for unit tests — Node has no
2// Cache Storage API. Only implements what src/downloader actually calls
3// (open/match/put/delete/keys), keyed by request URL like the real API.
4
5class MockCache {
6    private store = new Map<string, Response>();
7
8    async match(request: RequestInfo): Promise<Response | undefined> {
9        const key = requestKey(request);
10        const resp = this.store.get(key);
11        return resp ? resp.clone() : undefined;
12    }
13
14    async put(request: RequestInfo, response: Response): Promise<void> {
15        const key = requestKey(request);
16        this.store.set(key, response.clone());
17    }
18
19    async delete(request: RequestInfo): Promise<boolean> {
20        const key = requestKey(request);
21        return this.store.delete(key);
22    }
23
24    async keys(): Promise<Request[]> {
25        return [...this.store.keys()].map(url => new Request(url));
26    }
27}
28
29function toAbsolute(url: string): string {
30    return url.startsWith('http') ? url : `https://mock.local${url}`;
31}
32
33// Requests/strings are always normalized to an absolute URL so a key looked up
34// via a plain string (put/match/delete by path) and via a Request object
35// returned from keys() (real Cache.delete(request) usage) resolve to the same
36// map entry — mirroring how the real Cache API keys by full request URL.
37function requestKey(request: RequestInfo): string {
38    return toAbsolute(typeof request === 'string' ? request : request.url);
39}
40
41class MockCacheStorage {
42    private named = new Map<string, MockCache>();
43
44    async open(name: string): Promise<MockCache> {
45        let c = this.named.get(name);
46        if (!c) {
47            c = new MockCache();
48            this.named.set(name, c);
49        }
50        return c;
51    }
52
53    async delete(name: string): Promise<boolean> {
54        return this.named.delete(name);
55    }
56
57    async has(name: string): Promise<boolean> {
58        return this.named.has(name);
59    }
60}
61
62/** Installs a fresh in-memory `caches` global and returns it. */
63export function installCacheMock(): MockCacheStorage {
64    const storage = new MockCacheStorage();
65    (globalThis as unknown as { caches: MockCacheStorage }).caches = storage;
66    return storage;
67}
68
69/** Installs a `navigator.storage.estimate()` mock returning the given usage/quota. */
70export function installStorageEstimate(usage: number, quota: number): void {
71    (globalThis.navigator as unknown as { storage: StorageManager }).storage = {
72        estimate: async () => ({ usage, quota }),
73    } as StorageManager;
74}
75