diff --git a/src/identity/IdentityProviderHttpHandler.ts b/src/identity/IdentityProviderHttpHandler.ts index b3453599b..4bd3405f7 100644 --- a/src/identity/IdentityProviderHttpHandler.ts +++ b/src/identity/IdentityProviderHttpHandler.ts @@ -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 }); diff --git a/src/identity/interaction/email-password/EmailPasswordUtil.ts b/src/identity/interaction/email-password/EmailPasswordUtil.ts index 3eab2dfd0..f4b555002 100644 --- a/src/identity/interaction/email-password/EmailPasswordUtil.ts +++ b/src/identity/interaction/email-password/EmailPasswordUtil.ts @@ -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 { try { rawResponse = (await fetch(url)) as DatasetResponse; } 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; } diff --git a/src/ldp/AuthenticatedLdpHandler.ts b/src/ldp/AuthenticatedLdpHandler.ts index dd0114597..976854776 100644 --- a/src/ldp/AuthenticatedLdpHandler.ts +++ b/src/ldp/AuthenticatedLdpHandler.ts @@ -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 }); } } diff --git a/src/ldp/http/BasicResponseWriter.ts b/src/ldp/http/BasicResponseWriter.ts index f71536ba1..679808c9f 100644 --- a/src/ldp/http/BasicResponseWriter.ts +++ b/src/ldp/http/BasicResponseWriter.ts @@ -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 { - 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 { + const contentType = input.result.metadata?.contentType; + if (isInternalContentType(contentType)) { + throw new NotImplementedHttpError(`Cannot serialize the internal content type ${contentType}`); } } diff --git a/src/ldp/http/SparqlUpdateBodyParser.ts b/src/ldp/http/SparqlUpdateBodyParser.ts index cf8d60f11..b9028a6ff 100644 --- a/src/ldp/http/SparqlUpdateBodyParser.ts +++ b/src/ldp/http/SparqlUpdateBodyParser.ts @@ -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 diff --git a/src/server/BaseHttpServerFactory.ts b/src/server/BaseHttpServerFactory.ts index 3b0cd048d..d840f57cd 100644 --- a/src/server/BaseHttpServerFactory.ts +++ b/src/server/BaseHttpServerFactory.ts @@ -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 { diff --git a/src/storage/DataAccessorBasedStore.ts b/src/storage/DataAccessorBasedStore.ts index 439b0713c..adf4dbdba 100644 --- a/src/storage/DataAccessorBasedStore.ts +++ b/src/storage/DataAccessorBasedStore.ts @@ -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)}`); } } })); diff --git a/src/storage/accessors/SparqlDataAccessor.ts b/src/storage/accessors/SparqlDataAccessor.ts index 91d99989a..9ef5f7302 100644 --- a/src/storage/accessors/SparqlDataAccessor.ts +++ b/src/storage/accessors/SparqlDataAccessor.ts @@ -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; } } diff --git a/src/storage/conversion/ConversionUtil.ts b/src/storage/conversion/ConversionUtil.ts index 45953995d..0ded29faa 100644 --- a/src/storage/conversion/ConversionUtil.ts +++ b/src/storage/conversion/ConversionUtil.ts @@ -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); +} diff --git a/src/util/errors/ErrorUtil.ts b/src/util/errors/ErrorUtil.ts index 3d2dbce8b..84e94716e 100644 --- a/src/util/errors/ErrorUtil.ts +++ b/src/util/errors/ErrorUtil.ts @@ -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. */ diff --git a/src/util/errors/HttpError.ts b/src/util/errors/HttpError.ts index 9d213dc64..b5b206aa0 100644 --- a/src/util/errors/HttpError.ts +++ b/src/util/errors/HttpError.ts @@ -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'; } } diff --git a/src/util/handlers/WaterfallHandler.ts b/src/util/handlers/WaterfallHandler.ts index cfd3bd7e0..d87a718fb 100644 --- a/src/util/handlers/WaterfallHandler.ts +++ b/src/util/handlers/WaterfallHandler.ts @@ -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 implements AsyncHandler { } 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))); } } } diff --git a/test/unit/identity/interaction/email-password/EmailPasswordUtil.test.ts b/test/unit/identity/interaction/email-password/EmailPasswordUtil.test.ts index e2e6f1999..096140f70 100644 --- a/test/unit/identity/interaction/email-password/EmailPasswordUtil.test.ts +++ b/test/unit/identity/interaction/email-password/EmailPasswordUtil.test.ts @@ -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, })); }); diff --git a/test/unit/identity/interaction/email-password/handler/ResetPasswordHandler.test.ts b/test/unit/identity/interaction/email-password/handler/ResetPasswordHandler.test.ts index a8c49dc7a..7effde8a5 100644 --- a/test/unit/identity/interaction/email-password/handler/ResetPasswordHandler.test.ts +++ b/test/unit/identity/interaction/email-password/handler/ResetPasswordHandler.test.ts @@ -86,7 +86,7 @@ describe('A ResetPasswordHandler', (): void => { }); it('has a default error for non-native errors.', async(): Promise => { - 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(); diff --git a/test/unit/identity/interaction/util/IdpRouteController.test.ts b/test/unit/identity/interaction/util/IdpRouteController.test.ts index 49ebaec99..b72264967 100644 --- a/test/unit/identity/interaction/util/IdpRouteController.test.ts +++ b/test/unit/identity/interaction/util/IdpRouteController.test.ts @@ -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: {}}, }); }); diff --git a/test/unit/identity/util/FetchUtil.test.ts b/test/unit/identity/util/FetchUtil.test.ts index 83cd78246..4165e6626 100644 --- a/test/unit/identity/util/FetchUtil.test.ts +++ b/test/unit/identity/util/FetchUtil.test.ts @@ -22,19 +22,13 @@ describe('FetchUtil', (): void => { it('errors if there was an issue fetching.', async(): Promise => { 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 => { (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 => { diff --git a/test/unit/ldp/http/BasicResponseWriter.test.ts b/test/unit/ldp/http/BasicResponseWriter.test.ts index 41b01673b..91c07106b 100644 --- a/test/unit/ldp/http/BasicResponseWriter.test.ts +++ b/test/unit/ldp/http/BasicResponseWriter.test.ts @@ -25,12 +25,13 @@ describe('A BasicResponseWriter', (): void => { }); it('requires the input to be a binary ResponseDescription.', async(): Promise => { - 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(); }); diff --git a/test/unit/server/BaseHttpServerFactory.test.ts b/test/unit/server/BaseHttpServerFactory.test.ts index a49ee6c64..5e1894a2a 100644 --- a/test/unit/server/BaseHttpServerFactory.test.ts +++ b/test/unit/server/BaseHttpServerFactory.test.ts @@ -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 => { + it('does not print the stack if that option is disabled.', async(): Promise => { const error = new Error('dummyError'); handler.handleSafe.mockRejectedValueOnce(error); diff --git a/test/unit/storage/DataAccessorBasedStore.test.ts b/test/unit/storage/DataAccessorBasedStore.test.ts index 75d423958..9b8f2d3b8 100644 --- a/test/unit/storage/DataAccessorBasedStore.test.ts +++ b/test/unit/storage/DataAccessorBasedStore.test.ts @@ -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 => { - const resourceID = { path: root }; - const mock = jest.spyOn(quadUtil, 'parseQuads').mockImplementationOnce(async(): Promise => { - 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 => { 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!', ); }); }); diff --git a/test/unit/storage/conversion/ConversionUtil.test.ts b/test/unit/storage/conversion/ConversionUtil.test.ts index 379c99297..1e66ce351 100644 --- a/test/unit/storage/conversion/ConversionUtil.test.ts +++ b/test/unit/storage/conversion/ConversionUtil.test.ts @@ -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 => { + expect(isInternalContentType('internal/quads')).toBeTruthy(); + + expect(isInternalContentType()).toBeFalsy(); + expect(isInternalContentType('text/turtle')).toBeFalsy(); + }); + }); }); diff --git a/test/unit/util/errors/ErrorUtil.test.ts b/test/unit/util/errors/ErrorUtil.test.ts index 8214c699e..be242152a 100644 --- a/test/unit/util/errors/ErrorUtil.test.ts +++ b/test/unit/util/errors/ErrorUtil.test.ts @@ -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 => { - expect(isNativeError(new Error('error'))).toBe(true); + expect(isError(new Error('error'))).toBe(true); + }); + + it('returns true on error-like objects.', async(): Promise => { + expect(isError({ name: 'name', message: 'message', stack: 'stack' })).toBe(true); + }); + + it('returns true on errors without a stack.', async(): Promise => { + expect(isError({ name: 'name', message: 'message' })).toBe(true); }); it('returns false on other values.', async(): Promise => { - expect(isNativeError('apple')).toBe(false); + expect(isError('apple')).toBe(false); }); }); - describe('#assertNativeError', (): void => { + describe('#assertError', (): void => { it('returns undefined on native errors.', async(): Promise => { - expect(assertNativeError(new Error('error'))).toBeUndefined(); + expect(assertError(new Error('error'))).toBeUndefined(); }); it('throws on other values.', async(): Promise => { - 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 => { + expect(createErrorMessage(new Error('error msg'))).toBe('error msg'); + }); + + it('tries to put the object in a string .', async(): Promise => { + expect(createErrorMessage('apple')).toBe('Unknown error: apple'); }); }); diff --git a/test/unit/util/handlers/WaterfallHandler.test.ts b/test/unit/util/handlers/WaterfallHandler.test.ts index 1bb20f4b2..3a8f3eeb3 100644 --- a/test/unit/util/handlers/WaterfallHandler.test.ts +++ b/test/unit/util/handlers/WaterfallHandler.test.ts @@ -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 => {