summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/conftest.py34
-rw-r--r--test/data.py18
-rw-r--r--test/data/defined_namespaces/adms.rdf277
-rw-r--r--test/data/defined_namespaces/adms.ttl175
-rw-r--r--test/data/defined_namespaces/rdfs.rdf130
-rwxr-xr-xtest/data/fetcher.py15
-rw-r--r--test/test_graph/test_graph.py61
-rw-r--r--test/test_graph/test_graph_redirect.py45
-rw-r--r--test/test_misc/test_input_source.py17
-rw-r--r--test/test_misc/test_networking_redirect.py217
-rw-r--r--test/utils/exceptions.py29
-rw-r--r--test/utils/http.py9
-rw-r--r--test/utils/httpfileserver.py10
13 files changed, 1002 insertions, 35 deletions
diff --git a/test/conftest.py b/test/conftest.py
index 2f61c9fe..38f4dabc 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -44,22 +44,44 @@ def rdfs_graph() -> Graph:
return Graph().parse(TEST_DATA_DIR / "defined_namespaces/rdfs.ttl", format="turtle")
+_ServedBaseHTTPServerMocks = Tuple[ServedBaseHTTPServerMock, ServedBaseHTTPServerMock]
+
+
@pytest.fixture(scope="session")
-def _session_function_httpmock() -> Generator[ServedBaseHTTPServerMock, None, None]:
+def _session_function_httpmocks() -> Generator[_ServedBaseHTTPServerMocks, None, None]:
"""
This fixture is session scoped, but it is reset for each function in
:func:`function_httpmock`. This should not be used directly.
"""
- with ServedBaseHTTPServerMock() as httpmock:
- yield httpmock
+ with ServedBaseHTTPServerMock() as httpmock_a, ServedBaseHTTPServerMock() as httpmock_b:
+ yield httpmock_a, httpmock_b
@pytest.fixture(scope="function")
def function_httpmock(
- _session_function_httpmock: ServedBaseHTTPServerMock,
+ _session_function_httpmocks: _ServedBaseHTTPServerMocks,
) -> Generator[ServedBaseHTTPServerMock, None, None]:
- _session_function_httpmock.reset()
- yield _session_function_httpmock
+ """
+ HTTP server mock that is reset for each test function.
+ """
+ (mock, _) = _session_function_httpmocks
+ mock.reset()
+ yield mock
+
+
+@pytest.fixture(scope="function")
+def function_httpmocks(
+ _session_function_httpmocks: _ServedBaseHTTPServerMocks,
+) -> Generator[Tuple[ServedBaseHTTPServerMock, ServedBaseHTTPServerMock], None, None]:
+ """
+ Alternative HTTP server mock that is reset for each test function.
+
+ This exists in case a tests needs to work with two different HTTP servers.
+ """
+ (mock_a, mock_b) = _session_function_httpmocks
+ mock_a.reset()
+ mock_b.reset()
+ yield mock_a, mock_b
@pytest.fixture(scope="session", autouse=True)
diff --git a/test/data.py b/test/data.py
index f1271aae..779c522a 100644
--- a/test/data.py
+++ b/test/data.py
@@ -1,6 +1,7 @@
from pathlib import Path
from rdflib import URIRef
+from rdflib.graph import Graph
TEST_DIR = Path(__file__).parent
TEST_DATA_DIR = TEST_DIR / "data"
@@ -19,3 +20,20 @@ cheese = URIRef("urn:example:cheese")
context0 = URIRef("urn:example:context-0")
context1 = URIRef("urn:example:context-1")
context2 = URIRef("urn:example:context-2")
+
+
+simple_triple_graph = Graph().add(
+ (
+ URIRef("http://example.org/subject"),
+ URIRef("http://example.org/predicate"),
+ URIRef("http://example.org/object"),
+ )
+)
+"""
+A simple graph with a single triple. This is equivalent to the following RDF files:
+
+* ``test/data/variants/simple_triple.nq``
+* ``test/data/variants/simple_triple.nt``
+* ``test/data/variants/simple_triple.ttl``
+* ``test/data/variants/simple_triple.xml``
+"""
diff --git a/test/data/defined_namespaces/adms.rdf b/test/data/defined_namespaces/adms.rdf
new file mode 100644
index 00000000..cb56922a
--- /dev/null
+++ b/test/data/defined_namespaces/adms.rdf
@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="utf-8"?>
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <ns1:Ontology xmlns:ns1="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms">
+ <ns2:issued xmlns:ns2="http://purl.org/dc/terms/">2023-04-05</ns2:issued>
+ <ns3:license xmlns:ns3="http://purl.org/dc/terms/"
+ rdf:resource="https://creativecommons.org/licenses/by/4.0/"/>
+ <ns4:mediator xmlns:ns4="http://purl.org/dc/terms/">
+ <rdf:Description>
+ <ns5:homepage xmlns:ns5="http://xmlns.com/foaf/0.1/"
+ rdf:resource="https://semic.eu"/>
+ <ns6:name xmlns:ns6="http://xmlns.com/foaf/0.1/">Semantic Interoperability Community (SEMIC)</ns6:name>
+ </rdf:Description>
+ </ns4:mediator>
+ <ns7:label xmlns:ns7="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">adms</ns7:label>
+ <ns8:label xmlns:ns8="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="nl">adms</ns8:label>
+ <ns9:editor xmlns:ns9="http://www.w3.org/2001/02pd/rec54#">
+ <ns10:Person xmlns:ns10="http://xmlns.com/foaf/0.1/">
+ <ns10:firstName>Bert</ns10:firstName>
+ <ns10:lastName>Van Nuffelen</ns10:lastName>
+ <ns10:mbox rdf:resource="mailto:bert.van.nuffelen@tenforce.com"/>
+ <ns11:affiliation xmlns:ns11="https://schema.org/">
+ <rdf:Description>
+ <ns10:name>TenForce</ns10:name>
+ </rdf:Description>
+ </ns11:affiliation>
+ </ns10:Person>
+ </ns9:editor>
+ <ns12:editor xmlns:ns12="http://www.w3.org/2001/02pd/rec54#">
+ <ns13:Person xmlns:ns13="http://xmlns.com/foaf/0.1/">
+ <ns13:firstName>Natasa</ns13:firstName>
+ <ns13:lastName>Sofou</ns13:lastName>
+ </ns13:Person>
+ </ns12:editor>
+ <ns14:editor xmlns:ns14="http://www.w3.org/2001/02pd/rec54#">
+ <ns15:Person xmlns:ns15="http://xmlns.com/foaf/0.1/">
+ <ns15:firstName>Pavlina</ns15:firstName>
+ <ns15:lastName>Fragkou</ns15:lastName>
+ <ns16:affiliation xmlns:ns16="https://schema.org/">
+ <rdf:Description>
+ <ns15:name>SEMIC EU</ns15:name>
+ </rdf:Description>
+ </ns16:affiliation>
+ </ns15:Person>
+ </ns14:editor>
+ <ns17:editor xmlns:ns17="http://www.w3.org/2001/02pd/rec54#">
+ <ns18:Person xmlns:ns18="http://xmlns.com/foaf/0.1/">
+ <ns18:firstName>Makx</ns18:firstName>
+ <ns18:lastName>Dekkers</ns18:lastName>
+ </ns18:Person>
+ </ns17:editor>
+ <ns19:maker xmlns:ns19="http://xmlns.com/foaf/0.1/">
+ <ns19:Person>
+ <ns19:firstName>Pavlina</ns19:firstName>
+ <ns19:lastName>Fragkou</ns19:lastName>
+ <ns20:affiliation xmlns:ns20="https://schema.org/">
+ <rdf:Description>
+ <ns19:name>SEMIC EU</ns19:name>
+ </rdf:Description>
+ </ns20:affiliation>
+ </ns19:Person>
+ </ns19:maker>
+ </ns1:Ontology>
+ <ns21:Class xmlns:ns21="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#Asset">
+ <ns22:comment xmlns:ns22="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">An abstract entity that reflects the intellectual content of the asset and represents those characteristics of the asset that are independent of its physical embodiment. This abstract entity combines the FRBR entities work (a distinct intellectual or artistic creation) and expression (the intellectual or artistic realization of a work)</ns22:comment>
+ <ns23:isDefinedBy xmlns:ns23="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns24:label xmlns:ns24="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">Asset</ns24:label>
+ </ns21:Class>
+ <ns25:Class xmlns:ns25="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#AssetDistribution">
+ <ns26:comment xmlns:ns26="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">A particular physical embodiment of an Asset, which is an example of the FRBR entity manifestation (the physical embodiment of an expression of a work).</ns26:comment>
+ <ns27:isDefinedBy xmlns:ns27="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns28:label xmlns:ns28="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">Asset Distribution</ns28:label>
+ </ns25:Class>
+ <ns29:Class xmlns:ns29="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#AssetRepository">
+ <ns30:comment xmlns:ns30="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">A system or service that provides facilities for storage and maintenance of descriptions of Assets and Asset Distributions, and functionality that allows users to search and access these descriptions. An Asset Repository will typically contain descriptions of several Assets and related Asset Distributions.</ns30:comment>
+ <ns31:isDefinedBy xmlns:ns31="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns32:label xmlns:ns32="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">Asset repository</ns32:label>
+ </ns29:Class>
+ <ns33:Class xmlns:ns33="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#Identifier">
+ <ns34:comment xmlns:ns34="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">This is based on the UN/CEFACT Identifier class.</ns34:comment>
+ <ns35:isDefinedBy xmlns:ns35="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns36:label xmlns:ns36="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">Identifier</ns36:label>
+ </ns33:Class>
+ <ns37:ObjectProperty xmlns:ns37="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#identifier">
+ <ns38:comment xmlns:ns38="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">Links a resource to an adms:Identifier class.</ns38:comment>
+ <ns39:domain xmlns:ns39="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns40:isDefinedBy xmlns:ns40="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns41:label xmlns:ns41="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">identifier</ns41:label>
+ <ns42:range xmlns:ns42="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms#Identifier"/>
+ </ns37:ObjectProperty>
+ <ns43:ObjectProperty xmlns:ns43="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#includedAsset">
+ <ns44:comment xmlns:ns44="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">An Asset that is contained in the Asset being described, e.g. when there are several vocabularies defined in a single document.</ns44:comment>
+ <ns45:domain xmlns:ns45="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms#Asset"/>
+ <ns46:isDefinedBy xmlns:ns46="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns47:label xmlns:ns47="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">included asset</ns47:label>
+ <ns48:range xmlns:ns48="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms#Asset"/>
+ </ns43:ObjectProperty>
+ <ns49:ObjectProperty xmlns:ns49="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#interoperabilityLevel">
+ <ns50:comment xmlns:ns50="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">The interoperability level for which the Asset is relevant.</ns50:comment>
+ <ns51:domain xmlns:ns51="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms#Asset"/>
+ <ns52:isDefinedBy xmlns:ns52="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns53:label xmlns:ns53="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">interoperability level</ns53:label>
+ <ns54:range xmlns:ns54="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2004/02/skos/core#Concept"/>
+ </ns49:ObjectProperty>
+ <ns55:ObjectProperty xmlns:ns55="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#last">
+ <ns56:comment xmlns:ns56="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">A link to the current or latest version of the Asset.</ns56:comment>
+ <ns57:domain xmlns:ns57="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns58:isDefinedBy xmlns:ns58="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns59:label xmlns:ns59="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">last</ns59:label>
+ <ns60:range xmlns:ns60="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns61:subPropertyOf xmlns:ns61="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/1999/xhtml/vocab#last"/>
+ </ns55:ObjectProperty>
+ <ns62:ObjectProperty xmlns:ns62="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#next">
+ <ns63:comment xmlns:ns63="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">A link to the next version of the Asset.</ns63:comment>
+ <ns64:domain xmlns:ns64="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns65:isDefinedBy xmlns:ns65="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns66:label xmlns:ns66="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">next</ns66:label>
+ <ns67:range xmlns:ns67="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns68:subPropertyOf xmlns:ns68="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/1999/xhtml/vocab#next"/>
+ </ns62:ObjectProperty>
+ <ns69:ObjectProperty xmlns:ns69="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#prev">
+ <ns70:comment xmlns:ns70="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">A link to the previous version of the Asset.</ns70:comment>
+ <ns71:domain xmlns:ns71="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns72:isDefinedBy xmlns:ns72="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns73:label xmlns:ns73="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">prev</ns73:label>
+ <ns74:range xmlns:ns74="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns75:subPropertyOf xmlns:ns75="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/1999/xhtml/vocab#prev"/>
+ </ns69:ObjectProperty>
+ <ns76:ObjectProperty xmlns:ns76="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#representationTechnique">
+ <ns77:comment xmlns:ns77="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">More information about the format in which an Asset Distribution is released. This is different from the file format as, for example, a ZIP file (file format) could contain an XML schema (representation technique).</ns77:comment>
+ <ns78:domain xmlns:ns78="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns79:isDefinedBy xmlns:ns79="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns80:label xmlns:ns80="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">representation technique</ns80:label>
+ <ns81:range xmlns:ns81="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2004/02/skos/core#Concept"/>
+ </ns76:ObjectProperty>
+ <ns82:ObjectProperty xmlns:ns82="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#sample">
+ <ns83:comment xmlns:ns83="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">Links to a sample of an Asset (which is itself an Asset).</ns83:comment>
+ <ns84:domain xmlns:ns84="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns85:isDefinedBy xmlns:ns85="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns86:label xmlns:ns86="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">sample</ns86:label>
+ <ns87:range xmlns:ns87="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ </ns82:ObjectProperty>
+ <ns88:DatatypeProperty xmlns:ns88="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#schemaAgency">
+ <ns89:comment xmlns:ns89="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">The name of the agency that issued the identifier.</ns89:comment>
+ <ns90:domain xmlns:ns90="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms#Identifier"/>
+ <ns91:isDefinedBy xmlns:ns91="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns92:label xmlns:ns92="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">schema agency</ns92:label>
+ <ns93:range xmlns:ns93="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
+ </ns88:DatatypeProperty>
+ <ns94:ObjectProperty xmlns:ns94="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#status">
+ <ns95:comment xmlns:ns95="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">The status of the Asset in the context of a particular workflow process.</ns95:comment>
+ <ns96:domain xmlns:ns96="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns97:isDefinedBy xmlns:ns97="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns98:label xmlns:ns98="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">status</ns98:label>
+ <ns99:range xmlns:ns99="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2004/02/skos/core#Concept"/>
+ </ns94:ObjectProperty>
+ <ns100:ObjectProperty xmlns:ns100="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#supportedSchema">
+ <ns101:comment xmlns:ns101="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">A schema according to which the Asset Repository can provide data about its content, e.g. ADMS.</ns101:comment>
+ <ns102:domain xmlns:ns102="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns103:isDefinedBy xmlns:ns103="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns104:label xmlns:ns104="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">supported schema</ns104:label>
+ <ns105:range xmlns:ns105="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms#Asset"/>
+ </ns100:ObjectProperty>
+ <ns106:ObjectProperty xmlns:ns106="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#translation">
+ <ns107:comment xmlns:ns107="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">Links Assets that are translations of each other.</ns107:comment>
+ <ns108:domain xmlns:ns108="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns109:isDefinedBy xmlns:ns109="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns110:label xmlns:ns110="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">translation</ns110:label>
+ <ns111:range xmlns:ns111="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ </ns106:ObjectProperty>
+ <ns112:DatatypeProperty xmlns:ns112="http://www.w3.org/2002/07/owl#"
+ rdf:about="http://www.w3.org/ns/adms#versionNotes">
+ <ns113:comment xmlns:ns113="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">A description of changes between this version and the previous version of the Asset.</ns113:comment>
+ <ns114:domain xmlns:ns114="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <ns115:isDefinedBy xmlns:ns115="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/ns/adms"/>
+ <ns116:label xmlns:ns116="http://www.w3.org/2000/01/rdf-schema#"
+ xml:lang="en">version notes</ns116:label>
+ <ns117:range xmlns:ns117="http://www.w3.org/2000/01/rdf-schema#"
+ rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
+ </ns112:DatatypeProperty>
+</rdf:RDF>
diff --git a/test/data/defined_namespaces/adms.ttl b/test/data/defined_namespaces/adms.ttl
new file mode 100644
index 00000000..86561101
--- /dev/null
+++ b/test/data/defined_namespaces/adms.ttl
@@ -0,0 +1,175 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+
+<http://www.w3.org/ns/adms>
+ <http://purl.org/dc/terms/issued> "2023-04-05" ;
+ <http://purl.org/dc/terms/license> <https://creativecommons.org/licenses/by/4.0/> ;
+ <http://purl.org/dc/terms/mediator> [
+ <http://xmlns.com/foaf/0.1/homepage> <https://semic.eu> ;
+ <http://xmlns.com/foaf/0.1/name> "Semantic Interoperability Community (SEMIC)"
+ ] ;
+ a <http://www.w3.org/2002/07/owl#Ontology> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "adms"@en, "adms"@nl ;
+ <http://www.w3.org/2001/02pd/rec54#editor> [
+ a <http://xmlns.com/foaf/0.1/Person> ;
+ <http://xmlns.com/foaf/0.1/firstName> "Bert" ;
+ <http://xmlns.com/foaf/0.1/lastName> "Van Nuffelen" ;
+ <http://xmlns.com/foaf/0.1/mbox> <mailto:bert.van.nuffelen@tenforce.com> ;
+ <https://schema.org/affiliation> [
+ <http://xmlns.com/foaf/0.1/name> "TenForce"
+ ]
+ ], [
+ a <http://xmlns.com/foaf/0.1/Person> ;
+ <http://xmlns.com/foaf/0.1/firstName> "Natasa" ;
+ <http://xmlns.com/foaf/0.1/lastName> "Sofou"
+ ], [
+ a <http://xmlns.com/foaf/0.1/Person> ;
+ <http://xmlns.com/foaf/0.1/firstName> "Pavlina" ;
+ <http://xmlns.com/foaf/0.1/lastName> "Fragkou" ;
+ <https://schema.org/affiliation> [
+ <http://xmlns.com/foaf/0.1/name> "SEMIC EU"
+ ]
+ ], [
+ a <http://xmlns.com/foaf/0.1/Person> ;
+ <http://xmlns.com/foaf/0.1/firstName> "Makx" ;
+ <http://xmlns.com/foaf/0.1/lastName> "Dekkers"
+ ] ;
+ <http://xmlns.com/foaf/0.1/maker> [
+ a <http://xmlns.com/foaf/0.1/Person> ;
+ <http://xmlns.com/foaf/0.1/firstName> "Pavlina" ;
+ <http://xmlns.com/foaf/0.1/lastName> "Fragkou" ;
+ <https://schema.org/affiliation> [
+ <http://xmlns.com/foaf/0.1/name> "SEMIC EU"
+ ]
+ ] .
+
+<http://www.w3.org/ns/adms#Asset>
+ a <http://www.w3.org/2002/07/owl#Class> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "An abstract entity that reflects the intellectual content of the asset and represents those characteristics of the asset that are independent of its physical embodiment. This abstract entity combines the FRBR entities work (a distinct intellectual or artistic creation) and expression (the intellectual or artistic realization of a work)"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "Asset"@en .
+
+<http://www.w3.org/ns/adms#AssetDistribution>
+ a <http://www.w3.org/2002/07/owl#Class> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "A particular physical embodiment of an Asset, which is an example of the FRBR entity manifestation (the physical embodiment of an expression of a work)."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "Asset Distribution"@en .
+
+<http://www.w3.org/ns/adms#AssetRepository>
+ a <http://www.w3.org/2002/07/owl#Class> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "A system or service that provides facilities for storage and maintenance of descriptions of Assets and Asset Distributions, and functionality that allows users to search and access these descriptions. An Asset Repository will typically contain descriptions of several Assets and related Asset Distributions."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "Asset repository"@en .
+
+<http://www.w3.org/ns/adms#Identifier>
+ a <http://www.w3.org/2002/07/owl#Class> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "This is based on the UN/CEFACT Identifier class."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "Identifier"@en .
+
+<http://www.w3.org/ns/adms#identifier>
+ a <http://www.w3.org/2002/07/owl#ObjectProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "Links a resource to an adms:Identifier class."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "identifier"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/ns/adms#Identifier> .
+
+<http://www.w3.org/ns/adms#includedAsset>
+ a <http://www.w3.org/2002/07/owl#ObjectProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "An Asset that is contained in the Asset being described, e.g. when there are several vocabularies defined in a single document."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/ns/adms#Asset> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "included asset"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/ns/adms#Asset> .
+
+<http://www.w3.org/ns/adms#interoperabilityLevel>
+ a <http://www.w3.org/2002/07/owl#ObjectProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "The interoperability level for which the Asset is relevant."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/ns/adms#Asset> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "interoperability level"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2004/02/skos/core#Concept> .
+
+<http://www.w3.org/ns/adms#last>
+ a <http://www.w3.org/2002/07/owl#ObjectProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "A link to the current or latest version of the Asset."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "last"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/1999/xhtml/vocab#last> .
+
+<http://www.w3.org/ns/adms#next>
+ a <http://www.w3.org/2002/07/owl#ObjectProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "A link to the next version of the Asset."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "next"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/1999/xhtml/vocab#next> .
+
+<http://www.w3.org/ns/adms#prev>
+ a <http://www.w3.org/2002/07/owl#ObjectProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "A link to the previous version of the Asset."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "prev"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/1999/xhtml/vocab#prev> .
+
+<http://www.w3.org/ns/adms#representationTechnique>
+ a <http://www.w3.org/2002/07/owl#ObjectProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "More information about the format in which an Asset Distribution is released. This is different from the file format as, for example, a ZIP file (file format) could contain an XML schema (representation technique)."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "representation technique"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2004/02/skos/core#Concept> .
+
+<http://www.w3.org/ns/adms#sample>
+ a <http://www.w3.org/2002/07/owl#ObjectProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "Links to a sample of an Asset (which is itself an Asset)."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "sample"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Resource> .
+
+<http://www.w3.org/ns/adms#schemaAgency>
+ a <http://www.w3.org/2002/07/owl#DatatypeProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "The name of the agency that issued the identifier."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/ns/adms#Identifier> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "schema agency"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Literal> .
+
+<http://www.w3.org/ns/adms#status>
+ a <http://www.w3.org/2002/07/owl#ObjectProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "The status of the Asset in the context of a particular workflow process."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "status"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2004/02/skos/core#Concept> .
+
+<http://www.w3.org/ns/adms#supportedSchema>
+ a <http://www.w3.org/2002/07/owl#ObjectProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "A schema according to which the Asset Repository can provide data about its content, e.g. ADMS."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "supported schema"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/ns/adms#Asset> .
+
+<http://www.w3.org/ns/adms#translation>
+ a <http://www.w3.org/2002/07/owl#ObjectProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "Links Assets that are translations of each other."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "translation"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Resource> .
+
+<http://www.w3.org/ns/adms#versionNotes>
+ a <http://www.w3.org/2002/07/owl#DatatypeProperty> ;
+ <http://www.w3.org/2000/01/rdf-schema#comment> "A description of changes between this version and the previous version of the Asset."@en ;
+ <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> ;
+ <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/ns/adms> ;
+ <http://www.w3.org/2000/01/rdf-schema#label> "version notes"@en ;
+ <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Literal> .
+
diff --git a/test/data/defined_namespaces/rdfs.rdf b/test/data/defined_namespaces/rdfs.rdf
new file mode 100644
index 00000000..bf17bab0
--- /dev/null
+++ b/test/data/defined_namespaces/rdfs.rdf
@@ -0,0 +1,130 @@
+<rdf:RDF
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+ xmlns:owl="http://www.w3.org/2002/07/owl#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+
+ <owl:Ontology
+ rdf:about="http://www.w3.org/2000/01/rdf-schema#"
+ dc:title="The RDF Schema vocabulary (RDFS)"/>
+
+<rdfs:Class rdf:about="http://www.w3.org/2000/01/rdf-schema#Resource">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>Resource</rdfs:label>
+ <rdfs:comment>The class resource, everything.</rdfs:comment>
+</rdfs:Class>
+
+<rdfs:Class rdf:about="http://www.w3.org/2000/01/rdf-schema#Class">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>Class</rdfs:label>
+ <rdfs:comment>The class of classes.</rdfs:comment>
+ <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+</rdfs:Class>
+
+<rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#subClassOf">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>subClassOf</rdfs:label>
+ <rdfs:comment>The subject is a subclass of a class.</rdfs:comment>
+ <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Class"/>
+ <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Class"/>
+</rdf:Property>
+
+<rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#subPropertyOf">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>subPropertyOf</rdfs:label>
+ <rdfs:comment>The subject is a subproperty of a property.</rdfs:comment>
+ <rdfs:range rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
+ <rdfs:domain rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
+</rdf:Property>
+
+<rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#comment">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>comment</rdfs:label>
+ <rdfs:comment>A description of the subject resource.</rdfs:comment>
+ <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
+</rdf:Property>
+
+<rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#label">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>label</rdfs:label>
+ <rdfs:comment>A human-readable name for the subject.</rdfs:comment>
+ <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
+</rdf:Property>
+
+<rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#domain">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>domain</rdfs:label>
+ <rdfs:comment>A domain of the subject property.</rdfs:comment>
+ <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Class"/>
+ <rdfs:domain rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
+</rdf:Property>
+
+<rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#range">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>range</rdfs:label>
+ <rdfs:comment>A range of the subject property.</rdfs:comment>
+ <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Class"/>
+ <rdfs:domain rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
+</rdf:Property>
+
+<rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#seeAlso">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>seeAlso</rdfs:label>
+ <rdfs:comment>Further information about the subject resource.</rdfs:comment>
+ <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+</rdf:Property>
+
+<rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#isDefinedBy">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:subPropertyOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#seeAlso"/>
+ <rdfs:label>isDefinedBy</rdfs:label>
+ <rdfs:comment>The defininition of the subject resource.</rdfs:comment>
+ <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+</rdf:Property>
+
+<rdfs:Class rdf:about="http://www.w3.org/2000/01/rdf-schema#Literal">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>Literal</rdfs:label>
+ <rdfs:comment>The class of literal values, eg. textual strings and integers.</rdfs:comment>
+ <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+</rdfs:Class>
+
+<rdfs:Class rdf:about="http://www.w3.org/2000/01/rdf-schema#Container">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>Container</rdfs:label>
+ <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <rdfs:comment>The class of RDF containers.</rdfs:comment>
+</rdfs:Class>
+
+<rdfs:Class rdf:about="http://www.w3.org/2000/01/rdf-schema#ContainerMembershipProperty">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>ContainerMembershipProperty</rdfs:label>
+ <rdfs:comment>The class of container membership properties, rdf:_1, rdf:_2, ...,
+ all of which are sub-properties of 'member'.</rdfs:comment>
+ <rdfs:subClassOf rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
+</rdfs:Class>
+
+<rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#member">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>member</rdfs:label>
+ <rdfs:comment>A member of the subject resource.</rdfs:comment>
+ <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+ <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
+</rdf:Property>
+
+<rdfs:Class rdf:about="http://www.w3.org/2000/01/rdf-schema#Datatype">
+ <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
+ <rdfs:label>Datatype</rdfs:label>
+ <rdfs:comment>The class of RDF datatypes.</rdfs:comment>
+ <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Class"/>
+</rdfs:Class>
+
+<rdf:Description rdf:about="http://www.w3.org/2000/01/rdf-schema#">
+ <rdfs:seeAlso rdf:resource="http://www.w3.org/2000/01/rdf-schema-more"/>
+</rdf:Description>
+
+</rdf:RDF>
diff --git a/test/data/fetcher.py b/test/data/fetcher.py
index 7c9e4ff0..1ea8e337 100755
--- a/test/data/fetcher.py
+++ b/test/data/fetcher.py
@@ -249,6 +249,21 @@ RESOURCES: List[Resource] = [
local_path=(DATA_PATH / "defined_namespaces/rdfs.ttl"),
),
FileResource(
+ remote=Request(
+ "http://www.w3.org/2000/01/rdf-schema#",
+ headers={"Accept": "application/rdf+xml"},
+ ),
+ local_path=(DATA_PATH / "defined_namespaces/rdfs.rdf"),
+ ),
+ FileResource(
+ remote=Request("http://www.w3.org/ns/adms.rdf"),
+ local_path=(DATA_PATH / "defined_namespaces/adms.rdf"),
+ ),
+ FileResource(
+ remote=Request("http://www.w3.org/ns/adms.ttl"),
+ local_path=(DATA_PATH / "defined_namespaces/adms.ttl"),
+ ),
+ FileResource(
remote=Request("https://www.w3.org/ns/rdftest.ttl"),
local_path=(DATA_PATH / "defined_namespaces/rdftest.ttl"),
),
diff --git a/test/test_graph/test_graph.py b/test/test_graph/test_graph.py
index b133c2b5..289d577a 100644
--- a/test/test_graph/test_graph.py
+++ b/test/test_graph/test_graph.py
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
import logging
import os
+from contextlib import ExitStack
from pathlib import Path
from test.data import TEST_DATA_DIR, bob, cheese, hates, likes, michel, pizza, tarek
from test.utils import GraphHelper, get_unique_plugin_names
+from test.utils.exceptions import ExceptionChecker
from test.utils.httpfileserver import HTTPFileServer, ProtoFileResource
-from typing import Callable, Optional, Set
+from typing import Callable, Optional, Set, Tuple, Union
from urllib.error import HTTPError, URLError
import pytest
@@ -342,14 +344,6 @@ def test_guess_format_for_parse(
# only getting HTML
with pytest.raises(PluginException):
graph.parse(location=file_info.request_url)
-
- try:
- graph.parse(location="http://www.w3.org/ns/adms.ttl")
- graph.parse(location="http://www.w3.org/ns/adms.rdf")
- except (URLError, HTTPError):
- # this endpoint is currently not available, ignore this test.
- pass
-
try:
# persistent Australian Government online RDF resource without a file-like ending
graph.parse(location="https://linked.data.gov.au/def/agrif?_format=text/turtle")
@@ -358,6 +352,55 @@ def test_guess_format_for_parse(
pass
+@pytest.mark.parametrize(
+ ("file", "content_type", "expected_result"),
+ (
+ (TEST_DATA_DIR / "defined_namespaces/adms.rdf", "application/rdf+xml", 132),
+ (TEST_DATA_DIR / "defined_namespaces/adms.ttl", "text/turtle", 132),
+ (TEST_DATA_DIR / "defined_namespaces/adms.ttl", None, 132),
+ (
+ TEST_DATA_DIR / "defined_namespaces/adms.rdf",
+ None,
+ ExceptionChecker(
+ ParserError,
+ r"Could not guess RDF format .* from file extension so tried Turtle",
+ ),
+ ),
+ ),
+)
+def test_guess_format_for_parse_http(
+ make_graph: GraphFactory,
+ http_file_server: HTTPFileServer,
+ file: Path,
+ content_type: Optional[str],
+ expected_result: Union[int, ExceptionChecker],
+) -> None:
+ graph = make_graph()
+ headers: Tuple[Tuple[str, str], ...] = tuple()
+ if content_type is not None:
+ headers = (("Content-Type", content_type),)
+
+ file_info = http_file_server.add_file_with_caching(
+ ProtoFileResource(headers, file),
+ suffix=f"/{file.name}",
+ )
+ catcher: Optional[pytest.ExceptionInfo[Exception]] = None
+
+ assert 0 == len(graph)
+ with ExitStack() as exit_stack:
+ if isinstance(expected_result, ExceptionChecker):
+ catcher = exit_stack.enter_context(pytest.raises(expected_result.type))
+ graph.parse(location=file_info.request_url)
+
+ if catcher is not None:
+ # assert catcher.value is not None
+ assert isinstance(expected_result, ExceptionChecker)
+ logging.debug("graph = %s", list(graph.triples((None, None, None))))
+ else:
+ assert isinstance(expected_result, int)
+ assert expected_result == len(graph)
+
+
def test_parse_file_uri(make_graph: GraphFactory):
EG = Namespace("http://example.org/#")
g = make_graph()
diff --git a/test/test_graph/test_graph_redirect.py b/test/test_graph/test_graph_redirect.py
new file mode 100644
index 00000000..c61adbc5
--- /dev/null
+++ b/test/test_graph/test_graph_redirect.py
@@ -0,0 +1,45 @@
+from test.data import TEST_DATA_DIR, simple_triple_graph
+from test.utils import GraphHelper
+from test.utils.http import MethodName, MockHTTPResponse
+from test.utils.httpservermock import ServedBaseHTTPServerMock
+from typing import Tuple
+from urllib.parse import urlparse
+
+from rdflib.graph import Graph
+
+
+def test_graph_redirect_new_host(
+ function_httpmocks: Tuple[ServedBaseHTTPServerMock, ServedBaseHTTPServerMock]
+) -> None:
+ """
+ Redirect to new host results in a request with the right Host header
+ parameter.
+ """
+
+ mock_a, mock_b = function_httpmocks
+
+ mock_a.responses[MethodName.GET].append(
+ MockHTTPResponse(
+ 308,
+ "Permanent Redirect",
+ b"",
+ {"Location": [f"{mock_b.url}/b/data.ttl"]},
+ )
+ )
+
+ mock_b.responses[MethodName.GET].append(
+ MockHTTPResponse(
+ 200,
+ "OK",
+ (TEST_DATA_DIR / "variants" / "simple_triple.ttl").read_bytes(),
+ {"Content-Type": ["text/turtle"]},
+ )
+ )
+
+ graph = Graph()
+ graph.parse(location=f"{mock_a.url}/a/data.ttl")
+ GraphHelper.assert_sets_equals(graph, simple_triple_graph)
+ for mock in function_httpmocks:
+ assert 1 == len(mock.requests[MethodName.GET])
+ for request in mock.requests[MethodName.GET]:
+ assert request.headers["Host"] == urlparse(mock.url).netloc
diff --git a/test/test_misc/test_input_source.py b/test/test_misc/test_input_source.py
index f3da062b..90e6e238 100644
--- a/test/test_misc/test_input_source.py
+++ b/test/test_misc/test_input_source.py
@@ -11,6 +11,7 @@ from dataclasses import dataclass
# from itertools import product
from pathlib import Path
from test.utils import GraphHelper
+from test.utils.exceptions import ExceptionChecker
from test.utils.httpfileserver import (
HTTPFileInfo,
HTTPFileServer,
@@ -27,7 +28,6 @@ from typing import ( # Callable,
Generic,
Iterable,
Optional,
- Pattern,
TextIO,
Tuple,
Type,
@@ -251,21 +251,6 @@ def call_create_input_source(
yield input_source
-@dataclass
-class ExceptionChecker:
- type: Type[Exception]
- pattern: Optional[Pattern[str]] = None
-
- def check(self, exception: Exception) -> None:
- try:
- assert isinstance(exception, self.type)
- if self.pattern is not None:
- assert self.pattern.match(f"{exception}")
- except Exception:
- logging.error("problem checking exception", exc_info=exception)
- raise
-
-
AnyT = TypeVar("AnyT")
diff --git a/test/test_misc/test_networking_redirect.py b/test/test_misc/test_networking_redirect.py
new file mode 100644
index 00000000..acde10d7
--- /dev/null
+++ b/test/test_misc/test_networking_redirect.py
@@ -0,0 +1,217 @@
+from contextlib import ExitStack
+from copy import deepcopy
+from test.utils.exceptions import ExceptionChecker
+from test.utils.http import headers_as_message as headers_as_message
+from typing import Any, Dict, Iterable, Optional, Type, TypeVar, Union
+from urllib.error import HTTPError
+from urllib.request import HTTPRedirectHandler, Request
+
+import pytest
+from _pytest.mark.structures import ParameterSet
+
+from rdflib._networking import _make_redirect_request
+
+AnyT = TypeVar("AnyT")
+
+
+def with_attrs(object: AnyT, **kwargs: Any) -> AnyT:
+ for key, value in kwargs.items():
+ setattr(object, key, value)
+ return object
+
+
+class RaisesIdentity:
+ pass
+
+
+def generate_make_redirect_request_cases() -> Iterable[ParameterSet]:
+ yield pytest.param(
+ Request("http://example.com/data.ttl"),
+ HTTPError(
+ "",
+ 308,
+ "Permanent Redirect",
+ headers_as_message({}),
+ None,
+ ),
+ RaisesIdentity,
+ {},
+ id="Exception passes through if no Location header is present",
+ )
+ yield pytest.param(
+ Request("http://example.com/data.ttl"),
+ HTTPError(
+ "",
+ 308,
+ "Permanent Redirect",
+ headers_as_message({"Location": [100]}), # type: ignore[arg-type]
+ None,
+ ),
+ ExceptionChecker(ValueError, "Location header 100 is not a string"),
+ {},
+ id="Location must be a string",
+ )
+ yield pytest.param(
+ Request("http://example.com/data.ttl"),
+ HTTPError(
+ "",
+ 308,
+ "Permanent Redirect",
+ headers_as_message({"Location": ["example:data.ttl"]}),
+ None,
+ ),
+ ExceptionChecker(
+ HTTPError,
+ "HTTP Error 308: Permanent Redirect - Redirection to url 'example:data.ttl' is not allowed",
+ {"code": 308},
+ ),
+ {},
+ id="Error passes through with a slight alterations if the Location header is not a supported URL",
+ )
+
+ url_prefix = "http://example.com"
+ for request_url_suffix, redirect_location, new_url_suffix in [
+ ("/data.ttl", "", "/data.ttl"),
+ ("", "", ""),
+ ("/data.ttl", "a", "/a"),
+ ("", "a", "/a"),
+ ("/a/b/c/", ".", "/a/b/c/"),
+ ("/a/b/c", ".", "/a/b/"),
+ ("/a/b/c/", "..", "/a/b/"),
+ ("/a/b/c", "..", "/a/"),
+ ("/a/b/c/", "/", "/"),
+ ("/a/b/c/", "/x/", "/x/"),
+ ("/a/b/c/", "/x/y", "/x/y"),
+ ("/a/b/c/", f"{url_prefix}", ""),
+ ("/a/b/c/", f"{url_prefix}/", "/"),
+ ("/a/b/c/", f"{url_prefix}/a/../b", "/a/../b"),
+ ("/", f"{url_prefix}/ /data.ttl", "/%20%20%20/data.ttl"),
+ ]:
+ request_url = f"http://example.com{request_url_suffix}"
+ new_url = f"http://example.com{new_url_suffix}"
+ yield pytest.param(
+ Request(request_url),
+ HTTPError(
+ "",
+ 308,
+ "Permanent Redirect",
+ headers_as_message({"Location": [redirect_location]}),
+ None,
+ ),
+ Request(new_url, unverifiable=True),
+ {new_url: 1},
+ id=f"Redirect from {request_url!r} to {redirect_location!r} is correctly handled",
+ )
+
+ yield pytest.param(
+ Request(
+ "http://example.com/data.ttl",
+ b"foo",
+ headers={
+ "Content-Type": "text/plain",
+ "Content-Length": "3",
+ "Accept": "text/turtle",
+ },
+ ),
+ HTTPError(
+ "",
+ 308,
+ "Permanent Redirect",
+ headers_as_message({"Location": ["http://example.org/data.ttl"]}),
+ None,
+ ),
+ Request(
+ "http://example.org/data.ttl",
+ headers={"Accept": "text/turtle"},
+ origin_req_host="example.com",
+ unverifiable=True,
+ ),
+ {"http://example.org/data.ttl": 1},
+ id="Headers transfer correctly",
+ )
+
+ yield pytest.param(
+ with_attrs(
+ Request(
+ "http://example.com/data1.ttl",
+ ),
+ redirect_dict=dict(
+ (f"http://example.com/redirect/{index}", 1)
+ for index in range(HTTPRedirectHandler.max_redirections)
+ ),
+ ),
+ HTTPError(
+ "",
+ 308,
+ "Permanent Redirect",
+ headers_as_message({"Location": ["http://example.org/data2.ttl"]}),
+ None,
+ ),
+ ExceptionChecker(
+ HTTPError,
+ f"HTTP Error 308: {HTTPRedirectHandler.inf_msg}Permanent Redirect",
+ ),
+ {},
+ id="Max redirects is respected",
+ )
+
+ yield pytest.param(
+ with_attrs(
+ Request(
+ "http://example.com/data1.ttl",
+ ),
+ redirect_dict={
+ "http://example.org/data2.ttl": HTTPRedirectHandler.max_repeats
+ },
+ ),
+ HTTPError(
+ "",
+ 308,
+ "Permanent Redirect",
+ headers_as_message({"Location": ["http://example.org/data2.ttl"]}),
+ None,
+ ),
+ ExceptionChecker(
+ HTTPError,
+ f"HTTP Error 308: {HTTPRedirectHandler.inf_msg}Permanent Redirect",
+ ),
+ {},
+ id="Max repeats is respected",
+ )
+
+
+@pytest.mark.parametrize(
+ ("http_request", "http_error", "expected_result", "expected_redirect_dict"),
+ generate_make_redirect_request_cases(),
+)
+def test_make_redirect_request(
+ http_request: Request,
+ http_error: HTTPError,
+ expected_result: Union[Type[RaisesIdentity], ExceptionChecker, Request],
+ expected_redirect_dict: Dict[str, int],
+) -> None:
+ """
+ `_make_redirect_request` correctly handles redirects.
+ """
+ catcher: Optional[pytest.ExceptionInfo[Exception]] = None
+ result: Optional[Request] = None
+ with ExitStack() as stack:
+ if isinstance(expected_result, ExceptionChecker):
+ catcher = stack.enter_context(pytest.raises(expected_result.type))
+ elif expected_result is RaisesIdentity:
+ catcher = stack.enter_context(pytest.raises(HTTPError))
+ result = _make_redirect_request(http_request, http_error)
+
+ if isinstance(expected_result, ExceptionChecker):
+ assert catcher is not None
+ expected_result.check(catcher.value)
+ elif isinstance(expected_result, type):
+ assert catcher is not None
+ assert http_error is catcher.value
+ else:
+ assert expected_redirect_dict == getattr(result, "redirect_dict", None)
+ assert expected_redirect_dict == getattr(http_request, "redirect_dict", None)
+ check = deepcopy(expected_result)
+ check.unverifiable = True
+ check = with_attrs(check, redirect_dict=expected_redirect_dict)
+ assert vars(check) == vars(result)
diff --git a/test/utils/exceptions.py b/test/utils/exceptions.py
new file mode 100644
index 00000000..a814f9b4
--- /dev/null
+++ b/test/utils/exceptions.py
@@ -0,0 +1,29 @@
+import logging
+import re
+from dataclasses import dataclass
+from typing import Any, Dict, Optional, Pattern, Type, Union
+
+
+@dataclass(frozen=True)
+class ExceptionChecker:
+ type: Type[Exception]
+ pattern: Optional[Union[Pattern[str], str]] = None
+ attributes: Optional[Dict[str, Any]] = None
+
+ def check(self, exception: Exception) -> None:
+ logging.debug("checking exception %s/%r", type(exception), exception)
+ pattern = self.pattern
+ if pattern is not None and not isinstance(pattern, re.Pattern):
+ pattern = re.compile(pattern)
+ try:
+ assert isinstance(exception, self.type)
+ if pattern is not None:
+ assert pattern.match(f"{exception}")
+ if self.attributes is not None:
+ for key, value in self.attributes.items():
+ logging.debug("checking exception attribute %s=%r", key, value)
+ assert hasattr(exception, key)
+ assert getattr(exception, key) == value
+ except Exception:
+ logging.error("problem checking exception", exc_info=exception)
+ raise
diff --git a/test/utils/http.py b/test/utils/http.py
index fa13a2ed..e40d2a8c 100644
--- a/test/utils/http.py
+++ b/test/utils/http.py
@@ -108,3 +108,12 @@ def ctx_http_server(server: HTTPServerT) -> Iterator[HTTPServerT]:
server.shutdown()
server.socket.close()
server_thread.join()
+
+
+def headers_as_message(headers: HeadersT) -> email.message.Message:
+ message = email.message.Message()
+ for header, value in header_items(headers):
+ # This will append the value to any existing values for the header
+ # instead of replacing it.
+ message[header] = value
+ return message
diff --git a/test/utils/httpfileserver.py b/test/utils/httpfileserver.py
index 1989070a..49c92e80 100644
--- a/test/utils/httpfileserver.py
+++ b/test/utils/httpfileserver.py
@@ -74,7 +74,7 @@ class HTTPFileInfo:
:param effective_url: The URL that the file will be served from after
redirects.
:param redirects: A sequence of redirects that will be given to the client
- if it uses the ``request_url``. This sequence will terimate in the
+ if it uses the ``request_url``. This sequence will terminate in the
``effective_url``.
"""
@@ -128,15 +128,17 @@ class HTTPFileServer(HTTPServer):
self,
proto_file: ProtoFileResource,
proto_redirects: Optional[Sequence[ProtoRedirectResource]] = None,
+ suffix: str = "",
) -> HTTPFileInfo:
- return self.add_file(proto_file, proto_redirects)
+ return self.add_file(proto_file, proto_redirects, suffix)
def add_file(
self,
proto_file: ProtoFileResource,
proto_redirects: Optional[Sequence[ProtoRedirectResource]] = None,
+ suffix: str = "",
) -> HTTPFileInfo:
- url_path = f"/file/{uuid4().hex}"
+ url_path = f"/file/{uuid4().hex}{suffix}"
url = urljoin(self.url, url_path)
file_resource = FileResource(
url_path=url_path,
@@ -151,7 +153,7 @@ class HTTPFileServer(HTTPServer):
redirects: List[RedirectResource] = []
for proto_redirect in reversed(proto_redirects):
- redirect_url_path = f"/redirect/{uuid4().hex}"
+ redirect_url_path = f"/redirect/{uuid4().hex}{suffix}"
if proto_redirect.location_type == LocationType.URL:
location = url
elif proto_redirect.location_type == LocationType.ABSOLUTE_PATH: