CommunitySolidServer/test/unit/server/HandlerServerConfigurator.test.ts
Joachim Van Herwegen 6248ed0938 refactor: Replace linting configurations
The previous package was outdated, preventing us from updating TS.
This one also lints YAML and JSON,
and applies many more rules to the test files,
explaining all the changes in this PR.
2023-11-02 09:49:17 +01:00

144 lines
5.6 KiB
TypeScript

import { EventEmitter } from 'events';
import type { IncomingMessage, Server, ServerResponse } from 'http';
import { Readable } from 'stream';
import type { Logger } from '../../../src/logging/Logger';
import { getLoggerFor } from '../../../src/logging/LogUtil';
import { HandlerServerConfigurator } from '../../../src/server/HandlerServerConfigurator';
import type { HttpHandler } from '../../../src/server/HttpHandler';
import { flushPromises } from '../../util/Util';
jest.mock('../../../src/logging/LogUtil', (): any => {
const logger: Logger =
{ error: jest.fn(), info: jest.fn() } as any;
return { getLoggerFor: (): Logger => logger };
});
describe('A HandlerServerConfigurator', (): void => {
const logger: jest.Mocked<Logger> = getLoggerFor('mock') as any;
let request: jest.Mocked<IncomingMessage>;
let response: jest.Mocked<ServerResponse>;
let server: Server;
let handler: jest.Mocked<HttpHandler>;
let listener: HandlerServerConfigurator;
beforeEach(async(): Promise<void> => {
// Clearing the logger mock
jest.clearAllMocks();
request = Readable.from('') as any;
request.method = 'GET';
request.url = '/';
response = {
headersSent: false,
end: jest.fn(),
setHeader: jest.fn(),
writeHead: jest.fn(),
} as any;
response.end.mockImplementation((): any => {
(response as any).headersSent = true;
});
response.writeHead.mockReturnValue(response);
server = new EventEmitter() as any;
handler = {
handleSafe: jest.fn((): void => {
(response as any).headersSent = true;
}),
} as any;
listener = new HandlerServerConfigurator(handler);
await listener.handle(server);
});
it('sends incoming requests to the handler.', async(): Promise<void> => {
server.emit('request', request, response);
await flushPromises();
expect(handler.handleSafe).toHaveBeenCalledTimes(1);
expect(handler.handleSafe).toHaveBeenLastCalledWith({ request, response });
expect(response.setHeader).toHaveBeenCalledTimes(0);
expect(response.writeHead).toHaveBeenCalledTimes(0);
expect(response.end).toHaveBeenCalledTimes(0);
});
it('returns a 404 when the handler does not do anything.', async(): Promise<void> => {
handler.handleSafe.mockImplementation(jest.fn());
server.emit('request', request, response);
await flushPromises();
expect(response.setHeader).toHaveBeenCalledTimes(0);
expect(response.writeHead).toHaveBeenCalledTimes(1);
expect(response.writeHead).toHaveBeenLastCalledWith(404);
expect(response.end).toHaveBeenCalledTimes(1);
expect(response.end).toHaveBeenLastCalledWith();
});
it('writes an error to the HTTP response without the stack trace.', async(): Promise<void> => {
handler.handleSafe.mockRejectedValueOnce(new Error('dummyError'));
server.emit('request', request, response);
await flushPromises();
expect(response.setHeader).toHaveBeenCalledTimes(1);
expect(response.setHeader).toHaveBeenLastCalledWith('Content-Type', 'text/plain; charset=utf-8');
expect(response.writeHead).toHaveBeenCalledTimes(1);
expect(response.writeHead).toHaveBeenLastCalledWith(500);
expect(response.end).toHaveBeenCalledTimes(1);
expect(response.end).toHaveBeenLastCalledWith('Error: dummyError\n');
});
it('does not write an error if the response had been started.', async(): Promise<void> => {
(response as any).headersSent = true;
handler.handleSafe.mockRejectedValueOnce(new Error('dummyError'));
server.emit('request', request, response);
await flushPromises();
expect(response.setHeader).toHaveBeenCalledTimes(0);
expect(response.writeHead).toHaveBeenCalledTimes(0);
expect(response.end).toHaveBeenCalledTimes(1);
expect(response.end).toHaveBeenLastCalledWith();
});
it('throws unknown errors if its handler throw non-Error objects.', async(): Promise<void> => {
handler.handleSafe.mockRejectedValueOnce('apple');
server.emit('request', request, response);
await flushPromises();
expect(response.setHeader).toHaveBeenCalledTimes(1);
expect(response.setHeader).toHaveBeenLastCalledWith('Content-Type', 'text/plain; charset=utf-8');
expect(response.writeHead).toHaveBeenCalledTimes(1);
expect(response.writeHead).toHaveBeenLastCalledWith(500);
expect(response.end).toHaveBeenCalledTimes(1);
expect(response.end).toHaveBeenLastCalledWith('Unknown error: apple.\n');
});
it('can handle errors on the HttpResponse.', async(): Promise<void> => {
handler.handleSafe.mockImplementationOnce(async(input): Promise<void> => {
input.request.emit('error', new Error('bad request'));
});
server.emit('request', request, response);
await flushPromises();
expect(logger.error).toHaveBeenCalledTimes(1);
expect(logger.error).toHaveBeenLastCalledWith('Request error: bad request');
});
it('prints the stack trace if that option is enabled.', async(): Promise<void> => {
server.removeAllListeners();
listener = new HandlerServerConfigurator(handler, true);
await listener.handle(server);
const error = new Error('dummyError');
handler.handleSafe.mockRejectedValueOnce(error);
server.emit('request', request, response);
await flushPromises();
expect(response.setHeader).toHaveBeenCalledTimes(1);
expect(response.setHeader).toHaveBeenLastCalledWith('Content-Type', 'text/plain; charset=utf-8');
expect(response.writeHead).toHaveBeenCalledTimes(1);
expect(response.writeHead).toHaveBeenLastCalledWith(500);
expect(response.end).toHaveBeenCalledTimes(1);
expect(response.end).toHaveBeenLastCalledWith(`${error.stack}\n`);
});
});