Skip to content

Commit accda1c

Browse files
authored
Merge PR #1 from shubham-kv/feat/clear-cache-cmd
feat: `clear-cache` command
2 parents 810f2a0 + 1bd4a43 commit accda1c

File tree

3 files changed

+142
-44
lines changed

3 files changed

+142
-44
lines changed

src/actions.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { Command } from "@commander-js/extra-typings";
2+
import { existsSync } from "fs";
3+
import { rm } from "fs/promises";
24

35
import { configureAndStartCacheProxy } from "./proxy";
46
import { isIntegerString } from "./utils/string";
57
import { cacheDirPath, testCacheDirPath } from "./constants";
68

79
type StartCommand = Command<[string], { port: string }, {}>;
810

9-
export function startServer(this: StartCommand) {
11+
export function startServer(this: StartCommand): void {
1012
const [origin] = this.processedArgs;
1113
const { port } = this.opts();
1214

@@ -32,3 +34,19 @@ export function startServer(this: StartCommand) {
3234
process.env.NODE_ENV === "test" ? testCacheDirPath : cacheDirPath,
3335
});
3436
}
37+
38+
export async function clearCache(this: Command<[], {}, {}>): Promise<void> {
39+
const cacheDir =
40+
process.env.NODE_ENV === "test" ? testCacheDirPath : cacheDirPath;
41+
42+
if (existsSync(cacheDir)) {
43+
try {
44+
await rm(cacheDir, { force: true, recursive: true });
45+
console.log('Cleared the cache successfully!');
46+
} catch (e) {
47+
this.error("Failed to remove the cache directory, try again");
48+
}
49+
} else {
50+
this.error("Invalid cache directory");
51+
}
52+
}

src/cli.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { Command } from "@commander-js/extra-typings";
44
import { description, version } from "../package.json";
55

6-
import { startServer } from "./actions";
6+
import { clearCache, startServer } from "./actions";
77

88
const program = new Command()
99
.name("caching-proxy")
@@ -19,8 +19,13 @@ program
1919
)
2020
.requiredOption(
2121
"-p, --port <port>",
22-
"Port number on which this server will run",
22+
"Port number on which this server will run"
2323
)
2424
.action(startServer);
2525

26+
program
27+
.command("clear-cache")
28+
.description("Clear the entire cache of the proxy server")
29+
.action(clearCache);
30+
2631
program.parse(process.argv);

test/app.spec.ts

Lines changed: 116 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { afterAll, beforeAll, describe, expect, test } from "vitest";
22
import path from "path";
33
import { existsSync, rmSync } from "fs";
4-
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
4+
import { ChildProcessWithoutNullStreams, spawn, spawnSync } from "child_process";
55
import axios, { AxiosInstance, AxiosResponse } from "axios";
66

77
import { sampleData } from "./__fixtures__/sample-data";
@@ -11,6 +11,46 @@ import { testCacheDirPath } from "../src/constants";
1111
import { ReqResConfig, StartCommandOptions } from "./types";
1212

1313
describe("caching-proxy-cli", () => {
14+
const originHost = "localhost";
15+
const originPort = 3000;
16+
const originHref = `http://${originHost}:${originPort}`;
17+
const proxyServerHost = "localhost";
18+
const proxyPort = "4000";
19+
const proxyHref = `http://${proxyServerHost}:${proxyPort}`;
20+
21+
const reqResConfigs: ReqResConfig[] = [
22+
{
23+
requestPath: "/api/sample-json",
24+
responseContentType: "application/json",
25+
responseData: sampleData.json,
26+
},
27+
{
28+
requestPath: "/api/sample-xml",
29+
responseContentType: "application/xml",
30+
responseData: sampleData.xml,
31+
},
32+
{
33+
requestPath: "/api/sample-plain-txt",
34+
responseContentType: "text/plain",
35+
responseData: sampleData.plainTxt,
36+
},
37+
{
38+
requestPath: "/public/sample-html",
39+
responseContentType: "text/html",
40+
responseData: sampleData.html,
41+
},
42+
{
43+
requestPath: "/public/sample-styles.css",
44+
responseContentType: "text/css",
45+
responseData: sampleData.css,
46+
},
47+
{
48+
requestPath: "/public/sample-script",
49+
responseContentType: "application/javascript",
50+
responseData: sampleData.javascript,
51+
},
52+
];
53+
1454
describe("start command with invalid options", () => {
1555
const invalidOptions: StartCommandOptions[] = [
1656
{ origin: "", port: "" },
@@ -67,13 +107,6 @@ describe("caching-proxy-cli", () => {
67107
});
68108

69109
describe("start command with valid options", () => {
70-
const originHost = "localhost";
71-
const originPort = 3000;
72-
const originHref = `http://${originHost}:${originPort}`;
73-
const proxyServerHost = "localhost";
74-
const proxyPort = "4000";
75-
const proxyHref = `http://${proxyServerHost}:${proxyPort}`;
76-
77110
let proxyChildProcess: ChildProcessWithoutNullStreams;
78111

79112
setupMockServer({ host: originHost, port: originPort });
@@ -120,39 +153,6 @@ describe("caching-proxy-cli", () => {
120153
});
121154
});
122155

123-
const reqResConfigs: ReqResConfig[] = [
124-
{
125-
requestPath: "/api/sample-json",
126-
responseContentType: "application/json",
127-
responseData: sampleData.json,
128-
},
129-
{
130-
requestPath: "/api/sample-xml",
131-
responseContentType: "application/xml",
132-
responseData: sampleData.xml,
133-
},
134-
{
135-
requestPath: "/api/sample-plain-txt",
136-
responseContentType: "text/plain",
137-
responseData: sampleData.plainTxt,
138-
},
139-
{
140-
requestPath: "/public/sample-html",
141-
responseContentType: "text/html",
142-
responseData: sampleData.html,
143-
},
144-
{
145-
requestPath: "/public/sample-styles.css",
146-
responseContentType: "text/css",
147-
responseData: sampleData.css,
148-
},
149-
{
150-
requestPath: "/public/sample-script",
151-
responseContentType: "application/javascript",
152-
responseData: sampleData.javascript,
153-
},
154-
];
155-
156156
describe.each(reqResConfigs)(
157157
"when proxy server is fetched for $responseContentType response content type API",
158158
(config) => {
@@ -220,4 +220,79 @@ describe("caching-proxy-cli", () => {
220220
}
221221
);
222222
});
223+
224+
describe("'clear-cache' command", () => {
225+
let proxyChildProcess: ChildProcessWithoutNullStreams;
226+
227+
setupMockServer({ host: originHost, port: originPort });
228+
229+
beforeAll(async () => {
230+
proxyChildProcess = spawn("ts-node", [
231+
path.join(__dirname, "../src/cli.ts"),
232+
"start",
233+
originHref,
234+
"-p",
235+
proxyPort,
236+
]);
237+
238+
// Wait for the proxy server to start
239+
await new Promise<void>((resolve, reject) => {
240+
const timeout = 5 * 1000;
241+
242+
proxyChildProcess.stdout.once("data", (chunk: any) => {
243+
const data = chunk.toString();
244+
245+
if (/caching proxy running/gi.test(data)) {
246+
resolve();
247+
} else {
248+
reject(new Error("Unexpected stdout data"));
249+
}
250+
});
251+
252+
setTimeout(() => {
253+
const err = new Error(
254+
`Failed to receive proxyChildProcess.stdout 'data' event within ${
255+
timeout / 1000
256+
}s`
257+
);
258+
reject(err);
259+
}, timeout);
260+
});
261+
262+
// Make a request to have something in cache directory to delete
263+
const axiosInstance = axios.create({
264+
baseURL: proxyHref,
265+
});
266+
267+
try {
268+
await axiosInstance.get(reqResConfigs[0].requestPath);
269+
} catch (e) {
270+
throw e;
271+
}
272+
});
273+
274+
afterAll(() => {
275+
const res = proxyChildProcess?.kill("SIGTERM");
276+
console.log(
277+
`Kill ${res ? "succeeded" : "failed"} for the spawned process`
278+
);
279+
});
280+
281+
describe("when 'clear-cache' command is executed", () => {
282+
let data: string | undefined = undefined
283+
284+
beforeAll(() => {
285+
const buffer = spawnSync("ts-node", [
286+
path.join(__dirname, "../src/cli.ts"),
287+
"clear-cache",
288+
]);
289+
290+
data = buffer.stdout.toString()
291+
})
292+
293+
test("should clear the cache directory", () => {
294+
expect(data).toMatch(/cleared the cache successfully/gi)
295+
});
296+
});
297+
});
223298
});

0 commit comments

Comments
 (0)