From 2aa9604ec0569a947e0cb1fa4868ab90caaff0a4 Mon Sep 17 00:00:00 2001 From: Lukas Plank Date: Mon, 16 Dec 2024 11:47:00 +0100 Subject: [PATCH] test: test adaptions for QueryConstructor rewrite --- tests/data/models/dummy_model.py | 14 --- .../test_adapter_grouped_pagination.py | 80 ++++++++----- .../test_adapter_ungrouped_pagination.py | 1 + .../params}/count_query_parameters.py | 13 ++- .../test_query_constructor_items_query.py | 105 ++++++++++++++++++ ...l_bindings_mapper_model_bool_parameters.py | 16 +++ .../model_bindings_mapper_parameters.py | 14 ++- .../models/author_array_collection_model.py | 0 .../params}/models/author_work_title_model.py | 0 .../params}/models/basic_model.py | 0 .../params}/models/grouping_model.py | 0 .../params}/models/nested_grouping_model.py | 0 .../test_model_bindings_mapper.py | 6 +- .../test_model_bindings_mapper_model_bool.py | 2 +- tests/unit/test_construct_count_query.py | 36 ------ ...sad_path_get_bindings_from_query_result.py | 17 --- .../test_add_solution_modifier.py | 72 ++++++++++++ .../test_get_query_projection.py | 63 +++++++++++ .../test_inject_subquery.py | 6 +- .../test_remove_sparql_prefixes.py | 61 ++++++++++ .../test_replace_query_select_clause.py | 0 ...st_sad_path_replace_query_select_clause.py | 0 .../tests_utils/test_field_bindings_map.py | 25 +++++ tests/utils/utils.py | 11 ++ 24 files changed, 436 insertions(+), 106 deletions(-) delete mode 100644 tests/data/models/dummy_model.py create mode 100644 tests/tests_adapter/test_adapter_ungrouped_pagination.py rename tests/{data/parameters => tests_constructor/params}/count_query_parameters.py (90%) create mode 100644 tests/tests_constructor/test_query_constructor_items_query.py rename tests/{data/parameters => tests_mapper/params}/model_bindings_mapper_model_bool_parameters.py (88%) rename tests/{data/parameters => tests_mapper/params}/model_bindings_mapper_parameters.py (94%) rename tests/{data => tests_mapper/params}/models/author_array_collection_model.py (100%) rename tests/{data => tests_mapper/params}/models/author_work_title_model.py (100%) rename tests/{data => tests_mapper/params}/models/basic_model.py (100%) rename tests/{data => tests_mapper/params}/models/grouping_model.py (100%) rename tests/{data => tests_mapper/params}/models/nested_grouping_model.py (100%) delete mode 100644 tests/unit/test_construct_count_query.py delete mode 100644 tests/unit/test_sad_path_get_bindings_from_query_result.py create mode 100644 tests/unit/tests_sparql_utils/test_add_solution_modifier.py create mode 100644 tests/unit/tests_sparql_utils/test_get_query_projection.py rename tests/unit/{ => tests_sparql_utils}/test_inject_subquery.py (95%) create mode 100644 tests/unit/tests_sparql_utils/test_remove_sparql_prefixes.py rename tests/unit/{ => tests_sparql_utils}/test_replace_query_select_clause.py (100%) rename tests/unit/{ => tests_sparql_utils}/test_sad_path_replace_query_select_clause.py (100%) create mode 100644 tests/unit/tests_utils/test_field_bindings_map.py create mode 100644 tests/utils/utils.py diff --git a/tests/data/models/dummy_model.py b/tests/data/models/dummy_model.py deleted file mode 100644 index 041bf4d..0000000 --- a/tests/data/models/dummy_model.py +++ /dev/null @@ -1,14 +0,0 @@ -"""Simple dummy models e.g. for count query constructor testing.""" - -from pydantic import BaseModel -from rdfproxy import ConfigDict - - -class Dummy(BaseModel): - pass - - -class GroupedDummy(BaseModel): - model_config = ConfigDict(group_by="x") - - x: int diff --git a/tests/tests_adapter/test_adapter_grouped_pagination.py b/tests/tests_adapter/test_adapter_grouped_pagination.py index 7fd68a0..16832a8 100644 --- a/tests/tests_adapter/test_adapter_grouped_pagination.py +++ b/tests/tests_adapter/test_adapter_grouped_pagination.py @@ -1,5 +1,6 @@ """Basic tests for rdfproxy.SPARQLModelAdapter pagination with grouped models.""" +from functools import partial from typing import Annotated, Any, NamedTuple import pytest @@ -7,10 +8,12 @@ from pydantic import BaseModel from rdfproxy import ( ConfigDict, + HttpxStrategy, Page, QueryParameters, SPARQLBinding, SPARQLModelAdapter, + SPARQLWrapperStrategy, ) @@ -57,28 +60,43 @@ class Parent(BaseModel): children: list[Child] -binding_adapter = SPARQLModelAdapter( - target="https://graphdb.r11.eu/repositories/RELEVEN", - query=binding_query, - model=BindingParent, -) +@pytest.fixture() +def adapters(): + _adapter_base = partial( + SPARQLModelAdapter, + target="https://graphdb.r11.eu/repositories/RELEVEN", + query=query, + model=Parent, + ) -adapter = SPARQLModelAdapter( - target="https://graphdb.r11.eu/repositories/RELEVEN", - query=query, - model=Parent, -) + return [ + _adapter_base(sparql_strategy=HttpxStrategy), + _adapter_base(sparql_strategy=SPARQLWrapperStrategy), + ] + + +@pytest.fixture() +def binding_adapters(): + _binding_adapter_base = partial( + SPARQLModelAdapter, + target="https://graphdb.r11.eu/repositories/RELEVEN", + query=binding_query, + model=BindingParent, + ) + + return [ + _binding_adapter_base(sparql_strategy=HttpxStrategy), + _binding_adapter_base(sparql_strategy=SPARQLWrapperStrategy), + ] class AdapterParameter(NamedTuple): - adapter: SPARQLModelAdapter query_parameters: dict[str, Any] expected: Page -adapter_parameters = [ +binding_adapter_parameters = [ AdapterParameter( - adapter=binding_adapter, query_parameters={"page": 1, "size": 2}, expected=Page[BindingParent]( items=[ @@ -92,7 +110,6 @@ class AdapterParameter(NamedTuple): ), ), AdapterParameter( - adapter=binding_adapter, query_parameters={"page": 2, "size": 2}, expected=Page[BindingParent]( items=[{"parent": "z", "children": []}], @@ -103,7 +120,6 @@ class AdapterParameter(NamedTuple): ), ), AdapterParameter( - adapter=binding_adapter, query_parameters={"page": 1, "size": 1}, expected=Page[BindingParent]( items=[{"parent": "x", "children": [{"name": "foo"}]}], @@ -114,22 +130,21 @@ class AdapterParameter(NamedTuple): ), ), AdapterParameter( - adapter=binding_adapter, query_parameters={"page": 2, "size": 1}, expected=Page[BindingParent]( items=[{"parent": "y", "children": []}], page=2, size=1, total=3, pages=3 ), ), AdapterParameter( - adapter=binding_adapter, query_parameters={"page": 3, "size": 1}, expected=Page[BindingParent]( items=[{"parent": "z", "children": []}], page=3, size=1, total=3, pages=3 ), ), - # +] +# +adapter_parameters = [ AdapterParameter( - adapter=adapter, query_parameters={"page": 1, "size": 2}, expected=Page[Parent]( items=[ @@ -143,7 +158,6 @@ class AdapterParameter(NamedTuple): ), ), AdapterParameter( - adapter=adapter, query_parameters={"page": 2, "size": 2}, expected=Page[Parent]( items=[{"parent": "z", "children": []}], @@ -154,7 +168,6 @@ class AdapterParameter(NamedTuple): ), ), AdapterParameter( - adapter=adapter, query_parameters={"page": 1, "size": 1}, expected=Page[Parent]( items=[{"parent": "x", "children": [{"name": "foo"}]}], @@ -165,14 +178,12 @@ class AdapterParameter(NamedTuple): ), ), AdapterParameter( - adapter=adapter, query_parameters={"page": 2, "size": 1}, expected=Page[Parent]( items=[{"parent": "y", "children": []}], page=2, size=1, total=3, pages=3 ), ), AdapterParameter( - adapter=adapter, query_parameters={"page": 3, "size": 1}, expected=Page[Parent]( items=[{"parent": "z", "children": []}], page=3, size=1, total=3, pages=3 @@ -183,8 +194,23 @@ class AdapterParameter(NamedTuple): @pytest.mark.remote @pytest.mark.parametrize( - ["adapter", "query_parameters", "expected"], adapter_parameters + ["query_parameters", "expected"], + adapter_parameters, +) +def test_basic_adapter_grouped_pagination(adapters, query_parameters, expected): + for adapter in adapters: + parameters = QueryParameters(**query_parameters) + assert adapter.query(parameters) == expected + + +@pytest.mark.remote +@pytest.mark.parametrize( + ["query_parameters", "expected"], + binding_adapter_parameters, ) -def test_basic_adapter_grouped_pagination(adapter, query_parameters, expected): - parameters = QueryParameters(**query_parameters) - assert adapter.query(parameters) == expected +def test_basic_binding_adapter_grouped_pagination( + binding_adapters, query_parameters, expected +): + for adapter in binding_adapters: + parameters = QueryParameters(**query_parameters) + assert adapter.query(parameters) == expected diff --git a/tests/tests_adapter/test_adapter_ungrouped_pagination.py b/tests/tests_adapter/test_adapter_ungrouped_pagination.py new file mode 100644 index 0000000..06ce8bb --- /dev/null +++ b/tests/tests_adapter/test_adapter_ungrouped_pagination.py @@ -0,0 +1 @@ +"""Basic tests for rdfproxy.SPARQLModelAdapter pagination with ungrouped models.""" diff --git a/tests/data/parameters/count_query_parameters.py b/tests/tests_constructor/params/count_query_parameters.py similarity index 90% rename from tests/data/parameters/count_query_parameters.py rename to tests/tests_constructor/params/count_query_parameters.py index e7aa9ad..7d93b32 100644 --- a/tests/data/parameters/count_query_parameters.py +++ b/tests/tests_constructor/params/count_query_parameters.py @@ -1,7 +1,18 @@ -from tests.data.models.dummy_model import Dummy, GroupedDummy +from pydantic import BaseModel +from rdfproxy import ConfigDict from tests.utils._types import CountQueryParameter +class Dummy(BaseModel): + pass + + +class GroupedDummy(BaseModel): + model_config = ConfigDict(group_by="x") + + x: int + + construct_count_query_parameters = [ CountQueryParameter( query=""" diff --git a/tests/tests_constructor/test_query_constructor_items_query.py b/tests/tests_constructor/test_query_constructor_items_query.py new file mode 100644 index 0000000..79ad389 --- /dev/null +++ b/tests/tests_constructor/test_query_constructor_items_query.py @@ -0,0 +1,105 @@ +"""Basic tests for the QueryConstructor class.""" + +from typing import NamedTuple + +import pytest + +from pydantic import BaseModel +from rdfproxy.constructor import QueryConstructor +from rdfproxy.utils._types import ConfigDict +from rdfproxy.utils.models import QueryParameters + + +class UngroupedModel(BaseModel): + x: int + y: int + + +class GroupedModel(BaseModel): + model_config = ConfigDict(group_by="x") + + x: int + y: list[int] + + +class Expected(NamedTuple): + count_query: str + items_query: str + + +class QueryConstructorParameters(NamedTuple): + query: str + query_parameters: QueryParameters + model: type[BaseModel] + + expected: Expected + + +parameters = [ + # ungrouped + QueryConstructorParameters( + query="select * where {?s ?p ?o}", + query_parameters=QueryParameters(), + model=UngroupedModel, + expected=Expected( + count_query="select (count(*) as ?cnt) where {?s ?p ?o}", + items_query="select * where {?s ?p ?o} order by ?s limit 100 offset 0", + ), + ), + QueryConstructorParameters( + query="select ?p ?o where {?s ?p ?o}", + query_parameters=QueryParameters(), + model=UngroupedModel, + expected=Expected( + count_query="select (count(*) as ?cnt) where {?s ?p ?o}", + items_query="select ?p ?o where {?s ?p ?o} order by ?p limit 100 offset 0", + ), + ), + QueryConstructorParameters( + query="select * where {?s ?p ?o}", + query_parameters=QueryParameters(page=2, size=2), + model=UngroupedModel, + expected=Expected( + count_query="select (count(*) as ?cnt) where {?s ?p ?o}", + items_query="select * where {?s ?p ?o} order by ?s limit 2 offset 2", + ), + ), + # grouped + QueryConstructorParameters( + query="select * where {?x a ?y}", + query_parameters=QueryParameters(), + model=GroupedModel, + expected=Expected( + count_query="select (count(distinct ?x) as ?cnt) where {?x a ?y}", + items_query="select * where {?x a ?y {select distinct ?x where {?x a ?y} order by ?x limit 100 offset 0} }", + ), + ), + QueryConstructorParameters( + query="select ?x ?y where {?x a ?y}", + query_parameters=QueryParameters(), + model=GroupedModel, + expected=Expected( + count_query="select (count(distinct ?x) as ?cnt) where {?x a ?y}", + items_query="select ?x ?y where {?x a ?y {select distinct ?x where {?x a ?y} order by ?x limit 100 offset 0} }", + ), + ), + QueryConstructorParameters( + query="select ?x ?y where {?x a ?y}", + query_parameters=QueryParameters(page=2, size=2), + model=GroupedModel, + expected=Expected( + count_query="select (count(distinct ?x) as ?cnt) where {?x a ?y}", + items_query="select ?x ?y where {?x a ?y {select distinct ?x where {?x a ?y} order by ?x limit 2 offset 2} }", + ), + ), +] + + +@pytest.mark.parametrize(["query", "query_parameters", "model", "expected"], parameters) +def test_query_constructor_items_query(query, query_parameters, model, expected): + constructor = QueryConstructor( + query=query, query_parameters=query_parameters, model=model + ) + + assert constructor.get_count_query() == expected.count_query + assert constructor.get_items_query() == expected.items_query diff --git a/tests/data/parameters/model_bindings_mapper_model_bool_parameters.py b/tests/tests_mapper/params/model_bindings_mapper_model_bool_parameters.py similarity index 88% rename from tests/data/parameters/model_bindings_mapper_model_bool_parameters.py rename to tests/tests_mapper/params/model_bindings_mapper_model_bool_parameters.py index 5721292..a4c23b5 100644 --- a/tests/data/parameters/model_bindings_mapper_model_bool_parameters.py +++ b/tests/tests_mapper/params/model_bindings_mapper_model_bool_parameters.py @@ -43,6 +43,13 @@ class Child5(BaseModel): child: str | None = Field(default=None, exclude=True) +class Child6(BaseModel): + model_config = ConfigDict(model_bool=["name", "child"]) + + name: str | None = None + child: str | None = None + + def _create_parent_with_child(child: type[BaseModel]) -> type[BaseModel]: model = create_model( "Parent", @@ -112,4 +119,13 @@ def _create_parent_with_child(child: type[BaseModel]) -> type[BaseModel]: {"parent": "z", "children": []}, ], ), + ModelBindingsMapperParameter( + model=_create_parent_with_child(Child6), + bindings=bindings, + expected=[ + {"parent": "x", "children": [{"name": "foo", "child": "c"}]}, + {"parent": "y", "children": []}, + {"parent": "z", "children": []}, + ], + ), ] diff --git a/tests/data/parameters/model_bindings_mapper_parameters.py b/tests/tests_mapper/params/model_bindings_mapper_parameters.py similarity index 94% rename from tests/data/parameters/model_bindings_mapper_parameters.py rename to tests/tests_mapper/params/model_bindings_mapper_parameters.py index 5d04fd4..8f9b70b 100644 --- a/tests/data/parameters/model_bindings_mapper_parameters.py +++ b/tests/tests_mapper/params/model_bindings_mapper_parameters.py @@ -1,12 +1,16 @@ -from tests.data.models.author_array_collection_model import Author as ArrayAuthor -from tests.data.models.author_work_title_model import Author -from tests.data.models.basic_model import ( +from tests.tests_mapper.params.models.author_array_collection_model import ( + Author as ArrayAuthor, +) +from tests.tests_mapper.params.models.author_work_title_model import Author +from tests.tests_mapper.params.models.basic_model import ( BasicComplexModel, BasicNestedModel, BasicSimpleModel, ) -from tests.data.models.grouping_model import GroupingComplexModel -from tests.data.models.nested_grouping_model import NestedGroupingComplexModel +from tests.tests_mapper.params.models.grouping_model import GroupingComplexModel +from tests.tests_mapper.params.models.nested_grouping_model import ( + NestedGroupingComplexModel, +) from tests.utils._types import ModelBindingsMapperParameter diff --git a/tests/data/models/author_array_collection_model.py b/tests/tests_mapper/params/models/author_array_collection_model.py similarity index 100% rename from tests/data/models/author_array_collection_model.py rename to tests/tests_mapper/params/models/author_array_collection_model.py diff --git a/tests/data/models/author_work_title_model.py b/tests/tests_mapper/params/models/author_work_title_model.py similarity index 100% rename from tests/data/models/author_work_title_model.py rename to tests/tests_mapper/params/models/author_work_title_model.py diff --git a/tests/data/models/basic_model.py b/tests/tests_mapper/params/models/basic_model.py similarity index 100% rename from tests/data/models/basic_model.py rename to tests/tests_mapper/params/models/basic_model.py diff --git a/tests/data/models/grouping_model.py b/tests/tests_mapper/params/models/grouping_model.py similarity index 100% rename from tests/data/models/grouping_model.py rename to tests/tests_mapper/params/models/grouping_model.py diff --git a/tests/data/models/nested_grouping_model.py b/tests/tests_mapper/params/models/nested_grouping_model.py similarity index 100% rename from tests/data/models/nested_grouping_model.py rename to tests/tests_mapper/params/models/nested_grouping_model.py diff --git a/tests/tests_mapper/test_model_bindings_mapper.py b/tests/tests_mapper/test_model_bindings_mapper.py index 4824674..52c47ed 100644 --- a/tests/tests_mapper/test_model_bindings_mapper.py +++ b/tests/tests_mapper/test_model_bindings_mapper.py @@ -1,10 +1,10 @@ """Pytest entry point for basic rdfproxy.mapper.ModelBindingsMapper.""" -from pydantic import BaseModel import pytest -from rdfproxy.mapper import ModelBindingsMapper -from tests.data.parameters.model_bindings_mapper_parameters import ( +from pydantic import BaseModel +from rdfproxy.mapper import ModelBindingsMapper +from tests.tests_mapper.params.model_bindings_mapper_parameters import ( author_array_collection_parameters, author_work_title_parameters, basic_parameters, diff --git a/tests/tests_mapper/test_model_bindings_mapper_model_bool.py b/tests/tests_mapper/test_model_bindings_mapper_model_bool.py index f9bde75..a14077b 100644 --- a/tests/tests_mapper/test_model_bindings_mapper_model_bool.py +++ b/tests/tests_mapper/test_model_bindings_mapper_model_bool.py @@ -4,7 +4,7 @@ from pydantic import BaseModel from rdfproxy.mapper import ModelBindingsMapper -from tests.data.parameters.model_bindings_mapper_model_bool_parameters import ( +from tests.tests_mapper.params.model_bindings_mapper_model_bool_parameters import ( parent_child_parameters, ) diff --git a/tests/unit/test_construct_count_query.py b/tests/unit/test_construct_count_query.py deleted file mode 100644 index 94f7d10..0000000 --- a/tests/unit/test_construct_count_query.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Unit tests for rdfproxy.utils.sparql_utils.construct_count_query.""" - -import pytest - -from rdflib import Graph -from rdflib.plugins.sparql.processor import SPARQLResult -from rdfproxy.utils.sparql_utils import construct_count_query -from tests.data.parameters.count_query_parameters import ( - construct_count_query_parameters, -) - - -def _get_cnt_value_from_sparql_result( - result: SPARQLResult, count_var: str = "cnt" -) -> int: - """Get the 'cnt' binding of a count query from a SPARQLResult object.""" - return int(result.bindings[0][count_var]) - - -@pytest.mark.parametrize( - ["query", "model", "expected"], construct_count_query_parameters -) -def test_basic_construct_count_query(query, model, expected): - """Check the count of a grouped model. - - The count query constructed based on a grouped value must only count - distinct values according to the grouping specified in the model. - """ - - graph: Graph = Graph() - count_query: str = construct_count_query(query, model) - query_result: SPARQLResult = graph.query(count_query) - - cnt: int = _get_cnt_value_from_sparql_result(query_result) - - assert cnt == expected diff --git a/tests/unit/test_sad_path_get_bindings_from_query_result.py b/tests/unit/test_sad_path_get_bindings_from_query_result.py deleted file mode 100644 index 2bc655c..0000000 --- a/tests/unit/test_sad_path_get_bindings_from_query_result.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Sad path tests for rdfprox.utils.sparql_utils.get_bindings_from_query_result.""" - -from unittest import mock - -import pytest - -from rdfproxy.utils.sparql_utils import get_bindings_from_query_result - - -def test_basic_sad_path_get_bindings_from_query_result(): - with mock.patch("SPARQLWrapper.QueryResult") as mock_query_result: - mock_query_result.return_value.requestedFormat = "xml" - exception_message = ( - "Only QueryResult objects with JSON format are currently supported." - ) - with pytest.raises(Exception, match=exception_message): - get_bindings_from_query_result(mock_query_result) diff --git a/tests/unit/tests_sparql_utils/test_add_solution_modifier.py b/tests/unit/tests_sparql_utils/test_add_solution_modifier.py new file mode 100644 index 0000000..a276286 --- /dev/null +++ b/tests/unit/tests_sparql_utils/test_add_solution_modifier.py @@ -0,0 +1,72 @@ +from typing import NamedTuple + +import pytest +from rdfproxy.utils.sparql_utils import add_solution_modifier + + +class AddSolutionModifierParameter(NamedTuple): + query: str + parameters: dict + expected: str + + +parameters = [ + # basics + AddSolutionModifierParameter( + query="prefix ns: select * where {?s ?p ?o }", + parameters={"order_by": None, "limit": None, "offset": None}, + expected="prefix ns: select * where {?s ?p ?o }", + ), + AddSolutionModifierParameter( + query="prefix ns: select * where {?s ?p ?o }", + parameters={"order_by": None, "limit": None, "offset": 1}, + expected="prefix ns: select * where {?s ?p ?o } offset 1", + ), + AddSolutionModifierParameter( + query="prefix ns: select * where {?s ?p ?o }", + parameters={"order_by": None, "limit": 1, "offset": None}, + expected="prefix ns: select * where {?s ?p ?o } limit 1", + ), + AddSolutionModifierParameter( + query="prefix ns: select * where {?s ?p ?o }", + parameters={"order_by": "x", "limit": None, "offset": None}, + expected="prefix ns: select * where {?s ?p ?o } order by ?x", + ), + AddSolutionModifierParameter( + query="prefix ns: select * where {?s ?p ?o }", + parameters={"order_by": "x", "limit": 1, "offset": 1}, + expected="prefix ns: select * where {?s ?p ?o } order by ?x limit 1 offset 1", + ), + # order + AddSolutionModifierParameter( + query="prefix ns: select * where {?s ?p ?o }", + parameters={"offset": 1, "limit": 1, "order_by": None}, + expected="prefix ns: select * where {?s ?p ?o } limit 1 offset 1", + ), + AddSolutionModifierParameter( + query="prefix ns: select * where {?s ?p ?o }", + parameters={"offset": 1, "limit": 1, "order_by": None}, + expected="prefix ns: select * where {?s ?p ?o } limit 1 offset 1", + ), + AddSolutionModifierParameter( + query="prefix ns: select * where {?s ?p ?o }", + parameters={"offset": 1, "limit": None, "order_by": "x"}, + expected="prefix ns: select * where {?s ?p ?o } order by ?x offset 1", + ), + AddSolutionModifierParameter( + query="prefix ns: select * where {?s ?p ?o }", + parameters={"offset": None, "limit": 1, "order_by": "x"}, + expected="prefix ns: select * where {?s ?p ?o } order by ?x limit 1", + ), + AddSolutionModifierParameter( + query="prefix ns: select * where {?s ?p ?o }", + parameters={"offset": 1, "limit": 1, "order_by": "x"}, + expected="prefix ns: select * where {?s ?p ?o } order by ?x limit 1 offset 1", + ), +] + + +@pytest.mark.parametrize(["query", "parameters", "expected"], parameters) +def test_add_solution_modifier(query, parameters, expected): + modified_query = add_solution_modifier(query, **parameters) + assert modified_query == expected diff --git a/tests/unit/tests_sparql_utils/test_get_query_projection.py b/tests/unit/tests_sparql_utils/test_get_query_projection.py new file mode 100644 index 0000000..2459950 --- /dev/null +++ b/tests/unit/tests_sparql_utils/test_get_query_projection.py @@ -0,0 +1,63 @@ +"""Unit tests for sparql_utils.get_query_projection.""" + +from typing import NamedTuple + +import pytest + +from rdfproxy.utils.sparql_utils import get_query_projection + + +class QueryProjectionParameter(NamedTuple): + query: str + expected: list[str] + + +parameters = [ + # explicit projection + QueryProjectionParameter( + query="select ?s ?p ?o where {?s ?p ?o}", expected=["s", "p", "o"] + ), + QueryProjectionParameter( + query=""" + PREFIX crm: + select ?s ?o where {?s ?p ?o}""", + expected=["s", "o"], + ), + # implicit projection + QueryProjectionParameter( + query="select * where {?s ?p ?o}", + expected=["s", "p", "o"], + ), + QueryProjectionParameter( + query=""" + PREFIX crm: + select * where {?s ?p ?o}""", + expected=["s", "p", "o"], + ), + # implicit projection with values clause + QueryProjectionParameter( + query=""" + select * where { + values (?s ?p ?o) + { (1 2 3) } + } + """, + expected=["s", "p", "o"], + ), + QueryProjectionParameter( + query=""" + PREFIX crm: + select * where { + values (?s ?p ?o) + { (1 2 3) } + } + """, + expected=["s", "p", "o"], + ), +] + + +@pytest.mark.parametrize(["query", "expected"], parameters) +def test_get_query_projection(query, expected): + projection = [str(binding) for binding in get_query_projection(query)] + assert projection == expected diff --git a/tests/unit/test_inject_subquery.py b/tests/unit/tests_sparql_utils/test_inject_subquery.py similarity index 95% rename from tests/unit/test_inject_subquery.py rename to tests/unit/tests_sparql_utils/test_inject_subquery.py index e95494d..a80765c 100644 --- a/tests/unit/test_inject_subquery.py +++ b/tests/unit/tests_sparql_utils/test_inject_subquery.py @@ -3,7 +3,8 @@ from typing import NamedTuple import pytest -from rdfproxy.utils.sparql_utils import inject_subquery + +from rdfproxy.utils.sparql_utils import inject_into_query, remove_sparql_prefixes class InjectSubqueryParameter(NamedTuple): @@ -118,5 +119,6 @@ class InjectSubqueryParameter(NamedTuple): @pytest.mark.parametrize(["query", "subquery", "expected"], inject_subquery_parameters) def test_inject_subquery(query, subquery, expected): - injected = inject_subquery(query=query, subquery=subquery) + injectant = remove_sparql_prefixes(subquery) + injected = inject_into_query(query=query, injectant=injectant) assert injected == expected diff --git a/tests/unit/tests_sparql_utils/test_remove_sparql_prefixes.py b/tests/unit/tests_sparql_utils/test_remove_sparql_prefixes.py new file mode 100644 index 0000000..e26e7b0 --- /dev/null +++ b/tests/unit/tests_sparql_utils/test_remove_sparql_prefixes.py @@ -0,0 +1,61 @@ +from typing import NamedTuple + +import pytest + +from rdfproxy.utils.sparql_utils import remove_sparql_prefixes +from tests.utils.utils import normalize_query + + +class SPARQLRemovePrefixParameter(NamedTuple): + query: str + expected: str + + +parameters = [ + SPARQLRemovePrefixParameter( + query=""" + select * where { ?s ?p ?o .} + """, + expected="select * where { ?s ?p ?o . }", + ), + SPARQLRemovePrefixParameter( + query=""" + prefix ns: + prefix other_ns: + select * where { ?s ?p ?o .} + """, + expected="select * where { ?s ?p ?o . }", + ), + SPARQLRemovePrefixParameter( + query=""" + prefix ns: prefix other_ns: + select * where { ?s ?p ?o .} + """, + expected="select * where { ?s ?p ?o . }", + ), + SPARQLRemovePrefixParameter( + query=""" + prefix ns: + + prefix other_ns: + select * where { ?s ?p ?o .} + """, + expected="select * where { ?s ?p ?o . }", + ), + SPARQLRemovePrefixParameter( + query=""" + prefix ns: + + prefix other_ns: + + select * where { ?s ?p ?o .} + """, + expected="select * where { ?s ?p ?o . }", + ), +] + + +@pytest.mark.parametrize(["query", "expected"], parameters) +def test_remove_sparql_prefixes(query, expected): + modified_query = remove_sparql_prefixes(query) + assert normalize_query(modified_query) == expected diff --git a/tests/unit/test_replace_query_select_clause.py b/tests/unit/tests_sparql_utils/test_replace_query_select_clause.py similarity index 100% rename from tests/unit/test_replace_query_select_clause.py rename to tests/unit/tests_sparql_utils/test_replace_query_select_clause.py diff --git a/tests/unit/test_sad_path_replace_query_select_clause.py b/tests/unit/tests_sparql_utils/test_sad_path_replace_query_select_clause.py similarity index 100% rename from tests/unit/test_sad_path_replace_query_select_clause.py rename to tests/unit/tests_sparql_utils/test_sad_path_replace_query_select_clause.py diff --git a/tests/unit/tests_utils/test_field_bindings_map.py b/tests/unit/tests_utils/test_field_bindings_map.py new file mode 100644 index 0000000..0364e7c --- /dev/null +++ b/tests/unit/tests_utils/test_field_bindings_map.py @@ -0,0 +1,25 @@ +"""Basic unit tests for FieldBindingsMap""" + +from typing import Annotated + +from pydantic import BaseModel +from rdfproxy.utils._types import SPARQLBinding +from rdfproxy.utils.utils import FieldsBindingsMap + + +class Point(BaseModel): + x: int + y: Annotated[int, SPARQLBinding("Y_ALIAS")] + z: Annotated[list[int], SPARQLBinding("Z_ALIAS")] + + +def test_basic_fields_bindings_map(): + mapping = FieldsBindingsMap(model=Point) + + assert mapping["x"] == "x" + assert mapping["y"] == "Y_ALIAS" + assert mapping["z"] == "Z_ALIAS" + + assert mapping.reverse["x"] == "x" + assert mapping.reverse["Y_ALIAS"] == "y" + assert mapping.reverse["Z_ALIAS"] == "z" diff --git a/tests/utils/utils.py b/tests/utils/utils.py new file mode 100644 index 0000000..deb354d --- /dev/null +++ b/tests/utils/utils.py @@ -0,0 +1,11 @@ +"""Testing utils.""" + +import re + + +def normalize_query(select_query: str) -> str: + """Normalize whitespace chars in a SPARQL query.""" + normalized_select_query = re.sub( + r"(?