mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Add better support for non-native errors
This commit is contained in:
parent
cefc866109
commit
7cfb87e516
@ -5,7 +5,7 @@ import type { RepresentationPreferences } from '../ldp/representation/Representa
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import type { HttpHandlerInput } from '../server/HttpHandler';
|
||||
import { HttpHandler } from '../server/HttpHandler';
|
||||
import { assertNativeError, isNativeError } from '../util/errors/ErrorUtil';
|
||||
import { assertError, createErrorMessage } from '../util/errors/ErrorUtil';
|
||||
import type { IdentityProviderFactory } from './IdentityProviderFactory';
|
||||
import type { InteractionHttpHandler } from './interaction/InteractionHttpHandler';
|
||||
import type { InteractionPolicy } from './interaction/InteractionPolicy';
|
||||
@ -55,7 +55,7 @@ export class IdentityProviderHttpHandler extends HttpHandler {
|
||||
try {
|
||||
this.provider = await this.providerFactory.createProvider(this.interactionPolicy);
|
||||
} catch (err: unknown) {
|
||||
this.logger.error(`Failed to create Provider: ${isNativeError(err) ? err.message : 'Unknown error'}`);
|
||||
this.logger.error(`Failed to create Provider: ${createErrorMessage(err)}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@ -75,7 +75,7 @@ export class IdentityProviderHttpHandler extends HttpHandler {
|
||||
try {
|
||||
await this.interactionHttpHandler.handle({ ...input, provider });
|
||||
} catch (error: unknown) {
|
||||
assertNativeError(error);
|
||||
assertError(error);
|
||||
const preferences: RepresentationPreferences = { type: { 'text/plain': 1 }};
|
||||
const result = await this.errorHandler.handleSafe({ error, preferences });
|
||||
await this.responseWriter.handleSafe({ response: input.response, result });
|
||||
|
@ -1,5 +1,5 @@
|
||||
import assert from 'assert';
|
||||
import { isNativeError } from '../../../util/errors/ErrorUtil';
|
||||
import { createErrorMessage } from '../../../util/errors/ErrorUtil';
|
||||
import { HttpError } from '../../../util/errors/HttpError';
|
||||
import { IdpInteractionError } from '../util/IdpInteractionError';
|
||||
|
||||
@ -18,10 +18,8 @@ export function throwIdpInteractionError(error: unknown, prefilled: Record<strin
|
||||
}
|
||||
} else if (HttpError.isInstance(error)) {
|
||||
throw new IdpInteractionError(error.statusCode, error.message, prefilled);
|
||||
} else if (isNativeError(error)) {
|
||||
throw new IdpInteractionError(500, error.message, prefilled);
|
||||
} else {
|
||||
throw new IdpInteractionError(500, 'Unknown Error', prefilled);
|
||||
throw new IdpInteractionError(500, createErrorMessage(error), prefilled);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { getLoggerFor } from '../../../../logging/LogUtil';
|
||||
import type { HttpHandlerInput } from '../../../../server/HttpHandler';
|
||||
import { HttpHandler } from '../../../../server/HttpHandler';
|
||||
import type { RenderHandler } from '../../../../server/util/RenderHandler';
|
||||
import { isNativeError } from '../../../../util/errors/ErrorUtil';
|
||||
import { createErrorMessage } from '../../../../util/errors/ErrorUtil';
|
||||
import { getFormDataRequestBody } from '../../util/FormDataUtil';
|
||||
import { assertPassword } from '../EmailPasswordUtil';
|
||||
import type { AccountStore } from '../storage/AccountStore';
|
||||
@ -53,11 +53,10 @@ export class ResetPasswordHandler extends HttpHandler {
|
||||
},
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
const errorMessage: string = isNativeError(err) ? err.message : 'An unknown error occurred';
|
||||
await this.renderHandler.handleSafe({
|
||||
response: input.response,
|
||||
props: {
|
||||
errorMessage,
|
||||
errorMessage: createErrorMessage(err),
|
||||
recordId: prefilledRecordId,
|
||||
},
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { HttpHandler } from '../../../server/HttpHandler';
|
||||
import { RouterHandler } from '../../../server/util/RouterHandler';
|
||||
import { isNativeError } from '../../../util/errors/ErrorUtil';
|
||||
import { createErrorMessage } from '../../../util/errors/ErrorUtil';
|
||||
import type { InteractionHttpHandlerInput } from '../InteractionHttpHandler';
|
||||
import { IdpInteractionError } from './IdpInteractionError';
|
||||
import type { IdpRenderHandler } from './IdpRenderHandler';
|
||||
@ -35,9 +35,8 @@ export class IdpRouteController extends RouterHandler {
|
||||
try {
|
||||
await this.handler.handleSafe(input);
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = isNativeError(err) ? err.message : 'An unknown error occurred';
|
||||
const prefilled = IdpInteractionError.isInstance(err) ? err.prefilled : {};
|
||||
await this.render(input, errorMessage, prefilled);
|
||||
await this.render(input, createErrorMessage(err), prefilled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import fetch from '@rdfjs/fetch';
|
||||
import type { DatasetResponse } from '@rdfjs/fetch-lite';
|
||||
import type { Dataset } from 'rdf-js';
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import { isNativeError } from '../../util/errors/ErrorUtil';
|
||||
import { createErrorMessage } from '../../util/errors/ErrorUtil';
|
||||
|
||||
const logger = getLoggerFor('FetchUtil');
|
||||
|
||||
@ -14,17 +14,16 @@ export async function fetchDataset(url: string): Promise<Dataset> {
|
||||
try {
|
||||
rawResponse = (await fetch(url)) as DatasetResponse<Dataset>;
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = `Cannot fetch ${url}: ${isNativeError(err) ? err.message : 'Unknown error'}`;
|
||||
logger.error(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
logger.error(`Cannot fetch ${url}: ${createErrorMessage(err)}`);
|
||||
throw new Error(`Cannot fetch ${url}`);
|
||||
}
|
||||
let dataset: Dataset;
|
||||
try {
|
||||
dataset = await rawResponse.dataset();
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = `Could not parse RDF in ${url}: ${isNativeError(err) ? err.message : 'Unknown error'}`;
|
||||
logger.error(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
logger.error(`Could not parse RDF in ${url}: ${createErrorMessage(err)}`);
|
||||
// Keeping the error message the same to prevent leaking possible information about intranet
|
||||
throw new Error(`Cannot fetch ${url}`);
|
||||
}
|
||||
return dataset;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import type { HttpHandlerInput } from '../server/HttpHandler';
|
||||
import { HttpHandler } from '../server/HttpHandler';
|
||||
import type { HttpRequest } from '../server/HttpRequest';
|
||||
import type { HttpResponse } from '../server/HttpResponse';
|
||||
import { assertNativeError } from '../util/errors/ErrorUtil';
|
||||
import { assertError } from '../util/errors/ErrorUtil';
|
||||
import type { ErrorHandler } from './http/ErrorHandler';
|
||||
import type { RequestParser } from './http/RequestParser';
|
||||
import type { ResponseDescription } from './http/response/ResponseDescription';
|
||||
@ -103,7 +103,7 @@ export class AuthenticatedLdpHandler extends HttpHandler {
|
||||
try {
|
||||
writeData = { response: input.response, result: await this.runHandlers(input.request) };
|
||||
} catch (error: unknown) {
|
||||
assertNativeError(error);
|
||||
assertError(error);
|
||||
// We don't know the preferences yet at this point
|
||||
const preferences: RepresentationPreferences = { type: { 'text/plain': 1 }};
|
||||
const result = await this.errorHandler.handleSafe({ error, preferences });
|
||||
@ -129,7 +129,7 @@ export class AuthenticatedLdpHandler extends HttpHandler {
|
||||
try {
|
||||
return await this.handleOperation(request, operation);
|
||||
} catch (error: unknown) {
|
||||
assertNativeError(error);
|
||||
assertError(error);
|
||||
return await this.errorHandler.handleSafe({ error, preferences: operation.preferences });
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import type { HttpResponse } from '../../server/HttpResponse';
|
||||
import { INTERNAL_QUADS } from '../../util/ContentTypes';
|
||||
import { isNativeError } from '../../util/errors/ErrorUtil';
|
||||
import { isInternalContentType } from '../../storage/conversion/ConversionUtil';
|
||||
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
|
||||
import { pipeSafely } from '../../util/StreamUtil';
|
||||
import type { MetadataWriter } from './metadata/MetadataWriter';
|
||||
@ -20,9 +19,10 @@ export class BasicResponseWriter extends ResponseWriter {
|
||||
this.metadataWriter = metadataWriter;
|
||||
}
|
||||
|
||||
public async canHandle(input: { response: HttpResponse; result: ResponseDescription | Error }): Promise<void> {
|
||||
if (isNativeError(input.result) || input.result.metadata?.contentType === INTERNAL_QUADS) {
|
||||
throw new NotImplementedHttpError('Only successful binary responses are supported');
|
||||
public async canHandle(input: { response: HttpResponse; result: ResponseDescription }): Promise<void> {
|
||||
const contentType = input.result.metadata?.contentType;
|
||||
if (isInternalContentType(contentType)) {
|
||||
throw new NotImplementedHttpError(`Cannot serialize the internal content type ${contentType}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { translate } from 'sparqlalgebrajs';
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import { APPLICATION_SPARQL_UPDATE } from '../../util/ContentTypes';
|
||||
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
|
||||
import { isNativeError } from '../../util/errors/ErrorUtil';
|
||||
import { createErrorMessage } from '../../util/errors/ErrorUtil';
|
||||
import { UnsupportedMediaTypeHttpError } from '../../util/errors/UnsupportedMediaTypeHttpError';
|
||||
import { guardedStreamFrom, readableToString } from '../../util/StreamUtil';
|
||||
import type { BodyParserArgs } from './BodyParser';
|
||||
@ -29,10 +29,7 @@ export class SparqlUpdateBodyParser extends BodyParser {
|
||||
algebra = translate(sparql, { quads: true, baseIRI: metadata.identifier.value });
|
||||
} catch (error: unknown) {
|
||||
this.logger.warn('Could not translate SPARQL query to SPARQL algebra', { error });
|
||||
if (isNativeError(error)) {
|
||||
throw new BadRequestHttpError(error.message);
|
||||
}
|
||||
throw new BadRequestHttpError();
|
||||
throw new BadRequestHttpError(createErrorMessage(error));
|
||||
}
|
||||
|
||||
// Prevent body from being requested again
|
||||
|
@ -4,7 +4,7 @@ import { createServer as createHttpServer } from 'http';
|
||||
import { createServer as createHttpsServer } from 'https';
|
||||
import { URL } from 'url';
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import { isNativeError } from '../util/errors/ErrorUtil';
|
||||
import { isError } from '../util/errors/ErrorUtil';
|
||||
import { guardStream } from '../util/GuardedStream';
|
||||
import type { HttpHandler } from './HttpHandler';
|
||||
import type { HttpServerFactory } from './HttpServerFactory';
|
||||
@ -67,8 +67,8 @@ export class BaseHttpServerFactory implements HttpServerFactory {
|
||||
await this.handler.handleSafe({ request: guardStream(request), response });
|
||||
} catch (error: unknown) {
|
||||
let errMsg: string;
|
||||
if (!isNativeError(error)) {
|
||||
errMsg = 'Unknown error.\n';
|
||||
if (!isError(error)) {
|
||||
errMsg = `Unknown error: ${error}.\n`;
|
||||
} else if (this.options.showStackTrace && error.stack) {
|
||||
errMsg = `${error.stack}\n`;
|
||||
} else {
|
||||
|
@ -11,7 +11,7 @@ import { getLoggerFor } from '../logging/LogUtil';
|
||||
import { INTERNAL_QUADS } from '../util/ContentTypes';
|
||||
import { BadRequestHttpError } from '../util/errors/BadRequestHttpError';
|
||||
import { ConflictHttpError } from '../util/errors/ConflictHttpError';
|
||||
import { isNativeError } from '../util/errors/ErrorUtil';
|
||||
import { createErrorMessage } from '../util/errors/ErrorUtil';
|
||||
import { ForbiddenHttpError } from '../util/errors/ForbiddenHttpError';
|
||||
import { MethodNotAllowedHttpError } from '../util/errors/MethodNotAllowedHttpError';
|
||||
import { NotFoundHttpError } from '../util/errors/NotFoundHttpError';
|
||||
@ -344,10 +344,7 @@ export class DataAccessorBasedStore implements ResourceStore {
|
||||
quads = await parseQuads(representation.data, { format: contentType, baseIRI: identifier.value });
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (isNativeError(error)) {
|
||||
throw new BadRequestHttpError(`Can only create containers with RDF data. ${error.message}`);
|
||||
}
|
||||
throw error;
|
||||
throw new BadRequestHttpError(`Can only create containers with RDF data. ${createErrorMessage(error)}`);
|
||||
}
|
||||
|
||||
// Solid, §5.3: "Servers MUST NOT allow HTTP POST, PUT and PATCH to update a container’s containment triples;
|
||||
@ -481,8 +478,7 @@ export class DataAccessorBasedStore implements ResourceStore {
|
||||
deleted.push(identifier);
|
||||
} catch (error: unknown) {
|
||||
if (!NotFoundHttpError.isInstance(error)) {
|
||||
const errorMsg = isNativeError(error) ? error.message : error;
|
||||
this.logger.error(`Problem deleting auxiliary resource ${identifier.path}: ${errorMsg}`);
|
||||
this.logger.error(`Error deleting auxiliary resource ${identifier.path}: ${createErrorMessage(error)}`);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
@ -19,7 +19,7 @@ import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdenti
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import { INTERNAL_QUADS } from '../../util/ContentTypes';
|
||||
import { ConflictHttpError } from '../../util/errors/ConflictHttpError';
|
||||
import { isNativeError } from '../../util/errors/ErrorUtil';
|
||||
import { createErrorMessage } from '../../util/errors/ErrorUtil';
|
||||
import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError';
|
||||
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
|
||||
import { UnsupportedMediaTypeHttpError } from '../../util/errors/UnsupportedMediaTypeHttpError';
|
||||
@ -300,9 +300,7 @@ export class SparqlDataAccessor implements DataAccessor {
|
||||
try {
|
||||
return guardStream(await this.fetcher.fetchTriples(this.endpoint, query));
|
||||
} catch (error: unknown) {
|
||||
if (isNativeError(error)) {
|
||||
this.logger.error(`SPARQL endpoint ${this.endpoint} error: ${error.message}`);
|
||||
}
|
||||
this.logger.error(`SPARQL endpoint ${this.endpoint} error: ${createErrorMessage(error)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@ -317,9 +315,7 @@ export class SparqlDataAccessor implements DataAccessor {
|
||||
try {
|
||||
return await this.fetcher.fetchUpdate(this.endpoint, query);
|
||||
} catch (error: unknown) {
|
||||
if (isNativeError(error)) {
|
||||
this.logger.error(`SPARQL endpoint ${this.endpoint} error: ${error.message}`);
|
||||
}
|
||||
this.logger.error(`SPARQL endpoint ${this.endpoint} error: ${createErrorMessage(error)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
@ -156,3 +156,15 @@ export function matchesMediaType(mediaA: string, mediaB: string): boolean {
|
||||
}
|
||||
return subTypeA === subTypeB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given content type is an internal content type such as internal/quads.
|
||||
* Response will be `false` if the input type is undefined.
|
||||
*
|
||||
* Do not use this for media ranges.
|
||||
*
|
||||
* @param contentType - Type to check.
|
||||
*/
|
||||
export function isInternalContentType(contentType?: string): boolean {
|
||||
return typeof contentType !== 'undefined' && matchesMediaType(contentType, INTERNAL_ALL);
|
||||
}
|
||||
|
@ -4,20 +4,28 @@ import { HttpError } from './HttpError';
|
||||
/**
|
||||
* Checks if the input is an {@link Error}.
|
||||
*/
|
||||
export function isNativeError(error: any): error is Error {
|
||||
return types.isNativeError(error);
|
||||
export function isError(error: any): error is Error {
|
||||
return types.isNativeError(error) ||
|
||||
(error &&
|
||||
typeof error.name === 'string' &&
|
||||
typeof error.message === 'string' &&
|
||||
(typeof error.stack === 'undefined' || typeof error.stack === 'string'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the input is a native error.
|
||||
* If not the input will be re-thrown.
|
||||
*/
|
||||
export function assertNativeError(error: any): asserts error is Error {
|
||||
if (!isNativeError(error)) {
|
||||
export function assertError(error: unknown): asserts error is Error {
|
||||
if (!isError(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function createErrorMessage(error: unknown): string {
|
||||
return isError(error) ? error.message : `Unknown error: ${error}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP status code corresponding to the error.
|
||||
*/
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { isNativeError } from './ErrorUtil';
|
||||
import { isError } from './ErrorUtil';
|
||||
|
||||
/**
|
||||
* A class for all errors that could be thrown by Solid.
|
||||
@ -21,6 +21,6 @@ export class HttpError extends Error {
|
||||
}
|
||||
|
||||
public static isInstance(error: any): error is HttpError {
|
||||
return isNativeError(error) && typeof (error as any).statusCode === 'number';
|
||||
return isError(error) && typeof (error as any).statusCode === 'number';
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import { BadRequestHttpError } from '../errors/BadRequestHttpError';
|
||||
import { isNativeError } from '../errors/ErrorUtil';
|
||||
import { createErrorMessage } from '../errors/ErrorUtil';
|
||||
import { HttpError } from '../errors/HttpError';
|
||||
import { InternalServerError } from '../errors/InternalServerError';
|
||||
import type { AsyncHandler } from './AsyncHandler';
|
||||
@ -87,10 +87,8 @@ export class WaterfallHandler<TIn, TOut> implements AsyncHandler<TIn, TOut> {
|
||||
} catch (error: unknown) {
|
||||
if (HttpError.isInstance(error)) {
|
||||
errors.push(error);
|
||||
} else if (isNativeError(error)) {
|
||||
errors.push(new InternalServerError(error.message));
|
||||
} else {
|
||||
errors.push(new InternalServerError('Unknown error'));
|
||||
errors.push(new InternalServerError(createErrorMessage(error)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ describe('EmailPasswordUtil', (): void => {
|
||||
const error = 'Error!';
|
||||
expect((): never => throwIdpInteractionError(error, prefilled)).toThrow(expect.objectContaining({
|
||||
statusCode: 500,
|
||||
message: 'Unknown Error',
|
||||
message: 'Unknown error: Error!',
|
||||
prefilled,
|
||||
}));
|
||||
});
|
||||
|
@ -86,7 +86,7 @@ describe('A ResetPasswordHandler', (): void => {
|
||||
});
|
||||
|
||||
it('has a default error for non-native errors.', async(): Promise<void> => {
|
||||
const errorMessage = 'An unknown error occurred';
|
||||
const errorMessage = 'Unknown error: not native';
|
||||
request = createPostFormRequest({ recordId, password: 'password!', confirmPassword: 'password!' });
|
||||
(accountStore.getForgotPasswordRecord as jest.Mock).mockRejectedValueOnce('not native');
|
||||
await expect(handler.handle({ request, response })).resolves.toBeUndefined();
|
||||
|
@ -74,7 +74,7 @@ describe('An IdpRouteController', (): void => {
|
||||
expect(renderHandler.handleSafe).toHaveBeenCalledTimes(1);
|
||||
expect(renderHandler.handleSafe).toHaveBeenLastCalledWith({
|
||||
response,
|
||||
props: { errorMessage: 'An unknown error occurred', prefilled: {}},
|
||||
props: { errorMessage: 'Unknown error: apple!', prefilled: {}},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -22,19 +22,13 @@ describe('FetchUtil', (): void => {
|
||||
|
||||
it('errors if there was an issue fetching.', async(): Promise<void> => {
|
||||
fetchMock.mockRejectedValueOnce(new Error('Invalid webId!'));
|
||||
await expect(fetchDataset(url)).rejects.toThrow(`Cannot fetch ${url}: Invalid webId!`);
|
||||
await expect(fetchDataset(url)).rejects.toThrow(`Cannot fetch ${url}`);
|
||||
expect(fetchMock).toHaveBeenCalledWith(url);
|
||||
|
||||
fetchMock.mockRejectedValueOnce('apple');
|
||||
await expect(fetchDataset(url)).rejects.toThrow(`Cannot fetch ${url}: Unknown error`);
|
||||
});
|
||||
|
||||
it('errors if there was an issue parsing the returned RDF.', async(): Promise<void> => {
|
||||
(datasetResponse.dataset as jest.Mock).mockRejectedValueOnce(new Error('Invalid RDF!'));
|
||||
await expect(fetchDataset(url)).rejects.toThrow(`Could not parse RDF in ${url}: Invalid RDF!`);
|
||||
|
||||
(datasetResponse.dataset as jest.Mock).mockRejectedValueOnce('apple');
|
||||
await expect(fetchDataset(url)).rejects.toThrow(`Could not parse RDF in ${url}: Unknown error`);
|
||||
await expect(fetchDataset(url)).rejects.toThrow(`Cannot fetch ${url}`);
|
||||
});
|
||||
|
||||
it('returns the resulting Dataset.', async(): Promise<void> => {
|
||||
|
@ -25,12 +25,13 @@ describe('A BasicResponseWriter', (): void => {
|
||||
});
|
||||
|
||||
it('requires the input to be a binary ResponseDescription.', async(): Promise<void> => {
|
||||
await expect(writer.canHandle({ response, result: new Error('error') }))
|
||||
.rejects.toThrow(NotImplementedHttpError);
|
||||
const metadata = new RepresentationMetadata(INTERNAL_QUADS);
|
||||
await expect(writer.canHandle({ response, result: { statusCode: 201, metadata }}))
|
||||
.rejects.toThrow(NotImplementedHttpError);
|
||||
await expect(writer.canHandle({ response, result }))
|
||||
metadata.contentType = 'text/turtle';
|
||||
await expect(writer.canHandle({ response, result: { statusCode: 201, metadata }}))
|
||||
.resolves.toBeUndefined();
|
||||
await expect(writer.canHandle({ response, result: { statusCode: 201 }}))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
|
@ -88,7 +88,7 @@ describe('A BaseHttpServerFactory', (): void => {
|
||||
handler.handleSafe.mockRejectedValueOnce('apple');
|
||||
|
||||
const res = await request(server).get('/').expect(500);
|
||||
expect(res.text).toContain('Unknown error.');
|
||||
expect(res.text).toContain('Unknown error: apple.');
|
||||
});
|
||||
});
|
||||
|
||||
@ -107,7 +107,7 @@ describe('A BaseHttpServerFactory', (): void => {
|
||||
server.close();
|
||||
});
|
||||
|
||||
it('throws unknown errors if its handler throw non-Error objects.', async(): Promise<void> => {
|
||||
it('does not print the stack if that option is disabled.', async(): Promise<void> => {
|
||||
const error = new Error('dummyError');
|
||||
handler.handleSafe.mockRejectedValueOnce(error);
|
||||
|
||||
|
@ -20,7 +20,6 @@ import { NotImplementedHttpError } from '../../../src/util/errors/NotImplemented
|
||||
import type { Guarded } from '../../../src/util/GuardedStream';
|
||||
import { SingleRootIdentifierStrategy } from '../../../src/util/identifiers/SingleRootIdentifierStrategy';
|
||||
import { trimTrailingSlashes } from '../../../src/util/PathUtil';
|
||||
import * as quadUtil from '../../../src/util/QuadUtil';
|
||||
import { guardedStreamFrom } from '../../../src/util/StreamUtil';
|
||||
import { CONTENT_TYPE, SOLID_HTTP, LDP, PIM, RDF } from '../../../src/util/Vocabularies';
|
||||
import quad = DataFactory.quad;
|
||||
@ -236,16 +235,6 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
await expect(store.addResource(resourceID, representation)).rejects.toThrow(BadRequestHttpError);
|
||||
});
|
||||
|
||||
it('passes the result along if the MetadataController throws a non-Error.', async(): Promise<void> => {
|
||||
const resourceID = { path: root };
|
||||
const mock = jest.spyOn(quadUtil, 'parseQuads').mockImplementationOnce(async(): Promise<any> => {
|
||||
throw 'apple';
|
||||
});
|
||||
representation.metadata.add(RDF.type, LDP.terms.Container);
|
||||
await expect(store.addResource(resourceID, representation)).rejects.toBe('apple');
|
||||
mock.mockRestore();
|
||||
});
|
||||
|
||||
it('can write resources.', async(): Promise<void> => {
|
||||
const resourceID = { path: root };
|
||||
representation.metadata.removeAll(RDF.type);
|
||||
@ -584,7 +573,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
expect(accessor.data[`${root}resource.dummy`]).not.toBeUndefined();
|
||||
expect(logger.error).toHaveBeenCalledTimes(1);
|
||||
expect(logger.error).toHaveBeenLastCalledWith(
|
||||
'Problem deleting auxiliary resource http://test.com/resource.dummy: auxiliary error!',
|
||||
'Error deleting auxiliary resource http://test.com/resource.dummy: auxiliary error!',
|
||||
);
|
||||
});
|
||||
|
||||
@ -607,7 +596,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
expect(accessor.data[`${root}resource.dummy`]).not.toBeUndefined();
|
||||
expect(logger.error).toHaveBeenCalledTimes(1);
|
||||
expect(logger.error).toHaveBeenLastCalledWith(
|
||||
'Problem deleting auxiliary resource http://test.com/resource.dummy: auxiliary error!',
|
||||
'Error deleting auxiliary resource http://test.com/resource.dummy: Unknown error: auxiliary error!',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
getBestPreference,
|
||||
getConversionTarget,
|
||||
getTypeWeight,
|
||||
getWeightedPreferences,
|
||||
getWeightedPreferences, isInternalContentType,
|
||||
matchesMediaPreferences,
|
||||
matchesMediaType,
|
||||
} from '../../../../src/storage/conversion/ConversionUtil';
|
||||
@ -144,4 +144,13 @@ describe('ConversionUtil', (): void => {
|
||||
expect(matchesMediaType('text/plain', 'text/turtle')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isInternalContentType', (): void => {
|
||||
it('only returns true on internal types.', async(): Promise<void> => {
|
||||
expect(isInternalContentType('internal/quads')).toBeTruthy();
|
||||
|
||||
expect(isInternalContentType()).toBeFalsy();
|
||||
expect(isInternalContentType('text/turtle')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,24 +1,42 @@
|
||||
import { assertNativeError, getStatusCode, isNativeError } from '../../../../src/util/errors/ErrorUtil';
|
||||
import { assertError, createErrorMessage, getStatusCode, isError } from '../../../../src/util/errors/ErrorUtil';
|
||||
import { NotFoundHttpError } from '../../../../src/util/errors/NotFoundHttpError';
|
||||
|
||||
describe('ErrorUtil', (): void => {
|
||||
describe('#isNativeError', (): void => {
|
||||
describe('#isError', (): void => {
|
||||
it('returns true on native errors.', async(): Promise<void> => {
|
||||
expect(isNativeError(new Error('error'))).toBe(true);
|
||||
expect(isError(new Error('error'))).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true on error-like objects.', async(): Promise<void> => {
|
||||
expect(isError({ name: 'name', message: 'message', stack: 'stack' })).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true on errors without a stack.', async(): Promise<void> => {
|
||||
expect(isError({ name: 'name', message: 'message' })).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false on other values.', async(): Promise<void> => {
|
||||
expect(isNativeError('apple')).toBe(false);
|
||||
expect(isError('apple')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#assertNativeError', (): void => {
|
||||
describe('#assertError', (): void => {
|
||||
it('returns undefined on native errors.', async(): Promise<void> => {
|
||||
expect(assertNativeError(new Error('error'))).toBeUndefined();
|
||||
expect(assertError(new Error('error'))).toBeUndefined();
|
||||
});
|
||||
|
||||
it('throws on other values.', async(): Promise<void> => {
|
||||
expect((): void => assertNativeError('apple')).toThrow('apple');
|
||||
expect((): void => assertError('apple')).toThrow('apple');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createErrorMessage', (): void => {
|
||||
it('returns the given message for normal Errors.', async(): Promise<void> => {
|
||||
expect(createErrorMessage(new Error('error msg'))).toBe('error msg');
|
||||
});
|
||||
|
||||
it('tries to put the object in a string .', async(): Promise<void> => {
|
||||
expect(createErrorMessage('apple')).toBe('Unknown error: apple');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -53,7 +53,7 @@ describe('A WaterfallHandler', (): void => {
|
||||
};
|
||||
const handler = new WaterfallHandler([ handlerFalse, handlerFalse ]);
|
||||
|
||||
await expect(handler.canHandle(null)).rejects.toThrow('[Unknown error, Unknown error]');
|
||||
await expect(handler.canHandle(null)).rejects.toThrow('[Unknown error: apple, Unknown error: apple]');
|
||||
});
|
||||
|
||||
it('handles data if a handler supports it.', async(): Promise<void> => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user