mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-05-27 16:25:12 +02:00
Add 30+ SPARQL 1.1 built-in functions and the MINUS algebra operator to the custom SPARQL query backend. String functions: - SUBSTR (2-arg and 3-arg forms), STRBEFORE, STRAFTER - REPLACE (regex with flags), ENCODE_FOR_URI Numeric functions: - FLOOR, CEIL, ROUND, ABS Date/time accessors: - YEAR, MONTH, DAY, HOURS, MINUTES, SECONDS - NOW, TZ Hash functions: - MD5, SHA1, SHA256, SHA512 Term constructors: - IRI/URI, BNODE, UUID, STRUUID Other functions: - LANGMATCHES, RAND - EXISTS / NOT EXISTS (with async pre-evaluation to bridge the sync expression evaluator and async algebra evaluator) Algebra: - MINUS set-difference operator - HAVING already works via rdflib's Filter mapping (verified) Fix SPARQL ORDER handling Includes 653 lines of new unit tests covering all added functionality across expressions, solutions, and algebra layers.
856 lines
31 KiB
Python
856 lines
31 KiB
Python
"""
|
|
Tests for SPARQL FILTER expression evaluator.
|
|
"""
|
|
|
|
import pytest
|
|
from trustgraph.schema import Term, IRI, LITERAL, BLANK
|
|
from trustgraph.query.sparql.expressions import (
|
|
evaluate_expression, _effective_boolean, _to_string, _to_numeric,
|
|
_comparable_value,
|
|
)
|
|
|
|
|
|
# --- Helpers ---
|
|
|
|
def iri(v):
|
|
return Term(type=IRI, iri=v)
|
|
|
|
def lit(v, datatype="", language=""):
|
|
return Term(type=LITERAL, value=v, datatype=datatype, language=language)
|
|
|
|
def blank(v):
|
|
return Term(type=BLANK, id=v)
|
|
|
|
XSD = "http://www.w3.org/2001/XMLSchema#"
|
|
|
|
|
|
class TestEvaluateExpression:
|
|
"""Test expression evaluation with rdflib algebra nodes."""
|
|
|
|
def test_variable_bound(self):
|
|
from rdflib.term import Variable
|
|
result = evaluate_expression(Variable("x"), {"x": lit("hello")})
|
|
assert result.value == "hello"
|
|
|
|
def test_variable_unbound(self):
|
|
from rdflib.term import Variable
|
|
result = evaluate_expression(Variable("x"), {})
|
|
assert result is None
|
|
|
|
def test_uriref_constant(self):
|
|
from rdflib import URIRef
|
|
result = evaluate_expression(
|
|
URIRef("http://example.com/a"), {}
|
|
)
|
|
assert result.type == IRI
|
|
assert result.iri == "http://example.com/a"
|
|
|
|
def test_literal_constant(self):
|
|
from rdflib import Literal
|
|
result = evaluate_expression(Literal("hello"), {})
|
|
assert result.type == LITERAL
|
|
assert result.value == "hello"
|
|
|
|
def test_boolean_constant(self):
|
|
assert evaluate_expression(True, {}) is True
|
|
assert evaluate_expression(False, {}) is False
|
|
|
|
def test_numeric_constant(self):
|
|
assert evaluate_expression(42, {}) == 42
|
|
assert evaluate_expression(3.14, {}) == 3.14
|
|
|
|
def test_none_returns_true(self):
|
|
assert evaluate_expression(None, {}) is True
|
|
|
|
|
|
class TestRelationalExpressions:
|
|
"""Test comparison operators via CompValue nodes."""
|
|
|
|
def _make_relational(self, left, op, right):
|
|
from rdflib.plugins.sparql.parserutils import CompValue
|
|
return CompValue("RelationalExpression",
|
|
expr=left, op=op, other=right)
|
|
|
|
def test_equal_literals(self):
|
|
from rdflib import Literal
|
|
expr = self._make_relational(Literal("a"), "=", Literal("a"))
|
|
assert evaluate_expression(expr, {}) is True
|
|
|
|
def test_not_equal_literals(self):
|
|
from rdflib import Literal
|
|
expr = self._make_relational(Literal("a"), "!=", Literal("b"))
|
|
assert evaluate_expression(expr, {}) is True
|
|
|
|
def test_less_than(self):
|
|
from rdflib import Literal
|
|
expr = self._make_relational(Literal("a"), "<", Literal("b"))
|
|
assert evaluate_expression(expr, {}) is True
|
|
|
|
def test_greater_than(self):
|
|
from rdflib import Literal
|
|
expr = self._make_relational(Literal("b"), ">", Literal("a"))
|
|
assert evaluate_expression(expr, {}) is True
|
|
|
|
def test_equal_with_variables(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_relational(Variable("x"), "=", Variable("y"))
|
|
sol = {"x": lit("same"), "y": lit("same")}
|
|
assert evaluate_expression(expr, sol) is True
|
|
|
|
def test_unequal_with_variables(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_relational(Variable("x"), "=", Variable("y"))
|
|
sol = {"x": lit("one"), "y": lit("two")}
|
|
assert evaluate_expression(expr, sol) is False
|
|
|
|
def test_none_operand_returns_false(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_relational(Variable("x"), "=", Literal("a"))
|
|
assert evaluate_expression(expr, {}) is False
|
|
|
|
|
|
class TestLogicalExpressions:
|
|
|
|
def _make_and(self, exprs):
|
|
from rdflib.plugins.sparql.parserutils import CompValue
|
|
return CompValue("ConditionalAndExpression",
|
|
expr=exprs[0], other=exprs[1:])
|
|
|
|
def _make_or(self, exprs):
|
|
from rdflib.plugins.sparql.parserutils import CompValue
|
|
return CompValue("ConditionalOrExpression",
|
|
expr=exprs[0], other=exprs[1:])
|
|
|
|
def _make_not(self, expr):
|
|
from rdflib.plugins.sparql.parserutils import CompValue
|
|
return CompValue("UnaryNot", expr=expr)
|
|
|
|
def test_and_true_true(self):
|
|
result = evaluate_expression(self._make_and([True, True]), {})
|
|
assert result is True
|
|
|
|
def test_and_true_false(self):
|
|
result = evaluate_expression(self._make_and([True, False]), {})
|
|
assert result is False
|
|
|
|
def test_or_false_true(self):
|
|
result = evaluate_expression(self._make_or([False, True]), {})
|
|
assert result is True
|
|
|
|
def test_or_false_false(self):
|
|
result = evaluate_expression(self._make_or([False, False]), {})
|
|
assert result is False
|
|
|
|
def test_not_true(self):
|
|
result = evaluate_expression(self._make_not(True), {})
|
|
assert result is False
|
|
|
|
def test_not_false(self):
|
|
result = evaluate_expression(self._make_not(False), {})
|
|
assert result is True
|
|
|
|
|
|
class TestBuiltinFunctions:
|
|
|
|
def _make_builtin(self, name, **kwargs):
|
|
from rdflib.plugins.sparql.parserutils import CompValue
|
|
return CompValue(f"Builtin_{name}", **kwargs)
|
|
|
|
def test_bound_true(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("BOUND", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("hi")}) is True
|
|
|
|
def test_bound_false(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("BOUND", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {}) is False
|
|
|
|
def test_isiri_true(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("isIRI", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": iri("http://x")}) is True
|
|
|
|
def test_isiri_false(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("isIRI", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("hello")}) is False
|
|
|
|
def test_isliteral_true(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("isLITERAL", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("hello")}) is True
|
|
|
|
def test_isliteral_false(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("isLITERAL", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": iri("http://x")}) is False
|
|
|
|
def test_isblank_true(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("isBLANK", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": blank("b1")}) is True
|
|
|
|
def test_isblank_false(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("isBLANK", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": iri("http://x")}) is False
|
|
|
|
def test_str(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("STR", arg=Variable("x"))
|
|
result = evaluate_expression(expr, {"x": iri("http://example.com/a")})
|
|
assert result.type == LITERAL
|
|
assert result.value == "http://example.com/a"
|
|
|
|
def test_lang(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("LANG", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("hello", language="en")}
|
|
)
|
|
assert result.value == "en"
|
|
|
|
def test_lang_no_tag(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("LANG", arg=Variable("x"))
|
|
result = evaluate_expression(expr, {"x": lit("hello")})
|
|
assert result.value == ""
|
|
|
|
def test_datatype(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("DATATYPE", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("42", datatype=XSD + "integer")}
|
|
)
|
|
assert result.type == IRI
|
|
assert result.iri == XSD + "integer"
|
|
|
|
def test_strlen(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("STRLEN", arg=Variable("x"))
|
|
result = evaluate_expression(expr, {"x": lit("hello")})
|
|
assert result == 5
|
|
|
|
def test_ucase(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("UCASE", arg=Variable("x"))
|
|
result = evaluate_expression(expr, {"x": lit("hello")})
|
|
assert result.value == "HELLO"
|
|
|
|
def test_lcase(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("LCASE", arg=Variable("x"))
|
|
result = evaluate_expression(expr, {"x": lit("HELLO")})
|
|
assert result.value == "hello"
|
|
|
|
def test_contains_true(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("CONTAINS",
|
|
arg1=Variable("x"), arg2=Literal("ell"))
|
|
assert evaluate_expression(expr, {"x": lit("hello")}) is True
|
|
|
|
def test_contains_false(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("CONTAINS",
|
|
arg1=Variable("x"), arg2=Literal("xyz"))
|
|
assert evaluate_expression(expr, {"x": lit("hello")}) is False
|
|
|
|
def test_strstarts_true(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("STRSTARTS",
|
|
arg1=Variable("x"), arg2=Literal("hel"))
|
|
assert evaluate_expression(expr, {"x": lit("hello")}) is True
|
|
|
|
def test_strends_true(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("STRENDS",
|
|
arg1=Variable("x"), arg2=Literal("llo"))
|
|
assert evaluate_expression(expr, {"x": lit("hello")}) is True
|
|
|
|
def test_regex_match(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("REGEX",
|
|
text=Variable("x"),
|
|
pattern=Literal("^hel"),
|
|
flags=None)
|
|
assert evaluate_expression(expr, {"x": lit("hello")}) is True
|
|
|
|
def test_regex_case_insensitive(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("REGEX",
|
|
text=Variable("x"),
|
|
pattern=Literal("HELLO"),
|
|
flags=Literal("i"))
|
|
assert evaluate_expression(expr, {"x": lit("hello")}) is True
|
|
|
|
def test_regex_no_match(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("REGEX",
|
|
text=Variable("x"),
|
|
pattern=Literal("^world"),
|
|
flags=None)
|
|
assert evaluate_expression(expr, {"x": lit("hello")}) is False
|
|
|
|
def test_substr_three_args(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("SUBSTR",
|
|
arg=Variable("x"),
|
|
start=Literal(1),
|
|
length=Literal(4))
|
|
result = evaluate_expression(expr, {"x": lit("2024-03-15")})
|
|
assert result.type == LITERAL
|
|
assert result.value == "2024"
|
|
|
|
def test_substr_two_args(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("SUBSTR",
|
|
arg=Variable("x"),
|
|
start=Literal(6),
|
|
length=None)
|
|
result = evaluate_expression(expr, {"x": lit("2024-03-15")})
|
|
assert result.type == LITERAL
|
|
assert result.value == "03-15"
|
|
|
|
def test_substr_middle(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("SUBSTR",
|
|
arg=Variable("x"),
|
|
start=Literal(6),
|
|
length=Literal(2))
|
|
result = evaluate_expression(expr, {"x": lit("2024-03-15")})
|
|
assert result.type == LITERAL
|
|
assert result.value == "03"
|
|
|
|
def test_substr_null_start(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("SUBSTR",
|
|
arg=Variable("x"),
|
|
start=Variable("missing"),
|
|
length=None)
|
|
result = evaluate_expression(expr, {"x": lit("hello")})
|
|
assert result is None
|
|
|
|
def test_year(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("YEAR", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("2024-03-15", datatype=XSD + "date")}
|
|
)
|
|
assert result == 2024
|
|
|
|
def test_month(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("MONTH", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("2024-03-15", datatype=XSD + "date")}
|
|
)
|
|
assert result == 3
|
|
|
|
def test_day(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("DAY", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("2024-03-15", datatype=XSD + "date")}
|
|
)
|
|
assert result == 15
|
|
|
|
def test_hours(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("HOURS", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("2024-03-15T10:30:45", datatype=XSD + "dateTime")}
|
|
)
|
|
assert result == 10
|
|
|
|
def test_minutes(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("MINUTES", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("2024-03-15T10:30:45", datatype=XSD + "dateTime")}
|
|
)
|
|
assert result == 30
|
|
|
|
def test_seconds(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("SECONDS", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("2024-03-15T10:30:45", datatype=XSD + "dateTime")}
|
|
)
|
|
assert result == 45
|
|
|
|
def test_year_from_datetime(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("YEAR", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("2024-03-15T10:30:45", datatype=XSD + "dateTime")}
|
|
)
|
|
assert result == 2024
|
|
|
|
def test_hours_from_date_returns_zero(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("HOURS", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("2024-03-15", datatype=XSD + "date")}
|
|
)
|
|
assert result == 0
|
|
|
|
def test_year_invalid_date(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("YEAR", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("not-a-date")}
|
|
)
|
|
assert result is None
|
|
|
|
def test_floor(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("FLOOR", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("3.7")}) == 3
|
|
|
|
def test_floor_negative(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("FLOOR", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("-2.3")}) == -3
|
|
|
|
def test_floor_none(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("FLOOR", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("abc")}) is None
|
|
|
|
def test_ceil(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("CEIL", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("3.2")}) == 4
|
|
|
|
def test_ceil_negative(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("CEIL", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("-2.7")}) == -2
|
|
|
|
def test_abs_positive(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("ABS", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("42")}) == 42
|
|
|
|
def test_abs_negative(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("ABS", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("-42")}) == 42
|
|
|
|
def test_abs_none(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("ABS", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("abc")}) is None
|
|
|
|
def test_replace_simple(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("REPLACE",
|
|
arg=Variable("x"),
|
|
pattern=Literal(" BC"),
|
|
replacement=Literal(""),
|
|
flags=None)
|
|
result = evaluate_expression(expr, {"x": lit("500 BC")})
|
|
assert result.type == LITERAL
|
|
assert result.value == "500"
|
|
|
|
def test_replace_regex(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("REPLACE",
|
|
arg=Variable("x"),
|
|
pattern=Literal("[0-9]+"),
|
|
replacement=Literal("X"),
|
|
flags=None)
|
|
result = evaluate_expression(expr, {"x": lit("abc123def456")})
|
|
assert result.value == "abcXdefX"
|
|
|
|
def test_replace_case_insensitive(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("REPLACE",
|
|
arg=Variable("x"),
|
|
pattern=Literal("hello"),
|
|
replacement=Literal("world"),
|
|
flags=Literal("i"))
|
|
result = evaluate_expression(expr, {"x": lit("HELLO there")})
|
|
assert result.value == "world there"
|
|
|
|
def test_round_up(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("ROUND", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("3.7")}) == 4
|
|
|
|
def test_round_down(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("ROUND", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("3.2")}) == 3
|
|
|
|
def test_round_none(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("ROUND", arg=Variable("x"))
|
|
assert evaluate_expression(expr, {"x": lit("abc")}) is None
|
|
|
|
def test_strbefore(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("STRBEFORE",
|
|
arg1=Variable("x"), arg2=Literal("-"))
|
|
result = evaluate_expression(expr, {"x": lit("2024-03-15")})
|
|
assert result.value == "2024"
|
|
|
|
def test_strbefore_not_found(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("STRBEFORE",
|
|
arg1=Variable("x"), arg2=Literal("/"))
|
|
result = evaluate_expression(expr, {"x": lit("hello")})
|
|
assert result.value == ""
|
|
|
|
def test_strafter(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("STRAFTER",
|
|
arg1=Variable("x"), arg2=Literal("-"))
|
|
result = evaluate_expression(expr, {"x": lit("2024-03-15")})
|
|
assert result.value == "03-15"
|
|
|
|
def test_strafter_not_found(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("STRAFTER",
|
|
arg1=Variable("x"), arg2=Literal("/"))
|
|
result = evaluate_expression(expr, {"x": lit("hello")})
|
|
assert result.value == ""
|
|
|
|
def test_encode_for_uri(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("ENCODE_FOR_URI", arg=Variable("x"))
|
|
result = evaluate_expression(expr, {"x": lit("hello world")})
|
|
assert result.value == "hello%20world"
|
|
|
|
def test_encode_for_uri_special_chars(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("ENCODE_FOR_URI", arg=Variable("x"))
|
|
result = evaluate_expression(expr, {"x": lit("a/b?c=d&e")})
|
|
assert result.value == "a%2Fb%3Fc%3Dd%26e"
|
|
|
|
def test_langmatches_basic(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("LANGMATCHES",
|
|
arg1=Literal("en"), arg2=Literal("en"))
|
|
assert evaluate_expression(expr, {}) is True
|
|
|
|
def test_langmatches_subtag(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("LANGMATCHES",
|
|
arg1=Literal("en-US"), arg2=Literal("en"))
|
|
assert evaluate_expression(expr, {}) is True
|
|
|
|
def test_langmatches_wildcard(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("LANGMATCHES",
|
|
arg1=Literal("fr"), arg2=Literal("*"))
|
|
assert evaluate_expression(expr, {}) is True
|
|
|
|
def test_langmatches_wildcard_empty(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("LANGMATCHES",
|
|
arg1=Literal(""), arg2=Literal("*"))
|
|
assert evaluate_expression(expr, {}) is False
|
|
|
|
def test_langmatches_no_match(self):
|
|
from rdflib.term import Variable
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("LANGMATCHES",
|
|
arg1=Literal("fr"), arg2=Literal("en"))
|
|
assert evaluate_expression(expr, {}) is False
|
|
|
|
def test_iri_constructor(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("IRI", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("http://example.com/test")}
|
|
)
|
|
assert result.type == IRI
|
|
assert result.iri == "http://example.com/test"
|
|
|
|
def test_uri_constructor(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("URI", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("http://example.com/test")}
|
|
)
|
|
assert result.type == IRI
|
|
assert result.iri == "http://example.com/test"
|
|
|
|
def test_bnode_no_arg(self):
|
|
expr = self._make_builtin("BNODE")
|
|
result = evaluate_expression(expr, {})
|
|
assert result.type == BLANK
|
|
assert len(result.id) > 0
|
|
|
|
def test_bnode_with_label(self):
|
|
from rdflib import Literal
|
|
expr = self._make_builtin("BNODE", arg=Literal("mynode"))
|
|
result = evaluate_expression(expr, {})
|
|
assert result.type == BLANK
|
|
assert result.id == "mynode"
|
|
|
|
def test_now(self):
|
|
import re as re_mod
|
|
expr = self._make_builtin("NOW")
|
|
result = evaluate_expression(expr, {})
|
|
assert result.type == LITERAL
|
|
assert result.datatype == XSD + "dateTime"
|
|
assert re_mod.match(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}", result.value)
|
|
|
|
def test_tz_with_utc(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("TZ", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("2024-03-15T10:30:45+0000",
|
|
datatype=XSD + "dateTime")}
|
|
)
|
|
assert result.type == LITERAL
|
|
assert result.value == "+00:00"
|
|
|
|
def test_tz_no_timezone(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("TZ", arg=Variable("x"))
|
|
result = evaluate_expression(
|
|
expr, {"x": lit("2024-03-15T10:30:45",
|
|
datatype=XSD + "dateTime")}
|
|
)
|
|
assert result.value == ""
|
|
|
|
def test_rand(self):
|
|
expr = self._make_builtin("RAND")
|
|
result = evaluate_expression(expr, {})
|
|
assert isinstance(result, float)
|
|
assert 0.0 <= result < 1.0
|
|
|
|
def test_uuid(self):
|
|
import re as re_mod
|
|
expr = self._make_builtin("UUID")
|
|
result = evaluate_expression(expr, {})
|
|
assert result.type == IRI
|
|
assert result.iri.startswith("urn:uuid:")
|
|
uuid_part = result.iri[len("urn:uuid:"):]
|
|
assert re_mod.match(
|
|
r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
|
|
uuid_part
|
|
)
|
|
|
|
def test_struuid(self):
|
|
import re as re_mod
|
|
expr = self._make_builtin("STRUUID")
|
|
result = evaluate_expression(expr, {})
|
|
assert result.type == LITERAL
|
|
assert re_mod.match(
|
|
r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
|
|
result.value
|
|
)
|
|
|
|
def test_md5(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("MD5", arg=Variable("x"))
|
|
result = evaluate_expression(expr, {"x": lit("hello")})
|
|
assert result.type == LITERAL
|
|
assert result.value == "5d41402abc4b2a76b9719d911017c592"
|
|
|
|
def test_sha1(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("SHA1", arg=Variable("x"))
|
|
result = evaluate_expression(expr, {"x": lit("hello")})
|
|
assert result.type == LITERAL
|
|
assert result.value == "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"
|
|
|
|
def test_sha256(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("SHA256", arg=Variable("x"))
|
|
result = evaluate_expression(expr, {"x": lit("hello")})
|
|
assert result.type == LITERAL
|
|
assert result.value == (
|
|
"2cf24dba5fb0a30e26e83b2ac5b9e29e"
|
|
"1b161e5c1fa7425e73043362938b9824"
|
|
)
|
|
|
|
def test_sha512(self):
|
|
from rdflib.term import Variable
|
|
expr = self._make_builtin("SHA512", arg=Variable("x"))
|
|
result = evaluate_expression(expr, {"x": lit("hello")})
|
|
assert result.type == LITERAL
|
|
assert len(result.value) == 128
|
|
|
|
def test_exists_with_callback(self):
|
|
from rdflib.plugins.sparql.parserutils import CompValue
|
|
graph = CompValue("BGP")
|
|
expr = self._make_builtin("EXISTS", graph=graph)
|
|
cb = lambda g, s: True
|
|
result = evaluate_expression(expr, {}, exists_cb=cb)
|
|
assert result is True
|
|
|
|
def test_exists_callback_false(self):
|
|
from rdflib.plugins.sparql.parserutils import CompValue
|
|
graph = CompValue("BGP")
|
|
expr = self._make_builtin("EXISTS", graph=graph)
|
|
cb = lambda g, s: False
|
|
result = evaluate_expression(expr, {}, exists_cb=cb)
|
|
assert result is False
|
|
|
|
def test_notexists_with_callback(self):
|
|
from rdflib.plugins.sparql.parserutils import CompValue
|
|
graph = CompValue("BGP")
|
|
expr = self._make_builtin("NOTEXISTS", graph=graph)
|
|
cb = lambda g, s: True
|
|
result = evaluate_expression(expr, {}, exists_cb=cb)
|
|
assert result is False
|
|
|
|
def test_notexists_callback_false(self):
|
|
from rdflib.plugins.sparql.parserutils import CompValue
|
|
graph = CompValue("BGP")
|
|
expr = self._make_builtin("NOTEXISTS", graph=graph)
|
|
cb = lambda g, s: False
|
|
result = evaluate_expression(expr, {}, exists_cb=cb)
|
|
assert result is True
|
|
|
|
|
|
class TestEffectiveBoolean:
|
|
|
|
def test_true(self):
|
|
assert _effective_boolean(True) is True
|
|
|
|
def test_false(self):
|
|
assert _effective_boolean(False) is False
|
|
|
|
def test_none(self):
|
|
assert _effective_boolean(None) is False
|
|
|
|
def test_nonzero_int(self):
|
|
assert _effective_boolean(42) is True
|
|
|
|
def test_zero_int(self):
|
|
assert _effective_boolean(0) is False
|
|
|
|
def test_nonempty_string(self):
|
|
assert _effective_boolean("hello") is True
|
|
|
|
def test_empty_string(self):
|
|
assert _effective_boolean("") is False
|
|
|
|
def test_iri_term(self):
|
|
assert _effective_boolean(iri("http://x")) is True
|
|
|
|
def test_nonempty_literal(self):
|
|
assert _effective_boolean(lit("hello")) is True
|
|
|
|
def test_empty_literal(self):
|
|
assert _effective_boolean(lit("")) is False
|
|
|
|
def test_boolean_literal_true(self):
|
|
assert _effective_boolean(
|
|
lit("true", datatype=XSD + "boolean")
|
|
) is True
|
|
|
|
def test_boolean_literal_false(self):
|
|
assert _effective_boolean(
|
|
lit("false", datatype=XSD + "boolean")
|
|
) is False
|
|
|
|
def test_numeric_literal_nonzero(self):
|
|
assert _effective_boolean(
|
|
lit("42", datatype=XSD + "integer")
|
|
) is True
|
|
|
|
def test_numeric_literal_zero(self):
|
|
assert _effective_boolean(
|
|
lit("0", datatype=XSD + "integer")
|
|
) is False
|
|
|
|
|
|
class TestToString:
|
|
|
|
def test_none(self):
|
|
assert _to_string(None) == ""
|
|
|
|
def test_string(self):
|
|
assert _to_string("hello") == "hello"
|
|
|
|
def test_iri_term(self):
|
|
assert _to_string(iri("http://example.com")) == "http://example.com"
|
|
|
|
def test_literal_term(self):
|
|
assert _to_string(lit("hello")) == "hello"
|
|
|
|
def test_blank_term(self):
|
|
assert _to_string(blank("b1")) == "b1"
|
|
|
|
|
|
class TestToNumeric:
|
|
|
|
def test_none(self):
|
|
assert _to_numeric(None) is None
|
|
|
|
def test_int(self):
|
|
assert _to_numeric(42) == 42
|
|
|
|
def test_float(self):
|
|
assert _to_numeric(3.14) == 3.14
|
|
|
|
def test_integer_literal(self):
|
|
assert _to_numeric(lit("42")) == 42
|
|
|
|
def test_decimal_literal(self):
|
|
assert _to_numeric(lit("3.14")) == 3.14
|
|
|
|
def test_non_numeric_literal(self):
|
|
assert _to_numeric(lit("hello")) is None
|
|
|
|
def test_numeric_string(self):
|
|
assert _to_numeric("42") == 42
|
|
|
|
def test_non_numeric_string(self):
|
|
assert _to_numeric("abc") is None
|
|
|
|
|
|
class TestComparableValue:
|
|
|
|
def test_none(self):
|
|
assert _comparable_value(None) == (0, "")
|
|
|
|
def test_int(self):
|
|
assert _comparable_value(42) == (2, 42)
|
|
|
|
def test_iri(self):
|
|
assert _comparable_value(iri("http://x")) == (4, "http://x")
|
|
|
|
def test_literal(self):
|
|
assert _comparable_value(lit("hello")) == (3, "hello")
|
|
|
|
def test_numeric_literal(self):
|
|
assert _comparable_value(lit("42")) == (2, 42)
|
|
|
|
def test_ordering(self):
|
|
vals = [lit("b"), lit("a"), lit("c")]
|
|
sorted_vals = sorted(vals, key=_comparable_value)
|
|
assert sorted_vals[0].value == "a"
|
|
assert sorted_vals[1].value == "b"
|
|
assert sorted_vals[2].value == "c"
|