Skip to content

Commit c4711b4

Browse files
test: createCachedFetch
1 parent 6d31575 commit c4711b4

2 files changed

Lines changed: 171 additions & 1 deletion

File tree

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import { createCachedFetch } from "../createCachedFetch";
2+
3+
describe("createCachedFetch", () => {
4+
// Helper to create a controlled promise
5+
const createControlledPromise = <T>(_value?: T) => {
6+
let resolve: (value: T) => void;
7+
let reject: (reason: any) => void;
8+
const promise = new Promise<T>((res, rej) => {
9+
resolve = res;
10+
reject = rej;
11+
});
12+
return { promise, resolve: resolve!, reject: reject! };
13+
};
14+
15+
it("should cache results for identical arguments", async () => {
16+
const fetchFn = vi.fn().mockResolvedValue("cached result");
17+
const cachedFn = createCachedFetch(fetchFn);
18+
19+
const result1 = await cachedFn("arg1", "arg2");
20+
const result2 = await cachedFn("arg1", "arg2");
21+
22+
expect(result1).toBe("cached result");
23+
expect(result2).toBe("cached result");
24+
expect(fetchFn).toHaveBeenCalledTimes(1);
25+
expect(fetchFn).toHaveBeenCalledWith("arg1", "arg2");
26+
});
27+
28+
it("should make separate calls for different arguments", async () => {
29+
const fetchFn = vi
30+
.fn()
31+
.mockResolvedValueOnce("result1")
32+
.mockResolvedValueOnce("result2");
33+
const cachedFn = createCachedFetch(fetchFn);
34+
35+
const result1 = await cachedFn("arg1");
36+
const result2 = await cachedFn("arg2");
37+
38+
expect(result1).toBe("result1");
39+
expect(result2).toBe("result2");
40+
expect(fetchFn).toHaveBeenCalledTimes(2);
41+
});
42+
43+
it("should handle concurrent calls with the same arguments", async () => {
44+
// Create a controlled promise to manually resolve the fetch
45+
const { promise, resolve } = createControlledPromise<string>();
46+
const fetchFn = vi.fn().mockReturnValue(promise);
47+
const cachedFn = createCachedFetch(fetchFn);
48+
49+
// Start two concurrent requests
50+
const request1 = cachedFn("concurrent");
51+
const request2 = cachedFn("concurrent");
52+
53+
// The fetch function should only be called once
54+
expect(fetchFn).toHaveBeenCalledTimes(1);
55+
56+
// Resolve the underlying fetch
57+
resolve("shared result");
58+
59+
// Both requests should resolve with the same result
60+
const [result1, result2] = await Promise.all([request1, request2]);
61+
expect(result1).toBe("shared result");
62+
expect(result2).toBe("shared result");
63+
});
64+
65+
it("should propagate errors and not cache failed results", async () => {
66+
const error = new Error("fetch failed");
67+
const fetchFn = vi
68+
.fn()
69+
.mockRejectedValueOnce(error)
70+
.mockResolvedValueOnce("result after error");
71+
const cachedFn = createCachedFetch(fetchFn);
72+
73+
// First call should reject
74+
await expect(cachedFn("error-test")).rejects.toThrow("fetch failed");
75+
76+
// Second call should retry fetch and succeed
77+
const result = await cachedFn("error-test");
78+
expect(result).toBe("result after error");
79+
expect(fetchFn).toHaveBeenCalledTimes(2);
80+
});
81+
82+
it("should use the custom UID resolver when provided", async () => {
83+
const fetchFn = vi.fn().mockResolvedValue("custom uid result");
84+
// Create custom resolver that only uses the first argument
85+
const uidResolver = (arg1: string) => arg1;
86+
const cachedFn = createCachedFetch(fetchFn, uidResolver);
87+
88+
await cachedFn("same", "different1");
89+
await cachedFn("same", "different2");
90+
91+
// Should only call once since our UID resolver only cares about first arg
92+
expect(fetchFn).toHaveBeenCalledTimes(1);
93+
});
94+
95+
it("should clear the cache when clearCache is called", async () => {
96+
const fetchFn = vi
97+
.fn()
98+
.mockResolvedValueOnce("first call")
99+
.mockResolvedValueOnce("after clear");
100+
const cachedFn = createCachedFetch(fetchFn);
101+
102+
// Make initial call
103+
const result1 = await cachedFn("clear-test");
104+
expect(result1).toBe("first call");
105+
106+
// Clear the cache
107+
cachedFn.clearCache();
108+
109+
// Make same call again - should fetch again
110+
const result2 = await cachedFn("clear-test");
111+
expect(result2).toBe("after clear");
112+
expect(fetchFn).toHaveBeenCalledTimes(2);
113+
});
114+
115+
it("should clear pending promises when clearCache is called", async () => {
116+
// Create a controlled promise
117+
const { promise, resolve } = createControlledPromise<string>();
118+
const fetchFn = vi.fn().mockReturnValue(promise);
119+
const cachedFn = createCachedFetch(fetchFn);
120+
121+
// Start a request
122+
const pendingRequest = cachedFn("pending");
123+
124+
// Clear cache while request is pending
125+
cachedFn.clearCache();
126+
127+
// Setup new mock for next call
128+
fetchFn.mockResolvedValueOnce("new result");
129+
130+
// New request should cause a new fetch
131+
const newRequest = cachedFn("pending");
132+
133+
// Resolve the original promise
134+
resolve("pending result");
135+
136+
// Check both results
137+
const pendingResult = await pendingRequest;
138+
const newResult = await newRequest;
139+
140+
expect(pendingResult).toBe("pending result");
141+
expect(newResult).toBe("new result");
142+
expect(fetchFn).toHaveBeenCalledTimes(2);
143+
});
144+
145+
it("should handle errors in concurrent requests", async () => {
146+
// Create a controlled promise
147+
const { promise, reject } = createControlledPromise<string>();
148+
const fetchFn = vi.fn().mockReturnValue(promise);
149+
const cachedFn = createCachedFetch(fetchFn);
150+
151+
// Start two concurrent requests
152+
const request1 = cachedFn("error-concurrent");
153+
const request2 = cachedFn("error-concurrent");
154+
155+
// Reject the promise
156+
const error = new Error("concurrent error");
157+
reject(error);
158+
159+
// Both requests should fail with the same error
160+
await expect(request1).rejects.toThrow("concurrent error");
161+
await expect(request2).rejects.toThrow("concurrent error");
162+
163+
// Setup next call to succeed
164+
fetchFn.mockResolvedValueOnce("after error");
165+
166+
// Next call should retry
167+
const result = await cachedFn("error-concurrent");
168+
expect(result).toBe("after error");
169+
expect(fetchFn).toHaveBeenCalledTimes(2);
170+
});
171+
});

src/visualBuilder/utils/getEntryPermissionsCached.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { getEntryPermissions } from "./getEntryPermissions";
22
import { createCachedFetch } from "./createCachedFetch";
33

4-
console.log("Loading module: getEntryPermissionsCached [original]");
54
export const getEntryPermissionsCached = createCachedFetch(
65
getEntryPermissions,
76
({ entryUid, contentTypeUid, locale }) =>

0 commit comments

Comments
 (0)