Files
privacy.sexy/tests/unit/application/Parser/ContextualError.spec.ts
undergroundwires 4212c7b9e0 Improve context for errors thrown by compiler
This commit introduces a custom error object to provide additional
context for errors throwing during parsing and compiling operations,
improving troubleshooting.

By integrating error context handling, the error messages become more
informative and user-friendly, providing sequence of trace with context
to aid in troubleshooting.

Changes include:

- Introduce custom error object that extends errors with contextual
  information. This replaces previous usages of `AggregateError` which
  is not displayed well by browsers when logged.
- Improve parsing functions to encapsulate error context with more
  details.
- Increase unit test coverage and refactor the related code to be more
  testable.
2024-05-25 13:55:30 +02:00

122 lines
3.7 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { CustomError } from '@/application/Common/CustomError';
import { wrapErrorWithAdditionalContext } from '@/application/Parser/ContextualError';
describe('wrapErrorWithAdditionalContext', () => {
it('preserves the original error when wrapped', () => {
// arrange
const expectedError = new Error();
const context = new TestContext()
.withError(expectedError);
// act
const error = context.wrap();
// assert
const actualError = extractInnerErrorFromContextualError(error);
expect(actualError).to.equal(expectedError);
});
it('maintains the original error when re-wrapped', () => {
// arrange
const expectedError = new Error();
// act
const firstError = new TestContext()
.withError(expectedError)
.withAdditionalContext('first error')
.wrap();
const secondError = new TestContext()
.withError(firstError)
.withAdditionalContext('second error')
.wrap();
// assert
const actualError = extractInnerErrorFromContextualError(secondError);
expect(actualError).to.equal(expectedError);
});
it(`the object extends ${CustomError.name}`, () => {
// arrange
const expected = CustomError;
// act
const error = new TestContext()
.wrap();
// assert
expect(error).to.be.an.instanceof(expected);
});
describe('error message construction', () => {
it('includes the message from the original error', () => {
// arrange
const expectedOriginalErrorMessage = 'Message from the inner error';
// act
const error = new TestContext()
.withError(new Error(expectedOriginalErrorMessage))
.wrap();
// assert
expect(error.message).contains(expectedOriginalErrorMessage);
});
it('appends provided additional context to the error message', () => {
// arrange
const expectedAdditionalContext = 'Expected additional context message';
// act
const error = new TestContext()
.withAdditionalContext(expectedAdditionalContext)
.wrap();
// assert
expect(error.message).contains(expectedAdditionalContext);
});
it('appends multiple contexts to the error message in sequential order', () => {
// arrange
const expectedFirstContext = 'First context';
const expectedSecondContext = 'Second context';
// act
const firstError = new TestContext()
.withAdditionalContext(expectedFirstContext)
.wrap();
const secondError = new TestContext()
.withError(firstError)
.withAdditionalContext(expectedSecondContext)
.wrap();
// assert
const messageLines = secondError.message.split('\n');
expect(messageLines).to.contain(`1: ${expectedFirstContext}`);
expect(messageLines).to.contain(`2: ${expectedSecondContext}`);
});
});
});
class TestContext {
private error: Error = new Error();
private additionalContext = `[${TestContext.name}] additional context`;
public withError(error: Error) {
this.error = error;
return this;
}
public withAdditionalContext(additionalContext: string) {
this.additionalContext = additionalContext;
return this;
}
public wrap(): ReturnType<typeof wrapErrorWithAdditionalContext> {
return wrapErrorWithAdditionalContext(
this.error,
this.additionalContext,
);
}
}
function extractInnerErrorFromContextualError(error: Error): Error {
const innerErrorProperty = 'innerError';
if (!(innerErrorProperty in error)) {
throw new Error(`${innerErrorProperty} property is missing`);
}
const actualError = error[innerErrorProperty];
return actualError as Error;
}