fix: Set max-len to 120

This commit is contained in:
Joachim Van Herwegen 2020-07-24 14:59:41 +02:00
parent dcff424f58
commit aaba113563
23 changed files with 152 additions and 71 deletions

View File

@ -19,6 +19,7 @@ module.exports = {
'comma-dangle': ['error', 'always-multiline'], 'comma-dangle': ['error', 'always-multiline'],
'dot-location': ['error', 'property'], 'dot-location': ['error', 'property'],
'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }], 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }],
'max-len': ['error', { code: 120, ignoreUrls: true }],
'no-underscore-dangle': 'off', // conflicts with external libraries 'no-underscore-dangle': 'off', // conflicts with external libraries
'padding-line-between-statements': 'off', 'padding-line-between-statements': 'off',
'tsdoc/syntax': 'error', 'tsdoc/syntax': 'error',

View File

@ -25,14 +25,15 @@ export class AcceptPreferenceParser extends PreferenceParser {
public async handle(input: HttpRequest): Promise<RepresentationPreferences> { public async handle(input: HttpRequest): Promise<RepresentationPreferences> {
const result: RepresentationPreferences = {}; const result: RepresentationPreferences = {};
const headers: { [T in keyof RepresentationPreferences]: { val?: string; func: (input: string) => AcceptHeader[] }} = { const headers:
{ [T in keyof RepresentationPreferences]: { val?: string; func: (input: string) => AcceptHeader[] }} = {
type: { val: input.headers.accept, func: parseAccept }, type: { val: input.headers.accept, func: parseAccept },
charset: { val: input.headers['accept-charset'] as string, func: parseAcceptCharset }, charset: { val: input.headers['accept-charset'] as string, func: parseAcceptCharset },
encoding: { val: input.headers['accept-encoding'] as string, func: parseAcceptEncoding }, encoding: { val: input.headers['accept-encoding'] as string, func: parseAcceptEncoding },
language: { val: input.headers['accept-language'], func: parseAcceptLanguage }, language: { val: input.headers['accept-language'], func: parseAcceptLanguage },
}; };
(Object.keys(headers) as (keyof RepresentationPreferences)[]).forEach((key): void => { (Object.keys(headers) as (keyof RepresentationPreferences)[]).forEach((key): void => {
const preferences = this.parseHeader(headers[key]!.val, headers[key]!.func); const preferences = this.parseHeader(headers[key]!.func, headers[key]!.val);
if (preferences.length > 0) { if (preferences.length > 0) {
result[key] = preferences; result[key] = preferences;
} }
@ -53,10 +54,11 @@ export class AcceptPreferenceParser extends PreferenceParser {
* *
* @returns A list of {@link RepresentationPreference}. Returns an empty list if input was not defined. * @returns A list of {@link RepresentationPreference}. Returns an empty list if input was not defined.
*/ */
private parseHeader(input: string | undefined, parseFunction: (input: string) => AcceptHeader[]): RepresentationPreference[] { private parseHeader(parseFunction: (input: string) => AcceptHeader[], input?: string): RepresentationPreference[] {
if (!input) { if (!input) {
return []; return [];
} }
return parseFunction(input).map((accept): RepresentationPreference => ({ value: accept.range, weight: accept.weight })); return parseFunction(input).map((accept): RepresentationPreference =>
({ value: accept.range, weight: accept.weight }));
} }
} }

View File

@ -6,4 +6,5 @@ import { ResponseDescription } from '../operations/ResponseDescription';
* Writes to the HttpResponse. * Writes to the HttpResponse.
* Response depends on the operation result and potentially which errors was thrown. * Response depends on the operation result and potentially which errors was thrown.
*/ */
export abstract class ResponseWriter extends AsyncHandler<{ response: HttpResponse; result: ResponseDescription | Error }> {} export abstract class ResponseWriter
extends AsyncHandler<{ response: HttpResponse; result: ResponseDescription | Error }> {}

View File

@ -20,24 +20,31 @@ export class LockingResourceStore implements AtomicResourceStore {
this.locks = locks; this.locks = locks;
} }
public async addResource(container: ResourceIdentifier, representation: Representation, conditions?: Conditions): Promise<ResourceIdentifier> { public async addResource(container: ResourceIdentifier, representation: Representation,
return this.lockedRun(container, async(): Promise<ResourceIdentifier> => this.source.addResource(container, representation, conditions)); conditions?: Conditions): Promise<ResourceIdentifier> {
return this.lockedRun(container,
async(): Promise<ResourceIdentifier> => this.source.addResource(container, representation, conditions));
} }
public async deleteResource(identifier: ResourceIdentifier, conditions?: Conditions): Promise<void> { public async deleteResource(identifier: ResourceIdentifier, conditions?: Conditions): Promise<void> {
return this.lockedRun(identifier, async(): Promise<void> => this.source.deleteResource(identifier, conditions)); return this.lockedRun(identifier, async(): Promise<void> => this.source.deleteResource(identifier, conditions));
} }
public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences, conditions?: Conditions): Promise<Representation> { public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences,
return this.lockedRun(identifier, async(): Promise<Representation> => this.source.getRepresentation(identifier, preferences, conditions)); conditions?: Conditions): Promise<Representation> {
return this.lockedRun(identifier,
async(): Promise<Representation> => this.source.getRepresentation(identifier, preferences, conditions));
} }
public async modifyResource(identifier: ResourceIdentifier, patch: Patch, conditions?: Conditions): Promise<void> { public async modifyResource(identifier: ResourceIdentifier, patch: Patch, conditions?: Conditions): Promise<void> {
return this.lockedRun(identifier, async(): Promise<void> => this.source.modifyResource(identifier, patch, conditions)); return this.lockedRun(identifier,
async(): Promise<void> => this.source.modifyResource(identifier, patch, conditions));
} }
public async setRepresentation(identifier: ResourceIdentifier, representation: Representation, conditions?: Conditions): Promise<void> { public async setRepresentation(identifier: ResourceIdentifier, representation: Representation,
return this.lockedRun(identifier, async(): Promise<void> => this.source.setRepresentation(identifier, representation, conditions)); conditions?: Conditions): Promise<void> {
return this.lockedRun(identifier,
async(): Promise<void> => this.source.setRepresentation(identifier, representation, conditions));
} }
private async lockedRun<T>(identifier: ResourceIdentifier, func: () => Promise<T>): Promise<T> { private async lockedRun<T>(identifier: ResourceIdentifier, func: () => Promise<T>): Promise<T> {

View File

@ -20,7 +20,8 @@ export class PatchingStore implements ResourceStore {
this.patcher = patcher; this.patcher = patcher;
} }
public async addResource(container: ResourceIdentifier, representation: Representation, conditions?: Conditions): Promise<ResourceIdentifier> { public async addResource(container: ResourceIdentifier, representation: Representation,
conditions?: Conditions): Promise<ResourceIdentifier> {
return this.source.addResource(container, representation, conditions); return this.source.addResource(container, representation, conditions);
} }
@ -28,11 +29,13 @@ export class PatchingStore implements ResourceStore {
return this.source.deleteResource(identifier, conditions); return this.source.deleteResource(identifier, conditions);
} }
public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences, conditions?: Conditions): Promise<Representation> { public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences,
conditions?: Conditions): Promise<Representation> {
return this.source.getRepresentation(identifier, preferences, conditions); return this.source.getRepresentation(identifier, preferences, conditions);
} }
public async setRepresentation(identifier: ResourceIdentifier, representation: Representation, conditions?: Conditions): Promise<void> { public async setRepresentation(identifier: ResourceIdentifier, representation: Representation,
conditions?: Conditions): Promise<void> {
return this.source.setRepresentation(identifier, representation, conditions); return this.source.setRepresentation(identifier, representation, conditions);
} }

View File

@ -64,7 +64,8 @@ export class SimpleResourceStore implements ResourceStore {
* *
* @returns The corresponding Representation. * @returns The corresponding Representation.
*/ */
public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences): Promise<Representation> { public async getRepresentation(identifier: ResourceIdentifier,
preferences: RepresentationPreferences): Promise<Representation> {
const path = this.parseIdentifier(identifier); const path = this.parseIdentifier(identifier);
return this.generateRepresentation(this.store[path], preferences); return this.generateRepresentation(this.store[path], preferences);
} }

View File

@ -48,12 +48,14 @@ export class SimpleSparqlUpdatePatchHandler extends PatchHandler {
if (!inserts.every((pattern): boolean => pattern.graph.equals(def))) { if (!inserts.every((pattern): boolean => pattern.graph.equals(def))) {
throw new UnsupportedHttpError('GRAPH statements are not supported.'); throw new UnsupportedHttpError('GRAPH statements are not supported.');
} }
if (op.where ?? deletes.some((pattern): boolean => someTerms(pattern, (term): boolean => term.termType === 'Variable'))) { if (op.where ?? deletes.some((pattern): boolean =>
someTerms(pattern, (term): boolean => term.termType === 'Variable'))) {
throw new UnsupportedHttpError('WHERE statements are not supported.'); throw new UnsupportedHttpError('WHERE statements are not supported.');
} }
const lock = await this.locker.acquire(input.identifier); const lock = await this.locker.acquire(input.identifier);
const quads = await this.source.getRepresentation(input.identifier, { type: [{ value: 'internal/quads', weight: 1 }]}); const quads = await this.source.getRepresentation(input.identifier,
{ type: [{ value: 'internal/quads', weight: 1 }]});
const store = new Store<BaseQuad>(); const store = new Store<BaseQuad>();
const importEmitter = store.import(quads.data); const importEmitter = store.import(quads.data);
await new Promise((resolve, reject): void => { await new Promise((resolve, reject): void => {

View File

@ -38,7 +38,8 @@ import { UnsupportedHttpError } from './errors/UnsupportedHttpError';
// language-range = (1*8ALPHA *("-" 1*8alphanum)) / "*" // language-range = (1*8ALPHA *("-" 1*8alphanum)) / "*"
// alphanum = ALPHA / DIGIT // alphanum = ALPHA / DIGIT
// //
// Delimiters are chosen from the set of US-ASCII visual characters not allowed in a token (DQUOTE and "(),/:;<=>?@[\]{}"). // Delimiters are chosen from the set of US-ASCII visual characters
// not allowed in a token (DQUOTE and "(),/:;<=>?@[\]{}").
// token = 1*tchar // token = 1*tchar
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" // / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
@ -65,7 +66,10 @@ export interface Accept extends AcceptHeader {
parameters: { parameters: {
/** Media type parameters. These are the parameters that came before the q value. */ /** Media type parameters. These are the parameters that came before the q value. */
mediaType: { [key: string]: string }; mediaType: { [key: string]: string };
/** Extension parameters. These are the parameters that came after the q value. Value will be an empty string if there was none. */ /**
* Extension parameters. These are the parameters that came after the q value.
* Value will be an empty string if there was none.
*/
extension: { [key: string]: string }; extension: { [key: string]: string };
}; };
} }
@ -101,7 +105,9 @@ const transformQuotedStrings = (input: string): { result: string; replacements:
const result = input.replace(/"(?:[^"\\]|\\.)*"/gu, (match): string => { const result = input.replace(/"(?:[^"\\]|\\.)*"/gu, (match): string => {
// Not all characters allowed in quoted strings, see BNF above // Not all characters allowed in quoted strings, see BNF above
if (!/^"(?:[\t !\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|(?:\\[\t\u0020-\u007e\u0080-\u00ff]))*"$/u.test(match)) { if (!/^"(?:[\t !\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|(?:\\[\t\u0020-\u007e\u0080-\u00ff]))*"$/u.test(match)) {
throw new UnsupportedHttpError(`Invalid quoted string in Accept header: ${match}. Check which characters are allowed`); throw new UnsupportedHttpError(
`Invalid quoted string in Accept header: ${match}. Check which characters are allowed`,
);
} }
const replacement = `"${idx}"`; const replacement = `"${idx}"`;
replacements[replacement] = match; replacements[replacement] = match;
@ -131,7 +137,9 @@ const splitAndClean = (input: string): string[] =>
*/ */
const testQValue = (qvalue: string): void => { const testQValue = (qvalue: string): void => {
if (!/^q=(?:(?:0(?:\.\d{0,3})?)|(?:1(?:\.0{0,3})?))$/u.test(qvalue)) { if (!/^q=(?:(?:0(?:\.\d{0,3})?)|(?:1(?:\.0{0,3})?))$/u.test(qvalue)) {
throw new UnsupportedHttpError(`Invalid q value: ${qvalue} does not match ("q=" ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] )).`); throw new UnsupportedHttpError(
`Invalid q value: ${qvalue} does not match ("q=" ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] )).`,
);
} }
}; };
@ -155,7 +163,9 @@ const parseAcceptPart = (part: string, replacements: { [id: string]: string }):
// No reason to test differently for * since we don't check if the type exists // No reason to test differently for * since we don't check if the type exists
const [ type, subtype ] = range.split('/'); const [ type, subtype ] = range.split('/');
if (!type || !subtype || !token.test(type) || !token.test(subtype)) { if (!type || !subtype || !token.test(type) || !token.test(subtype)) {
throw new Error(`Invalid Accept range: ${range} does not match ( "*/*" / ( token "/" "*" ) / ( token "/" token ) )`); throw new Error(
`Invalid Accept range: ${range} does not match ( "*/*" / ( token "/" "*" ) / ( token "/" token ) )`,
);
} }
let weight = 1; let weight = 1;
@ -174,9 +184,12 @@ const parseAcceptPart = (part: string, replacements: { [id: string]: string }):
// Test replaced string for easier check // Test replaced string for easier check
// parameter = token "=" ( token / quoted-string ) // parameter = token "=" ( token / quoted-string )
// second part is optional for extension parameters // second part is optional for extension parameters
if (!token.test(name) || !((map === extensionParams && !value) || (value && (/^"\d+"$/u.test(value) || token.test(value))))) { if (!token.test(name) ||
throw new UnsupportedHttpError(`Invalid Accept parameter: ${param} does not match (token "=" ( token / quoted-string )). ` + !((map === extensionParams && !value) || (value && (/^"\d+"$/u.test(value) || token.test(value))))) {
'Second part optional for extension parameters.'); throw new UnsupportedHttpError(
`Invalid Accept parameter: ${param} does not match (token "=" ( token / quoted-string )). ` +
`Second part is optional for extension parameters.`,
);
} }
let actualValue = value; let actualValue = value;
@ -256,7 +269,9 @@ export const parseAcceptCharset = (input: string): AcceptCharset[] => {
const results = parseNoParameters(input); const results = parseNoParameters(input);
results.forEach((result): void => { results.forEach((result): void => {
if (!token.test(result.range)) { if (!token.test(result.range)) {
throw new UnsupportedHttpError(`Invalid Accept-Charset range: ${result.range} does not match (content-coding / "identity" / "*")`); throw new UnsupportedHttpError(
`Invalid Accept-Charset range: ${result.range} does not match (content-coding / "identity" / "*")`,
);
} }
}); });
return results; return results;
@ -297,7 +312,9 @@ export const parseAcceptLanguage = (input: string): AcceptLanguage[] => {
results.forEach((result): void => { results.forEach((result): void => {
// (1*8ALPHA *("-" 1*8alphanum)) / "*" // (1*8ALPHA *("-" 1*8alphanum)) / "*"
if (result.range !== '*' && !/^[a-zA-Z]{1,8}(?:-[a-zA-Z0-9]{1,8})*$/u.test(result.range)) { if (result.range !== '*' && !/^[a-zA-Z]{1,8}(?:-[a-zA-Z0-9]{1,8})*$/u.test(result.range)) {
throw new UnsupportedHttpError(`Invalid Accept-Language range: ${result.range} does not match ((1*8ALPHA *("-" 1*8alphanum)) / "*")`); throw new UnsupportedHttpError(
`Invalid Accept-Language range: ${result.range} does not match ((1*8ALPHA *("-" 1*8alphanum)) / "*")`,
);
} }
}); });
return results; return results;

View File

@ -46,7 +46,8 @@ export class CompositeAsyncHandler<TIn, TOut> implements AsyncHandler<TIn, TOut>
} }
/** /**
* Identical to {@link AsyncHandler.handleSafe} but optimized for composite by only needing 1 canHandle call on members. * Identical to {@link AsyncHandler.handleSafe} but optimized for composite
* by only needing 1 canHandle call on members.
* @param input - The input data. * @param input - The input data.
* *
* @returns A promise corresponding to the handle call of a handler that supports the input. * @returns A promise corresponding to the handle call of a handler that supports the input.

View File

@ -31,7 +31,8 @@ import { createResponse, MockResponse } from 'node-mocks-http';
import { namedNode, quad } from '@rdfjs/data-model'; import { namedNode, quad } from '@rdfjs/data-model';
import * as url from 'url'; import * as url from 'url';
const call = async(handler: HttpHandler, requestUrl: url.URL, method: string, headers: IncomingHttpHeaders, data: string[]): Promise<MockResponse<any>> => { const call = async(handler: HttpHandler, requestUrl: url.URL, method: string,
headers: IncomingHttpHeaders, data: string[]): Promise<MockResponse<any>> => {
const request = streamifyArray(data) as HttpRequest; const request = streamifyArray(data) as HttpRequest;
request.url = requestUrl.pathname; request.url = requestUrl.pathname;
request.method = method; request.method = method;

View File

@ -39,7 +39,9 @@ describe('An AuthenticatedLdpHandler', (): void => {
it('can check if it handles input.', async(): Promise<void> => { it('can check if it handles input.', async(): Promise<void> => {
const handler = new AuthenticatedLdpHandler(args); const handler = new AuthenticatedLdpHandler(args);
await expect(handler.canHandle({ request: {} as HttpRequest, response: {} as HttpResponse })).resolves.toBeUndefined(); await expect(handler.canHandle(
{ request: {} as HttpRequest, response: {} as HttpResponse },
)).resolves.toBeUndefined();
}); });
it('can handle input.', async(): Promise<void> => { it('can handle input.', async(): Promise<void> => {

View File

@ -18,22 +18,29 @@ describe('An AcceptPreferenceParser', (): void => {
}); });
it('parses accept-charset headers.', async(): Promise<void> => { it('parses accept-charset headers.', async(): Promise<void> => {
await expect(preferenceParser.handle({ headers: { 'accept-charset': 'iso-8859-5, unicode-1-1;q=0.8' }} as unknown as HttpRequest)) await expect(preferenceParser.handle(
.resolves.toEqual({ charset: [{ value: 'iso-8859-5', weight: 1 }, { value: 'unicode-1-1', weight: 0.8 }]}); { headers: { 'accept-charset': 'iso-8859-5, unicode-1-1;q=0.8' }} as unknown as HttpRequest,
)).resolves.toEqual({ charset: [{ value: 'iso-8859-5', weight: 1 }, { value: 'unicode-1-1', weight: 0.8 }]});
}); });
it('parses accept-datetime headers.', async(): Promise<void> => { it('parses accept-datetime headers.', async(): Promise<void> => {
await expect(preferenceParser.handle({ headers: { 'accept-datetime': 'Tue, 20 Mar 2001 20:35:00 GMT' }} as unknown as HttpRequest)) await expect(preferenceParser.handle(
.resolves.toEqual({ datetime: [{ value: 'Tue, 20 Mar 2001 20:35:00 GMT', weight: 1 }]}); { headers: { 'accept-datetime': 'Tue, 20 Mar 2001 20:35:00 GMT' }} as unknown as HttpRequest,
)).resolves.toEqual({ datetime: [{ value: 'Tue, 20 Mar 2001 20:35:00 GMT', weight: 1 }]});
}); });
it('parses accept-encoding headers.', async(): Promise<void> => { it('parses accept-encoding headers.', async(): Promise<void> => {
await expect(preferenceParser.handle({ headers: { 'accept-encoding': 'gzip;q=1.0, identity; q=0.5, *;q=0' }} as unknown as HttpRequest)) await expect(preferenceParser.handle(
.resolves.toEqual({ encoding: [{ value: 'gzip', weight: 1 }, { value: 'identity', weight: 0.5 }, { value: '*', weight: 0 }]}); { headers: { 'accept-encoding': 'gzip;q=1.0, identity; q=0.5, *;q=0' }} as unknown as HttpRequest,
)).resolves.toEqual(
{ encoding: [{ value: 'gzip', weight: 1 }, { value: 'identity', weight: 0.5 }, { value: '*', weight: 0 }]},
);
}); });
it('parses accept-language headers.', async(): Promise<void> => { it('parses accept-language headers.', async(): Promise<void> => {
await expect(preferenceParser.handle({ headers: { 'accept-language': 'da, en-gb;q=0.8, en;q=0.7' }} as HttpRequest)) await expect(preferenceParser.handle({ headers: { 'accept-language': 'da, en-gb;q=0.8, en;q=0.7' }} as HttpRequest))
.resolves.toEqual({ language: [{ value: 'da', weight: 1 }, { value: 'en-gb', weight: 0.8 }, { value: 'en', weight: 0.7 }]}); .resolves.toEqual(
{ language: [{ value: 'da', weight: 1 }, { value: 'en-gb', weight: 0.8 }, { value: 'en', weight: 0.7 }]},
);
}); });
}); });

View File

@ -15,9 +15,12 @@ describe('A SimpleResponseWriter', (): void => {
}); });
it('requires the description body to be a string or binary stream if present.', async(): Promise<void> => { it('requires the description body to be a string or binary stream if present.', async(): Promise<void> => {
await expect(writer.canHandle({ response, result: { body: { dataType: 'quad' }} as ResponseDescription })).rejects.toThrow(UnsupportedHttpError); await expect(writer.canHandle({ response, result: { body: { dataType: 'quad' }} as ResponseDescription }))
await expect(writer.canHandle({ response, result: { body: { dataType: 'string' }} as ResponseDescription })).resolves.toBeUndefined(); .rejects.toThrow(UnsupportedHttpError);
await expect(writer.canHandle({ response, result: { body: { dataType: 'binary' }} as ResponseDescription })).resolves.toBeUndefined(); await expect(writer.canHandle({ response, result: { body: { dataType: 'string' }} as ResponseDescription }))
.resolves.toBeUndefined();
await expect(writer.canHandle({ response, result: { body: { dataType: 'binary' }} as ResponseDescription }))
.resolves.toBeUndefined();
}); });
it('responds with status code 200 and a location header if there is a description.', async(): Promise<void> => { it('responds with status code 200 and a location header if there is a description.', async(): Promise<void> => {

View File

@ -11,12 +11,15 @@ describe('A SimpleSparqlUpdateBodyParser', (): void => {
it('only accepts application/sparql-update content.', async(): Promise<void> => { it('only accepts application/sparql-update content.', async(): Promise<void> => {
await expect(bodyParser.canHandle({ headers: {}} as HttpRequest)).rejects.toThrow(UnsupportedMediaTypeHttpError); await expect(bodyParser.canHandle({ headers: {}} as HttpRequest)).rejects.toThrow(UnsupportedMediaTypeHttpError);
await expect(bodyParser.canHandle({ headers: { 'content-type': 'text/plain' }} as HttpRequest)).rejects.toThrow(UnsupportedMediaTypeHttpError); await expect(bodyParser.canHandle({ headers: { 'content-type': 'text/plain' }} as HttpRequest))
await expect(bodyParser.canHandle({ headers: { 'content-type': 'application/sparql-update' }} as HttpRequest)).resolves.toBeUndefined(); .rejects.toThrow(UnsupportedMediaTypeHttpError);
await expect(bodyParser.canHandle({ headers: { 'content-type': 'application/sparql-update' }} as HttpRequest))
.resolves.toBeUndefined();
}); });
it('errors when handling invalid SPARQL updates.', async(): Promise<void> => { it('errors when handling invalid SPARQL updates.', async(): Promise<void> => {
await expect(bodyParser.handle(streamifyArray([ 'VERY INVALID UPDATE' ]) as HttpRequest)).rejects.toThrow(UnsupportedHttpError); await expect(bodyParser.handle(streamifyArray([ 'VERY INVALID UPDATE' ]) as HttpRequest))
.rejects.toThrow(UnsupportedHttpError);
}); });
it('converts SPARQL updates to algebra.', async(): Promise<void> => { it('converts SPARQL updates to algebra.', async(): Promise<void> => {

View File

@ -16,7 +16,8 @@ describe('A SimpleTargetExtractor', (): void => {
}); });
it('uses https protocol if the connection is secure.', async(): Promise<void> => { it('uses https protocol if the connection is secure.', async(): Promise<void> => {
await expect(extractor.handle({ url: 'url', headers: { host: 'test.com' }, connection: { encrypted: true } as any } as any)) await expect(extractor.handle(
.resolves.toEqual({ path: 'https://test.com/url' }); { url: 'url', headers: { host: 'test.com' }, connection: { encrypted: true } as any } as any,
)).resolves.toEqual({ path: 'https://test.com/url' });
}); });
}); });

View File

@ -17,7 +17,8 @@ describe('A SimpleDeleteOperationHandler', (): void => {
}); });
it('deletes the resource from the store and returns its identifier.', async(): Promise<void> => { it('deletes the resource from the store and returns its identifier.', async(): Promise<void> => {
await expect(handler.handle({ target: { path: 'url' }} as Operation)).resolves.toEqual({ identifier: { path: 'url' }}); await expect(handler.handle({ target: { path: 'url' }} as Operation))
.resolves.toEqual({ identifier: { path: 'url' }});
expect(store.deleteResource).toHaveBeenCalledTimes(1); expect(store.deleteResource).toHaveBeenCalledTimes(1);
expect(store.deleteResource).toHaveBeenLastCalledWith({ path: 'url' }); expect(store.deleteResource).toHaveBeenLastCalledWith({ path: 'url' });
}); });

View File

@ -11,8 +11,10 @@ describe('A SimplePostOperationHandler', (): void => {
const handler = new SimplePostOperationHandler(store); const handler = new SimplePostOperationHandler(store);
it('only supports POST operations with a body.', async(): Promise<void> => { it('only supports POST operations with a body.', async(): Promise<void> => {
await expect(handler.canHandle({ method: 'POST', body: { dataType: 'test' }} as Operation)).resolves.toBeUndefined(); await expect(handler.canHandle({ method: 'POST', body: { dataType: 'test' }} as Operation))
await expect(handler.canHandle({ method: 'GET', body: { dataType: 'test' }} as Operation)).rejects.toThrow(UnsupportedHttpError); .resolves.toBeUndefined();
await expect(handler.canHandle({ method: 'GET', body: { dataType: 'test' }} as Operation))
.rejects.toThrow(UnsupportedHttpError);
await expect(handler.canHandle({ method: 'POST' } as Operation)).rejects.toThrow(UnsupportedHttpError); await expect(handler.canHandle({ method: 'POST' } as Operation)).rejects.toThrow(UnsupportedHttpError);
}); });
@ -21,6 +23,7 @@ describe('A SimplePostOperationHandler', (): void => {
}); });
it('adds the given representation to the store and returns the new identifier.', async(): Promise<void> => { it('adds the given representation to the store and returns the new identifier.', async(): Promise<void> => {
await expect(handler.handle({ method: 'POST', body: { dataType: 'test' }} as Operation)).resolves.toEqual({ identifier: { path: 'newPath' }}); await expect(handler.handle({ method: 'POST', body: { dataType: 'test' }} as Operation))
.resolves.toEqual({ identifier: { path: 'newPath' }});
}); });
}); });

View File

@ -53,7 +53,8 @@ describe('ExpressHttpServer', (): void => {
'access-control-allow-origin': '*', 'access-control-allow-origin': '*',
'access-control-allow-headers': 'content-type', 'access-control-allow-headers': 'content-type',
})); }));
const corsMethods = res.header['access-control-allow-methods'].split(',').map((method: string): string => method.trim()); const corsMethods = res.header['access-control-allow-methods'].split(',')
.map((method: string): string => method.trim());
const allowedMethods = [ 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE' ]; const allowedMethods = [ 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE' ];
expect(corsMethods).toEqual(expect.arrayContaining(allowedMethods)); expect(corsMethods).toEqual(expect.arrayContaining(allowedMethods));
expect(corsMethods.length).toBe(allowedMethods.length); expect(corsMethods.length).toBe(allowedMethods.length);

View File

@ -24,11 +24,16 @@ describe('A LockingResourceStore', (): void => {
}; };
source = { source = {
getRepresentation: jest.fn(async(): Promise<any> => new Promise((resolve): any => delayedResolve(resolve, 'getRepresentation'))), getRepresentation: jest.fn(async(): Promise<any> =>
addResource: jest.fn(async(): Promise<any> => new Promise((resolve): any => delayedResolve(resolve, 'addResource'))), new Promise((resolve): any => delayedResolve(resolve, 'getRepresentation'))),
setRepresentation: jest.fn(async(): Promise<any> => new Promise((resolve): any => delayedResolve(resolve, 'setRepresentation'))), addResource: jest.fn(async(): Promise<any> =>
deleteResource: jest.fn(async(): Promise<any> => new Promise((resolve): any => delayedResolve(resolve, 'deleteResource'))), new Promise((resolve): any => delayedResolve(resolve, 'addResource'))),
modifyResource: jest.fn(async(): Promise<any> => new Promise((resolve): any => delayedResolve(resolve, 'modifyResource'))), setRepresentation: jest.fn(async(): Promise<any> =>
new Promise((resolve): any => delayedResolve(resolve, 'setRepresentation'))),
deleteResource: jest.fn(async(): Promise<any> =>
new Promise((resolve): any => delayedResolve(resolve, 'deleteResource'))),
modifyResource: jest.fn(async(): Promise<any> =>
new Promise((resolve): any => delayedResolve(resolve, 'modifyResource'))),
}; };
release = jest.fn(async(): Promise<any> => order.push('release')); release = jest.fn(async(): Promise<any> => order.push('release'));
locker = { locker = {

View File

@ -31,9 +31,11 @@ describe('A SimpleResourceStore', (): void => {
it('errors if a resource was not found.', async(): Promise<void> => { it('errors if a resource was not found.', async(): Promise<void> => {
await expect(store.getRepresentation({ path: `${base}wrong` }, {})).rejects.toThrow(NotFoundHttpError); await expect(store.getRepresentation({ path: `${base}wrong` }, {})).rejects.toThrow(NotFoundHttpError);
await expect(store.addResource({ path: 'http://wrong.com/wrong' }, representation)).rejects.toThrow(NotFoundHttpError); await expect(store.addResource({ path: 'http://wrong.com/wrong' }, representation))
.rejects.toThrow(NotFoundHttpError);
await expect(store.deleteResource({ path: 'wrong' })).rejects.toThrow(NotFoundHttpError); await expect(store.deleteResource({ path: 'wrong' })).rejects.toThrow(NotFoundHttpError);
await expect(store.setRepresentation({ path: 'http://wrong.com/' }, representation)).rejects.toThrow(NotFoundHttpError); await expect(store.setRepresentation({ path: 'http://wrong.com/' }, representation))
.rejects.toThrow(NotFoundHttpError);
}); });
it('errors when modifying resources.', async(): Promise<void> => { it('errors when modifying resources.', async(): Promise<void> => {

View File

@ -63,7 +63,9 @@ describe('A SimpleSparqlUpdatePatchHandler', (): void => {
const basicChecks = async(quads: Quad[]): Promise<void> => { const basicChecks = async(quads: Quad[]): Promise<void> => {
expect(source.getRepresentation).toHaveBeenCalledTimes(1); expect(source.getRepresentation).toHaveBeenCalledTimes(1);
expect(source.getRepresentation).toHaveBeenLastCalledWith({ path: 'path' }, { type: [{ value: 'internal/quads', weight: 1 }]}); expect(source.getRepresentation).toHaveBeenLastCalledWith(
{ path: 'path' }, { type: [{ value: 'internal/quads', weight: 1 }]},
);
expect(source.setRepresentation).toHaveBeenCalledTimes(1); expect(source.setRepresentation).toHaveBeenCalledTimes(1);
expect(order).toEqual([ 'acquire', 'getRepresentation', 'setRepresentation', 'release' ]); expect(order).toEqual([ 'acquire', 'getRepresentation', 'setRepresentation', 'release' ]);
const setParams = (source.setRepresentation as jest.Mock).mock.calls[0]; const setParams = (source.setRepresentation as jest.Mock).mock.calls[0];
@ -76,7 +78,8 @@ describe('A SimpleSparqlUpdatePatchHandler', (): void => {
}; };
it('only accepts SPARQL updates.', async(): Promise<void> => { it('only accepts SPARQL updates.', async(): Promise<void> => {
const input = { identifier: { path: 'path' }, patch: { dataType: 'sparql-algebra', algebra: {}} as SparqlUpdatePatch }; const input = { identifier: { path: 'path' },
patch: { dataType: 'sparql-algebra', algebra: {}} as SparqlUpdatePatch };
await expect(handler.canHandle(input)).resolves.toBeUndefined(); await expect(handler.canHandle(input)).resolves.toBeUndefined();
input.patch.dataType = 'notAlgebra'; input.patch.dataType = 'notAlgebra';
await expect(handler.canHandle(input)).rejects.toThrow(UnsupportedHttpError); await expect(handler.canHandle(input)).rejects.toThrow(UnsupportedHttpError);
@ -102,7 +105,9 @@ describe('A SimpleSparqlUpdatePatchHandler', (): void => {
{ quads: true }, { quads: true },
) } as SparqlUpdatePatch }); ) } as SparqlUpdatePatch });
await basicChecks( await basicChecks(
[ quad(namedNode('http://test.com/startS2'), namedNode('http://test.com/startP2'), namedNode('http://test.com/startO2')) ], [ quad(namedNode('http://test.com/startS2'),
namedNode('http://test.com/startP2'),
namedNode('http://test.com/startO2')) ],
); );
}); });
@ -113,7 +118,9 @@ describe('A SimpleSparqlUpdatePatchHandler', (): void => {
{ quads: true }, { quads: true },
) } as SparqlUpdatePatch }); ) } as SparqlUpdatePatch });
await basicChecks( await basicChecks(
[ quad(namedNode('http://test.com/startS2'), namedNode('http://test.com/startP2'), namedNode('http://test.com/startO2')) ], [ quad(namedNode('http://test.com/startS2'),
namedNode('http://test.com/startP2'),
namedNode('http://test.com/startO2')) ],
); );
}); });
@ -125,16 +132,21 @@ describe('A SimpleSparqlUpdatePatchHandler', (): void => {
'WHERE {}', 'WHERE {}',
{ quads: true }, { quads: true },
) } as SparqlUpdatePatch }); ) } as SparqlUpdatePatch });
await basicChecks( await basicChecks([
[ quad(namedNode('http://test.com/startS2'), namedNode('http://test.com/startP2'), namedNode('http://test.com/startO2')), quad(namedNode('http://test.com/startS2'),
quad(namedNode('http://test.com/s1'), namedNode('http://test.com/p1'), namedNode('http://test.com/o1')) ], namedNode('http://test.com/startP2'),
); namedNode('http://test.com/startO2')),
quad(namedNode('http://test.com/s1'),
namedNode('http://test.com/p1'),
namedNode('http://test.com/o1')),
]);
}); });
it('rejects GRAPH inserts.', async(): Promise<void> => { it('rejects GRAPH inserts.', async(): Promise<void> => {
const handle = handler.handle({ identifier: { path: 'path' }, const handle = handler.handle({ identifier: { path: 'path' },
patch: { algebra: translate( patch: { algebra: translate(
'INSERT DATA { GRAPH <http://test.com/graph> { <http://test.com/startS1> <http://test.com/startP1> <http://test.com/startO1> } }', 'INSERT DATA { GRAPH <http://test.com/graph> { ' +
'<http://test.com/startS1> <http://test.com/startP1> <http://test.com/startO1> } }',
{ quads: true }, { quads: true },
) } as SparqlUpdatePatch }); ) } as SparqlUpdatePatch });
await expect(handle).rejects.toThrow('GRAPH statements are not supported.'); await expect(handle).rejects.toThrow('GRAPH statements are not supported.');
@ -144,7 +156,8 @@ describe('A SimpleSparqlUpdatePatchHandler', (): void => {
it('rejects GRAPH deletes.', async(): Promise<void> => { it('rejects GRAPH deletes.', async(): Promise<void> => {
const handle = handler.handle({ identifier: { path: 'path' }, const handle = handler.handle({ identifier: { path: 'path' },
patch: { algebra: translate( patch: { algebra: translate(
'DELETE DATA { GRAPH <http://test.com/graph> { <http://test.com/startS1> <http://test.com/startP1> <http://test.com/startO1> } }', 'DELETE DATA { GRAPH <http://test.com/graph> { ' +
'<http://test.com/startS1> <http://test.com/startP1> <http://test.com/startO1> } }',
{ quads: true }, { quads: true },
) } as SparqlUpdatePatch }); ) } as SparqlUpdatePatch });
await expect(handle).rejects.toThrow('GRAPH statements are not supported.'); await expect(handle).rejects.toThrow('GRAPH statements are not supported.');

View File

@ -25,7 +25,9 @@ describe('AcceptParser', (): void => {
}); });
it('parses complex Accept headers.', async(): Promise<void> => { it('parses complex Accept headers.', async(): Promise<void> => {
expect(parseAccept('text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4,text/x-dvi; q=0.8; mxb=100000; mxt')).toEqual([ expect(parseAccept(
'text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4,text/x-dvi; q=0.8; mxb=100000; mxt',
)).toEqual([
{ range: 'text/html', weight: 1, parameters: { mediaType: { level: '1' }, extension: {}}}, { range: 'text/html', weight: 1, parameters: { mediaType: { level: '1' }, extension: {}}},
{ range: 'text/x-dvi', weight: 0.8, parameters: { mediaType: {}, extension: { mxb: '100000', mxt: '' }}}, { range: 'text/x-dvi', weight: 0.8, parameters: { mediaType: {}, extension: { mxb: '100000', mxt: '' }}},
{ range: 'text/html', weight: 0.7, parameters: { mediaType: {}, extension: {}}}, { range: 'text/html', weight: 0.7, parameters: { mediaType: {}, extension: {}}},
@ -35,7 +37,9 @@ describe('AcceptParser', (): void => {
it('parses Accept headers with double quoted values.', async(): Promise<void> => { it('parses Accept headers with double quoted values.', async(): Promise<void> => {
expect(parseAccept('audio/basic; param1="val" ; q=0.5 ;param2="\\\\\\"valid"')).toEqual([ expect(parseAccept('audio/basic; param1="val" ; q=0.5 ;param2="\\\\\\"valid"')).toEqual([
{ range: 'audio/basic', weight: 0.5, parameters: { mediaType: { param1: '"val"' }, extension: { param2: '"\\\\\\"valid"' }}}, { range: 'audio/basic',
weight: 0.5,
parameters: { mediaType: { param1: '"val"' }, extension: { param2: '"\\\\\\"valid"' }}},
]); ]);
}); });

View File

@ -67,7 +67,7 @@ describe('A CompositeAsyncHandler', (): void => {
expect(handleFn).toHaveBeenCalledTimes(1); expect(handleFn).toHaveBeenCalledTimes(1);
}); });
it('throws the same error as canHandle when calling handleSafe if no handler supports the data.', async(): Promise<void> => { it('throws the canHandle error when calling handleSafe if the data is not supported.', async(): Promise<void> => {
const handler = new CompositeAsyncHandler([ handlerFalse, handlerFalse ]); const handler = new CompositeAsyncHandler([ handlerFalse, handlerFalse ]);
await expect(handler.handleSafe(null)).rejects.toThrow('[Not supported., Not supported.]'); await expect(handler.handleSafe(null)).rejects.toThrow('[Not supported., Not supported.]');