In this example, we define a MemoryStore
class that uses a Map
to store promises for previously fetched resources. The get
method takes in a URL and a function that returns a promise to fetch the resource at that URL. If the URL is already in the cache, the method returns the existing promise. Otherwise, the method calls the fetchData
function to get a new promise, stores it in the cache, and returns it.
We also define a ResourceController
class that uses the MemoryStore
class to improve the performance of resource requests. The getResource
method takes in a request object and a response object and extracts the URL from the request parameters. It then creates a fetchData
function that uses the fetch
API to fetch the resource at the specified URL. It calls the get
method of the MemoryStore
object to get a promise for the resource, and then uses the then
method to extract the text body of the response and send it back in the response.
The clearCache
method simply calls the clear
method of the MemoryStore
object to clear the cache.
Finally, we define an Express.js app and set up two endpoints for the getResource
and clearCache
methods. We create an instance of the ResourceController
class and pass it to the route handlers.
Note that in order to use this class, you'll need to have the fetch
API available in your project. Also, make sure to handle any errors that may occur during the fetching process, such as network errors or errors with the resource server.
import express, { Request, Response } from 'express';
class MemoryStore {
private cache: Map<string, Promise<Response>>;
constructor() {
this.cache = new Map();
}
get(url: string, fetchData: () => Promise<Response>): Promise<Response> {
if (this.cache.has(url)) {
return this.cache.get(url)!;
}
const promise = fetchData();
this.cache.set(url, promise);
return promise;
}
clear(): void {
this.cache.clear();
}
}
class ResourceController {
private store: MemoryStore;
constructor() {
this.store = new MemoryStore();
}
getResource(req: Request, res: Response): void {
const url = req.params.url;
const fetchData = async () => {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch resource from ${url}`);
}
return response;
};
const promise = this.store.get(url, fetchData);
promise
.then(response => response.text())
.then(body => {
res.send(body);
})
.catch(error => {
console.error(error);
res.status(500).send('Error fetching resource');
});
}
clearCache(req: Request, res: Response): void {
this.store.clear();
res.send('Cache cleared successfully');
}
}
const app = express();
const controller = new ResourceController();
app.get('/resource/:url', (req, res) => controller.getResource(req, res));
app.delete('/cache', (req, res) => controller.clearCache(req, res));
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
Using Supertest to test the getResource
and clearCache
methods of the ResourceController
import supertest from 'supertest';
import { MemoryStore, ResourceController } from './ResourceController';
describe('ResourceController', () => {
let store: MemoryStore;
let controller: ResourceController;
let app: Express.Application;
beforeEach(() => {
store = new MemoryStore();
controller = new ResourceController(store);
app = express();
app.get('/resource/:url', (req, res) => controller.getResource(req, res));
app.delete('/cache', (req, res) => controller.clearCache(req, res));
});
afterEach(() => {
jest.restoreAllMocks();
});
describe('getResource', () => {
it('should return resource body when resource is fetched successfully', async () => {
const expectedBody = 'Resource body';
const url = 'http://example.com/resource';
const response = new Response(expectedBody);
jest.spyOn(global, 'fetch').mockResolvedValueOnce(response);
const result = await supertest(app).get(`/resource/${url}`).expect(200);
expect(global.fetch).toHaveBeenCalledWith(url);
expect(result.text).toBe(expectedBody);
});
it('should return 500 error when resource fetch fails', async () => {
const url = 'http://example.com/resource';
jest.spyOn(global, 'fetch').mockRejectedValueOnce(new Error('Failed to fetch resource'));
const result = await supertest(app).get(`/resource/${url}`).expect(500);
expect(global.fetch).toHaveBeenCalledWith(url);
expect(result.text).toBe('Error fetching resource');
});
});
describe('clearCache', () => {
it('should clear the memory store cache', async () => {
const url = 'http://example.com/resource';
const response = new Response('Resource body');
jest.spyOn(global, 'fetch').mockResolvedValueOnce(response);
await supertest(app).get(`/resource/${url}`);
await supertest(app).delete('/cache').expect(200);
expect(store['cache'].size).toBe(0);
});
});
});
In this example, we import the supertest
library and define a Jest test suite for the ResourceController
class. We create a new MemoryStore
object and a new ResourceController
object using the store, and we define an Express.js app with routes that use the controller methods.
We then define two test cases for the getResource
method. The first test case simulates a successful fetch request, sets up a mock response from the fetch
API using Jest, and uses supertest
to make a request to the app. It expects a 200 response code and the expected resource body. The second test case simulates a failed fetch request and expects a 500 error response.
Finally, we define a test case for the clearCache
method that makes a request to the app to clear the cache and checks that the cache
property of the MemoryStore
object is empty.
Note that in this example, we use the global.fetch
method from the Jest environment to set up mock responses for the fetch
API. If you're using a different testing environment, you may need to use a different method to mock the fetch
API.
Top comments (0)