Skip to content

Commit a21097e

Browse files
committed
add OpenProcessing API caching tests
- Add comprehensive tests for getCurationSketches memoization - Test getSketch cache usage and fallback to individual API calls - Test getSketchSize API calls during build simulation - Verify API call count regression to prevent performance issues - Include test for individual sketch fetching when not in cache
1 parent 6c6f9a3 commit a21097e

File tree

2 files changed

+152
-2
lines changed

2 files changed

+152
-2
lines changed

src/api/OpenProcessing.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ export type OpenProcessingCurationResponse = Array<{
3333
fullname: string;
3434
}>;
3535

36+
// Selected Sketches from the 2025 curation
37+
export const priorityIds = ['2690038', '2484739', '2688829', '2689119', '2690571', '2690405','2684408' , '2693274', '2693345', '2691712']
38+
3639
/**
3740
* Get basic info for the sketches contained in a Curation
3841
* from the OpenProcessing API
@@ -60,8 +63,7 @@ export const getCurationSketches = memoize(async (
6063
}
6164
const payload2 = await response2.json();
6265

63-
// Selected Sketches from the 2025 curation
64-
const priorityIds = ['2690038', '2484739', '2688829', '2689119', '2690571', '2690405','2684408' , '2693274', '2693345', '2691712']
66+
6567

6668
const prioritySketches = payload2.filter(
6769
(sketch: OpenProcessingCurationResponse[number]) => priorityIds.includes(String(sketch.visualID)))

test/api/OpenProcessing.test.ts

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// src/api/OpenProcessing.test.ts
2+
3+
import { getCurationSketches, getSketch, getSketchSize, priorityIds, type OpenProcessingCurationResponse } from '@/src/api/OpenProcessing';
4+
import { describe, it, expect, vi, beforeEach } from 'vitest';
5+
6+
const mockFetch = vi.fn();
7+
8+
vi.stubGlobal('fetch', mockFetch);
9+
10+
// Test data: first item is mock data, second uses actual priority ID from current curation
11+
const getCurationSketchesData : OpenProcessingCurationResponse = [{
12+
visualID: 101,
13+
title: 'Sketch One',
14+
description: 'Description One',
15+
instructions: 'Instructions One',
16+
mode: 'p5js',
17+
createdOn: '2025-01-01',
18+
userID: 'User One',
19+
submittedOn: '2025-01-01',
20+
fullname: 'Fullname One'
21+
},
22+
{
23+
visualID: Number(priorityIds[0]), // Real ID from current curation priority list
24+
title: 'Sketch Two',
25+
description: 'Description Two',
26+
instructions: 'Instructions Two',
27+
mode: 'p5js',
28+
createdOn: '2025-01-01',
29+
userID: 'User Two',
30+
submittedOn: '2025-01-01',
31+
fullname: 'Fullname Two'
32+
}]
33+
34+
describe('OpenProcessing API Caching', () => {
35+
36+
beforeEach(() => {
37+
vi.clearAllMocks();
38+
39+
getCurationSketches.cache.clear?.();
40+
getSketch.cache.clear?.();
41+
getSketchSize.cache.clear?.();
42+
});
43+
44+
// Case 1: Verify caching for getCurationSketches
45+
it('should only call the API once even if getCurationSketches is called multiple times', async () => {
46+
47+
mockFetch.mockResolvedValue({
48+
ok: true,
49+
json: () => Promise.resolve(getCurationSketchesData),
50+
});
51+
52+
await getCurationSketches();
53+
await getCurationSketches();
54+
55+
// Check if fetch was called exactly 2 times (for the two curation IDs).
56+
// If this number becomes 4, it means the caching is broken.
57+
expect(mockFetch).toHaveBeenCalledTimes(2);
58+
});
59+
60+
// Case 2: Verify getSketch uses cached data from getCurationSketches
61+
it('should use cached data from getCurationSketches for getSketch calls', async () => {
62+
63+
mockFetch.mockResolvedValueOnce({ // for curationId
64+
ok: true,
65+
json: () => Promise.resolve([getCurationSketchesData[0]]),
66+
}).mockResolvedValueOnce({ // for newCurationId
67+
ok: true,
68+
json: () => Promise.resolve([getCurationSketchesData[1]]),
69+
});
70+
71+
// Call the main function to populate the cache.
72+
await getCurationSketches();
73+
// At this point, fetch has been called twice.
74+
expect(mockFetch).toHaveBeenCalledTimes(2);
75+
76+
// Now, call getSketch with an ID that should be in the cache.
77+
const sketch = await getSketch(getCurationSketchesData[0].visualID);
78+
expect(sketch.title).toBe('Sketch One');
79+
const sketch2 = await getSketch(getCurationSketchesData[1].visualID);
80+
expect(sketch2.title).toBe('Sketch Two');
81+
82+
// Verify that no additional fetch calls were made.
83+
// The call count should still be 2 because the data came from the cache.
84+
expect(mockFetch).toHaveBeenCalledTimes(2);
85+
});
86+
87+
// Case 3: Verify getSketch fetches individual sketch when not in cache
88+
it('should fetch individual sketch when not in cache', async () => {
89+
90+
// for curationId
91+
mockFetch.mockResolvedValueOnce({
92+
ok: true,
93+
json: () => Promise.resolve([])
94+
}).mockResolvedValueOnce({ // for newCurationId
95+
ok: true,
96+
json: () => Promise.resolve([])
97+
});
98+
99+
await getCurationSketches(); // Create empty cache
100+
101+
// Individual sketch API call
102+
mockFetch.mockResolvedValueOnce({
103+
ok: true,
104+
json: () => Promise.resolve({ visualID: 999, title: 'Individual Sketch' })
105+
});
106+
107+
const sketch = await getSketch(999);
108+
expect(sketch.title).toBe('Individual Sketch');
109+
expect(mockFetch).toHaveBeenCalledTimes(3); // 2 for empty curations in getCurationSketches + 1 for individual call in getSketch
110+
});
111+
112+
// Case 4: Overall regression test for total sketch page generation
113+
it('should not exceed the expected number of API calls during a build simulation', async () => {
114+
115+
// Mock the responses for getCurationSketches.
116+
mockFetch.mockResolvedValueOnce({ // for curationId
117+
ok: true,
118+
json: () => Promise.resolve([getCurationSketchesData[0]]),
119+
}).mockResolvedValueOnce({ // for newCurationId
120+
ok: true,
121+
json: () => Promise.resolve([getCurationSketchesData[1]]),
122+
});
123+
124+
// 2. Mock the response for getSketchSize calls.
125+
mockFetch.mockResolvedValue({ // for all subsequent calls
126+
ok: true,
127+
json: () => Promise.resolve([{ code: 'createCanvas(400, 400);' }]),
128+
});
129+
130+
// --- sketch page build simulation ---
131+
// This simulates what happens during `getStaticPaths`.
132+
const sketches = await getCurationSketches(); // Makes 2 API calls.
133+
134+
// This simulates what happens as each page is generated.
135+
for (const sketch of sketches) {
136+
// Inside the page component, getSketch and getSketchSize would be called.
137+
await getSketch(sketch.visualID); // Uses cache (0 new calls).
138+
await getSketchSize(sketch.visualID); // Makes 1 new API call.
139+
}
140+
// --- simulation end ---
141+
142+
// Calculate the total expected calls.
143+
// 2 for getCurationSketches + 1 for each sketch's getSketchSize call.
144+
const expectedCalls = 2 + sketches.length;
145+
expect(mockFetch).toHaveBeenCalledTimes(expectedCalls);
146+
147+
});
148+
});

0 commit comments

Comments
 (0)