mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
fix: Prevent warnings for expected errors
This commit is contained in:
parent
b0f38a1f55
commit
5f8ec5dd15
@ -23,6 +23,15 @@ export async function readableToString(stream: Readable): Promise<string> {
|
|||||||
return (await arrayifyStream(stream)).join('');
|
return (await arrayifyStream(stream)).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These error messages usually indicate expected behaviour so should not give a warning.
|
||||||
|
// We compare against the error message instead of the code
|
||||||
|
// since the second one is from an external library that does not assign an error code.
|
||||||
|
// At the time of writing the first one gets thrown in Node 16 and the second one in Node 14.
|
||||||
|
const safeErrors = new Set([
|
||||||
|
'Cannot call write after a stream was destroyed',
|
||||||
|
'premature close',
|
||||||
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pipes one stream into another and emits errors of the first stream with the second.
|
* Pipes one stream into another and emits errors of the first stream with the second.
|
||||||
* In case of an error in the first stream the second one will be destroyed with the given error.
|
* In case of an error in the first stream the second one will be destroyed with the given error.
|
||||||
@ -53,7 +62,9 @@ export function pipeSafely<T extends Writable>(readable: NodeJS.ReadableStream,
|
|||||||
// In case the input readable is guarded, it will no longer log errors since `pump` attaches a new error listener
|
// In case the input readable is guarded, it will no longer log errors since `pump` attaches a new error listener
|
||||||
pump(readable, destination, (error): void => {
|
pump(readable, destination, (error): void => {
|
||||||
if (error) {
|
if (error) {
|
||||||
logger.warn(`Piped stream errored with ${error.message}`);
|
const msg = `Piped stream errored with ${error.message}`;
|
||||||
|
logger.log(safeErrors.has(error.message) ? 'debug' : 'warn', msg);
|
||||||
|
|
||||||
// Make sure the final error can be handled in a normal streaming fashion
|
// Make sure the final error can be handled in a normal streaming fashion
|
||||||
destination.emit('error', mapError ? mapError(error) : error);
|
destination.emit('error', mapError ? mapError(error) : error);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,8 @@ import type { Representation,
|
|||||||
Logger } from '../../src';
|
Logger } from '../../src';
|
||||||
|
|
||||||
jest.mock('../../src/logging/LogUtil', (): any => {
|
jest.mock('../../src/logging/LogUtil', (): any => {
|
||||||
const logger: Logger = { error: jest.fn(), debug: jest.fn(), warn: jest.fn(), info: jest.fn() } as any;
|
const logger: Logger =
|
||||||
|
{ error: jest.fn(), debug: jest.fn(), warn: jest.fn(), info: jest.fn(), log: jest.fn() } as any;
|
||||||
return { getLoggerFor: (): Logger => logger };
|
return { getLoggerFor: (): Logger => logger };
|
||||||
});
|
});
|
||||||
const logger: jest.Mocked<Logger> = getLoggerFor('GuardedStream') as any;
|
const logger: jest.Mocked<Logger> = getLoggerFor('GuardedStream') as any;
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
import { PassThrough } from 'stream';
|
import { PassThrough } from 'stream';
|
||||||
import arrayifyStream from 'arrayify-stream';
|
import arrayifyStream from 'arrayify-stream';
|
||||||
import streamifyArray from 'streamify-array';
|
import streamifyArray from 'streamify-array';
|
||||||
|
import type { Logger } from '../../../src/logging/Logger';
|
||||||
|
import { getLoggerFor } from '../../../src/logging/LogUtil';
|
||||||
import { isHttpRequest } from '../../../src/server/HttpRequest';
|
import { isHttpRequest } from '../../../src/server/HttpRequest';
|
||||||
import { guardedStreamFrom, pipeSafely, transformSafely, readableToString } from '../../../src/util/StreamUtil';
|
import { guardedStreamFrom, pipeSafely, transformSafely, readableToString } from '../../../src/util/StreamUtil';
|
||||||
|
|
||||||
|
jest.mock('../../../src/logging/LogUtil', (): any => {
|
||||||
|
const logger: Logger = { warn: jest.fn(), log: jest.fn() } as any;
|
||||||
|
return { getLoggerFor: (): Logger => logger };
|
||||||
|
});
|
||||||
|
const logger: jest.Mocked<Logger> = getLoggerFor('StreamUtil') as any;
|
||||||
|
|
||||||
jest.mock('../../../src/server/HttpRequest', (): any => ({
|
jest.mock('../../../src/server/HttpRequest', (): any => ({
|
||||||
isHttpRequest: jest.fn(),
|
isHttpRequest: jest.fn(),
|
||||||
}));
|
}));
|
||||||
@ -18,7 +26,7 @@ describe('StreamUtil', (): void => {
|
|||||||
|
|
||||||
describe('#pipeSafely', (): void => {
|
describe('#pipeSafely', (): void => {
|
||||||
beforeEach(async(): Promise<void> => {
|
beforeEach(async(): Promise<void> => {
|
||||||
(isHttpRequest as unknown as jest.Mock).mockClear();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('pipes data from one stream to the other.', async(): Promise<void> => {
|
it('pipes data from one stream to the other.', async(): Promise<void> => {
|
||||||
@ -37,6 +45,8 @@ describe('StreamUtil', (): void => {
|
|||||||
const output = new PassThrough();
|
const output = new PassThrough();
|
||||||
const piped = pipeSafely(input, output);
|
const piped = pipeSafely(input, output);
|
||||||
await expect(readableToString(piped)).rejects.toThrow('error');
|
await expect(readableToString(piped)).rejects.toThrow('error');
|
||||||
|
expect(logger.log).toHaveBeenCalledTimes(1);
|
||||||
|
expect(logger.log).toHaveBeenLastCalledWith('warn', 'Piped stream errored with error');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports mapping errors to something else.', async(): Promise<void> => {
|
it('supports mapping errors to something else.', async(): Promise<void> => {
|
||||||
@ -50,6 +60,22 @@ describe('StreamUtil', (): void => {
|
|||||||
await expect(readableToString(piped)).rejects.toThrow('other error');
|
await expect(readableToString(piped)).rejects.toThrow('other error');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('logs specific safer errors as debug.', async(): Promise<void> => {
|
||||||
|
const input = streamifyArray([ 'data' ]);
|
||||||
|
input.read = (): any => {
|
||||||
|
input.emit('error', new Error('Cannot call write after a stream was destroyed'));
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
const output = new PassThrough();
|
||||||
|
const piped = pipeSafely(input, output);
|
||||||
|
await expect(readableToString(piped)).rejects.toThrow('Cannot call write after a stream was destroyed');
|
||||||
|
expect(logger.log).toHaveBeenCalledTimes(1);
|
||||||
|
expect(logger.log).toHaveBeenLastCalledWith(
|
||||||
|
'debug',
|
||||||
|
'Piped stream errored with Cannot call write after a stream was destroyed',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('destroys the source stream in case the destinations becomes unpiped.', async(): Promise<void> => {
|
it('destroys the source stream in case the destinations becomes unpiped.', async(): Promise<void> => {
|
||||||
const input = new PassThrough();
|
const input = new PassThrough();
|
||||||
const output = new PassThrough();
|
const output = new PassThrough();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user