mustrd 0.3.3__py3-none-any.whl → 0.3.4a1__py3-none-any.whl

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.
@@ -169,6 +169,7 @@ must:SparqlSourceShape
169
169
  a sh:NodeShape ;
170
170
  sh:targetClass must:SparqlSource ;
171
171
  sh:property [ sh:path must:queryType ;
172
+ sh:in ( must:SelectSparql must:ConstructSparql must:UpdateSparql) ;
172
173
  sh:minCount 1 ;
173
174
  sh:maxCount 1 ; ] .
174
175
 
mustrd/mustrd.py CHANGED
@@ -134,7 +134,7 @@ class TripleStoreConnectionError(SpecResult):
134
134
 
135
135
 
136
136
  @dataclass
137
- class SpecSkipped(SpecResult):
137
+ class SpecInvalid(SpecResult):
138
138
  message: str
139
139
  spec_file_name: str = "default.mustrd.ttl"
140
140
  spec_source_file: Path = Path("default.mustrd.ttl")
@@ -204,6 +204,12 @@ def validate_specs(
204
204
  f"Could not extract spec from {file} due to exception of type "
205
205
  f"{type(e).__name__} when parsing file"
206
206
  ]
207
+ invalid_specs += [
208
+ SpecInvalid(
209
+ "urn:invalid_spec_file", triple_store["type"], message, file.name, file
210
+ )
211
+ for triple_store in triple_stores
212
+ ]
207
213
  continue
208
214
 
209
215
  # run shacl validation
@@ -310,7 +316,7 @@ def add_spec_validation(
310
316
  error_messages.sort()
311
317
  error_message = "\n".join(msg for msg in error_messages)
312
318
  invalid_specs += [
313
- SpecSkipped(
319
+ SpecInvalid(
314
320
  subject_uri, triple_store["type"], error_message, file.name, file
315
321
  )
316
322
  for triple_store in triple_stores
@@ -324,15 +330,15 @@ def get_specs(
324
330
  run_config: dict,
325
331
  ):
326
332
  specs = []
327
- skipped_results = []
333
+ invalid_spec = []
328
334
  try:
329
335
  for triple_store in triple_stores:
330
336
  if "error" in triple_store:
331
337
  log.error(
332
338
  f"{triple_store['error']}. No specs run for this triple store."
333
339
  )
334
- skipped_results += [
335
- SpecSkipped(
340
+ invalid_spec += [
341
+ SpecInvalid(
336
342
  spec_uri,
337
343
  triple_store["type"],
338
344
  triple_store["error"],
@@ -360,8 +366,8 @@ def get_specs(
360
366
  )
361
367
  or "unknown"
362
368
  )
363
- skipped_results += [
364
- SpecSkipped(
369
+ invalid_spec += [
370
+ SpecInvalid(
365
371
  spec_uri,
366
372
  triple_store["type"],
367
373
  str(e),
@@ -380,7 +386,7 @@ def get_specs(
380
386
  log.error("No specifications will be run.")
381
387
 
382
388
  log.info(f"Extracted {len(specs)} specifications that will be run")
383
- return specs, skipped_results
389
+ return specs, invalid_spec
384
390
 
385
391
 
386
392
  def run_specs(specs) -> List[SpecResult]:
@@ -505,7 +511,7 @@ def run_spec(spec: Specification) -> SpecResult:
505
511
  upload_given(triple_store, spec.given)
506
512
  else:
507
513
  if triple_store["type"] == TRIPLESTORE.RdfLib:
508
- return SpecSkipped(
514
+ return SpecInvalid(
509
515
  spec_uri,
510
516
  triple_store["type"],
511
517
  "Unable to run Inherited State tests on Rdflib",
@@ -526,7 +532,6 @@ def run_spec(spec: Specification) -> SpecResult:
526
532
  except NotImplementedError as ex:
527
533
  log.error(f"NotImplementedError {ex}")
528
534
  raise ex
529
- # return SpecSkipped(spec_uri, triple_store["type"], ex.args[0])
530
535
  return check_result(spec, result)
531
536
  except (ConnectionError, TimeoutError, HTTPError, ConnectTimeout, OSError) as e:
532
537
  # close_connection = False
@@ -948,8 +953,8 @@ def write_result_diff_to_log(res, info):
948
953
  ):
949
954
  info(f"{Fore.RED}Failed {res.spec_uri} {res.triple_store}")
950
955
  info(res.exception)
951
- if isinstance(res, SpecSkipped):
952
- info(f"{Fore.YELLOW}Skipped {res.spec_uri} {res.triple_store}")
956
+ if isinstance(res, SpecInvalid):
957
+ info(f"{Fore.RED} Invalid {res.spec_uri} {res.triple_store}")
953
958
  info(res.message)
954
959
 
955
960
 
@@ -1046,7 +1051,7 @@ def review_results(results: List[SpecResult], verbose: bool) -> None:
1046
1051
  colours = {
1047
1052
  SpecPassed: Fore.GREEN,
1048
1053
  SpecPassedWithWarning: Fore.YELLOW,
1049
- SpecSkipped: Fore.YELLOW,
1054
+ SpecInvalid: Fore.RED,
1050
1055
  }
1051
1056
  # Populate dictionaries from results
1052
1057
  for result in results:
@@ -1103,12 +1108,12 @@ def review_results(results: List[SpecResult], verbose: bool) -> None:
1103
1108
 
1104
1109
  pass_count = statuses.count(SpecPassed)
1105
1110
  warning_count = statuses.count(SpecPassedWithWarning)
1106
- skipped_count = statuses.count(SpecSkipped)
1111
+ invalid_count = statuses.count(SpecInvalid)
1107
1112
  fail_count = len(
1108
1113
  list(
1109
1114
  filter(
1110
1115
  lambda status: status
1111
- not in [SpecPassed, SpecPassedWithWarning, SpecSkipped],
1116
+ not in [SpecPassed, SpecPassedWithWarning, SpecInvalid],
1112
1117
  statuses,
1113
1118
  )
1114
1119
  )
@@ -1116,18 +1121,18 @@ def review_results(results: List[SpecResult], verbose: bool) -> None:
1116
1121
 
1117
1122
  if fail_count:
1118
1123
  overview_colour = Fore.RED
1119
- elif warning_count or skipped_count:
1124
+ elif warning_count or invalid_count:
1120
1125
  overview_colour = Fore.YELLOW
1121
1126
  else:
1122
1127
  overview_colour = Fore.GREEN
1123
1128
 
1124
1129
  logger_setup.flush()
1125
1130
  log.info(
1126
- f"{overview_colour}===== {fail_count} failures, {skipped_count} skipped, {Fore.GREEN}{pass_count} passed, "
1131
+ f"{overview_colour}===== {fail_count} failures, {invalid_count} invalid, {Fore.GREEN}{pass_count} passed, "
1127
1132
  f"{overview_colour}{warning_count} passed with warnings ====="
1128
1133
  )
1129
1134
 
1130
- if verbose and (fail_count or warning_count or skipped_count):
1135
+ if verbose and (fail_count or warning_count or invalid_count):
1131
1136
  display_verbose(results)
1132
1137
 
1133
1138
 
@@ -1165,8 +1170,8 @@ def display_verbose(results: List[SpecResult]):
1165
1170
  ):
1166
1171
  log.info(f"{Fore.RED}Failed {res.spec_uri} {res.triple_store}")
1167
1172
  log.info(res.exception)
1168
- if isinstance(res, SpecSkipped):
1169
- log.info(f"{Fore.YELLOW}Skipped {res.spec_uri} {res.triple_store}")
1173
+ if isinstance(res, SpecInvalid):
1174
+ log.info(f"{Fore.YELLOW}Invalid {res.spec_uri} {res.triple_store}")
1170
1175
  log.info(res.message)
1171
1176
 
1172
1177
 
@@ -2,7 +2,7 @@ import logging
2
2
  from dataclasses import dataclass
3
3
  import pytest
4
4
  import os
5
- from pathlib import Path, PosixPath
5
+ from pathlib import Path
6
6
  from rdflib.namespace import Namespace
7
7
  from rdflib import Graph, RDF
8
8
  from pytest import Session
@@ -11,20 +11,16 @@ from mustrd import logger_setup
11
11
  from mustrd.TestResult import ResultList, TestResult, get_result_list
12
12
  from mustrd.utils import get_mustrd_root
13
13
  from mustrd.mustrd import (
14
- write_result_diff_to_log,
15
- get_triple_store_graph,
16
- get_triple_stores,
17
- )
18
- from mustrd.mustrd import (
19
- Specification,
20
- SpecSkipped,
21
14
  validate_specs,
22
15
  get_specs,
23
16
  SpecPassed,
24
17
  run_spec,
18
+ write_result_diff_to_log,
19
+ get_triple_store_graph,
20
+ get_triple_stores,
21
+ SpecInvalid
25
22
  )
26
23
  from mustrd.namespace import MUST, TRIPLESTORE, MUSTRDTEST
27
- from typing import Union
28
24
  from pyshacl import validate
29
25
 
30
26
  import pathlib
@@ -171,13 +167,6 @@ class TestConfig:
171
167
  filter_on_tripleStore: str = None
172
168
 
173
169
 
174
- @dataclass(frozen=True)
175
- class TestParamWrapper:
176
- id: str
177
- test_config: TestConfig
178
- unit_test: Union[Specification, SpecSkipped]
179
-
180
-
181
170
  # Configure logging
182
171
  logger = logger_setup.setup_logger(__name__)
183
172
 
@@ -187,7 +176,6 @@ class MustrdTestPlugin:
187
176
  test_config_file: Path
188
177
  selected_tests: list
189
178
  secrets: str
190
- unit_tests: Union[Specification, SpecSkipped]
191
179
  items: list
192
180
  path_filter: str
193
181
  collect_error: BaseException
@@ -201,18 +189,17 @@ class MustrdTestPlugin:
201
189
  @pytest.hookimpl(tryfirst=True)
202
190
  def pytest_collection(self, session):
203
191
  logger.info("Starting test collection")
204
-
205
- self.unit_tests = []
192
+
206
193
  args = session.config.args
207
-
194
+
208
195
  # Split args into mustrd and regular pytest args
209
196
  mustrd_args = [arg for arg in args if ".mustrd.ttl" in arg]
210
197
  pytest_args = [arg for arg in args if arg != os.getcwd() and ".mustrd.ttl" not in arg]
211
-
198
+
212
199
  self.selected_tests = list(
213
200
  map(
214
201
  lambda arg: Path(arg.split("::")[0]),
215
- mustrd_args
202
+ mustrd_args
216
203
  )
217
204
  )
218
205
  logger.info(f"selected_tests is: {self.selected_tests}")
@@ -237,7 +224,7 @@ class MustrdTestPlugin:
237
224
 
238
225
  def get_file_name_from_arg(self, arg):
239
226
  if arg and len(arg) > 0 and "[" in arg and ".mustrd.ttl " in arg:
240
- return arg[arg.index("[") + 1 : arg.index(".mustrd.ttl ")]
227
+ return arg[arg.index("[") + 1: arg.index(".mustrd.ttl ")]
241
228
  return None
242
229
 
243
230
  @pytest.hookimpl
@@ -247,9 +234,9 @@ class MustrdTestPlugin:
247
234
  if not str(path).endswith('.ttl'):
248
235
  return None
249
236
  if Path(path).resolve() != Path(self.test_config_file).resolve():
250
- logger.debug(f"{self.test_config_file}: Skipping non-matching-config file: {path}")
251
- return None
252
-
237
+ logger.debug(f"{self.test_config_file}: Skipping non-matching-config file: {path}")
238
+ return None
239
+
253
240
  mustrd_file = MustrdFile.from_parent(parent, path=pathlib.Path(path), mustrd_plugin=self)
254
241
  mustrd_file.mustrd_plugin = self
255
242
  return mustrd_file
@@ -264,7 +251,7 @@ class MustrdTestPlugin:
264
251
  logger.debug("Generating tests for config: " + str(config))
265
252
  logger.debug(f"selected_tests {self.selected_tests}")
266
253
 
267
- valid_spec_uris, spec_graph, invalid_spec_results = validate_specs(
254
+ valid_spec_uris, spec_graph, invalid_specs = validate_specs(
268
255
  config,
269
256
  triple_stores,
270
257
  shacl_graph,
@@ -272,17 +259,6 @@ class MustrdTestPlugin:
272
259
  file_name or "*",
273
260
  selected_test_files=self.selected_tests,
274
261
  )
275
- # Convert invalid specs to SpecInvalid instead of SpecSkipped
276
- invalid_specs = [
277
- SpecInvalid(
278
- spec.spec_uri,
279
- spec.triple_store,
280
- spec.message,
281
- spec.spec_file_name,
282
- spec.spec_source_file
283
- ) for spec in invalid_spec_results
284
- ]
285
-
286
262
 
287
263
  specs, skipped_spec_results = get_specs(
288
264
  valid_spec_uris, spec_graph, triple_stores, config
@@ -291,18 +267,6 @@ class MustrdTestPlugin:
291
267
  # Return normal specs + skipped results
292
268
  return specs + skipped_spec_results + invalid_specs
293
269
 
294
- # Function called to generate the name of the test
295
- def get_test_name(self, spec):
296
- # FIXME: SpecSkipped should have the same structure?
297
- if isinstance(spec, SpecSkipped):
298
- triple_store = spec.triple_store
299
- else:
300
- triple_store = spec.triple_store["type"]
301
-
302
- triple_store_name = triple_store.replace("https://mustrd.com/model/", "")
303
- test_name = spec.spec_uri.replace(spnamespace, "").replace("_", " ")
304
- return spec.spec_file_name + " : " + triple_store_name + ": " + test_name
305
-
306
270
  # Get triple store configuration or default
307
271
  def get_triple_stores_from_file(self, test_config):
308
272
  if test_config.triplestore_spec_path:
@@ -397,13 +361,6 @@ class MustrdTestPlugin:
397
361
  with open(self.md_path, "w") as file:
398
362
  file.write(md)
399
363
 
400
- @dataclass(frozen=True)
401
- class SpecInvalid:
402
- spec_uri: str
403
- triple_store: str
404
- message: str
405
- spec_file_name: str = None
406
- spec_source_file: Path = None
407
364
 
408
365
  class MustrdFile(pytest.File):
409
366
  mustrd_plugin: MustrdTestPlugin
@@ -417,14 +374,14 @@ class MustrdFile(pytest.File):
417
374
  try:
418
375
  logger.info(f"{self.mustrd_plugin.test_config_file}: Collecting tests from file: {self.path=}")
419
376
  # Only process the specific mustrd config file we were given
420
-
377
+
421
378
  # if not str(self.fspath).endswith(".ttl"):
422
379
  # return []
423
380
  # Only process the specific mustrd config file we were given
424
381
  # if str(self.fspath) != str(self.mustrd_plugin.test_config_file):
425
382
  # logger.info(f"Skipping non-config file: {self.fspath}")
426
383
  # return []
427
-
384
+
428
385
  test_configs = parse_config(self.path)
429
386
  from collections import defaultdict
430
387
  pytest_path_grouped = defaultdict(list)
@@ -435,7 +392,7 @@ class MustrdFile(pytest.File):
435
392
  ):
436
393
  logger.info(f"Skipping test config due to path filter: {test_config.pytest_path=} {self.mustrd_plugin.path_filter=}")
437
394
  continue
438
-
395
+
439
396
  triple_stores = self.mustrd_plugin.get_triple_stores_from_file(test_config)
440
397
  try:
441
398
  specs = self.mustrd_plugin.generate_tests_for_config(
@@ -521,7 +478,7 @@ class MustrdItem(pytest.Item):
521
478
  f"Error: \n{excinfo.value}\n"
522
479
  f"Traceback:\n{tb_str}"
523
480
  )
524
-
481
+
525
482
  def reportinfo(self):
526
483
  r = "", 0, f"mustrd test: {self.name}"
527
484
  return r
@@ -531,9 +488,6 @@ class MustrdItem(pytest.Item):
531
488
  def run_test_spec(test_spec):
532
489
  logger = logging.getLogger("mustrd.test")
533
490
  logger.info(f"Running test spec: {getattr(test_spec, 'spec_uri', test_spec)}")
534
- if isinstance(test_spec, SpecSkipped):
535
- logger.warning(f"Test skipped: {test_spec.message}")
536
- pytest.skip(f"Invalid configuration, error : {test_spec.message}")
537
491
  try:
538
492
  result = run_spec(test_spec)
539
493
  logger.info(f"Result type: {type(result)} for spec: {getattr(test_spec, 'spec_uri', test_spec)}")
@@ -544,13 +498,11 @@ def run_test_spec(test_spec):
544
498
 
545
499
  if isinstance(test_spec, SpecInvalid):
546
500
  logger.error(f"Invalid test specification: {test_spec.message} {test_spec}")
547
- raise ValueError(f"Invalid test specification: {test_spec.message} {test_spec}")
548
- if type(result) == SpecSkipped:
549
- logger.warning("Test skipped due to unsupported configuration")
550
- pytest.skip("Unsupported configuration")
551
- if type(result) != SpecPassed:
501
+ pytest.fail(f"Invalid test specification: {test_spec.message} {test_spec}")
502
+ if not isinstance(result, SpecPassed):
552
503
  write_result_diff_to_log(result, logger.info)
553
504
  log_lines = []
505
+
554
506
  def log_to_string(message):
555
507
  log_lines.append(message)
556
508
  try:
@@ -562,4 +514,4 @@ def run_test_spec(test_spec):
562
514
  raise AssertionError("Test failed: " + "\n".join(log_lines))
563
515
 
564
516
  logger.info(f"Test PASSED: {getattr(test_spec, 'spec_uri', test_spec)}")
565
- return type(result) == SpecPassed
517
+ return isinstance(result, SpecPassed)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mustrd
3
- Version: 0.3.3
3
+ Version: 0.3.4a1
4
4
  Summary: A Spec By Example framework for RDF and SPARQL, Inspired by Cucumber.
5
5
  License: MIT
6
6
  Author: John Placek
@@ -5,18 +5,18 @@ mustrd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  mustrd/anzo_utils.py,sha256=CKNSF_oTFKc4v64EDXJzeirP1GxOIOCUKNfaJKk0oEc,4677
6
6
  mustrd/logger_setup.py,sha256=kuzrFaW8Ar-q5Bvjg1OqKn9l4U5MVDyfOQF13SYk8nQ,1780
7
7
  mustrd/model/catalog-v001.xml,sha256=IEtaw3FK4KGQWKancEe1HqzUQTrKdk89vNxnoLKGF4o,381
8
- mustrd/model/mustrdShapes.ttl,sha256=Qw99dIakGEra-vcENs3Oc9pJ33yYBZ0GEZJa2dLI3xo,10107
8
+ mustrd/model/mustrdShapes.ttl,sha256=OtnyOMZVUdwfUC7-F6FizSXf3P-9ubsFznYMe7gdTzY,10197
9
9
  mustrd/model/mustrdTestOntology.ttl,sha256=W7IRbPKrhpYFweZvc9QH8gdjBiuZMHKETIuHBepLnYw,2034
10
10
  mustrd/model/mustrdTestShapes.ttl,sha256=5PUpU9AzPSeLpF_UzNBVnACLOhs3hfoQpiz-ybsrbj8,818
11
11
  mustrd/model/ontology.ttl,sha256=orb8VgPiVrmzp3dKHc53hgkq_9ic9SbqIAyLZ_cHv38,17079
12
12
  mustrd/model/test-resources/resources.ttl,sha256=1Dsp1nuNxauj9bxeX-HShQsiO-CVy5Irwm2y2x0cdjI,1498
13
13
  mustrd/model/triplestoreOntology.ttl,sha256=9K5gj0hDOolRYjHc58UT4igex8cUnq9h7SUe4ToYbdw,5834
14
14
  mustrd/model/triplestoreshapes.ttl,sha256=G1kdgASdPa8s5JVGXL4KM2ewp-F5Vmbdist0f77VTBc,1706
15
- mustrd/mustrd.py,sha256=Ac42FUVY_6u1rt8uQmmIRo6-lV1vmOTy5fNu1CqPEIQ,41021
15
+ mustrd/mustrd.py,sha256=89DVyFWHkPkKdItGWUXTGBi5qpsKlTPeJUi2WicCal8,41157
16
16
  mustrd/mustrdAnzo.py,sha256=Vicc8B-zP3EwO2TF6hh7Cz-JHPNGLzG2M9KrfjzATxE,7882
17
17
  mustrd/mustrdGraphDb.py,sha256=Ro_fxDPFl64r-FAM18awhZydydEY1-IXO0zdKpvZD3U,5405
18
18
  mustrd/mustrdRdfLib.py,sha256=1dYoyohjDhonKItYMNkFybySFt9lgez3zYN2kU9mW-I,2369
19
- mustrd/mustrdTestPlugin.py,sha256=n6xISiYP1WrntcKOhtL0XZhdZigmBBHFfwrB5CEyV70,20577
19
+ mustrd/mustrdTestPlugin.py,sha256=S2KCdK5bZZBCmA6cQC7OQQw4brfIh1jXyv1VMJP4B3M,18785
20
20
  mustrd/namespace.py,sha256=1l8RJDFI7rYkWvmRokaTvSvqrDJEdRNIkq3lmPb0xpI,3854
21
21
  mustrd/run.py,sha256=5xZUgKPMBQ-03cWROAnwtbOs2Nb0Vat6n8Fi6EyfS-k,4257
22
22
  mustrd/spec_component.py,sha256=W4EWp-IJCp5CGG4Cy6m3D2-cViPoi0NrJCcSZiSqT2U,38643
@@ -25,8 +25,8 @@ mustrd/templates/md_ResultList_leaf_template.jinja,sha256=IzwZjliCx7-viipATDQK6M
25
25
  mustrd/templates/md_ResultList_template.jinja,sha256=_8joJ7vtw_qoqxv3HhUtBgRfhOeqmgfaRFwEo4MROvQ,203
26
26
  mustrd/templates/md_stats_template.jinja,sha256=96W62cMWu9UGLNv65ZQ8RYLjkxKHhJy-FlUtXgud6XY,155
27
27
  mustrd/utils.py,sha256=OGdLvw7GvjrFgTJo0J97Xwdh-_ZgSmapmOistrEchO0,1387
28
- mustrd-0.3.3.dist-info/LICENSE,sha256=r8nmh5fUct9h2w8_RDl13EIscvmwCLoarPr1kg35MnA,1078
29
- mustrd-0.3.3.dist-info/METADATA,sha256=aS5-0doN-yd9b-SPO9Qqm8DpxBmjjHtHxDLxbpU4w_Y,6423
30
- mustrd-0.3.3.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
31
- mustrd-0.3.3.dist-info/entry_points.txt,sha256=v7V7sN0_L1aB4Ug_9io5axlQSeJ1C0tNrQWwdXdV58s,50
32
- mustrd-0.3.3.dist-info/RECORD,,
28
+ mustrd-0.3.4a1.dist-info/LICENSE,sha256=r8nmh5fUct9h2w8_RDl13EIscvmwCLoarPr1kg35MnA,1078
29
+ mustrd-0.3.4a1.dist-info/METADATA,sha256=_JiUgYfLwXdnrs9H0V7toubBxy2Zu6aNU8NFWyU79bY,6425
30
+ mustrd-0.3.4a1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
31
+ mustrd-0.3.4a1.dist-info/entry_points.txt,sha256=v7V7sN0_L1aB4Ug_9io5axlQSeJ1C0tNrQWwdXdV58s,50
32
+ mustrd-0.3.4a1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.1
2
+ Generator: poetry-core 2.1.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any