Skip to content

Commit

Permalink
Add test suite for operators module
Browse files Browse the repository at this point in the history
  • Loading branch information
Viicos committed Apr 7, 2024
1 parent ccae3f7 commit 0c44926
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 7 deletions.
8 changes: 4 additions & 4 deletions src/jsonlogic/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING

from ._compat import Self, TypeAlias
from .json_schema.types import AnyType, JSONSchemaType
Expand Down Expand Up @@ -44,9 +44,9 @@ def from_expression(cls, operator: str, arguments: list[OperatorArgument]) -> Se
for checking the correct number of arguments and optionally the types.
"""

@abstractmethod
def apply(self, data: JSON) -> Any:
"""Evaluate the operator with the provided data."""
# @abstractmethod
# def apply(self, data: JSON) -> Any:
# """Evaluate the operator with the provided data."""

def typecheck(self, context: TypecheckContext) -> JSONSchemaType:
"""Typecheck the operator (and all children) given the data schema."""
Expand Down
19 changes: 18 additions & 1 deletion src/jsonlogic/operators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,30 @@
If,
LessThan,
LessThanOrEqual,
Map,
Minus,
Modulo,
NotEqual,
Plus,
Var,
)

__all__ = ("operator_registry",)
__all__ = (
"Division",
"Equal",
"GreaterThan",
"GreaterThanOrEqual",
"If",
"LessThan",
"LessThanOrEqual",
"Map",
"Minus",
"Modulo",
"NotEqual",
"Plus",
"Var",
"operator_registry",
)

operator_registry = OperatorRegistry()
"""The default operator registry."""
Expand All @@ -34,3 +50,4 @@
operator_registry.register("-", Minus)
operator_registry.register("/", Division)
operator_registry.register("%", Modulo)
operator_registry.register("map", Map)
2 changes: 1 addition & 1 deletion src/jsonlogic/operators/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class If(Operator):

@classmethod
def from_expression(cls, operator: str, arguments: list[OperatorArgument]) -> Self:
if len(arguments) % 2 == 1:
if len(arguments) % 2 == 0:
raise JSONLogicSyntaxError(f"{operator!r} expects an odd number of arguments, got {len(arguments)}")
return cls(operator=operator, if_elses=list(zip(arguments[::2], arguments[1::2])), leading_else=arguments[-1])

Expand Down
3 changes: 2 additions & 1 deletion tests/json_schema/test_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ def test_from_json_schema(


@pytest.mark.parametrize(
["type", "variable_casts", "expected"], [(param[2], param[1], param[0]) for param in json_schema_params]
["type", "variable_casts", "expected"],
[(param[2], param[1], param[0]) for param in json_schema_params], # Arguments order is reversed
)
def test_as_json_schema(
type: JSONSchemaType, variable_casts: dict[str, type[JSONSchemaType]], expected: dict[str, Any]
Expand Down
Empty file added tests/operators/__init__.py
Empty file.
90 changes: 90 additions & 0 deletions tests/operators/test_from_expression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import pytest

from jsonlogic.core import JSONLogicSyntaxError
from jsonlogic.operators import (
Equal,
GreaterThan,
If,
Map,
Minus,
Plus,
Var,
)
from jsonlogic.utils import UNSET


def test_var() -> None:
var_no_default_1 = Var.from_expression("var", ["some.path"])
assert var_no_default_1.variable_path == "some.path"

_equal = Equal.from_expression("==", [1, 1])
var_no_default_2 = Var.from_expression("var", [_equal])
assert var_no_default_2.variable_path is _equal

var_default = Var.from_expression("var", ["some.path", 1])
assert var_default.default_value == 1

with pytest.raises(JSONLogicSyntaxError, match="'var' expects one or two arguments, got 3"):
Var.from_expression("var", [1, 2, 3])

with pytest.raises(JSONLogicSyntaxError, match="Variable path must be a string on an operator, got <class 'list'>"):
Var.from_expression("var", [["invalid_array"]])


def test_equality_op() -> None:
# Note: this test is relevant for the `Equal` and `NotEqual` operator classes.
equal = Equal.from_expression("==", [1, 2])
assert equal.left == 1
assert equal.right == 2

with pytest.raises(JSONLogicSyntaxError, match="'==' expects two arguments, got 3"):
Equal.from_expression("==", [1, 2, 3])


def test_if() -> None:
if_ = If.from_expression("if", [1, 2, 3, 4, 5])
assert if_.if_elses == [(1, 2), (3, 4)]
assert if_.leading_else == 5

with pytest.raises(JSONLogicSyntaxError, match="'if' expects an odd number of arguments, got 6"):
If.from_expression("if", [1, 2, 3, 4, 5, 6])


def test_binary_op() -> None:
# Note: this test is relevant for all the binary operator classes.
greater_than = GreaterThan.from_expression(">", [1, 2])
assert greater_than.left == 1
assert greater_than.right == 2

with pytest.raises(JSONLogicSyntaxError, match="'>' expects two arguments, got 3"):
GreaterThan.from_expression(">", [1, 2, 3])


def test_plus() -> None:
plus = Plus.from_expression("+", [1, 2, 3, 4])
assert plus.arguments == [1, 2, 3, 4]

with pytest.raises(JSONLogicSyntaxError, match=r"'\+' expects at least two arguments, got 1"):
Plus.from_expression("+", [1])


def test_minus() -> None:
minus_unary = Minus.from_expression("-", [1])
assert minus_unary.left == 1
assert minus_unary.right is UNSET

minus = Minus.from_expression("-", [1, 2])
assert minus.left == 1
assert minus.right == 2

with pytest.raises(JSONLogicSyntaxError, match="'-' expects one or two arguments, got 3"):
Minus.from_expression("-", [1, 2, 3])


def test_map() -> None:
map = Map.from_expression("map", [[1, 2], 3])
assert map.vars == [1, 2]
assert map.func == 3

with pytest.raises(JSONLogicSyntaxError, match="'map' expects two arguments, got 3"):
Map.from_expression("map", [1, 2, 3])

0 comments on commit 0c44926

Please sign in to comment.