UNPKG

rdf-test-suite

Version:
302 lines (297 loc) 14.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TestCaseQueryEvaluation = exports.TestCaseQueryEvaluationHandler = void 0; const rdf_data_factory_1 = require("rdf-data-factory"); const rdf_object_1 = require("rdf-object"); const rdf_string_1 = require("rdf-string"); const rdf_terms_1 = require("rdf-terms"); const sparqljson_parse_1 = require("sparqljson-parse"); const sparqlxml_parse_1 = require("sparqlxml-parse"); const ErrorTest_1 = require("../../ErrorTest"); const Util_1 = require("../../Util"); const QueryResultBindings_1 = require("./QueryResultBindings"); const QueryResultBoolean_1 = require("./QueryResultBoolean"); const QueryResultQuads_1 = require("./QueryResultQuads"); const arrayify_stream_1 = require("arrayify-stream"); // tslint:disable:no-var-requires const stringifyStream = require('stream-to-string'); const DF = new rdf_data_factory_1.DataFactory(); /** * Test case handler for http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#QueryEvaluationTest. */ class TestCaseQueryEvaluationHandler { /** * Parse SPARQL query results in any of the following content types: * * application/sparql-results+xml (bindings/ask) * * application/sparql-results+json (bindings/ask) * * Any RDF serialization (quads) * * If the results has as first triple '[] a <http://www.w3.org/2001/sw/DataAccess/tests/result-set#ResultSet>', * the it will be considered as a DAWG result set, and parsed as such. (bindings) * * @param {string} contentType The content type. * @param {string} url The base IRI. * @param {NodeJS.ReadableStream} data The data stream to parse. * @return {Promise<IQueryResult>} A promise resolving to a SPARQL query result. */ static parseQueryResult(contentType, url, data) { return __awaiter(this, void 0, void 0, function* () { let queryResult; try { const rdfStream = Util_1.Util.parseRdfRaw(contentType, url, data); queryResult = new QueryResultQuads_1.QueryResultQuads(yield (0, arrayify_stream_1.default)(rdfStream)); } catch (e) { // Fallthrough to the next cases } if (contentType.indexOf('application/sparql-results+xml') >= 0 || url.endsWith('.srx')) { contentType = 'application/sparql-results+xml'; queryResult = yield TestCaseQueryEvaluationHandler.parseSparqlResults('xml', data); } if (contentType.indexOf('application/sparql-results+json') >= 0) { queryResult = yield TestCaseQueryEvaluationHandler.parseSparqlResults('json', data); } if (!queryResult) { throw new Error(`Could not parse the query result with content type ${contentType} at ${url}`); } // Discover DAWG result sets if (queryResult.type === 'quads' && queryResult.value.length > 0 && queryResult.value[0].predicate.value === 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' && queryResult.value[0].object.value === 'http://www.w3.org/2001/sw/DataAccess/tests/result-set#ResultSet') { return yield TestCaseQueryEvaluationHandler.parseDawgResultSet(queryResult.value); } return queryResult; }); } /** * Parses query results in either SPARQL/JSON or SPARQL/XML query results syntax. * @param {string} type json or xml * @param {NodeJS.ReadableStream} data The data stream to parse. * @return {Promise<IQueryResult>} A promise resolving to a SPARQL query result. */ static parseSparqlResults(type, data) { return __awaiter(this, void 0, void 0, function* () { let booleanPromise; let bindingsStream; if (type === 'json') { const parser = new sparqljson_parse_1.SparqlJsonParser({ prefixVariableQuestionMark: true }); booleanPromise = parser.parseJsonBooleanStream(data); bindingsStream = parser.parseJsonResultsStream(data); } else { const parser = new sparqlxml_parse_1.SparqlXmlParser({ prefixVariableQuestionMark: true }); booleanPromise = parser.parseXmlBooleanStream(data); bindingsStream = parser.parseXmlResultsStream(data); } const bindingsPromise = Promise.all([ new Promise((resolve) => { bindingsStream .on('variables', resolve) .on('end', _ => resolve([])); }), (0, arrayify_stream_1.default)(bindingsStream), ]); // Parse both in parallel, and silence the errors let bindingsError = null; let booleanError = null; const parsingResults = yield Promise.all([ booleanPromise.catch((e) => { booleanError = e; return null; }), bindingsPromise.catch((e) => { bindingsError = e; return null; }), ]); if (!booleanError) { return new QueryResultBoolean_1.QueryResultBoolean(parsingResults[0]); } else if (!bindingsError) { return new QueryResultBindings_1.QueryResultBindings(parsingResults[1][0].map((variable) => '?' + variable.value), parsingResults[1][1], false); } else { throw new Error('Found no valid ASK or SELECT query.\n' + bindingsError.message + '\n' + booleanError.message); } }); } /** * Parses query results in the DAWG vocabulary. * https://www.w3.org/2001/sw/DataAccess/tests/test-dawg.n3 * @param {Quad[]} quads An array of quads. * @return {Promise<IQueryResultBindings>} A promise resolving to a bindings results object. */ static parseDawgResultSet(quads) { return __awaiter(this, void 0, void 0, function* () { // Construct resources for easier interpretation of the bindings // tslint:disable:object-literal-sort-keys const objectLoader = new rdf_object_1.RdfObjectLoader({ context: { result: 'http://www.w3.org/2001/sw/DataAccess/tests/result-set#', bindings: 'result:binding', index: 'result:index', resultVariables: 'result:resultVariable', solutions: 'result:solution', value: 'result:value', variable: 'result:variable', }, }); // tslint:enable:object-literal-sort-keys yield objectLoader.importArray(quads); let resultSet = null; for (const resourceName in objectLoader.resources) { if (objectLoader.resources[resourceName] .isA((0, rdf_string_1.stringToTerm)('http://www.w3.org/2001/sw/DataAccess/tests/result-set#ResultSet'))) { resultSet = objectLoader.resources[resourceName]; break; } } if (!resultSet) { throw new Error('No valid DAWG result set was found in ' + quads); } // Get the variable names const variables = resultSet.properties.resultVariables .map((resource) => '?' + resource.value); // check if any binding has .index let checkOrder = false; const s = resultSet.properties.solutions; for (const solution of s) { if (solution.property.index) { checkOrder = true; break; } } // Ensure that the solutions are sorted by index const solutions = resultSet.properties.solutions; if (checkOrder) { solutions.sort((solution1, solution2) => { return parseInt(solution1.property.index.value, 10) - parseInt(solution2.property.index.value, 10); }); } // Collect the bindings as object const value = solutions.map((solution) => { return solution.properties.bindings.reduce((bindings, binding) => { bindings['?' + binding.property.variable.value] = binding.property.value.term; return bindings; }, {}); }); return new QueryResultBindings_1.QueryResultBindings(variables, value, checkOrder); }); } /** * Obtain all data links for the given query test action. * @param action A query test action. */ static getQueryDataLinks(action) { const queryDataLinks = []; if (action.property.data) { queryDataLinks.push({ dataUri: action.property.data.value, }); } for (const graphData of action.properties.graphData) { if (graphData.property.graph) { queryDataLinks.push({ dataUri: graphData.property.graph.value, dataGraph: DF.namedNode(Util_1.Util.normalizeBaseUrl(graphData.property.label.value)), }); } else { queryDataLinks.push({ dataUri: graphData.value, dataGraph: DF.namedNode(Util_1.Util.normalizeBaseUrl(graphData.value)), }); } } return queryDataLinks; } /** * Determine the quads for the given query data links. * @param queryDataLinks Links to query data. * @param options Fetch options. */ static resolveQueryDataLinks(queryDataLinks, options) { return __awaiter(this, void 0, void 0, function* () { let queryData = []; for (const queryDataLink of queryDataLinks) { let queryDataThis = yield (0, arrayify_stream_1.default)((yield Util_1.Util.fetchRdf(queryDataLink.dataUri, Object.assign(Object.assign({}, options), { normalizeUrl: true })))[1]); if (queryDataLink.dataGraph) { queryDataThis = queryDataThis.map((quad) => (0, rdf_terms_1.mapTerms)(quad, (value, key) => key === 'graph' ? queryDataLink.dataGraph : value)); } queryData = [...queryData, ...queryDataThis]; } return queryData; }); } resourceToTestCase(resource, testCaseData, options) { return __awaiter(this, void 0, void 0, function* () { if (!resource.property.action) { throw new Error(`Missing mf:action in ${resource}`); } if (!resource.property.result) { throw new Error(`Missing mf:result in ${resource}`); } const action = resource.property.action; if (!action.property.query) { throw new Error(`Missing qt:query in mf:action of ${resource}`); } // Determine links to data const queryDataLinks = TestCaseQueryEvaluationHandler.getQueryDataLinks(action); // Check for lax cardinality property let laxCardinality = false; if (resource.property.resultCardinality && resource.property.resultCardinality.value === 'http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#LaxCardinality') { laxCardinality = true; } // Collect all query data const queryData = yield TestCaseQueryEvaluationHandler.resolveQueryDataLinks(queryDataLinks, options); const queryResponse = yield Util_1.Util.fetchCached(resource.property.result.value, options); return new TestCaseQueryEvaluation(testCaseData, { baseIRI: Util_1.Util.normalizeBaseUrl(action.property.query.value), queryDataLinks, laxCardinality, queryData, queryResult: yield TestCaseQueryEvaluationHandler.parseQueryResult(Util_1.Util.identifyContentType(queryResponse.url, queryResponse.headers), queryResponse.url, queryResponse.body), queryString: yield stringifyStream((yield Util_1.Util.fetchCached(action.property.query.value, options)).body), resultSource: queryResponse, }); }); } } exports.TestCaseQueryEvaluationHandler = TestCaseQueryEvaluationHandler; class TestCaseQueryEvaluation { constructor(testCaseData, props) { this.type = "sparql"; Object.assign(this, testCaseData); Object.assign(this, props); } static queryDataLinksToString(queryDataLinks) { return queryDataLinks.map((queryDataLink) => queryDataLink.dataUri + (queryDataLink.dataGraph ? ` (named graph: ${queryDataLink.dataGraph.value})` : '')).join(',\n '); } test(engine, injectArguments) { return __awaiter(this, void 0, void 0, function* () { const result = yield engine.query(this.queryData, this.queryString, Object.assign({ baseIRI: this.baseIRI }, injectArguments)); if (!(yield this.queryResult.equals(result, this.laxCardinality))) { throw new ErrorTest_1.ErrorTest(`Invalid query evaluation Query:\n\n${this.queryString} Data links: ${TestCaseQueryEvaluation.queryDataLinksToString(this.queryDataLinks)} Result Source: ${this.resultSource.url} Expected: ${this.queryResult.toString()} Got: \n ${result.toString()} `); } }); } } exports.TestCaseQueryEvaluation = TestCaseQueryEvaluation; //# sourceMappingURL=TestCaseQueryEvaluation.js.map