Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Test
on:
- push
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.13]
steps:

- name: checkout
uses: actions/checkout@v3

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Install dev dependencies
run: uv sync

- name: Run tests
run: uv run pytest
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__
*swp
16 changes: 16 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[project]
name = "big-dctap"
version = "0.2.0"
description = "BIBFRAME Interoperability Group DCTap TSV files used to generate SHACL graphs for validation"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"dctap2shacl>=0.1.1",
"pyshacl>=0.31.0",
]

[dependency-groups]
dev = [
"pytest>=9.0.2",
"ruff>=0.15.1",
]
96 changes: 96 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"""Pytest fixtures for DCTap to SHACL conversion testing."""

from pathlib import Path
from typing import Callable

import pytest
from dctap2shacl import DCTap2SHACLTransformer
from rdflib import Graph


@pytest.fixture
def violation_query():
return """
PREFIX sh: <http://www.w3.org/ns/shacl#>

SELECT (COUNT(DISTINCT ?result) AS ?count)
WHERE {
?result sh:resultSeverity sh:Violation .
}
"""


@pytest.fixture
def warning_query():
return """
PREFIX sh: <http://www.w3.org/ns/shacl#>

SELECT (COUNT(DISTINCT ?result) AS ?count)
WHERE {
?result sh:resultSeverity sh:Warning .
}
"""


@pytest.fixture
def dctap_to_shacl() -> Callable[[str], Graph]:
"""
Factory fixture that converts a DCTap TSV file to a SHACL rdflib Graph.

This fixture returns a function that takes a TSV filename (or relative path)
and converts it to a SHACL validation graph. The function automatically
searches for the file in the Monograph DCTAP and Serials DCTAP directories.

Args:
tsv_filename: Name or relative path of the DCTap TSV file.
Examples: "Monograph_Work_Text.tsv",
"Monograph DCTAP/Monograph_Work_Text.tsv",
"Serials DCTAP/Serial_Work_Text.tsv"

Returns:
rdflib.Graph: SHACL validation graph

Example:
def test_monograph_work_validation(dctap_to_shacl):
shacl_graph = dctap_to_shacl("Monograph_Work_Text.tsv")
assert len(shacl_graph) > 0

def test_with_full_path(dctap_to_shacl):
shacl_graph = dctap_to_shacl("Monograph DCTAP/Monograph_Work_Text.tsv")
assert len(shacl_graph) > 0
"""

def _convert(tsv_filename: str) -> Graph:
"""Convert a DCTap TSV file to SHACL graph."""
# Get the project root (parent of tests directory)
project_root = Path(__file__).parent.parent

# If the filename includes a directory path, use it directly
if "/" in tsv_filename or "\\" in tsv_filename:
tsv_path = project_root / tsv_filename
else:
# Search for the file in common locations
possible_paths = [
project_root / "Monograph DCTAP" / tsv_filename,
project_root / "Serials DCTAP" / tsv_filename,
project_root / tsv_filename,
]

tsv_path = None
for path in possible_paths:
if path.exists():
tsv_path = path
break

if tsv_path is None:
raise FileNotFoundError(
f"DCTap file '{tsv_filename}' not found in Monograph DCTAP, "
f"Serials DCTAP, or project root directories"
)

# Convert to SHACL graph
transformer = DCTap2SHACLTransformer()
transformer.run(str(tsv_path))
return transformer.graph

return _convert
91 changes: 91 additions & 0 deletions tests/test_monograph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import pathlib

import pytest

from pyshacl import validate

TEST_DIRECTORY = pathlib.Path(__file__).parent


@pytest.mark.parametrize(
"rdf_file_path, violations, warnings",
[
("loc/monograph/12516952.cbd.rdf", 5, 2),
("loc/monograph/22932823.cbd.rdf", 6, 2),
("loc/monograph/23703536.cbd.rdf", 0, 3),
("loc/monograph/22483233.cbd.rdf", 0, 3),
("loc/monograph/23694998.cbd.rdf", 0, 3),
],
)
def test_text_works(
rdf_file_path: str,
violations: int,
warnings: int,
dctap_to_shacl,
violation_query,
warning_query,
):
"""Tests BIBFRAME Text Works"""
rdf_test_file = TEST_DIRECTORY / rdf_file_path
shacl_graph = dctap_to_shacl("Monograph_Work_Text.tsv")
results = validate(str(rdf_test_file), shacl_graph=shacl_graph, allow_warnings=True)
violation_count = int(list(results[1].query(violation_query))[0][0])
warning_count = int(list(results[1].query(warning_query))[0][0])
assert violations == violation_count
assert warnings == warning_count


@pytest.mark.parametrize(
"rdf_file_path, violations, warnings",
[
("loc/monograph/12516952.cbd.rdf", 3, 1),
("loc/monograph/22932823.cbd.rdf", 2, 1),
("loc/monograph/23703536.cbd.rdf", 0, 1),
("loc/monograph/22483233.cbd.rdf", 0, 1),
("loc/monograph/23694998.cbd.rdf", 0, 1),
],
)
def test_print_instances(
rdf_file_path: str,
violations: int,
warnings: int,
dctap_to_shacl,
violation_query,
warning_query,
):
"""Tests BIBFRAME Print Instances"""
rdf_test_file = TEST_DIRECTORY / rdf_file_path
shacl_graph = dctap_to_shacl("Monograph_Instance_Print.tsv")
results = validate(str(rdf_test_file), shacl_graph=shacl_graph, allow_warnings=True)
violation_count = int(list(results[1].query(violation_query))[0][0])
warning_count = int(list(results[1].query(warning_query))[0][0])
assert violations == violation_count
assert warnings == warning_count


@pytest.mark.parametrize(
"rdf_file_path, violations, warnings",
[
("loc/monograph/12516952.cbd.rdf", 24, 0),
("loc/monograph/22932823.cbd.rdf", 16, 0),
("loc/monograph/23703536.cbd.rdf", 16, 0),
("loc/monograph/22483233.cbd.rdf", 16, 0),
("loc/monograph/23694998.cbd.rdf", 16, 0),
],
)
def test_monograph_admin_metadata(
rdf_file_path: str,
violations: int,
warnings: int,
dctap_to_shacl,
violation_query,
warning_query,
):
"""Tests BIBFRAME Monograph Admin Metadata"""
rdf_test_file = TEST_DIRECTORY / rdf_file_path
shacl_graph = dctap_to_shacl("Monograph_AdminMetadata.tsv")
results = validate(str(rdf_test_file), shacl_graph=shacl_graph, allow_warnings=True)
violation_count = int(list(results[1].query(violation_query))[0][0])
warning_count = int(list(results[1].query(warning_query))[0][0])
assert violations == violation_count
assert warnings == warning_count
91 changes: 91 additions & 0 deletions tests/test_serials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import pathlib

import pytest

from pyshacl import validate

TEST_DIRECTORY = pathlib.Path(__file__).parent


@pytest.mark.parametrize(
"rdf_file_path, violations, warnings",
[
("loc/serial/11158534.cbd.rdf", 0, 8),
("loc/serial/21507607.cbd.rdf", 0, 6),
("loc/serial/23326748.cbd.rdf", 0, 3),
("loc/serial/23793113.cbd.rdf", 0, 3),
("loc/serial/23996113.cbd.rdf", 6, 3),
],
)
def test_serial_works(
rdf_file_path: str,
violations: int,
warnings: int,
dctap_to_shacl,
violation_query,
warning_query,
):
"""Tests BIBFRAME Serial Works"""
rdf_test_file = TEST_DIRECTORY / rdf_file_path
shacl_graph = dctap_to_shacl("Serial_Work_Text.tsv")
results = validate(str(rdf_test_file), shacl_graph=shacl_graph, allow_warnings=True)
violation_count = int(list(results[1].query(violation_query))[0][0])
warning_count = int(list(results[1].query(warning_query))[0][0])
assert violations == violation_count
assert warnings == warning_count


@pytest.mark.parametrize(
"rdf_file_path, violations, warnings",
[
("loc/serial/11158534.cbd.rdf", 0, 8),
("loc/serial/21507607.cbd.rdf", 0, 4),
("loc/serial/23326748.cbd.rdf", 0, 1),
("loc/serial/23793113.cbd.rdf", 0, 2),
("loc/serial/23996113.cbd.rdf", 4, 1),
],
)
def test_serials_instances(
rdf_file_path: str,
violations: int,
warnings: int,
dctap_to_shacl,
violation_query,
warning_query,
):
"""Tests BIBFRAME Serial Instances"""
rdf_test_file = TEST_DIRECTORY / rdf_file_path
shacl_graph = dctap_to_shacl("Serial_Instance_Electronic.tsv")
results = validate(str(rdf_test_file), shacl_graph=shacl_graph, allow_warnings=True)
violation_count = int(list(results[1].query(violation_query))[0][0])
warning_count = int(list(results[1].query(warning_query))[0][0])
assert violations == violation_count
assert warnings == warning_count


@pytest.mark.parametrize(
"rdf_file_path, violations, warnings",
[
("loc/serial/11158534.cbd.rdf", 16, 0),
("loc/serial/21507607.cbd.rdf", 24, 0),
("loc/serial/23326748.cbd.rdf", 16, 0),
("loc/serial/23793113.cbd.rdf", 16, 0),
("loc/serial/23996113.cbd.rdf", 32, 0),
],
)
def test_serials_admin_metadata(
rdf_file_path: str,
violations: int,
warnings: int,
dctap_to_shacl,
violation_query,
warning_query,
):
"""Tests BIBFRAME Serial Admin Metadata"""
rdf_test_file = TEST_DIRECTORY / rdf_file_path
shacl_graph = dctap_to_shacl("Serial_AdminMetadata.tsv")
results = validate(str(rdf_test_file), shacl_graph=shacl_graph, allow_warnings=True)
violation_count = int(list(results[1].query(violation_query))[0][0])
warning_count = int(list(results[1].query(warning_query))[0][0])
assert violations == violation_count
assert warnings == warning_count
Loading