Skip to content

Commit bed7c41

Browse files
committed
using vercel KV for last stats cache + mocking settimeout in tests
1 parent 33c1b68 commit bed7c41

File tree

6 files changed

+117
-16
lines changed

6 files changed

+117
-16
lines changed

package-lock.json

Lines changed: 46 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
},
1212
"dependencies": {
1313
"@next/font": "13.1.6",
14+
"@vercel/kv": "^2.0.0",
1415
"antd": "^5.3.1",
1516
"axios": "^1.0.0",
1617
"axios-cache-interceptor": "^1.5.3",

src/fetcher-utils.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Axios from "axios";
22
import { setupCache } from "axios-cache-interceptor";
3+
import { kv } from "@vercel/kv";
34

45
const instance = Axios.create({
56
baseURL: "https://codeforces.com/api",
@@ -9,5 +10,20 @@ const instance = Axios.create({
910
});
1011

1112
export const api = setupCache(instance);
12-
export const last_rating_cache = new Map();
13-
export const last_stats_cache = new Map();
13+
14+
class KVCache {
15+
constructor(type) {
16+
this.type = type;
17+
}
18+
19+
get(key) {
20+
return kv.get(key+"/"+this.type);
21+
}
22+
23+
set(key, value) {
24+
kv.set(key+"/"+this.type, value);
25+
}
26+
}
27+
28+
export const last_rating_cache = new KVCache("rating");
29+
export const last_stats_cache = new KVCache("stats");

src/fetcher.js

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@ import { api, last_rating_cache, last_stats_cache } from "@/fetcher-utils.js";
44
function fetch_error_handler(fetch, username, last_cache) {
55
return new Promise((resolve, reject) => {
66
const timeoutID = setTimeout(function () {
7-
const res = last_cache.get(username);
8-
if (res != null) {
9-
console.log("Using cached data for", username);
10-
resolve(res);
11-
}
12-
else reject({ status: 500, error: "Codeforces Server Error" });
13-
}, 4000);
7+
last_cache.get(username).then((res) => {
8+
if (res != null) {
9+
console.log("Using cached data for", username);
10+
resolve(res);
11+
}
12+
else reject({ status: 500, error: "Codeforces Server Error" });
13+
}).catch((error) => {
14+
console.error(error);
15+
reject({ status: 500, error: "Codeforces Server Error" });
16+
});
17+
18+
}, 5000);
1419
fetch()
1520
.then((result) => {
1621
clearTimeout(timeoutID);
@@ -51,12 +56,16 @@ export function get_rating(username, cache_seconds) {
5156
})
5257
.then((response) => {
5358
const res = response.data.result[0].rating || 0;
54-
last_rating_cache.set(username, res);
59+
try {
60+
last_rating_cache.set(username, res);
61+
} catch (error) {
62+
console.error(error);
63+
}
5564
resolve(res);
5665
})
5766
.catch((error) => {
5867
console.error(error);
59-
if (error.response.status === 400)
68+
if (error.response && error.response.status === 400)
6069
reject({ status: 400, error: "Codeforces Handle Not Found" });
6170
else reject({ status: 500, error: "Codeforces Server Error" });
6271
});
@@ -118,13 +127,17 @@ export function get_stats(username, cache_seconds) {
118127
friendOfCount,
119128
contribution,
120129
};
121-
122-
last_stats_cache.set(username, res);
130+
131+
try {
132+
last_stats_cache.set(username, res);
133+
} catch (error) {
134+
console.error(error);
135+
}
123136
resolve(res);
124137
})
125138
.catch((error) => {
126139
console.error(error);
127-
if (error.response.status === 400)
140+
if (error.response && error.response.status === 400)
128141
reject({ status: 400, error: "Codeforces Handle Not Found" });
129142
else reject({ status: 500, error: "Codeforces Server Error" });
130143
});

test/badge.test.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@ describe("badge handler", () => {
3030
send: jest.fn(),
3131
status: jest.fn().mockReturnThis(),
3232
};
33+
34+
jest.useFakeTimers();
3335
});
3436

3537
afterEach(() => {
3638
jest.clearAllMocks();
39+
jest.useRealTimers();
3740
});
3841

3942
it("should return a valid SVG response with proper headers", async () => {
@@ -122,7 +125,17 @@ describe("badge handler", () => {
122125
},
123126
});
124127

125-
await handler(req, res);
128+
last_rating_cache.get = () => {
129+
return new Promise((resolve) => {
130+
resolve(undefined);
131+
})
132+
};
133+
134+
const promise = handler(req, res);
135+
136+
jest.advanceTimersByTime(5000);
137+
138+
await promise;
126139

127140
expect(res.setHeader).toHaveBeenCalledWith("Content-Type", "text/plain");
128141
expect(res.setHeader).toHaveBeenCalledWith(

test/card.test.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,13 @@ describe("card handler", () => {
3232
send: jest.fn(),
3333
status: jest.fn().mockReturnThis(),
3434
};
35+
36+
jest.useFakeTimers();
3537
});
3638

3739
afterEach(() => {
3840
jest.clearAllMocks();
41+
jest.useRealTimers();
3942
});
4043

4144
it("should return a valid SVG response with proper headers", async () => {
@@ -95,8 +98,17 @@ describe("card handler", () => {
9598
status: 403,
9699
},
97100
});
101+
last_stats_cache.get = () => {
102+
return new Promise((resolve) => {
103+
resolve(undefined);
104+
})
105+
};
98106

99-
await handler(req, res);
107+
const promise = handler(req, res);
108+
109+
jest.advanceTimersByTime(5000);
110+
111+
await promise;
100112

101113
expect(res.setHeader).toHaveBeenCalledWith("Content-Type", "text/plain");
102114
expect(res.setHeader).toHaveBeenCalledWith(

0 commit comments

Comments
 (0)