refactor: Restructure source code folder

This way the location of certain classes should make more sense
This commit is contained in:
Joachim Van Herwegen
2021-10-08 10:58:35 +02:00
parent 012d9e0864
commit b3da9c9fcf
280 changed files with 684 additions and 673 deletions

View 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') },
});
});
});

View 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>.' ],
);
});
});

View 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> }' ],
);
});
});

View File

@@ -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();
});
});

View 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' });
});
});

View 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');
});
});

View 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);
});
});

View 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');
});
});

View File

@@ -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 }});
});
});