mustrd 0.1.7__tar.gz → 0.1.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {mustrd-0.1.7 → mustrd-0.1.8}/PKG-INFO +1 -1
  2. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/README.adoc +6 -6
  3. mustrd-0.1.8/mustrd/model/catalog-v001.xml +5 -0
  4. mustrd-0.1.8/mustrd/model/mustrdTestOntology.ttl +51 -0
  5. mustrd-0.1.8/mustrd/model/mustrdTestShapes.ttl +24 -0
  6. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/model/ontology.ttl +2 -1
  7. mustrd-0.1.8/mustrd/model/triplestoreOntology.ttl +174 -0
  8. mustrd-0.1.8/mustrd/model/triplestoreshapes.ttl +42 -0
  9. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/mustrd.py +49 -39
  10. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/mustrdTestPlugin.py +125 -58
  11. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/namespace.py +36 -29
  12. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/run.py +2 -2
  13. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/spec_component.py +11 -11
  14. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/steprunner.py +15 -15
  15. mustrd-0.1.8/mustrd/test/test_mustrd.py +5 -0
  16. {mustrd-0.1.7 → mustrd-0.1.8}/pyproject.toml +1 -1
  17. {mustrd-0.1.7 → mustrd-0.1.8}/LICENSE +0 -0
  18. {mustrd-0.1.7 → mustrd-0.1.8}/README.adoc +0 -0
  19. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/TestResult.py +0 -0
  20. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/__init__.py +0 -0
  21. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/logger_setup.py +0 -0
  22. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/model/mustrdShapes.ttl +0 -0
  23. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/model/test-resources/resources.ttl +0 -0
  24. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/mustrdAnzo.py +0 -0
  25. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/mustrdGraphDb.py +0 -0
  26. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/mustrdRdfLib.py +0 -0
  27. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/templates/md_ResultList_leaf_template.jinja +0 -0
  28. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/templates/md_ResultList_template.jinja +0 -0
  29. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/templates/md_stats_template.jinja +0 -0
  30. {mustrd-0.1.7 → mustrd-0.1.8}/mustrd/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mustrd
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: A Spec By Example framework for RDF and SPARQL, Inspired by Cucumber.
5
5
  Home-page: https://github.com/Semantic-partners/mustrd
6
6
  License: MIT
@@ -178,16 +178,16 @@ The configuration file should be serialised RDF. An example in Turtle format is
178
178
  must:GraphDbConfig1 a must:GraphDbConfig ;
179
179
  must:url "http://localhost";
180
180
  must:port "7200";
181
- must:username "test/triplestore_config/tripleStoreCredentials.toml" ;
182
- must:password "test/triplestore_config/tripleStoreCredentials.toml" ;
183
181
  must:inputGraph "http://localhost:7200/test-graph" ;
184
182
  must:repository "mustrd" .
185
183
  ----
186
- The triplestore credentials are held in a separate TOML file so that configurations can be shared without sharing credentials.
184
+ To avoid versioning secrets when you want to version triplestore configuration (for example in case you want to run mustrd in CI), you have to configure user/password in a different file.
185
+ This file must be named as the triple store configuration file, but with "_secrets" just before the extension. For example triplestores.ttl -> triplestores_secrets.ttl
186
+ Subjects in the two files must match, no need to redefine the type, for example:
187
187
  ----
188
- ["https://mustrd.com/model/GraphDbConfig1"]
189
- "username"="<username>"
190
- "password"="<password>"
188
+ @prefix must: <https://mustrd.com/model/> .
189
+ must:GraphDbConfig1 must:username 'test' ;
190
+ must:password 'test' .
191
191
  ----
192
192
 
193
193
  == Additional Notes for Developers
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <catalog prefer="public" xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
3
+ <uri id="User Entered Import Resolution" name="https://mustrd.com/triplestore/" uri="triplestoreOntology.ttl"/>
4
+ <group id="Folder Repository, directory=, recursive=true, Auto-Update=true, version=2" prefer="public" xml:base=""/>
5
+ </catalog>
@@ -0,0 +1,51 @@
1
+ @prefix : <https://mustrd.com/mustrdTest/> .
2
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
3
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
4
+ @prefix xml: <http://www.w3.org/XML/1998/namespace> .
5
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
6
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
7
+ @prefix triplestore: <https://mustrd.com/triplestore/> .
8
+ @base <https://mustrd.com/mustrdTest/> .
9
+
10
+ <https://mustrd.com/mustrdTest/> rdf:type owl:Ontology ;
11
+ owl:imports triplestore: ;
12
+ rdfs:comment "Model user input to generate mustrd pytests" ;
13
+ rdfs:label "Mustrd test ontology" .
14
+
15
+ #################################################################
16
+ # Object Properties
17
+ #################################################################
18
+
19
+ ### https://mustrd.com/mustrdTest/filterOnTripleStore
20
+ :filterOnTripleStore rdf:type owl:ObjectProperty ;
21
+ rdfs:domain :MustrdTest ;
22
+ rdfs:range triplestore:TripleStore .
23
+
24
+
25
+ #################################################################
26
+ # Data properties
27
+ #################################################################
28
+
29
+ ### https://mustrd.com/mustrdTest/hasDataPath
30
+ :hasDataPath rdf:type owl:DatatypeProperty ;
31
+ rdfs:domain :MustrdTest ;
32
+ rdfs:range xsd:string .
33
+
34
+
35
+ ### https://mustrd.com/mustrdTest/hasSpecPath
36
+ :hasSpecPath rdf:type owl:DatatypeProperty ;
37
+ rdfs:domain :MustrdTest ;
38
+ rdfs:range xsd:string .
39
+
40
+
41
+ #################################################################
42
+ # Classes
43
+ #################################################################
44
+
45
+ ### https://mustrd.com/mustrdTest/MustrdTest
46
+ :MustrdTest rdf:type owl:Class ;
47
+ rdfs:comment "Contains user input to parameter a mustrd pytest" ;
48
+ rdfs:label "Mustrd test" .
49
+
50
+
51
+ ### Generated by the OWL API (version 4.5.26.2023-07-17T20:34:13Z) https://github.com/owlcs/owlapi
@@ -0,0 +1,24 @@
1
+ @prefix : <https://mustrd.com/mustrdTest/> .
2
+ @prefix sh: <http://www.w3.org/ns/shacl#> .
3
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
4
+ @prefix triplestore: <https://mustrd.com/triplestore/> .
5
+ @base <https://mustrd.com/mustrdTest/> .
6
+
7
+
8
+
9
+
10
+ :MustrdTestShape
11
+ a sh:NodeShape ;
12
+ sh:targetClass :MustrdTest ;
13
+ sh:property [ sh:path :hasDataPath ;
14
+ sh:datatype xsd:string ;
15
+ sh:minCount 1 ;
16
+ sh:maxCount 1 ],
17
+ [ sh:path :hasSpecPath ;
18
+ sh:datatype xsd:string ;
19
+ sh:minCount 1 ;
20
+ sh:maxCount 1 ],
21
+ [ sh:path :hasPytestPath ;
22
+ sh:datatype xsd:string ;
23
+ sh:maxCount 1 ] .
24
+
@@ -11,7 +11,8 @@
11
11
  @base <https://mustrd.com/model/> .
12
12
 
13
13
  <https://mustrd.com/model> rdf:type owl:Ontology ;
14
- owl:imports rdf: ,
14
+ owl:imports <https://mustrd.com/triplestore/> ,
15
+ rdf: ,
15
16
  rdfs: ;
16
17
  rdfs:comment "" ;
17
18
  rdfs:label "Mustrd" .
@@ -0,0 +1,174 @@
1
+ @base <https://mustrd.com/triplestore/> .
2
+ @prefix : <https://mustrd.com/triplestore/> .
3
+ @prefix dc: <http://purl.org/dc/elements/1.1/> .
4
+ @prefix sh: <http://www.w3.org/ns/shacl#> .
5
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
6
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
7
+ @prefix xml: <http://www.w3.org/XML/1998/namespace> .
8
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
9
+ @prefix foaf: <http://xmlns.com/foaf/spec/> .
10
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
11
+ @prefix skos: <http://www.w3.org/2004/02/skos/core#> .
12
+ #
13
+ #
14
+ # #################################################################
15
+ # #
16
+ # # Data properties
17
+ # #
18
+ # #################################################################
19
+ #
20
+ #
21
+ # https://mustrd.com/triplestore/gqeURI
22
+ #
23
+ # https://mustrd.com/triplestore/inputGraph
24
+ #
25
+ # https://mustrd.com/triplestore/outputGraph
26
+ #
27
+ # https://mustrd.com/triplestore/password
28
+ #
29
+ # https://mustrd.com/triplestore/port
30
+ #
31
+ # https://mustrd.com/triplestore/repository
32
+ #
33
+ # https://mustrd.com/triplestore/url
34
+ #
35
+ # https://mustrd.com/triplestore/username
36
+ #
37
+ #
38
+ #
39
+ # #################################################################
40
+ # #
41
+ # # Classes
42
+ # #
43
+ # #################################################################
44
+ #
45
+ #
46
+ # https://mustrd.com/triplestore/Anzo
47
+ #
48
+ # https://mustrd.com/triplestore/ExternalTripleStore
49
+ #
50
+ # https://mustrd.com/triplestore/GraphDb
51
+ #
52
+ # https://mustrd.com/triplestore/InternalTripleStore
53
+ #
54
+ # https://mustrd.com/triplestore/RdfLib
55
+ #
56
+ # https://mustrd.com/triplestore/TripleStore
57
+ #
58
+ # Generated by the OWL API (version 4.5.26.2023-07-17T20:34:13Z) https://github.com/owlcs/owlapi
59
+
60
+ <> a owl:Ontology;
61
+ rdfs:label "triple store ontology" .
62
+
63
+ :gqeURI a owl:DatatypeProperty;
64
+ rdfs:domain :Anzo;
65
+ rdfs:range xsd:string;
66
+ rdfs:label "gqeURI" .
67
+
68
+ :inputGraph a owl:DatatypeProperty;
69
+ rdfs:domain :TripleStore;
70
+ rdfs:range xsd:string;
71
+ rdfs:label "inputGraph" .
72
+
73
+ :outputGraph a owl:DatatypeProperty;
74
+ rdfs:domain :TripleStore;
75
+ rdfs:range xsd:string;
76
+ rdfs:comment """Uri of the graph where to insert/delete the triples
77
+
78
+ In some triple stores there is a default insert graph like in graphDB:
79
+ In graphDB the default insert graph is called: <http://www.openrdf.org/schema/sesame#nil>
80
+ or
81
+ <http://rdf4j.org/schema/rdf4j#nil>
82
+
83
+ Other triple stores do not define default insert graph like anzograph.
84
+ For those triple stores, this property must be mandatory""";
85
+ rdfs:label "outputGraph" .
86
+
87
+ :password a owl:DatatypeProperty;
88
+ rdfs:domain :ExternalTripleStore;
89
+ rdfs:range xsd:string;
90
+ rdfs:label "password" .
91
+
92
+ :port a owl:DatatypeProperty;
93
+ rdfs:domain :ExternalTripleStore;
94
+ rdfs:range xsd:string;
95
+ rdfs:comment "Triple store port";
96
+ rdfs:label "port" .
97
+
98
+ :repository a owl:DatatypeProperty;
99
+ rdfs:domain :GraphDb;
100
+ rdfs:range xsd:string;
101
+ rdfs:label "repository" .
102
+
103
+ :url a owl:DatatypeProperty;
104
+ rdfs:domain :ExternalTripleStore;
105
+ rdfs:range xsd:string;
106
+ rdfs:comment "triple store URL";
107
+ rdfs:label "url" .
108
+
109
+ :username a owl:DatatypeProperty;
110
+ rdfs:domain :ExternalTripleStore;
111
+ rdfs:range xsd:string;
112
+ rdfs:label "username" .
113
+
114
+ :Anzo a owl:Class;
115
+ rdfs:subClassOf :ExternalTripleStore;
116
+ rdfs:comment """TODO: model this:
117
+
118
+ definition of RDF dataset:
119
+
120
+ default graph=
121
+ RDF merge of default-graph-uri if exists
122
+ else RDF merge of FROM if exists
123
+ else all graphs in anzo graph (if you query a graphmart, default-graph-uri will be automatically set to the layers of the graphmart), if you are not sysadmin and you do not define the default graph, then the query will fail since you don't have permissions on all graphs
124
+ if default-graph-uri is defined then FROM clauses are ignored
125
+
126
+ default named graph =
127
+ RDF merge of named-graph-uri if exists
128
+ else RDF merge of FROM NAMED if exists
129
+ else default graph if exists
130
+ all graphs in anzo graph (if you query a graphmart, default-graph-uri will be automatically set to the layers of the graphmart), if you are not sysadmin and you do not define the default graph, then the query will fail since you don't have permissions on all graphs
131
+ if named-graph-uri is defined then FROM NAMED clauses are ignored
132
+
133
+ There is no default insert graph. If you try to insert/delete without graph clause, your query will fail.""";
134
+ rdfs:label "Anzo" .
135
+
136
+ :ExternalTripleStore a owl:Class;
137
+ rdfs:subClassOf :TripleStore .
138
+
139
+ :GraphDb a owl:Class;
140
+ rdfs:subClassOf :ExternalTripleStore;
141
+ rdfs:comment """TODO: model this:
142
+
143
+ Definition of RDF dataset in graphDB:
144
+
145
+ default graph (in the sense of W3C, called default dataset in graphDB) =
146
+ Virtual graph that exist only for a query and represent all triples accessible outside of a graph clause in a sparql query =
147
+ RDF merge of default-graph-uri if exists
148
+ else RDF merge of FROM if exists
149
+ else all graphs in the repository, including default insert graph
150
+ you can query default insert graph by including it (<http://www.openrdf.org/schema/sesame#nil> or <http://rdf4j.org/schema/rdf4j#nil>) in a default-graph-uri parameter or a FROM clause
151
+ If default-graph-uri is defined, then FROM clause is ignored
152
+
153
+ default named graph =
154
+ All triples accessible inside a graph clause:
155
+ RDF merge of named-graph-uri if exists
156
+ else RDF merge of FROM NAMED if exist
157
+ else all graphs in the repository, EXCLUDING default insert graph
158
+ (To be exact you can query the default insert graph only if you name it GRAPH <http://www.openrdf.org/schema/sesame#nil> or GRAPH <http://rdf4j.org/schema/rdf4j#nil>, but you won't query it with GRAPH ?graph)
159
+ default insert graph can be added to named-graph-uri or FROM NAMED
160
+ If a default-graph-uri is defined but no named-graph-uri, then default named graph = void
161
+
162
+ default insert graph (called \"The default graph\" in graphDB) =
163
+ Graph where triples are inserted / deleted when no graph clause is given in an INSERT or DELETE clause""";
164
+ rdfs:label "GraphDb" .
165
+
166
+ :InternalTripleStore a owl:Class;
167
+ rdfs:subClassOf :TripleStore .
168
+
169
+ :RdfLib a owl:Class;
170
+ rdfs:subClassOf :InternalTripleStore;
171
+ rdfs:label "RdfLib" .
172
+
173
+ :TripleStore a owl:Class;
174
+ rdfs:label "TripleStore" .
@@ -0,0 +1,42 @@
1
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
2
+ @prefix sh: <http://www.w3.org/ns/shacl#> .
3
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
4
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
5
+ @prefix ex: <http://www.example.org/#> .
6
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
7
+ @prefix triplestore: <https://mustrd.com/triplestore/> .
8
+
9
+ triplestore:ExternalTripleStoreShape
10
+ a sh:NodeShape ;
11
+ sh:targetClass triplestore:ExternalTripleStore ;
12
+ sh:property [ sh:path triplestore:url ;
13
+ sh:minCount 1 ;
14
+ sh:maxCount 1 ],
15
+ [ sh:path triplestore:port ;
16
+ sh:minCount 1 ;
17
+ sh:maxCount 1 ],
18
+ [ sh:path triplestore:username ;
19
+ sh:maxCount 1 ],
20
+ [ sh:path triplestore:password ;
21
+ sh:maxCount 1 ] .
22
+
23
+ triplestore:AnzoShape
24
+ a sh:NodeShape ;
25
+ sh:targetClass triplestore:Anzo ;
26
+ sh:property [ sh:path triplestore:gqeURI ;
27
+ sh:minCount 1 ;
28
+ sh:maxCount 1 ],
29
+ [ sh:path triplestore:outputGraph ;
30
+ sh:minCount 1 ;
31
+ sh:maxCount 1 ],
32
+ # For anzo the input graph is not really necessary if the user is sysadmin
33
+ # but querying all graphs in AZG is usually not a good idea, so for the moment this is forbidden
34
+ [ sh:path triplestore:inputGraph ;
35
+ sh:minCount 1 ] .
36
+
37
+ triplestore:GraphDbShape
38
+ a sh:NodeShape ;
39
+ sh:targetClass triplestore:GraphDb ;
40
+ sh:property [ sh:path triplestore:repository ;
41
+ sh:minCount 1 ;
42
+ sh:maxCount 1 ] .
@@ -40,13 +40,13 @@ from rdflib import Graph, URIRef, RDF, XSD, SH, Literal
40
40
  from rdflib.compare import isomorphic, graph_diff
41
41
  import pandas
42
42
 
43
- from .namespace import MUST
43
+ from .namespace import MUST, TRIPLESTORE
44
44
  import requests
45
45
  import json
46
46
  from pandas import DataFrame
47
47
 
48
48
  from .spec_component import TableThenSpec, parse_spec_component, WhenSpec, ThenSpec
49
- from .utils import is_json
49
+ from .utils import is_json,get_mustrd_root
50
50
  from colorama import Fore, Style
51
51
  from tabulate import tabulate
52
52
  from collections import defaultdict
@@ -93,6 +93,7 @@ class Specification:
93
93
  given: Graph
94
94
  when: WhenSpec
95
95
  then: ThenSpec
96
+ spec_file_name: str = "default.mustrd.ttl"
96
97
 
97
98
 
98
99
  @dataclass
@@ -152,6 +153,7 @@ class TripleStoreConnectionError(SpecResult):
152
153
  @dataclass
153
154
  class SpecSkipped(SpecResult):
154
155
  message: str
156
+ spec_file_name: str = "default.mustrd.ttl"
155
157
 
156
158
 
157
159
  @dataclass
@@ -176,16 +178,15 @@ class UpdateSparqlQuery(SparqlAction):
176
178
 
177
179
  # https://github.com/Semantic-partners/mustrd/issues/19
178
180
 
179
- def validate_specs(run_config: dict, triple_stores: List, shacl_graph: Graph, ont_graph: Graph)\
181
+ def validate_specs(run_config: dict, triple_stores: List, shacl_graph: Graph, ont_graph: Graph, file_name: str = "*")\
180
182
  -> Tuple[List, Graph, List]:
181
183
  spec_graph = Graph()
182
184
  subject_uris = set()
183
185
  focus_uris = set()
184
186
  invalid_specs = []
185
- # ttl_files = list(spec_path.glob('**/*.mustrd.ttl'))
186
- ttl_files = list(run_config['spec_path'].glob('**/*.mustrd.ttl'))
187
+ ttl_files = list(run_config['spec_path'].glob(f'**/{file_name}.mustrd.ttl'))
187
188
  ttl_files.sort()
188
- log.info(f"Found {len(ttl_files)} mustrd.ttl files in {run_config['spec_path']}")
189
+ log.info(f"Found {len(ttl_files)} {file_name}.mustrd.ttl files in {run_config['spec_path']}")
189
190
 
190
191
  for file in ttl_files:
191
192
  error_messages = []
@@ -233,7 +234,7 @@ def validate_specs(run_config: dict, triple_stores: List, shacl_graph: Graph, on
233
234
  if len(error_messages) > 0:
234
235
  error_messages.sort()
235
236
  error_message = "\n".join(msg for msg in error_messages)
236
- invalid_specs += [SpecSkipped(subject_uri, triple_store["type"], error_message) for triple_store in
237
+ invalid_specs += [SpecSkipped(subject_uri, triple_store["type"], error_message, file.name) for triple_store in
237
238
  triple_stores]
238
239
  else:
239
240
  subject_uris.add(subject_uri)
@@ -241,13 +242,12 @@ def validate_specs(run_config: dict, triple_stores: List, shacl_graph: Graph, on
241
242
  this_spec_graph.parse(file)
242
243
  spec_uris_in_this_file = list(this_spec_graph.subjects(RDF.type, MUST.TestSpec))
243
244
  for spec in spec_uris_in_this_file:
244
- tripleToAdd = [spec, MUST.specSourceFile, Literal(file)]
245
245
  # print(f"adding {tripleToAdd}")
246
- this_spec_graph.add(tripleToAdd)
246
+ this_spec_graph.add([spec, MUST.specSourceFile, Literal(file)])
247
+ this_spec_graph.add([spec, MUST.specFileName, Literal(file.name)])
247
248
  # print(f"beforeadd: {spec_graph}" )
248
249
  # print(f"beforeadd: {str(this_spec_graph.serialize())}" )
249
250
  spec_graph += this_spec_graph
250
- # print(f"afteradd: {str(spec_graph.serialize())}" )
251
251
 
252
252
 
253
253
  sourceFiles = list(spec_graph.subject_objects(MUST.specSourceFile))
@@ -276,14 +276,14 @@ def get_specs(spec_uris: List[URIRef], spec_graph: Graph, triple_stores: List[di
276
276
  for triple_store in triple_stores:
277
277
  if "error" in triple_store:
278
278
  log.error(f"{triple_store['error']}. No specs run for this triple store.")
279
- skipped_results += [SpecSkipped(spec_uri, triple_store['type'], triple_store['error']) for spec_uri in
279
+ skipped_results += [SpecSkipped(spec_uri, triple_store['type'], triple_store['error'], get_spec_file(spec_uri, spec_graph)) for spec_uri in
280
280
  spec_uris]
281
281
  else:
282
282
  for spec_uri in spec_uris:
283
283
  try:
284
284
  specs += [get_spec(spec_uri, spec_graph, run_config, triple_store)]
285
285
  except (ValueError, FileNotFoundError, ConnectionError) as e:
286
- skipped_results += [SpecSkipped(spec_uri, triple_store['type'], e)]
286
+ skipped_results += [SpecSkipped(spec_uri, triple_store['type'], e, get_spec_file(spec_uri, spec_graph))]
287
287
 
288
288
  except (BadSyntax, FileNotFoundError) as e:
289
289
  template = "An exception of type {0} occurred when trying to parse the triple store configuration file. " \
@@ -303,11 +303,14 @@ def run_specs(specs) -> List[SpecResult]:
303
303
  results.append(run_spec(specification))
304
304
  return results
305
305
 
306
+ def get_spec_file(spec_uri: URIRef, spec_graph: Graph):
307
+ test = str(spec_graph.value(subject = spec_uri, predicate = MUST.specFileName, default = "default.mustrd.ttl"))
308
+ return test
306
309
 
307
310
  def get_spec(spec_uri: URIRef, spec_graph: Graph, run_config: dict, mustrd_triple_store: dict = None) -> Specification:
308
311
  try:
309
312
  if mustrd_triple_store is None:
310
- mustrd_triple_store = {"type": MUST.RdfLib}
313
+ mustrd_triple_store = {"type": TRIPLESTORE.RdfLib}
311
314
  components = []
312
315
  for predicate in MUST.given, MUST.when, MUST.then:
313
316
  components.append(parse_spec_component(subject=spec_uri,
@@ -316,9 +319,9 @@ def get_spec(spec_uri: URIRef, spec_graph: Graph, run_config: dict, mustrd_tripl
316
319
  run_config=run_config,
317
320
  mustrd_triple_store=mustrd_triple_store))
318
321
 
319
-
322
+ spec_file_name = get_spec_file(spec_uri, spec_graph)
320
323
  # https://github.com/Semantic-partners/mustrd/issues/92
321
- return Specification(spec_uri, mustrd_triple_store, components[0].value, components[1], components[2])
324
+ return Specification(spec_uri, mustrd_triple_store, components[0].value, components[1], components[2], spec_file_name)
322
325
 
323
326
  except (ValueError, FileNotFoundError) as e:
324
327
  template = "An exception of type {0} occurred. Arguments:\n{1!r}"
@@ -354,7 +357,7 @@ def run_spec(spec: Specification) -> SpecResult:
354
357
  log.debug(f"{given_as_turtle}")
355
358
  upload_given(triple_store, spec.given)
356
359
  else:
357
- if triple_store['type'] == MUST.RdfLib:
360
+ if triple_store['type'] == TRIPLESTORE.RdfLib:
358
361
  return SpecSkipped(spec_uri, triple_store['type'], "Unable to run Inherited State tests on Rdflib")
359
362
  try:
360
363
  for when in spec.when:
@@ -386,57 +389,64 @@ def get_triple_store_graph(triple_store_graph_path: Path, secrets: str):
386
389
  return Graph().parse(triple_store_graph_path).parse(data = secrets)
387
390
  else:
388
391
  secret_path = triple_store_graph_path.parent / Path(triple_store_graph_path.stem + "_secrets" + triple_store_graph_path.suffix)
389
- return Graph().parse(triple_store_graph_path).parse(source = secret_path)
392
+ return Graph().parse(triple_store_graph_path).parse(secret_path)
390
393
 
391
394
 
392
395
  def get_triple_stores(triple_store_graph: Graph) -> list[dict]:
393
396
  triple_stores = []
397
+ shacl_graph = Graph().parse(Path(os.path.join(get_mustrd_root(), "model/triplestoreshapes.ttl")))
398
+ ont_graph = Graph().parse(Path(os.path.join(get_mustrd_root(), "model/triplestoreOntology.ttl")))
399
+ conforms, results_graph, results_text = validate(
400
+ data_graph= triple_store_graph,
401
+ shacl_graph = shacl_graph,
402
+ ont_graph = ont_graph,
403
+ advanced= True,
404
+ inference= 'none'
405
+ )
406
+ if not conforms:
407
+ raise ValueError(f"Triple store configuration not conform to the shapes. SHACL report: {results_text}", results_graph)
394
408
  for triple_store_config, rdf_type, triple_store_type in triple_store_graph.triples((None, RDF.type, None)):
395
409
  triple_store = {}
396
- # Local rdf lib triple store
397
- if triple_store_type == MUST.RdfLibConfig:
398
- triple_store["type"] = MUST.RdfLib
410
+ triple_store["type"] = triple_store_type
411
+ triple_store["uri"] = triple_store_config
399
412
  # Anzo graph via anzo
400
- elif triple_store_type == MUST.AnzoConfig:
401
- triple_store["type"] = MUST.Anzo
402
- triple_store["url"] = triple_store_graph.value(subject=triple_store_config, predicate=MUST.url)
403
- triple_store["port"] = triple_store_graph.value(subject=triple_store_config, predicate=MUST.port)
413
+ if triple_store_type == TRIPLESTORE.Anzo:
414
+ triple_store["url"] = triple_store_graph.value(subject=triple_store_config, predicate=TRIPLESTORE.url)
415
+ triple_store["port"] = triple_store_graph.value(subject=triple_store_config, predicate=TRIPLESTORE.port)
404
416
  try:
405
- triple_store["username"] = str(triple_store_graph.value(subject=triple_store_config, predicate=MUST.username))
406
- triple_store["password"] = str(triple_store_graph.value(subject=triple_store_config, predicate=MUST.password))
417
+ triple_store["username"] = str(triple_store_graph.value(subject=triple_store_config, predicate=TRIPLESTORE.username))
418
+ triple_store["password"] = str(triple_store_graph.value(subject=triple_store_config, predicate=TRIPLESTORE.password))
407
419
  except (FileNotFoundError, ValueError) as e:
408
420
  triple_store["error"] = e
409
- triple_store["gqe_uri"] = triple_store_graph.value(subject=triple_store_config, predicate=MUST.gqeURI)
421
+ triple_store["gqe_uri"] = triple_store_graph.value(subject=triple_store_config, predicate=TRIPLESTORE.gqeURI)
410
422
  triple_store["input_graph"] = triple_store_graph.value(subject=triple_store_config,
411
- predicate=MUST.inputGraph)
423
+ predicate=TRIPLESTORE.inputGraph)
412
424
  triple_store["output_graph"] = triple_store_graph.value(subject=triple_store_config,
413
- predicate=MUST.outputGraph)
425
+ predicate=TRIPLESTORE.outputGraph)
414
426
  try:
415
427
  check_triple_store_params(triple_store, ["url", "port", "username", "password", "input_graph"])
416
428
  except ValueError as e:
417
429
  triple_store["error"] = e
418
430
  # GraphDB
419
- elif triple_store_type == MUST.GraphDbConfig:
420
- triple_store["type"] = MUST.GraphDb
421
- triple_store["url"] = triple_store_graph.value(subject=triple_store_config, predicate=MUST.url)
422
- triple_store["port"] = triple_store_graph.value(subject=triple_store_config, predicate=MUST.port)
431
+ elif triple_store_type == TRIPLESTORE.GraphDb:
432
+ triple_store["url"] = triple_store_graph.value(subject=triple_store_config, predicate=TRIPLESTORE.url)
433
+ triple_store["port"] = triple_store_graph.value(subject=triple_store_config, predicate=TRIPLESTORE.port)
423
434
  try:
424
- triple_store["username"] = str(triple_store_graph.value(subject=triple_store_config, predicate=MUST.username))
425
- triple_store["password"] = str(triple_store_graph.value(subject=triple_store_config, predicate=MUST.password))
435
+ triple_store["username"] = str(triple_store_graph.value(subject=triple_store_config, predicate=TRIPLESTORE.username))
436
+ triple_store["password"] = str(triple_store_graph.value(subject=triple_store_config, predicate=TRIPLESTORE.password))
426
437
  except (FileNotFoundError, ValueError) as e:
427
438
  log.error(f"Credential retrieval failed {e}")
428
439
  triple_store["error"] = e
429
440
  triple_store["repository"] = triple_store_graph.value(subject=triple_store_config,
430
- predicate=MUST.repository)
441
+ predicate=TRIPLESTORE.repository)
431
442
  triple_store["input_graph"] = triple_store_graph.value(subject=triple_store_config,
432
- predicate=MUST.inputGraph)
443
+ predicate=TRIPLESTORE.inputGraph)
433
444
 
434
445
  try:
435
446
  check_triple_store_params(triple_store, ["url", "port", "repository"])
436
447
  except ValueError as e:
437
448
  triple_store["error"] = e
438
- else:
439
- triple_store["type"] = triple_store_type
449
+ elif triple_store_type != TRIPLESTORE.RdfLib:
440
450
  triple_store["error"] = f"Triple store not implemented: {triple_store_type}"
441
451
 
442
452
  triple_stores.append(triple_store)
@@ -29,22 +29,30 @@ from pathlib import Path
29
29
  from rdflib.namespace import Namespace
30
30
  from rdflib import Graph, RDF
31
31
  from pytest import Session
32
- from typing import Dict
33
32
 
34
33
  from mustrd.TestResult import ResultList, TestResult, get_result_list
35
34
  from mustrd.utils import get_mustrd_root
36
35
  from mustrd.mustrd import get_triple_store_graph, get_triple_stores
37
- from mustrd.mustrd import SpecSkipped, validate_specs, get_specs, SpecPassed, run_spec
38
- from mustrd.namespace import MUST
39
- from collections import defaultdict
36
+ from mustrd.mustrd import Specification, SpecSkipped, validate_specs, get_specs, SpecPassed, run_spec
37
+ from mustrd.namespace import MUST, TRIPLESTORE, MUSTRDTEST
38
+ from typing import Union
39
+ from pyshacl import validate
40
40
 
41
41
  spnamespace = Namespace("https://semanticpartners.com/data/test/")
42
42
 
43
43
  mustrd_root = get_mustrd_root()
44
44
 
45
+ MUSTRD_PYTEST_PATH = "mustrd_tests/"
46
+
45
47
 
46
48
  def pytest_addoption(parser):
47
- group = parser.getgroup("md summary")
49
+ group = parser.getgroup("mustrd option")
50
+ group.addoption(
51
+ "--mustrd",
52
+ action="store_true",
53
+ dest="mustrd",
54
+ help="Activate/deactivate mustrd test generation.",
55
+ )
48
56
  group.addoption(
49
57
  "--md",
50
58
  action="store",
@@ -59,7 +67,6 @@ def pytest_addoption(parser):
59
67
  dest="configpath",
60
68
  metavar="pathToTestConfig",
61
69
  default=None,
62
- required=True,
63
70
  help="Ttl file containing the list of test to construct.",
64
71
  )
65
72
  group.addoption(
@@ -68,7 +75,6 @@ def pytest_addoption(parser):
68
75
  dest="secrets",
69
76
  metavar="Secrets",
70
77
  default=None,
71
- required=False,
72
78
  help="Give the secrets by command line in order to be able to store secrets safely in CI tools",
73
79
  )
74
80
  return
@@ -76,23 +82,40 @@ def pytest_addoption(parser):
76
82
 
77
83
  def pytest_configure(config) -> None:
78
84
  # Read configuration file
79
- test_configs: Dict[str, TestConfig] = defaultdict(lambda: defaultdict(list))
80
- config_graph = Graph().parse(config.getoption("configpath"))
81
- for test_config_subject in config_graph.subjects(predicate=RDF.type, object=MUST.TestConfig):
82
- test_function = get_config_param(config_graph, test_config_subject, MUST.hasTestFunction, str)
83
- spec_path = get_config_param(config_graph, test_config_subject, MUST.hasSpecPath, str)
84
- data_path = get_config_param(config_graph, test_config_subject, MUST.hasDataPath, str)
85
- triplestore_spec_path = get_config_param(config_graph, test_config_subject, MUST.triplestoreSpecPath, str)
85
+ if config.getoption("mustrd"):
86
+ test_configs = parse_config(config.getoption("configpath"))
87
+ config.pluginmanager.register(MustrdTestPlugin(config.getoption("mdpath"),
88
+ test_configs, config.getoption("secrets")))
89
+
90
+ def parse_config(config_path):
91
+ test_configs = []
92
+ config_graph = Graph().parse(config_path)
93
+ shacl_graph = Graph().parse(Path(os.path.join(mustrd_root, "model/mustrdTestShapes.ttl")))
94
+ ont_graph = Graph().parse(Path(os.path.join(mustrd_root, "model/mustrdTestOntology.ttl")))
95
+ conforms, results_graph, results_text = validate(
96
+ data_graph= config_graph,
97
+ shacl_graph = shacl_graph,
98
+ ont_graph = ont_graph,
99
+ advanced= True,
100
+ inference= 'none'
101
+ )
102
+ if not conforms:
103
+ raise ValueError(f"Mustrd test configuration not conform to the shapes. SHACL report: {results_text}", results_graph)
104
+
105
+ for test_config_subject in config_graph.subjects(predicate=RDF.type, object=MUSTRDTEST.MustrdTest):
106
+ spec_path = get_config_param(config_graph, test_config_subject, MUSTRDTEST.hasSpecPath, str)
107
+ data_path = get_config_param(config_graph, test_config_subject, MUSTRDTEST.hasDataPath, str)
108
+ triplestore_spec_path = get_config_param(config_graph, test_config_subject, MUSTRDTEST.triplestoreSpecPath, str)
109
+ pytest_path = get_config_param(config_graph, test_config_subject, MUSTRDTEST.hasPytestPath, str)
86
110
  filter_on_tripleStore = list(config_graph.objects(subject=test_config_subject,
87
- predicate=MUST.filterOnTripleStore))
111
+ predicate=MUSTRDTEST.filterOnTripleStore))
88
112
 
89
- test_configs[test_function] = TestConfig(test_function=test_function,
90
- spec_path=spec_path, data_path=data_path,
91
- triplestore_spec_path=triplestore_spec_path,
92
- filter_on_tripleStore=filter_on_tripleStore)
93
-
94
- config.pluginmanager.register(MustrdTestPlugin(config.getoption("mdpath"),
95
- test_configs, config.getoption("secrets")))
113
+ test_configs.append(TestConfig(spec_path=spec_path, data_path=data_path,
114
+ triplestore_spec_path=triplestore_spec_path,
115
+ pytest_path = pytest_path,
116
+ filter_on_tripleStore=filter_on_tripleStore))
117
+ return test_configs
118
+
96
119
 
97
120
 
98
121
  def get_config_param(config_graph, config_subject, config_param, convert_function):
@@ -102,65 +125,109 @@ def get_config_param(config_graph, config_subject, config_param, convert_functio
102
125
 
103
126
  @dataclass
104
127
  class TestConfig:
105
- test_function: str
106
128
  spec_path: str
107
129
  data_path: str
108
130
  triplestore_spec_path: str
109
- filter_on_tripleStore: str
110
-
111
- def __init__(self, test_function: str, spec_path: str, data_path: str, triplestore_spec_path: str,
112
- filter_on_tripleStore: str = None):
113
- self.test_function = test_function
114
- self.spec_path = spec_path
115
- self.data_path = data_path
116
- self.triplestore_spec_path = triplestore_spec_path
117
- self.filter_on_tripleStore = filter_on_tripleStore
131
+ pytest_path: str
132
+ filter_on_tripleStore: str = None
133
+
118
134
 
135
+ @dataclass
136
+ class TestParamWrapper:
137
+ test_config: TestConfig
138
+ unit_test: Union[Specification, SpecSkipped]
119
139
 
120
140
  class MustrdTestPlugin:
121
141
  md_path: str
122
- test_configs: Dict[str, TestConfig]
142
+ test_configs: list
123
143
  secrets: str
144
+ unit_tests: Union[Specification, SpecSkipped]
145
+ items: list
124
146
 
125
147
  def __init__(self, md_path, test_configs, secrets):
126
148
  self.md_path = md_path
127
149
  self.test_configs = test_configs
128
150
  self.secrets = secrets
151
+ self.items = []
152
+
153
+ @pytest.hookimpl(tryfirst=True)
154
+ def pytest_collection(self, session):
155
+ self.unit_tests = []
156
+ args = session.config.args
157
+ if len(args) > 0:
158
+ file_name = self.get_file_name_from_arg(args[0])
159
+ # Filter test to collect only specified path
160
+ config_to_collect = list(filter(lambda config:
161
+ # Case we want to collect everything
162
+ MUSTRD_PYTEST_PATH not in args[0]
163
+ # Case we want to collect a test or sub test
164
+ or (config.pytest_path or "") in args[0]
165
+ # Case we want to collect a whole test folder
166
+ or args[0].replace(f"./{MUSTRD_PYTEST_PATH}", "") in config.pytest_path,
167
+ self.test_configs))
168
+
169
+ # Redirect everything to test_mustrd.py, no need to filter on specified test: Only specified test will be collected anyway
170
+ session.config.args[0] = os.path.join(mustrd_root, "test/test_mustrd.py")
171
+ # Collecting only relevant tests
172
+
173
+ for one_test_config in config_to_collect:
174
+ triple_stores = self.get_triple_stores_from_file(one_test_config)
175
+
176
+ if one_test_config.filter_on_tripleStore and not triple_stores:
177
+ self.unit_tests.extend(list(map(lambda triple_store:
178
+ TestParamWrapper(test_config = one_test_config, unit_test=SpecSkipped(MUST.TestSpec, triple_store, "No triplestore found")),
179
+ one_test_config.filter_on_tripleStore)))
180
+ else:
181
+ specs = self.generate_tests_for_config({"spec_path": Path(one_test_config.spec_path),
182
+ "data_path": Path(one_test_config.data_path)},
183
+ triple_stores, file_name)
184
+ self.unit_tests.extend(list(map(lambda spec: TestParamWrapper(test_config = one_test_config, unit_test=spec),specs)))
185
+
186
+ def get_file_name_from_arg(self, arg):
187
+ if arg and len(arg) > 0 and "[" in arg and ".mustrd.ttl@" in arg:
188
+ return arg[arg.index("[") + 1: arg.index(".mustrd.ttl@")]
189
+ return None
190
+
191
+
192
+ @pytest.hookimpl(hookwrapper=True)
193
+ def pytest_pycollect_makeitem(self, collector, name, obj):
194
+ report = yield
195
+ if name == "test_unit":
196
+ items = report.get_result()
197
+ new_results = []
198
+ for item in items:
199
+ virtual_path = MUSTRD_PYTEST_PATH + (item.callspec.params["unit_tests"].test_config.pytest_path or "default")
200
+ item.fspath = Path(virtual_path)
201
+ item._nodeid = virtual_path + "::" + item.name
202
+ self.items.append(item)
203
+ new_results.append(item)
204
+ return new_results
205
+
129
206
 
130
207
  # Hook called at collection time: reads the configuration of the tests, and generate pytests from it
131
208
  def pytest_generate_tests(self, metafunc):
132
-
133
209
  if len(metafunc.fixturenames) > 0:
134
- if metafunc.function.__name__ in self.test_configs:
135
- one_test_config = self.test_configs[metafunc.function.__name__]
136
-
137
- triple_stores = self.get_triple_stores_from_file(one_test_config)
138
-
139
- unit_tests = []
140
- if one_test_config.filter_on_tripleStore and not triple_stores:
141
- unit_tests = list(map(lambda triple_store:
142
- SpecSkipped(MUST.TestSpec, triple_store, "No triplestore found"),
143
- one_test_config.filter_on_tripleStore))
144
- else:
145
- unit_tests = self.generate_tests_for_config({"spec_path": Path(one_test_config.spec_path),
146
- "data_path": Path(one_test_config.data_path)},
147
- triple_stores)
148
-
210
+ if metafunc.function.__name__ == "test_unit":
149
211
  # Create the test in itself
150
- if unit_tests:
151
- metafunc.parametrize(metafunc.fixturenames[0], unit_tests, ids=self.get_test_name)
212
+ if self.unit_tests:
213
+ metafunc.parametrize(metafunc.fixturenames[0], self.unit_tests,
214
+ ids=lambda test_param: (test_param.unit_test.spec_file_name or "") + "@" +
215
+ (test_param.test_config.pytest_path or ""))
152
216
  else:
153
217
  metafunc.parametrize(metafunc.fixturenames[0],
154
218
  [SpecSkipped(MUST.TestSpec, None, "No triplestore found")],
155
219
  ids=lambda x: "No configuration found for this test")
220
+
221
+
222
+
156
223
 
157
224
  # Generate test for each triple store available
158
- def generate_tests_for_config(self, config, triple_stores):
225
+ def generate_tests_for_config(self, config, triple_stores, file_name):
159
226
 
160
227
  shacl_graph = Graph().parse(Path(os.path.join(mustrd_root, "model/mustrdShapes.ttl")))
161
228
  ont_graph = Graph().parse(Path(os.path.join(mustrd_root, "model/ontology.ttl")))
162
229
  valid_spec_uris, spec_graph, invalid_spec_results = validate_specs(config, triple_stores,
163
- shacl_graph, ont_graph)
230
+ shacl_graph, ont_graph, file_name or "*")
164
231
 
165
232
  specs, skipped_spec_results = \
166
233
  get_specs(valid_spec_uris, spec_graph, triple_stores, config)
@@ -186,15 +253,15 @@ class MustrdTestPlugin:
186
253
  triple_stores = get_triple_stores(get_triple_store_graph(Path(test_config.triplestore_spec_path),
187
254
  self.secrets))
188
255
  except Exception as e:
189
- print(f"""No triple store configuration found at {test_config.triplestore_spec_path}.
190
- Fall back: only embedded rdflib will be executed""", e)
191
- triple_stores = [{'type': MUST.RdfLib}]
256
+ print(f"""Triplestore configuration parsing failed {test_config.triplestore_spec_path}.
257
+ Only rdflib will be executed""", e)
258
+ triple_stores = [{'type': TRIPLESTORE.RdfLib, 'uri': TRIPLESTORE.RdfLib}]
192
259
  else:
193
260
  print("No triple store configuration required: using embedded rdflib")
194
- triple_stores = [{'type': MUST.RdfLib}]
261
+ triple_stores = [{'type': TRIPLESTORE.RdfLib, 'uri': TRIPLESTORE.RdfLib}]
195
262
 
196
263
  if test_config.filter_on_tripleStore:
197
- triple_stores = list(filter(lambda triple_store: (triple_store["type"] in test_config.filter_on_tripleStore),
264
+ triple_stores = list(filter(lambda triple_store: (triple_store["uri"] in test_config.filter_on_tripleStore),
198
265
  triple_stores))
199
266
  return triple_stores
200
267
 
@@ -25,18 +25,10 @@ SOFTWARE.
25
25
  from rdflib import URIRef
26
26
  from rdflib.namespace import DefinedNamespace, Namespace
27
27
 
28
-
28
+ # Namespace for the test specifications
29
29
  class MUST(DefinedNamespace):
30
30
  _NS = Namespace("https://mustrd.com/model/")
31
-
32
- # test configuration
33
- TestConfig: URIRef
34
- hasTestFunction: URIRef
35
- hasSpecPath: URIRef
36
- hasDataPath: URIRef
37
- triplestoreSpecPath: URIRef
38
- filterOnTripleStore: URIRef
39
-
31
+
40
32
  # Specification classes
41
33
  TestSpec: URIRef
42
34
  SelectSparql: URIRef
@@ -50,8 +42,6 @@ class MUST(DefinedNamespace):
50
42
  given: URIRef
51
43
  when: URIRef
52
44
  then: URIRef
53
- inputGraph: URIRef
54
- outputGraph: URIRef # anzo specials?
55
45
  dataSource: URIRef
56
46
  file: URIRef
57
47
  fileName: URIRef
@@ -84,22 +74,10 @@ class MUST(DefinedNamespace):
84
74
  fileSource: URIRef
85
75
  loadedFromFile: URIRef
86
76
  specSourceFile: URIRef
77
+ specFileName: URIRef
87
78
 
88
79
  # Triple store config parameters
89
- url: URIRef
90
- port: URIRef
91
- username: URIRef
92
- password: URIRef
93
- inputGraph: URIRef
94
- repository: URIRef
95
-
96
- # RDFLib
97
- RdfLib: URIRef
98
- RdfLibConfig: URIRef
99
-
100
80
  # Anzo
101
- Anzo: URIRef
102
- AnzoConfig: URIRef
103
81
  AnzoGraphmartDataset: URIRef
104
82
  AnzoQueryBuilderSparqlSource: URIRef
105
83
  AnzoGraphmartStepSparqlSource: URIRef
@@ -107,12 +85,41 @@ class MUST(DefinedNamespace):
107
85
  AnzoGraphmartQueryDrivenTemplatedStepSparqlSource: URIRef
108
86
  anzoQueryStep: URIRef
109
87
  anzoGraphmartLayer: URIRef
110
-
111
88
 
112
89
  graphmart: URIRef
113
90
  layer: URIRef
114
- gqeURI: URIRef
115
91
 
116
- # GraphDb
92
+
93
+ # FIXME: There is nothing to do that by default?
94
+ @classmethod
95
+ def get_local_name(cls, uri: URIRef):
96
+ return str(uri).split(cls._NS)[1]
97
+
98
+ # Namespace for triplestores
99
+ class TRIPLESTORE(DefinedNamespace):
100
+ _NS = Namespace("https://mustrd.com/triplestore/")
101
+ RdfLib: URIRef
117
102
  GraphDb: URIRef
118
- GraphDbConfig: URIRef
103
+ Anzo: URIRef
104
+ ExternalTripleStore: URIRef
105
+ InternalTripleStore: URIRef
106
+
107
+ gqeURI: URIRef
108
+ inputGraph: URIRef
109
+ outputGraph: URIRef # anzo specials? # Triple store config parameters
110
+ url: URIRef
111
+ port: URIRef
112
+ username: URIRef
113
+ password: URIRef
114
+ repository: URIRef
115
+
116
+ # namespace for pytest_mustrd config
117
+ class MUSTRDTEST(DefinedNamespace):
118
+ _NS = Namespace("https://mustrd.com/mustrdTest/")
119
+ MustrdTest: URIRef
120
+ hasSpecPath: URIRef
121
+ hasDataPath: URIRef
122
+ triplestoreSpecPath: URIRef
123
+ hasPytestPath: URIRef
124
+ filterOnTripleStore: URIRef
125
+
@@ -29,7 +29,7 @@ import os
29
29
  from rdflib import Graph
30
30
  from .mustrd import get_triple_store_graph, run_specs, get_triple_stores, review_results, validate_specs, get_specs
31
31
  from pathlib import Path
32
- from .namespace import MUST
32
+ from .namespace import TRIPLESTORE
33
33
  from .utils import get_project_root
34
34
  import logging
35
35
  log = logger_setup.setup_logger(__name__)
@@ -68,7 +68,7 @@ def main(argv):
68
68
  triple_stores = get_triple_stores(get_triple_store_graph(triplestore_spec_path))
69
69
  else:
70
70
  log.info(f"No triple store configuration added, running default configuration")
71
- triple_stores = [{'type': MUST.RdfLib}]
71
+ triple_stores = [{'type': TRIPLESTORE.RdfLib}]
72
72
  log.info("Triple Stores: " + str(triple_stores))
73
73
  if args.data:
74
74
  run_config["data_path"] = Path(args.data)
@@ -37,7 +37,7 @@ import logging
37
37
 
38
38
  from . import logger_setup
39
39
  from .mustrdAnzo import get_queries_for_layer, get_queries_from_templated_step, get_spec_component_from_graphmart, get_query_from_querybuilder, get_query_from_step
40
- from .namespace import MUST
40
+ from .namespace import MUST, TRIPLESTORE
41
41
  from multimethods import MultiMethod, Default
42
42
 
43
43
  log = logger_setup.setup_logger(__name__)
@@ -417,7 +417,7 @@ def _get_spec_component_StatementsDataset(spec_component_details: SpecComponentD
417
417
  def _get_spec_component_AnzoGraphmartDataset(spec_component_details: SpecComponentDetails) -> SpecComponent:
418
418
  spec_component = init_spec_component(spec_component_details.predicate)
419
419
 
420
- if spec_component_details.mustrd_triple_store["type"] == MUST.Anzo:
420
+ if spec_component_details.mustrd_triple_store["type"] == TRIPLESTORE.Anzo:
421
421
  # Get GIVEN or THEN from anzo graphmart
422
422
  graphmart = spec_component_details.spec_graph.value(subject=spec_component_details.spec_component_node,
423
423
  predicate=MUST.graphmart)
@@ -428,7 +428,7 @@ def _get_spec_component_AnzoGraphmartDataset(spec_component_details: SpecCompone
428
428
  graphmart=graphmart,
429
429
  layer=layer)
430
430
  else:
431
- raise ValueError(f"You must define {MUST.AnzoConfig} to use {MUST.AnzoGraphmartDataset}")
431
+ raise ValueError(f"You must define {TRIPLESTORE.Anzo} to use {MUST.AnzoGraphmartDataset}")
432
432
 
433
433
  return spec_component
434
434
 
@@ -438,7 +438,7 @@ def _get_spec_component_AnzoQueryBuilderSparqlSource(spec_component_details: Spe
438
438
  spec_component = init_spec_component(spec_component_details.predicate)
439
439
 
440
440
  # Get WHEN specComponent from query builder
441
- if spec_component_details.mustrd_triple_store["type"] == MUST.Anzo:
441
+ if spec_component_details.mustrd_triple_store["type"] == TRIPLESTORE.Anzo:
442
442
  query_folder = spec_component_details.spec_graph.value(subject=spec_component_details.spec_component_node,
443
443
  predicate=MUST.queryFolder)
444
444
  query_name = spec_component_details.spec_graph.value(subject=spec_component_details.spec_component_node,
@@ -448,7 +448,7 @@ def _get_spec_component_AnzoQueryBuilderSparqlSource(spec_component_details: Spe
448
448
  query_name=query_name)
449
449
  # If anzo specific function is called but no anzo defined
450
450
  else:
451
- raise ValueError(f"You must define {MUST.AnzoConfig} to use {MUST.AnzoQueryBuilderSparqlSource}")
451
+ raise ValueError(f"You must define {TRIPLESTORE.Anzo} to use {MUST.AnzoQueryBuilderSparqlSource}")
452
452
 
453
453
  spec_component.queryType = spec_component_details.spec_graph.value(subject=spec_component_details.spec_component_node,
454
454
  predicate=MUST.queryType)
@@ -460,14 +460,14 @@ def _get_spec_component_AnzoGraphmartStepSparqlSource(spec_component_details: Sp
460
460
  spec_component = init_spec_component(spec_component_details.predicate)
461
461
 
462
462
  # Get WHEN specComponent from query builder
463
- if spec_component_details.mustrd_triple_store["type"] == MUST.Anzo:
463
+ if spec_component_details.mustrd_triple_store["type"] == TRIPLESTORE.Anzo:
464
464
  query_step_uri = spec_component_details.spec_graph.value(subject=spec_component_details.spec_component_node,
465
465
  predicate=MUST.anzoQueryStep)
466
466
  spec_component.value = get_query_from_step(triple_store=spec_component_details.mustrd_triple_store,
467
467
  query_step_uri=query_step_uri)
468
468
  # If anzo specific function is called but no anzo defined
469
469
  else:
470
- raise ValueError(f"You must define {MUST.AnzoConfig} to use {MUST.AnzoGraphmartStepSparqlSource}")
470
+ raise ValueError(f"You must define {TRIPLESTORE.Anzo} to use {MUST.AnzoGraphmartStepSparqlSource}")
471
471
 
472
472
  spec_component.queryType = spec_component_details.spec_graph.value(subject=spec_component_details.spec_component_node,
473
473
  predicate=MUST.queryType)
@@ -478,7 +478,7 @@ def _get_spec_component_AnzoGraphmartQueryDrivenTemplatedStepSparqlSource(spec_c
478
478
  spec_component = init_spec_component(spec_component_details.predicate, spec_component_details.mustrd_triple_store["type"] )
479
479
 
480
480
  # Get WHEN specComponent from query builder
481
- if spec_component_details.mustrd_triple_store["type"] == MUST.Anzo:
481
+ if spec_component_details.mustrd_triple_store["type"] == TRIPLESTORE.Anzo:
482
482
  query_step_uri = spec_component_details.spec_graph.value(subject=spec_component_details.spec_component_node,
483
483
  predicate=MUST.anzoQueryStep)
484
484
  queries = get_queries_from_templated_step(triple_store=spec_component_details.mustrd_triple_store,
@@ -487,7 +487,7 @@ def _get_spec_component_AnzoGraphmartQueryDrivenTemplatedStepSparqlSource(spec_c
487
487
  spec_component.queryTemplate = queries["query_template"]
488
488
  # If anzo specific function is called but no anzo defined
489
489
  else:
490
- raise ValueError(f"You must define {MUST.AnzoConfig} to use {MUST.AnzoGraphmartQueryDrivenTemplatedStepSparqlSource}")
490
+ raise ValueError(f"You must define {TRIPLESTORE.Anzo} to use {MUST.AnzoGraphmartQueryDrivenTemplatedStepSparqlSource}")
491
491
 
492
492
  spec_component.queryType = spec_component_details.spec_graph.value(subject=spec_component_details.spec_component_node,
493
493
  predicate=MUST.queryType)
@@ -497,7 +497,7 @@ def _get_spec_component_AnzoGraphmartQueryDrivenTemplatedStepSparqlSource(spec_c
497
497
  def _get_spec_component_AnzoGraphmartLayerSparqlSource(spec_component_details: SpecComponentDetails) -> list:
498
498
  spec_components = []
499
499
  # Get the ordered WHEN specComponents which is the transform and query driven template queries for the Layer
500
- if spec_component_details.mustrd_triple_store["type"] == MUST.Anzo:
500
+ if spec_component_details.mustrd_triple_store["type"] == TRIPLESTORE.Anzo:
501
501
  graphmart_layer_uri = spec_component_details.spec_graph.value(subject=spec_component_details.spec_component_node,
502
502
  predicate=MUST.anzoGraphmartLayer)
503
503
  queries = get_queries_for_layer(triple_store=spec_component_details.mustrd_triple_store,
@@ -529,7 +529,7 @@ def init_spec_component(predicate: URIRef, triple_store_type: URIRef = None ) ->
529
529
  if predicate == MUST.given:
530
530
  spec_component = GivenSpec()
531
531
  elif predicate == MUST.when:
532
- if triple_store_type == MUST.Anzo:
532
+ if triple_store_type == TRIPLESTORE.Anzo:
533
533
  spec_component = AnzoWhenSpec()
534
534
  else:
535
535
  spec_component = WhenSpec()
@@ -26,7 +26,7 @@ import json
26
26
 
27
27
  from . import logger_setup
28
28
  from multimethods import MultiMethod, Default
29
- from .namespace import MUST
29
+ from .namespace import MUST, TRIPLESTORE
30
30
  from rdflib import Graph, URIRef
31
31
  from .mustrdRdfLib import execute_select as execute_select_rdflib
32
32
  from .mustrdRdfLib import execute_construct as execute_construct_rdflib
@@ -53,17 +53,17 @@ def dispatch_upload_given(triple_store: dict, given: Graph):
53
53
  upload_given = MultiMethod('upload_given', dispatch_upload_given)
54
54
 
55
55
 
56
- @upload_given.method(MUST.RdfLib)
56
+ @upload_given.method(TRIPLESTORE.RdfLib)
57
57
  def _upload_given_rdflib(triple_store: dict, given: Graph):
58
58
  triple_store["given"] = given
59
59
 
60
60
 
61
- @upload_given.method(MUST.GraphDb)
61
+ @upload_given.method(TRIPLESTORE.GraphDb)
62
62
  def _upload_given_graphdb(triple_store: dict, given: Graph):
63
63
  upload_given_graphdb(triple_store, given)
64
64
 
65
65
 
66
- @upload_given.method(MUST.Anzo)
66
+ @upload_given.method(TRIPLESTORE.Anzo)
67
67
  def _upload_given_anzo(triple_store: dict, given: Graph):
68
68
  upload_given_anzo(triple_store, given)
69
69
 
@@ -78,52 +78,52 @@ def dispatch_run_when(spec_uri: URIRef, triple_store: dict, when: WhenSpec):
78
78
  run_when = MultiMethod('run_when', dispatch_run_when)
79
79
 
80
80
 
81
- @run_when.method((MUST.Anzo, MUST.UpdateSparql))
81
+ @run_when.method((TRIPLESTORE.Anzo, MUST.UpdateSparql))
82
82
  def _anzo_run_when_update(spec_uri: URIRef, triple_store: dict, when: AnzoWhenSpec):
83
83
  return execute_update_anzo(triple_store, when.value, when.bindings)
84
84
 
85
85
 
86
- @run_when.method((MUST.Anzo, MUST.ConstructSparql))
86
+ @run_when.method((TRIPLESTORE.Anzo, MUST.ConstructSparql))
87
87
  def _anzo_run_when_construct(spec_uri: URIRef, triple_store: dict, when: AnzoWhenSpec):
88
88
  return execute_construct_anzo(triple_store, when.value, when.bindings)
89
89
 
90
90
 
91
- @run_when.method((MUST.Anzo, MUST.SelectSparql))
91
+ @run_when.method((TRIPLESTORE.Anzo, MUST.SelectSparql))
92
92
  def _anzo_run_when_select(spec_uri: URIRef, triple_store: dict, when: AnzoWhenSpec):
93
93
  return execute_select_anzo(triple_store, when.value, when.bindings)
94
94
 
95
95
 
96
- @run_when.method((MUST.GraphDb, MUST.UpdateSparql))
96
+ @run_when.method((TRIPLESTORE.GraphDb, MUST.UpdateSparql))
97
97
  def _graphdb_run_when_update(spec_uri: URIRef, triple_store: dict, when: WhenSpec):
98
98
  return execute_update_graphdb(triple_store, when.value, when.bindings)
99
99
 
100
100
 
101
- @run_when.method((MUST.GraphDb, MUST.ConstructSparql))
101
+ @run_when.method((TRIPLESTORE.GraphDb, MUST.ConstructSparql))
102
102
  def _graphdb_run_when_construct(spec_uri: URIRef, triple_store: dict, when: WhenSpec):
103
103
  return execute_construct_graphdb(triple_store, when.value, when.bindings)
104
104
 
105
105
 
106
- @run_when.method((MUST.GraphDb, MUST.SelectSparql))
106
+ @run_when.method((TRIPLESTORE.GraphDb, MUST.SelectSparql))
107
107
  def _graphdb_run_when_select(spec_uri: URIRef, triple_store: dict, when: WhenSpec):
108
108
  return execute_select_graphdb(triple_store, when.value, when.bindings)
109
109
 
110
110
 
111
- @run_when.method((MUST.RdfLib, MUST.UpdateSparql))
111
+ @run_when.method((TRIPLESTORE.RdfLib, MUST.UpdateSparql))
112
112
  def _rdflib_run_when_update(spec_uri: URIRef, triple_store: dict, when: WhenSpec):
113
113
  return execute_update_rdflib(triple_store, triple_store["given"], when.value, when.bindings)
114
114
 
115
115
 
116
- @run_when.method((MUST.RdfLib, MUST.ConstructSparql))
116
+ @run_when.method((TRIPLESTORE.RdfLib, MUST.ConstructSparql))
117
117
  def _rdflib_run_when_construct(spec_uri: URIRef, triple_store: dict, when: WhenSpec):
118
118
  return execute_construct_rdflib(triple_store, triple_store["given"], when.value, when.bindings)
119
119
 
120
120
 
121
- @run_when.method((MUST.RdfLib, MUST.SelectSparql))
121
+ @run_when.method((TRIPLESTORE.RdfLib, MUST.SelectSparql))
122
122
  def _rdflib_run_when_select(spec_uri: URIRef, triple_store: dict, when: WhenSpec):
123
123
  return execute_select_rdflib(triple_store, triple_store["given"], when.value, when.bindings)
124
124
 
125
125
 
126
- @run_when.method((MUST.Anzo, MUST.AnzoQueryDrivenUpdateSparql))
126
+ @run_when.method((TRIPLESTORE.Anzo, MUST.AnzoQueryDrivenUpdateSparql))
127
127
  def _multi_run_when_anzo_query_driven_update(spec_uri: URIRef, triple_store: dict, when: AnzoWhenSpec):
128
128
  # run the parameters query to obtain the values for the template step and put them into a dictionary
129
129
  query_parameters = json.loads(execute_select_anzo(triple_store, when.paramQuery, None))
@@ -158,7 +158,7 @@ def _multi_run_when_default(spec_uri: URIRef, triple_store: dict, when: WhenSpec
158
158
  elif when.queryType == MUST.DescribeSparql:
159
159
  log.warning(f"Skipping {spec_uri}, SPARQL DESCRIBE not implemented.")
160
160
  msg = "SPARQL DESCRIBE not implemented."
161
- elif triple_store['type'] not in [MUST.Anzo, MUST.GraphDb, MUST.RdfLib]:
161
+ elif triple_store['type'] not in [TRIPLESTORE.Anzo, TRIPLESTORE.GraphDb, TRIPLESTORE.RdfLib]:
162
162
  msg = f"{when.queryType} not implemented for {triple_store['type']}"
163
163
  else:
164
164
  log.warning(f"Skipping {spec_uri}, {when.queryType} is not a valid SPARQL query type.")
@@ -0,0 +1,5 @@
1
+ from mustrd.mustrdTestPlugin import run_test_spec
2
+
3
+
4
+ def test_unit(unit_tests):
5
+ assert run_test_spec(unit_tests.unit_test)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "mustrd"
3
- version = "0.1.7"
3
+ version = "0.1.8"
4
4
  description = "A Spec By Example framework for RDF and SPARQL, Inspired by Cucumber."
5
5
  authors = ["John Placek <john.placek@semanticpartners.com>",
6
6
  "Juliane Piñeiro-Winkler <juliane.pineiro-winkler@semanticpartners.com>",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes