Skip to content

Commit b748f72

Browse files
Merge pull request #95 from MobilityData/feat/67-revalidate-cron-updated
feat: revalidation cron changed to gbfs
2 parents b0029b4 + 9c16c73 commit b748f72

3 files changed

Lines changed: 101 additions & 7 deletions

File tree

src/app/api/revalidate/route.spec.ts

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* @jest-environment node
33
*/
4-
import { POST } from './route';
4+
import { GET, POST } from './route';
55
import { revalidatePath, revalidateTag } from 'next/cache';
66

77
// Mock Next.js cache
@@ -15,6 +15,96 @@ jest.mock('../../../i18n/routing', () => ({
1515
AVAILABLE_LOCALES: ['en', 'fr'],
1616
}));
1717

18+
describe('GET /api/revalidate', () => {
19+
const mockRevalidatePath = revalidatePath as jest.MockedFunction<
20+
typeof revalidatePath
21+
>;
22+
const mockRevalidateTag = revalidateTag as jest.MockedFunction<
23+
typeof revalidateTag
24+
>;
25+
const originalEnv = process.env;
26+
27+
beforeEach(() => {
28+
jest.clearAllMocks();
29+
process.env = { ...originalEnv };
30+
});
31+
32+
afterAll(() => {
33+
process.env = originalEnv;
34+
});
35+
36+
it('returns 500 when CRON_SECRET is not configured', async () => {
37+
delete process.env.CRON_SECRET;
38+
39+
const request = new Request('http://localhost:3000/api/revalidate', {
40+
method: 'GET',
41+
headers: {
42+
authorization: 'Bearer some-secret',
43+
},
44+
});
45+
46+
const response = await GET(request);
47+
const json = await response.json();
48+
49+
expect(response.status).toBe(500);
50+
expect(json).toEqual({
51+
ok: false,
52+
error: 'Server misconfigured: CRON_SECRET missing',
53+
});
54+
expect(mockRevalidatePath).not.toHaveBeenCalled();
55+
expect(mockRevalidateTag).not.toHaveBeenCalled();
56+
});
57+
58+
it('returns 401 when authorization header does not match', async () => {
59+
process.env.CRON_SECRET = 'correct-secret';
60+
61+
const request = new Request('http://localhost:3000/api/revalidate', {
62+
method: 'GET',
63+
headers: {
64+
authorization: 'Bearer wrong-secret',
65+
},
66+
});
67+
68+
const response = await GET(request);
69+
const json = await response.json();
70+
71+
expect(response.status).toBe(401);
72+
expect(json).toEqual({
73+
ok: false,
74+
error: 'Unauthorized',
75+
});
76+
expect(mockRevalidatePath).not.toHaveBeenCalled();
77+
expect(mockRevalidateTag).not.toHaveBeenCalled();
78+
});
79+
80+
it('revalidates only GBFS feed pages for authorized cron requests', async () => {
81+
process.env.CRON_SECRET = 'test-secret';
82+
83+
const request = new Request('http://localhost:3000/api/revalidate', {
84+
method: 'GET',
85+
headers: {
86+
authorization: 'Bearer test-secret',
87+
},
88+
});
89+
90+
const response = await GET(request);
91+
const json = await response.json();
92+
93+
expect(response.status).toBe(200);
94+
expect(json).toEqual({
95+
ok: true,
96+
message: 'All GBFS feeds revalidated successfully',
97+
});
98+
expect(mockRevalidateTag).toHaveBeenCalledWith('feed-type-gbfs', 'max');
99+
expect(mockRevalidatePath).toHaveBeenCalledWith(
100+
'/[locale]/feeds/gbfs/[feedId]',
101+
'layout',
102+
);
103+
expect(mockRevalidateTag).toHaveBeenCalledTimes(1);
104+
expect(mockRevalidatePath).toHaveBeenCalledTimes(1);
105+
});
106+
});
107+
18108
describe('POST /api/revalidate', () => {
19109
const mockRevalidatePath = revalidatePath as jest.MockedFunction<
20110
typeof revalidatePath

src/app/api/revalidate/route.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ const defaultRevalidateOptions: RevalidateBody = {
2929
};
3030

3131
/**
32-
* GET handler for the Vercel cron job that revalidates all feed pages once a day.
32+
* GET handler for the Vercel cron job that revalidates all GBFS feed pages.
3333
* Vercel automatically passes Authorization: Bearer <CRON_SECRET> with each invocation.
34-
* Configured in vercel.json under "crons" (schedule: 0 9 * * * = 4am EST / 9am UTC).
34+
* Configured in vercel.json under "crons" for 4am UTC Monday-Saturday and 7am UTC Sunday.
3535
*/
3636
export async function GET(req: Request): Promise<NextResponse> {
3737
const authHeader = req.headers.get('authorization');
@@ -52,13 +52,13 @@ export async function GET(req: Request): Promise<NextResponse> {
5252
}
5353

5454
try {
55-
revalidateAllFeeds();
55+
revalidateAllGbfsFeeds();
5656
console.log(
57-
'[cron] revalidate /api/revalidate: all-feeds revalidation triggered',
57+
'[cron] revalidate /api/revalidate: all-gbfs-feeds revalidation triggered',
5858
);
5959
return NextResponse.json({
6060
ok: true,
61-
message: 'All feeds revalidated successfully',
61+
message: 'All GBFS feeds revalidated successfully',
6262
});
6363
} catch (error) {
6464
console.error(

vercel.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
"crons": [
33
{
44
"path": "/api/revalidate",
5-
"schedule": "0 9 * * *"
5+
"schedule": "0 4 * * 1-6"
6+
},
7+
{
8+
"path": "/api/revalidate",
9+
"schedule": "0 7 * * 0"
610
}
711
]
812
}

0 commit comments

Comments
 (0)