mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00

The previous package was outdated, preventing us from updating TS. This one also lints YAML and JSON, and applies many more rules to the test files, explaining all the changes in this PR.
203 lines
8.3 KiB
TypeScript
203 lines
8.3 KiB
TypeScript
import 'jest-rdf';
|
|
import { DataFactory, Store } from 'n3';
|
|
import type { Quad } from 'rdf-js';
|
|
import type { Algebra } from 'sparqlalgebrajs';
|
|
import { translate } from 'sparqlalgebrajs';
|
|
import { BasicRepresentation } from '../../../../src/http/representation/BasicRepresentation';
|
|
import type { RdfDatasetRepresentation } from '../../../../src/http/representation/RdfDatasetRepresentation';
|
|
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
|
import type { SparqlUpdatePatch } from '../../../../src/http/representation/SparqlUpdatePatch';
|
|
import type { RepresentationPatcherInput } from '../../../../src/storage/patch/RepresentationPatcher';
|
|
import { SparqlUpdatePatcher } from '../../../../src/storage/patch/SparqlUpdatePatcher';
|
|
import { InternalServerError } from '../../../../src/util/errors/InternalServerError';
|
|
import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError';
|
|
import { guardedStreamFrom } from '../../../../src/util/StreamUtil';
|
|
|
|
const { namedNode, quad } = DataFactory;
|
|
|
|
function getPatch(query: string): SparqlUpdatePatch {
|
|
const prefixedQuery = `prefix : <http://test.com/>\n${query}`;
|
|
return {
|
|
algebra: translate(prefixedQuery, { quads: true }) as Algebra.Update,
|
|
data: guardedStreamFrom(prefixedQuery),
|
|
metadata: new RepresentationMetadata(),
|
|
binary: true,
|
|
isEmpty: false,
|
|
};
|
|
}
|
|
|
|
describe('A SparqlUpdatePatcher', (): void => {
|
|
let patcher: SparqlUpdatePatcher;
|
|
let startQuads: Quad[];
|
|
const identifier = { path: 'http://test.com/foo' };
|
|
const fulfilledDataInsert = 'INSERT DATA { :s1 :p1 :o1 . :s2 :p2 :o2 . }';
|
|
let store: Store;
|
|
let input: RepresentationPatcherInput<RdfDatasetRepresentation>;
|
|
let representation: RdfDatasetRepresentation;
|
|
|
|
beforeEach(async(): Promise<void> => {
|
|
startQuads = [ quad(
|
|
namedNode('http://test.com/startS1'),
|
|
namedNode('http://test.com/startP1'),
|
|
namedNode('http://test.com/startO1'),
|
|
), quad(
|
|
namedNode('http://test.com/startS2'),
|
|
namedNode('http://test.com/startP2'),
|
|
namedNode('http://test.com/startO2'),
|
|
) ];
|
|
store = new Store(startQuads);
|
|
|
|
representation = new BasicRepresentation() as RdfDatasetRepresentation;
|
|
representation.dataset = store;
|
|
input = { identifier, patch: { algebra: {}} as SparqlUpdatePatch, representation };
|
|
|
|
patcher = new SparqlUpdatePatcher();
|
|
});
|
|
|
|
it('only accepts SPARQL updates.', async(): Promise<void> => {
|
|
await expect(patcher.canHandle(input)).resolves.toBeUndefined();
|
|
delete (input.patch as any).algebra;
|
|
await expect(patcher.canHandle(input)).rejects.toThrow(NotImplementedHttpError);
|
|
});
|
|
|
|
it('handles NOP operations by not doing anything.', async(): Promise<void> => {
|
|
input.patch = getPatch('');
|
|
await expect(patcher.handle(input)).resolves.toBe(representation);
|
|
|
|
input.representation = undefined;
|
|
input.patch = getPatch('');
|
|
await expect(patcher.handle(input)).rejects.toThrow(InternalServerError);
|
|
});
|
|
|
|
it('handles INSERT DATA updates.', async(): Promise<void> => {
|
|
input.patch = getPatch(fulfilledDataInsert);
|
|
const result = await patcher.handle(input);
|
|
expect(result.dataset).toBeRdfIsomorphic([
|
|
...startQuads,
|
|
quad(namedNode('http://test.com/s1'), namedNode('http://test.com/p1'), namedNode('http://test.com/o1')),
|
|
quad(namedNode('http://test.com/s2'), namedNode('http://test.com/p2'), namedNode('http://test.com/o2')),
|
|
]);
|
|
});
|
|
|
|
it('handles DELETE DATA updates.', async(): Promise<void> => {
|
|
input.patch = getPatch('DELETE DATA { :startS1 :startP1 :startO1 }');
|
|
const result = await patcher.handle(input);
|
|
expect(result.dataset).toBeRdfIsomorphic([
|
|
quad(namedNode('http://test.com/startS2'),
|
|
namedNode('http://test.com/startP2'),
|
|
namedNode('http://test.com/startO2')),
|
|
]);
|
|
});
|
|
|
|
it('handles DELETE WHERE updates with no variables.', async(): Promise<void> => {
|
|
input.patch = getPatch('DELETE WHERE { :startS1 :startP1 :startO1 }');
|
|
const result = await patcher.handle(input);
|
|
expect(result.dataset).toBeRdfIsomorphic([
|
|
quad(namedNode('http://test.com/startS2'),
|
|
namedNode('http://test.com/startP2'),
|
|
namedNode('http://test.com/startO2')),
|
|
]);
|
|
});
|
|
|
|
it('handles DELETE WHERE updates with variables.', async(): Promise<void> => {
|
|
input.patch = getPatch('DELETE WHERE { :startS1 :startP1 ?o }');
|
|
const result = await patcher.handle(input);
|
|
expect(result.dataset).toBeRdfIsomorphic([
|
|
quad(namedNode('http://test.com/startS2'),
|
|
namedNode('http://test.com/startP2'),
|
|
namedNode('http://test.com/startO2')),
|
|
]);
|
|
});
|
|
|
|
it('handles DELETE/INSERT updates with empty WHERE.', async(): Promise<void> => {
|
|
input.patch = getPatch('DELETE { :startS1 :startP1 :startO1 } INSERT { :s1 :p1 :o1 . } WHERE {}');
|
|
const result = await patcher.handle(input);
|
|
expect(result.dataset).toBeRdfIsomorphic([
|
|
quad(namedNode('http://test.com/startS2'),
|
|
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('handles composite INSERT/DELETE updates.', async(): Promise<void> => {
|
|
const query = 'INSERT DATA { :s1 :p1 :o1 . :s2 :p2 :o2 };' +
|
|
'DELETE WHERE { :s1 :p1 :o1 . :startS1 :startP1 :startO1 }';
|
|
input.patch = getPatch(query);
|
|
const result = await patcher.handle(input);
|
|
expect(result.dataset).toBeRdfIsomorphic([
|
|
quad(namedNode('http://test.com/startS2'),
|
|
namedNode('http://test.com/startP2'),
|
|
namedNode('http://test.com/startO2')),
|
|
quad(namedNode('http://test.com/s2'),
|
|
namedNode('http://test.com/p2'),
|
|
namedNode('http://test.com/o2')),
|
|
]);
|
|
});
|
|
|
|
it('handles composite DELETE/INSERT updates.', async(): Promise<void> => {
|
|
const query = 'DELETE DATA { :s1 :p1 :o1 . :startS1 :startP1 :startO1 } ;' +
|
|
'INSERT DATA { :s1 :p1 :o1 . :s2 :p2 :o2 }';
|
|
input.patch = getPatch(query);
|
|
const result = await patcher.handle(input);
|
|
expect(result.dataset).toBeRdfIsomorphic([
|
|
quad(namedNode('http://test.com/startS2'),
|
|
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')),
|
|
quad(namedNode('http://test.com/s2'),
|
|
namedNode('http://test.com/p2'),
|
|
namedNode('http://test.com/o2')),
|
|
]);
|
|
});
|
|
|
|
it('rejects GRAPH inserts.', async(): Promise<void> => {
|
|
const query = 'INSERT DATA { GRAPH :graph { :s1 :p1 :o1 } }';
|
|
input.patch = getPatch(query);
|
|
|
|
await expect(patcher.handle(input)).rejects.toThrow(NotImplementedHttpError);
|
|
});
|
|
|
|
it('rejects GRAPH deletes.', async(): Promise<void> => {
|
|
const query = 'DELETE DATA { GRAPH :graph { :s1 :p1 :o1 } }';
|
|
input.patch = getPatch(query);
|
|
|
|
await expect(patcher.handle(input)).rejects.toThrow(NotImplementedHttpError);
|
|
});
|
|
|
|
it('rejects DELETE/INSERT updates with non-BGP WHERE.', async(): Promise<void> => {
|
|
const query = 'DELETE { :s1 :p1 :o1 } INSERT { :s1 :p1 :o1 } WHERE { ?s ?p ?o. FILTER (?o > 5) }';
|
|
input.patch = getPatch(query);
|
|
|
|
await expect(patcher.handle(input)).rejects.toThrow(NotImplementedHttpError);
|
|
});
|
|
|
|
it('rejects INSERT WHERE updates with a UNION.', async(): Promise<void> => {
|
|
const query = 'INSERT { :s1 :p1 :o1 . } WHERE { { :s1 :p1 :o1 } UNION { :s1 :p1 :o2 } }';
|
|
input.patch = getPatch(query);
|
|
|
|
await expect(patcher.handle(input)).rejects.toThrow(NotImplementedHttpError);
|
|
});
|
|
|
|
it('rejects non-DELETE/INSERT updates.', async(): Promise<void> => {
|
|
const query = 'MOVE DEFAULT TO GRAPH :newGraph';
|
|
input.patch = getPatch(query);
|
|
|
|
await expect(patcher.handle(input)).rejects.toThrow(NotImplementedHttpError);
|
|
});
|
|
|
|
it('creates a new resource if it does not exist yet.', async(): Promise<void> => {
|
|
const query = 'INSERT DATA { <http://test.com/s1> <http://test.com/p1> <http://test.com/o1>. }';
|
|
input.patch = getPatch(query);
|
|
input.representation!.dataset = new Store();
|
|
const result = await patcher.handle(input);
|
|
expect(result.dataset).toBeRdfIsomorphic([
|
|
quad(namedNode('http://test.com/s1'), namedNode('http://test.com/p1'), namedNode('http://test.com/o1')),
|
|
]);
|
|
});
|
|
});
|