mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
205 lines
9.5 KiB
TypeScript
205 lines
9.5 KiB
TypeScript
import 'jest-rdf';
|
|
import type { Term } from '@rdfjs/types';
|
|
import { DataFactory } from 'n3';
|
|
import type { BodyParserArgs } from '../../../../../src/http/input/body/BodyParser';
|
|
import { N3PatchBodyParser } from '../../../../../src/http/input/body/N3PatchBodyParser';
|
|
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';
|
|
const { defaultGraph, literal, namedNode, quad, variable } = DataFactory;
|
|
|
|
describe('An N3PatchBodyParser', (): void => {
|
|
let input: BodyParserArgs;
|
|
const parser = new N3PatchBodyParser();
|
|
|
|
beforeEach(async(): Promise<void> => {
|
|
input = {
|
|
request: { headers: {}} as HttpRequest,
|
|
metadata: new RepresentationMetadata({ path: 'http://example.com/foo' }, 'text/n3'),
|
|
};
|
|
});
|
|
|
|
it('can only handle N3 data.', async(): Promise<void> => {
|
|
input.metadata.contentType = 'text/plain';
|
|
await expect(parser.canHandle(input)).rejects.toThrow(UnsupportedMediaTypeHttpError);
|
|
input.metadata.contentType = 'text/n3';
|
|
await expect(parser.canHandle(input)).resolves.toBeUndefined();
|
|
});
|
|
|
|
it('errors on invalid N3.', async(): Promise<void> => {
|
|
input.request = guardedStreamFrom([ 'invalid syntax' ]) as HttpRequest;
|
|
await expect(parser.handle(input)).rejects.toThrow(BadRequestHttpError);
|
|
});
|
|
|
|
it('extracts the patch quads from the request.', async(): Promise<void> => {
|
|
const n3 = `@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
@prefix ex: <http://www.example.org/terms#>.
|
|
|
|
_:rename a solid:InsertDeletePatch;
|
|
solid:where { ?person ex:familyName "Garcia"; ex:nickName "Garry". };
|
|
solid:inserts { ?person ex:givenName "Alex". };
|
|
solid:deletes { ?person ex:givenName "Claudia". }.`;
|
|
input.request = guardedStreamFrom([ n3 ]) as HttpRequest;
|
|
const patch = await parser.handle(input);
|
|
expect(patch.conditions).toBeRdfIsomorphic([
|
|
quad(variable('person'), namedNode('http://www.example.org/terms#familyName'), literal('Garcia')),
|
|
quad(variable('person'), namedNode('http://www.example.org/terms#nickName'), literal('Garry')),
|
|
]);
|
|
expect(patch.inserts).toBeRdfIsomorphic([
|
|
quad(variable('person'), namedNode('http://www.example.org/terms#givenName'), literal('Alex')),
|
|
]);
|
|
expect(patch.deletes).toBeRdfIsomorphic([
|
|
quad(variable('person'), namedNode('http://www.example.org/terms#givenName'), literal('Claudia')),
|
|
]);
|
|
});
|
|
|
|
it('strips the graph from the result quads.', async(): Promise<void> => {
|
|
const n3 = `@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
@prefix ex: <http://www.example.org/terms#>.
|
|
|
|
_:rename a solid:InsertDeletePatch;
|
|
solid:where { ?person ex:familyName "Garcia"; ex:nickName "Garry". };
|
|
solid:inserts { ?person ex:givenName "Alex". };
|
|
solid:deletes { ?person ex:givenName "Claudia". }.`;
|
|
input.request = guardedStreamFrom([ n3 ]) as HttpRequest;
|
|
const patch = await parser.handle(input);
|
|
const quads = [ ...patch.deletes, ...patch.inserts, ...patch.conditions ];
|
|
const uniqueGraphs = [ ...new Set(quads.map((entry): Term => entry.graph)) ];
|
|
expect(uniqueGraphs).toHaveLength(1);
|
|
expect(uniqueGraphs[0]).toEqualRdfTerm(defaultGraph());
|
|
});
|
|
|
|
it('errors if no solid:InsertDeletePatch is found.', async(): Promise<void> => {
|
|
const n3 = `@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
@prefix ex: <http://www.example.org/terms#>.
|
|
|
|
_:rename
|
|
solid:where { ?person ex:familyName "Garcia"; ex:nickName "Garry". };
|
|
solid:inserts { ?person ex:givenName "Alex". };
|
|
solid:deletes { ?person ex:givenName "Claudia". }.`;
|
|
input.request = guardedStreamFrom([ n3 ]) as HttpRequest;
|
|
await expect(parser.handle(input)).rejects.toThrow(
|
|
'This patcher only supports N3 Patch documents with exactly 1 solid:InsertDeletePatch entry, but received 0.',
|
|
);
|
|
});
|
|
|
|
it('errors if multiple solid:InsertDeletePatch entries are found.', async(): Promise<void> => {
|
|
const n3 = `@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
@prefix ex: <http://www.example.org/terms#>.
|
|
|
|
_:other a solid:InsertDeletePatch.
|
|
|
|
_:rename a solid:InsertDeletePatch;
|
|
solid:where { ?person ex:familyName "Garcia"; ex:nickName "Garry". };
|
|
solid:inserts { ?person ex:givenName "Alex". };
|
|
solid:deletes { ?person ex:givenName "Claudia". }.`;
|
|
input.request = guardedStreamFrom([ n3 ]) as HttpRequest;
|
|
await expect(parser.handle(input)).rejects.toThrow(
|
|
'This patcher only supports N3 Patch documents with exactly 1 solid:InsertDeletePatch entry, but received 2.',
|
|
);
|
|
});
|
|
|
|
it('errors if the patch subject is not a blank or named node.', async(): Promise<void> => {
|
|
const n3 = `@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
@prefix ex: <http://www.example.org/terms#>.
|
|
|
|
?rename a solid:InsertDeletePatch;
|
|
solid:where { ?person ex:familyName "Garcia"; ex:nickName "Garry". };
|
|
solid:inserts { ?person ex:givenName "Alex". };
|
|
solid:deletes { ?person ex:givenName "Claudia". }.`;
|
|
input.request = guardedStreamFrom([ n3 ]) as HttpRequest;
|
|
await expect(parser.handle(input)).rejects
|
|
.toThrow('An N3 Patch subject needs to be a blank or named node.');
|
|
});
|
|
|
|
it('errors if there are multiple where entries.', async(): Promise<void> => {
|
|
const n3 = `@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
@prefix ex: <http://www.example.org/terms#>.
|
|
|
|
_:rename a solid:InsertDeletePatch;
|
|
solid:where { ?person ex:familyName "Garcia"; ex:nickName "Garry". };
|
|
solid:where { ?person ex:givenName "Alex". }.`;
|
|
input.request = guardedStreamFrom([ n3 ]) as HttpRequest;
|
|
await expect(parser.handle(input)).rejects
|
|
.toThrow('An N3 Patch can have at most 1 http://www.w3.org/ns/solid/terms#where.');
|
|
});
|
|
|
|
it('errors if there are multiple delete entries.', async(): Promise<void> => {
|
|
const n3 = `@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
@prefix ex: <http://www.example.org/terms#>.
|
|
|
|
_:rename a solid:InsertDeletePatch;
|
|
solid:deletes { ex:person ex:familyName "Garcia". };
|
|
solid:deletes { ex:person ex:givenName "Alex". }.`;
|
|
input.request = guardedStreamFrom([ n3 ]) as HttpRequest;
|
|
await expect(parser.handle(input)).rejects
|
|
.toThrow('An N3 Patch can have at most 1 http://www.w3.org/ns/solid/terms#deletes.');
|
|
});
|
|
|
|
it('errors if there are multiple insert entries.', async(): Promise<void> => {
|
|
const n3 = `@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
@prefix ex: <http://www.example.org/terms#>.
|
|
|
|
_:rename a solid:InsertDeletePatch;
|
|
solid:inserts { ex:person ex:familyName "Garcia". };
|
|
solid:inserts { ex:person ex:givenName "Alex". }.`;
|
|
input.request = guardedStreamFrom([ n3 ]) as HttpRequest;
|
|
await expect(parser.handle(input)).rejects
|
|
.toThrow('An N3 Patch can have at most 1 http://www.w3.org/ns/solid/terms#inserts.');
|
|
});
|
|
|
|
it('errors if there are blank nodes in the delete formula.', async(): Promise<void> => {
|
|
const n3 = `@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
@prefix ex: <http://www.example.org/terms#>.
|
|
|
|
_:rename a solid:InsertDeletePatch;
|
|
solid:where { ?person ex:familyName "Garcia"; ex:nickName "Garry". };
|
|
solid:inserts { ?person ex:givenName "Alex". };
|
|
solid:deletes { _:person ex:givenName "Claudia". }.`;
|
|
input.request = guardedStreamFrom([ n3 ]) as HttpRequest;
|
|
await expect(parser.handle(input)).rejects
|
|
.toThrow('An N3 Patch delete/insert formula can not contain blank nodes.');
|
|
});
|
|
|
|
it('errors if there are blank nodes in the insert formula.', async(): Promise<void> => {
|
|
const n3 = `@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
@prefix ex: <http://www.example.org/terms#>.
|
|
|
|
_:rename a solid:InsertDeletePatch;
|
|
solid:where { ?person ex:familyName "Garcia"; ex:nickName "Garry". };
|
|
solid:inserts { _:person ex:givenName "Alex". };
|
|
solid:deletes { ?person ex:givenName "Claudia". }.`;
|
|
input.request = guardedStreamFrom([ n3 ]) as HttpRequest;
|
|
await expect(parser.handle(input)).rejects
|
|
.toThrow('An N3 Patch delete/insert formula can not contain blank nodes.');
|
|
});
|
|
|
|
it('errors if there are unknown variables in the delete formula.', async(): Promise<void> => {
|
|
const n3 = `@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
@prefix ex: <http://www.example.org/terms#>.
|
|
|
|
_:rename a solid:InsertDeletePatch;
|
|
solid:where { ?person ex:familyName "Garcia"; ex:nickName "Garry". };
|
|
solid:inserts { ?person ex:givenName "Alex". };
|
|
solid:deletes { ?person ex:givenName ?name. }.`;
|
|
input.request = guardedStreamFrom([ n3 ]) as HttpRequest;
|
|
await expect(parser.handle(input)).rejects
|
|
.toThrow('An N3 Patch delete/insert formula can only contain variables found in the conditions formula.');
|
|
});
|
|
|
|
it('errors if there are unknown variables in the insert formula.', async(): Promise<void> => {
|
|
const n3 = `@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
@prefix ex: <http://www.example.org/terms#>.
|
|
|
|
_:rename a solid:InsertDeletePatch;
|
|
solid:where { ?person ex:familyName "Garcia"; ex:nickName "Garry". };
|
|
solid:inserts { ?person ex:givenName ?name. };
|
|
solid:deletes { ?person ex:givenName "Claudia". }.`;
|
|
input.request = guardedStreamFrom([ n3 ]) as HttpRequest;
|
|
await expect(parser.handle(input)).rejects
|
|
.toThrow('An N3 Patch delete/insert formula can only contain variables found in the conditions formula.');
|
|
});
|
|
});
|