mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
refactor: Restructure source code folder
This way the location of certain classes should make more sense
This commit is contained in:
48
test/unit/http/input/BasicRequestParser.test.ts
Normal file
48
test/unit/http/input/BasicRequestParser.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { BasicRequestParser } from '../../../../src/http/input/BasicRequestParser';
|
||||
import type { BodyParser } from '../../../../src/http/input/body/BodyParser';
|
||||
import type { ConditionsParser } from '../../../../src/http/input/conditions/ConditionsParser';
|
||||
import type { TargetExtractor } from '../../../../src/http/input/identifier/TargetExtractor';
|
||||
import type { MetadataParser } from '../../../../src/http/input/metadata/MetadataParser';
|
||||
import type { PreferenceParser } from '../../../../src/http/input/preferences/PreferenceParser';
|
||||
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
||||
import { StaticAsyncHandler } from '../../../util/StaticAsyncHandler';
|
||||
|
||||
describe('A BasicRequestParser', (): void => {
|
||||
let targetExtractor: TargetExtractor;
|
||||
let preferenceParser: PreferenceParser;
|
||||
let metadataParser: MetadataParser;
|
||||
let conditionsParser: ConditionsParser;
|
||||
let bodyParser: BodyParser;
|
||||
let requestParser: BasicRequestParser;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
targetExtractor = new StaticAsyncHandler(true, 'target' as any);
|
||||
preferenceParser = new StaticAsyncHandler(true, 'preference' as any);
|
||||
metadataParser = new StaticAsyncHandler(true, undefined);
|
||||
conditionsParser = new StaticAsyncHandler(true, 'conditions' as any);
|
||||
bodyParser = new StaticAsyncHandler(true, 'body' as any);
|
||||
requestParser = new BasicRequestParser(
|
||||
{ targetExtractor, preferenceParser, metadataParser, conditionsParser, bodyParser },
|
||||
);
|
||||
});
|
||||
|
||||
it('can handle any input.', async(): Promise<void> => {
|
||||
await expect(requestParser.canHandle({} as any)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('errors if there is no input.', async(): Promise<void> => {
|
||||
await expect(requestParser.handle({ url: 'url' } as any))
|
||||
.rejects.toThrow('No method specified on the HTTP request');
|
||||
});
|
||||
|
||||
it('returns the output of all input parsers after calling handle.', async(): Promise<void> => {
|
||||
bodyParser.handle = ({ metadata }): any => ({ data: 'body', metadata });
|
||||
await expect(requestParser.handle({ url: 'url', method: 'GET' } as any)).resolves.toEqual({
|
||||
method: 'GET',
|
||||
target: 'target',
|
||||
preferences: 'preference',
|
||||
conditions: 'conditions',
|
||||
body: { data: 'body', metadata: new RepresentationMetadata('target') },
|
||||
});
|
||||
});
|
||||
});
|
||||
73
test/unit/http/input/body/RawBodyParser.test.ts
Normal file
73
test/unit/http/input/body/RawBodyParser.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import 'jest-rdf';
|
||||
import arrayifyStream from 'arrayify-stream';
|
||||
import type { BodyParserArgs } from '../../../../../src/http/input/body/BodyParser';
|
||||
import { RawBodyParser } from '../../../../../src/http/input/body/RawBodyParser';
|
||||
import { RepresentationMetadata } from '../../../../../src/http/representation/RepresentationMetadata';
|
||||
import type { HttpRequest } from '../../../../../src/server/HttpRequest';
|
||||
import { guardedStreamFrom } from '../../../../../src/util/StreamUtil';
|
||||
|
||||
describe('A RawBodyparser', (): void => {
|
||||
const bodyParser = new RawBodyParser();
|
||||
let input: BodyParserArgs;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
input = { request: { headers: {}} as HttpRequest, metadata: new RepresentationMetadata() };
|
||||
});
|
||||
|
||||
it('accepts all input.', async(): Promise<void> => {
|
||||
await expect(bodyParser.canHandle({} as any)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns empty output if there is no content length or transfer encoding.', async(): Promise<void> => {
|
||||
input.request = guardedStreamFrom([ '' ]) as HttpRequest;
|
||||
input.request.headers = {};
|
||||
await expect(bodyParser.handle(input)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
// https://github.com/solid/community-server/issues/498
|
||||
it('returns empty output if the content length is 0 and there is no content type.', async(): Promise<void> => {
|
||||
input.request = guardedStreamFrom([ '' ]) as HttpRequest;
|
||||
input.request.headers = { 'content-length': '0' };
|
||||
await expect(bodyParser.handle(input)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('errors when a content length is specified without content type.', async(): Promise<void> => {
|
||||
input.request = guardedStreamFrom([ 'abc' ]) as HttpRequest;
|
||||
input.request.headers = { 'content-length': '1' };
|
||||
await expect(bodyParser.handle(input)).rejects
|
||||
.toThrow('HTTP request body was passed without a Content-Type header');
|
||||
});
|
||||
|
||||
it('errors when a transfer encoding is specified without content type.', async(): Promise<void> => {
|
||||
input.request = guardedStreamFrom([ 'abc' ]) as HttpRequest;
|
||||
input.request.headers = { 'transfer-encoding': 'chunked' };
|
||||
await expect(bodyParser.handle(input)).rejects
|
||||
.toThrow('HTTP request body was passed without a Content-Type header');
|
||||
});
|
||||
|
||||
it('returns a Representation if there is empty data.', async(): Promise<void> => {
|
||||
input.request = guardedStreamFrom([]) as HttpRequest;
|
||||
input.request.headers = { 'content-length': '0', 'content-type': 'text/turtle' };
|
||||
const result = (await bodyParser.handle(input))!;
|
||||
expect(result).toEqual({
|
||||
binary: true,
|
||||
data: input.request,
|
||||
metadata: input.metadata,
|
||||
});
|
||||
await expect(arrayifyStream(result.data)).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
it('returns a Representation if there is non-empty data.', async(): Promise<void> => {
|
||||
input.request = guardedStreamFrom([ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ]) as HttpRequest;
|
||||
input.request.headers = { 'transfer-encoding': 'chunked', 'content-type': 'text/turtle' };
|
||||
const result = (await bodyParser.handle(input))!;
|
||||
expect(result).toEqual({
|
||||
binary: true,
|
||||
data: input.request,
|
||||
metadata: input.metadata,
|
||||
});
|
||||
await expect(arrayifyStream(result.data)).resolves.toEqual(
|
||||
[ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ],
|
||||
);
|
||||
});
|
||||
});
|
||||
88
test/unit/http/input/body/SparqlUpdateBodyParser.test.ts
Normal file
88
test/unit/http/input/body/SparqlUpdateBodyParser.test.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import 'jest-rdf';
|
||||
import { namedNode, quad } from '@rdfjs/data-model';
|
||||
import arrayifyStream from 'arrayify-stream';
|
||||
import { Algebra } from 'sparqlalgebrajs';
|
||||
import * as algebra from 'sparqlalgebrajs';
|
||||
import type { BodyParserArgs } from '../../../../../src/http/input/body/BodyParser';
|
||||
import { SparqlUpdateBodyParser } from '../../../../../src/http/input/body/SparqlUpdateBodyParser';
|
||||
import { RepresentationMetadata } from '../../../../../src/http/representation/RepresentationMetadata';
|
||||
import type { HttpRequest } from '../../../../../src/server/HttpRequest';
|
||||
import { BadRequestHttpError } from '../../../../../src/util/errors/BadRequestHttpError';
|
||||
import { UnsupportedMediaTypeHttpError } from '../../../../../src/util/errors/UnsupportedMediaTypeHttpError';
|
||||
import { guardedStreamFrom } from '../../../../../src/util/StreamUtil';
|
||||
|
||||
describe('A SparqlUpdateBodyParser', (): void => {
|
||||
const bodyParser = new SparqlUpdateBodyParser();
|
||||
let input: BodyParserArgs;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
input = { request: { headers: {}} as HttpRequest, metadata: new RepresentationMetadata() };
|
||||
});
|
||||
|
||||
it('only accepts application/sparql-update content.', async(): Promise<void> => {
|
||||
await expect(bodyParser.canHandle(input)).rejects.toThrow(UnsupportedMediaTypeHttpError);
|
||||
input.metadata.contentType = 'text/plain';
|
||||
await expect(bodyParser.canHandle(input)).rejects.toThrow(UnsupportedMediaTypeHttpError);
|
||||
input.metadata.contentType = 'application/sparql-update;charset=utf-8';
|
||||
await expect(bodyParser.canHandle(input)).rejects.toThrow(UnsupportedMediaTypeHttpError);
|
||||
input.metadata.contentType = 'application/sparql-update ; foo=bar';
|
||||
await expect(bodyParser.canHandle(input)).rejects.toThrow(UnsupportedMediaTypeHttpError);
|
||||
input.metadata.contentType = 'application/sparql-update';
|
||||
await expect(bodyParser.canHandle(input)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('errors when handling invalid SPARQL updates.', async(): Promise<void> => {
|
||||
input.request = guardedStreamFrom([ 'VERY INVALID UPDATE' ]) as HttpRequest;
|
||||
await expect(bodyParser.handle(input)).rejects.toThrow(BadRequestHttpError);
|
||||
});
|
||||
|
||||
it('errors when receiving an unexpected error.', async(): Promise<void> => {
|
||||
const mock = jest.spyOn(algebra, 'translate').mockImplementationOnce((): any => {
|
||||
throw 'apple';
|
||||
});
|
||||
input.request = guardedStreamFrom(
|
||||
[ 'DELETE DATA { <http://test.com/s> <http://test.com/p> <http://test.com/o> }' ],
|
||||
) as HttpRequest;
|
||||
await expect(bodyParser.handle(input)).rejects.toThrow(BadRequestHttpError);
|
||||
mock.mockRestore();
|
||||
});
|
||||
|
||||
it('converts SPARQL updates to algebra.', async(): Promise<void> => {
|
||||
input.request = guardedStreamFrom(
|
||||
[ 'DELETE DATA { <http://test.com/s> <http://test.com/p> <http://test.com/o> }' ],
|
||||
) as HttpRequest;
|
||||
const result = await bodyParser.handle(input);
|
||||
expect(result.algebra.type).toBe(Algebra.types.DELETE_INSERT);
|
||||
expect((result.algebra as Algebra.DeleteInsert).delete).toBeRdfIsomorphic([ quad(
|
||||
namedNode('http://test.com/s'),
|
||||
namedNode('http://test.com/p'),
|
||||
namedNode('http://test.com/o'),
|
||||
) ]);
|
||||
expect(result.binary).toBe(true);
|
||||
expect(result.metadata).toBe(input.metadata);
|
||||
|
||||
expect(await arrayifyStream(result.data)).toEqual(
|
||||
[ 'DELETE DATA { <http://test.com/s> <http://test.com/p> <http://test.com/o> }' ],
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts relative references.', async(): Promise<void> => {
|
||||
input.request = guardedStreamFrom(
|
||||
[ 'INSERT DATA { <#it> <http://test.com/p> <http://test.com/o> }' ],
|
||||
) as HttpRequest;
|
||||
input.metadata.identifier = namedNode('http://test.com/my-document.ttl');
|
||||
const result = await bodyParser.handle(input);
|
||||
expect(result.algebra.type).toBe(Algebra.types.DELETE_INSERT);
|
||||
expect((result.algebra as Algebra.DeleteInsert).insert).toBeRdfIsomorphic([ quad(
|
||||
namedNode('http://test.com/my-document.ttl#it'),
|
||||
namedNode('http://test.com/p'),
|
||||
namedNode('http://test.com/o'),
|
||||
) ]);
|
||||
expect(result.binary).toBe(true);
|
||||
expect(result.metadata).toBe(input.metadata);
|
||||
|
||||
expect(await arrayifyStream(result.data)).toEqual(
|
||||
[ 'INSERT DATA { <#it> <http://test.com/p> <http://test.com/o> }' ],
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
import { BasicConditionsParser } from '../../../../../src/http/input/conditions/BasicConditionsParser';
|
||||
import type { HttpRequest } from '../../../../../src/server/HttpRequest';
|
||||
|
||||
describe('A BasicConditionsParser', (): void => {
|
||||
const dateString = 'Wed, 21 Oct 2015 07:28:00 UTC';
|
||||
const date = new Date('2015-10-21T07:28:00.000Z');
|
||||
let request: HttpRequest;
|
||||
const parser = new BasicConditionsParser();
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
request = { headers: {}, method: 'GET' } as HttpRequest;
|
||||
});
|
||||
|
||||
it('returns undefined if there are no relevant headers.', async(): Promise<void> => {
|
||||
await expect(parser.handleSafe(request)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('parses the if-modified-since header.', async(): Promise<void> => {
|
||||
request.headers['if-modified-since'] = dateString;
|
||||
await expect(parser.handleSafe(request)).resolves.toEqual({ modifiedSince: date });
|
||||
});
|
||||
|
||||
it('parses the if-unmodified-since header.', async(): Promise<void> => {
|
||||
request.headers['if-unmodified-since'] = dateString;
|
||||
await expect(parser.handleSafe(request)).resolves.toEqual({ unmodifiedSince: date });
|
||||
});
|
||||
|
||||
it('parses the if-match header.', async(): Promise<void> => {
|
||||
request.headers['if-match'] = '"1234567", "abcdefg"';
|
||||
await expect(parser.handleSafe(request)).resolves.toEqual({ matchesETag: [ '"1234567"', '"abcdefg"' ]});
|
||||
});
|
||||
|
||||
it('parses the if-none-match header.', async(): Promise<void> => {
|
||||
request.headers['if-none-match'] = '*';
|
||||
await expect(parser.handleSafe(request)).resolves.toEqual({ notMatchesETag: [ '*' ]});
|
||||
});
|
||||
|
||||
it('does not parse the if-modified-since header if there is an if-none-match header.', async(): Promise<void> => {
|
||||
request.headers['if-modified-since'] = dateString;
|
||||
request.headers['if-none-match'] = '*';
|
||||
await expect(parser.handleSafe(request)).resolves.toEqual({ notMatchesETag: [ '*' ]});
|
||||
});
|
||||
|
||||
it('only parses the if-modified-since header for GET and HEAD requests.', async(): Promise<void> => {
|
||||
request.headers['if-modified-since'] = dateString;
|
||||
request.method = 'PUT';
|
||||
await expect(parser.handleSafe(request)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('does not parse the if-unmodified-since header if there is an if-match header.', async(): Promise<void> => {
|
||||
request.headers['if-unmodified-since'] = dateString;
|
||||
request.headers['if-match'] = '*';
|
||||
await expect(parser.handleSafe(request)).resolves.toEqual({ matchesETag: [ '*' ]});
|
||||
});
|
||||
|
||||
it('ignores invalid dates.', async(): Promise<void> => {
|
||||
request.headers['if-modified-since'] = 'notADate';
|
||||
await expect(parser.handleSafe(request)).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
133
test/unit/http/input/identifier/OriginalUrlExtractor.test.ts
Normal file
133
test/unit/http/input/identifier/OriginalUrlExtractor.test.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { OriginalUrlExtractor } from '../../../../../src/http/input/identifier/OriginalUrlExtractor';
|
||||
|
||||
describe('A OriginalUrlExtractor', (): void => {
|
||||
const extractor = new OriginalUrlExtractor();
|
||||
|
||||
it('can handle any input.', async(): Promise<void> => {
|
||||
await expect(extractor.canHandle({} as any)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('errors if there is no URL.', async(): Promise<void> => {
|
||||
await expect(extractor.handle({ request: { headers: { host: 'test.com' }} as any })).rejects.toThrow('Missing URL');
|
||||
});
|
||||
|
||||
it('errors if there is no host.', async(): Promise<void> => {
|
||||
await expect(extractor.handle({ request: { url: 'url', headers: {}} as any }))
|
||||
.rejects.toThrow('Missing Host header');
|
||||
});
|
||||
|
||||
it('errors if the host is invalid.', async(): Promise<void> => {
|
||||
await expect(extractor.handle({ request: { url: 'url', headers: { host: 'test.com/forbidden' }} as any }))
|
||||
.rejects.toThrow('The request has an invalid Host header: test.com/forbidden');
|
||||
});
|
||||
|
||||
it('returns the input URL.', async(): Promise<void> => {
|
||||
await expect(extractor.handle({ request: { url: 'url', headers: { host: 'test.com' }} as any }))
|
||||
.resolves.toEqual({ path: 'http://test.com/url' });
|
||||
});
|
||||
|
||||
it('returns an input URL with query string.', async(): Promise<void> => {
|
||||
const noQuery = new OriginalUrlExtractor({ includeQueryString: false });
|
||||
await expect(noQuery.handle({ request: { url: '/url?abc=def&xyz', headers: { host: 'test.com' }} as any }))
|
||||
.resolves.toEqual({ path: 'http://test.com/url' });
|
||||
});
|
||||
|
||||
it('drops the query string when includeQueryString is set to false.', async(): Promise<void> => {
|
||||
await expect(extractor.handle({ request: { url: '/url?abc=def&xyz', headers: { host: 'test.com' }} as any }))
|
||||
.resolves.toEqual({ path: 'http://test.com/url?abc=def&xyz' });
|
||||
});
|
||||
|
||||
it('supports host:port combinations.', async(): Promise<void> => {
|
||||
await expect(extractor.handle({ request: { url: 'url', headers: { host: 'localhost:3000' }} as any }))
|
||||
.resolves.toEqual({ path: 'http://localhost:3000/url' });
|
||||
});
|
||||
|
||||
it('uses https protocol if the connection is secure.', async(): Promise<void> => {
|
||||
await expect(extractor.handle(
|
||||
{ request: { url: 'url', headers: { host: 'test.com' }, connection: { encrypted: true } as any } as any },
|
||||
)).resolves.toEqual({ path: 'https://test.com/url' });
|
||||
});
|
||||
|
||||
it('encodes paths.', async(): Promise<void> => {
|
||||
await expect(extractor.handle({ request: { url: '/a%20path%26/name', headers: { host: 'test.com' }} as any }))
|
||||
.resolves.toEqual({ path: 'http://test.com/a%20path%26/name' });
|
||||
|
||||
await expect(extractor.handle({ request: { url: '/a path%26/name', headers: { host: 'test.com' }} as any }))
|
||||
.resolves.toEqual({ path: 'http://test.com/a%20path%26/name' });
|
||||
|
||||
await expect(extractor.handle({ request: { url: '/path&%26/name', headers: { host: 'test.com' }} as any }))
|
||||
.resolves.toEqual({ path: 'http://test.com/path%26%26/name' });
|
||||
});
|
||||
|
||||
it('encodes hosts.', async(): Promise<void> => {
|
||||
await expect(extractor.handle({ request: { url: '/', headers: { host: '點看' }} as any }))
|
||||
.resolves.toEqual({ path: 'http://xn--c1yn36f/' });
|
||||
});
|
||||
|
||||
it('ignores an irrelevant Forwarded header.', async(): Promise<void> => {
|
||||
const headers = {
|
||||
host: 'test.com',
|
||||
forwarded: 'by=203.0.113.60',
|
||||
};
|
||||
await expect(extractor.handle({ request: { url: '/foo/bar', headers } as any }))
|
||||
.resolves.toEqual({ path: 'http://test.com/foo/bar' });
|
||||
});
|
||||
|
||||
it('takes the Forwarded header into account.', async(): Promise<void> => {
|
||||
const headers = {
|
||||
host: 'test.com',
|
||||
forwarded: 'proto=https;host=pod.example',
|
||||
};
|
||||
await expect(extractor.handle({ request: { url: '/foo/bar', headers } as any }))
|
||||
.resolves.toEqual({ path: 'https://pod.example/foo/bar' });
|
||||
});
|
||||
|
||||
it('should fallback to x-fowarded-* headers.', async(): Promise<void> => {
|
||||
const headers = {
|
||||
host: 'test.com',
|
||||
'x-forwarded-host': 'pod.example',
|
||||
'x-forwarded-proto': 'https',
|
||||
};
|
||||
await expect(extractor.handle({ request: { url: '/foo/bar', headers } as any }))
|
||||
.resolves.toEqual({ path: 'https://pod.example/foo/bar' });
|
||||
});
|
||||
|
||||
it('should just take x-forwarded-host if provided.', async(): Promise<void> => {
|
||||
const headers = {
|
||||
host: 'test.com',
|
||||
'x-forwarded-host': 'pod.example',
|
||||
};
|
||||
await expect(extractor.handle({ request: { url: '/foo/bar', headers } as any }))
|
||||
.resolves.toEqual({ path: 'http://pod.example/foo/bar' });
|
||||
});
|
||||
|
||||
it('should just take x-forwarded-protocol if provided.', async(): Promise<void> => {
|
||||
const headers = {
|
||||
host: 'test.com',
|
||||
'x-forwarded-proto': 'https',
|
||||
};
|
||||
await expect(extractor.handle({ request: { url: '/foo/bar', headers } as any }))
|
||||
.resolves.toEqual({ path: 'https://test.com/foo/bar' });
|
||||
});
|
||||
|
||||
it('should prefer forwarded header to x-forwarded-* headers.', async(): Promise<void> => {
|
||||
const headers = {
|
||||
host: 'test.com',
|
||||
forwarded: 'proto=http;host=pod.example',
|
||||
'x-forwarded-proto': 'https',
|
||||
'x-forwarded-host': 'anotherpod.example',
|
||||
};
|
||||
await expect(extractor.handle({ request: { url: '/foo/bar', headers } as any }))
|
||||
.resolves.toEqual({ path: 'http://pod.example/foo/bar' });
|
||||
});
|
||||
|
||||
it('should just take the first x-forwarded-* value.', async(): Promise<void> => {
|
||||
const headers = {
|
||||
host: 'test.com',
|
||||
'x-forwarded-host': 'pod.example, another.domain',
|
||||
'x-forwarded-proto': 'http,https',
|
||||
};
|
||||
await expect(extractor.handle({ request: { url: '/foo/bar', headers } as any }))
|
||||
.resolves.toEqual({ path: 'http://pod.example/foo/bar' });
|
||||
});
|
||||
});
|
||||
26
test/unit/http/input/metadata/ContentTypeParser.test.ts
Normal file
26
test/unit/http/input/metadata/ContentTypeParser.test.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { ContentTypeParser } from '../../../../../src/http/input/metadata/ContentTypeParser';
|
||||
import { RepresentationMetadata } from '../../../../../src/http/representation/RepresentationMetadata';
|
||||
import type { HttpRequest } from '../../../../../src/server/HttpRequest';
|
||||
|
||||
describe('A ContentTypeParser', (): void => {
|
||||
const parser = new ContentTypeParser();
|
||||
let request: HttpRequest;
|
||||
let metadata: RepresentationMetadata;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
request = { headers: {}} as HttpRequest;
|
||||
metadata = new RepresentationMetadata();
|
||||
});
|
||||
|
||||
it('does nothing if there is no content-type header.', async(): Promise<void> => {
|
||||
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
||||
expect(metadata.quads()).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('sets the given content-type as metadata.', async(): Promise<void> => {
|
||||
request.headers['content-type'] = 'text/plain;charset=UTF-8';
|
||||
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
||||
expect(metadata.quads()).toHaveLength(1);
|
||||
expect(metadata.contentType).toBe('text/plain');
|
||||
});
|
||||
});
|
||||
55
test/unit/http/input/metadata/LinkParser.test.ts
Normal file
55
test/unit/http/input/metadata/LinkParser.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { LinkRelParser } from '../../../../../src/http/input/metadata/LinkRelParser';
|
||||
import { RepresentationMetadata } from '../../../../../src/http/representation/RepresentationMetadata';
|
||||
import type { HttpRequest } from '../../../../../src/server/HttpRequest';
|
||||
import { RDF } from '../../../../../src/util/Vocabularies';
|
||||
|
||||
describe('A LinkParser', (): void => {
|
||||
const parser = new LinkRelParser({ type: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' });
|
||||
let request: HttpRequest;
|
||||
let metadata: RepresentationMetadata;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
request = { headers: {}} as HttpRequest;
|
||||
metadata = new RepresentationMetadata();
|
||||
});
|
||||
|
||||
it('does nothing if there are no type headers.', async(): Promise<void> => {
|
||||
await parser.handle({ request, metadata });
|
||||
expect(metadata.quads()).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('stores link headers with rel matching the given value as metadata.', async(): Promise<void> => {
|
||||
request.headers.link = '<http://test.com/type>;rel="type"';
|
||||
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
||||
expect(metadata.quads()).toHaveLength(1);
|
||||
expect(metadata.get(RDF.type)?.value).toBe('http://test.com/type');
|
||||
});
|
||||
|
||||
it('supports multiple link headers.', async(): Promise<void> => {
|
||||
request.headers.link = [ '<http://test.com/typeA>;rel="type"', '<http://test.com/typeB>;rel=type' ];
|
||||
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
||||
expect(metadata.quads()).toHaveLength(2);
|
||||
expect(metadata.getAll(RDF.type).map((term): any => term.value))
|
||||
.toEqual([ 'http://test.com/typeA', 'http://test.com/typeB' ]);
|
||||
});
|
||||
|
||||
it('supports multiple link header values in the same entry.', async(): Promise<void> => {
|
||||
request.headers.link = '<http://test.com/typeA>;rel="type" , <http://test.com/typeB>;rel=type';
|
||||
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
||||
expect(metadata.quads()).toHaveLength(2);
|
||||
expect(metadata.getAll(RDF.type).map((term): any => term.value))
|
||||
.toEqual([ 'http://test.com/typeA', 'http://test.com/typeB' ]);
|
||||
});
|
||||
|
||||
it('ignores invalid link headers.', async(): Promise<void> => {
|
||||
request.headers.link = 'http://test.com/type;rel="type"';
|
||||
await parser.handle({ request, metadata });
|
||||
expect(metadata.quads()).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('ignores non-type link headers.', async(): Promise<void> => {
|
||||
request.headers.link = '<http://test.com/typeA>;rel="notype" , <http://test.com/typeB>';
|
||||
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
||||
expect(metadata.quads()).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
35
test/unit/http/input/metadata/SlugParser.test.ts
Normal file
35
test/unit/http/input/metadata/SlugParser.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { SlugParser } from '../../../../../src/http/input/metadata/SlugParser';
|
||||
import { RepresentationMetadata } from '../../../../../src/http/representation/RepresentationMetadata';
|
||||
import type { HttpRequest } from '../../../../../src/server/HttpRequest';
|
||||
import { BadRequestHttpError } from '../../../../../src/util/errors/BadRequestHttpError';
|
||||
import { SOLID_HTTP } from '../../../../../src/util/Vocabularies';
|
||||
|
||||
describe('A SlugParser', (): void => {
|
||||
const parser = new SlugParser();
|
||||
let request: HttpRequest;
|
||||
let metadata: RepresentationMetadata;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
request = { headers: {}} as HttpRequest;
|
||||
metadata = new RepresentationMetadata();
|
||||
});
|
||||
|
||||
it('does nothing if there is no slug header.', async(): Promise<void> => {
|
||||
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
||||
expect(metadata.quads()).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('errors if there are multiple slug headers.', async(): Promise<void> => {
|
||||
request.headers.slug = [ 'slugA', 'slugB' ];
|
||||
const result = parser.handle({ request, metadata });
|
||||
await expect(result).rejects.toThrow(BadRequestHttpError);
|
||||
await expect(result).rejects.toThrow('Request has multiple Slug headers');
|
||||
});
|
||||
|
||||
it('stores the slug metadata.', async(): Promise<void> => {
|
||||
request.headers.slug = 'slugA';
|
||||
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
||||
expect(metadata.quads()).toHaveLength(1);
|
||||
expect(metadata.get(SOLID_HTTP.slug)?.value).toBe('slugA');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
import { AcceptPreferenceParser } from '../../../../../src/http/input/preferences/AcceptPreferenceParser';
|
||||
import type { HttpRequest } from '../../../../../src/server/HttpRequest';
|
||||
|
||||
describe('An AcceptPreferenceParser', (): void => {
|
||||
const preferenceParser = new AcceptPreferenceParser();
|
||||
let request: HttpRequest;
|
||||
beforeEach(async(): Promise<void> => {
|
||||
request = { headers: {}} as HttpRequest;
|
||||
});
|
||||
|
||||
it('can handle all input.', async(): Promise<void> => {
|
||||
await expect(preferenceParser.canHandle({ request })).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns an empty result if there is no relevant input.', async(): Promise<void> => {
|
||||
await expect(preferenceParser.handle({ request })).resolves.toEqual({});
|
||||
|
||||
request.headers = { accept: '' };
|
||||
await expect(preferenceParser.handle({ request })).resolves.toEqual({});
|
||||
});
|
||||
|
||||
it('parses accept headers.', async(): Promise<void> => {
|
||||
request.headers = { accept: 'audio/*; q=0.2, audio/basic' };
|
||||
await expect(preferenceParser.handle({ request }))
|
||||
.resolves.toEqual({ type: { 'audio/basic': 1, 'audio/*': 0.2 }});
|
||||
});
|
||||
|
||||
it('parses accept-charset headers.', async(): Promise<void> => {
|
||||
request.headers = { 'accept-charset': 'iso-8859-5, unicode-1-1;q=0.8' };
|
||||
await expect(preferenceParser.handle({ request }))
|
||||
.resolves.toEqual({ charset: { 'iso-8859-5': 1, 'unicode-1-1': 0.8 }});
|
||||
});
|
||||
|
||||
it('parses accept-datetime headers.', async(): Promise<void> => {
|
||||
request.headers = { 'accept-datetime': 'Tue, 20 Mar 2001 20:35:00 GMT' };
|
||||
await expect(preferenceParser.handle({ request }))
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
.resolves.toEqual({ datetime: { 'Tue, 20 Mar 2001 20:35:00 GMT': 1 }});
|
||||
});
|
||||
|
||||
it('parses accept-encoding headers.', async(): Promise<void> => {
|
||||
request.headers = { 'accept-encoding': 'gzip;q=1.0, identity; q=0.5, *;q=0' };
|
||||
await expect(preferenceParser.handle({ request }))
|
||||
.resolves.toEqual({ encoding: { gzip: 1, identity: 0.5, '*': 0 }});
|
||||
});
|
||||
|
||||
it('parses accept-language headers.', async(): Promise<void> => {
|
||||
request.headers = { 'accept-language': 'da, en-gb;q=0.8, en;q=0.7' };
|
||||
await expect(preferenceParser.handle({ request }))
|
||||
.resolves.toEqual({ language: { da: 1, 'en-gb': 0.8, en: 0.7 }});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user