nucypher/tests/unit/conditions/test_utils.py

169 lines
5.8 KiB
Python

"""
This file is part of nucypher.
nucypher is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
nucypher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
from dataclasses import dataclass
from http import HTTPStatus
from typing import List, Optional, Tuple, Type
from unittest.mock import Mock, patch
import pytest
from marshmallow import fields
from web3.providers import BaseProvider
from nucypher.policy.conditions.exceptions import (
ConditionEvaluationFailed,
ContextVariableVerificationFailed,
InvalidCondition,
InvalidConditionLingo,
InvalidContextVariableData,
NoConnectionToChain,
RequiredContextVariable,
ReturnValueEvaluationError,
)
from nucypher.policy.conditions.lingo import ConditionLingo
from nucypher.policy.conditions.utils import (
CamelCaseSchema,
ConditionEvalError,
camel_case_to_snake,
evaluate_condition_lingo,
to_camelcase,
)
FAILURE_CASE_EXCEPTION_CODE_MATCHING = [
# (exception, constructor parameters, expected status code)
(ReturnValueEvaluationError, None, HTTPStatus.BAD_REQUEST),
(InvalidConditionLingo, None, HTTPStatus.BAD_REQUEST),
(InvalidCondition, None, HTTPStatus.BAD_REQUEST),
(RequiredContextVariable, None, HTTPStatus.BAD_REQUEST),
(InvalidContextVariableData, None, HTTPStatus.BAD_REQUEST),
(ContextVariableVerificationFailed, None, HTTPStatus.FORBIDDEN),
(NoConnectionToChain, [1], HTTPStatus.NOT_IMPLEMENTED),
(ConditionEvaluationFailed, None, HTTPStatus.BAD_REQUEST),
(Exception, None, HTTPStatus.INTERNAL_SERVER_ERROR),
]
@pytest.mark.parametrize("failure_case", FAILURE_CASE_EXCEPTION_CODE_MATCHING)
def test_evaluate_condition_exception_cases(
failure_case: Tuple[Type[Exception], Optional[List], int]
):
exception_class, exception_constructor_params, expected_status_code = failure_case
exception_constructor_params = exception_constructor_params or []
condition_lingo = Mock()
condition_lingo.eval.side_effect = exception_class(*exception_constructor_params)
with patch(
"nucypher.policy.conditions.lingo.ConditionLingo.from_dict"
) as mocked_from_dict:
mocked_from_dict.return_value = condition_lingo
with pytest.raises(ConditionEvalError) as eval_error:
evaluate_condition_lingo(
condition_lingo=condition_lingo
) # provider and context default to empty dicts
assert eval_error.value.status_code == expected_status_code
def test_evaluate_condition_invalid_lingo():
with pytest.raises(ConditionEvalError) as eval_error:
evaluate_condition_lingo(
condition_lingo={
"version": ConditionLingo.VERSION,
"condition": {"dont_mind_me": "nothing_to_see_here"},
}
) # provider and context default to empty dicts
assert "Invalid condition grammar" in eval_error.value.message
assert eval_error.value.status_code == HTTPStatus.BAD_REQUEST
def test_evaluate_condition_eval_returns_false():
condition_lingo = Mock()
condition_lingo.eval.return_value = False
with patch(
"nucypher.policy.conditions.lingo.ConditionLingo.from_dict"
) as mocked_from_dict:
mocked_from_dict.return_value = condition_lingo
with pytest.raises(ConditionEvalError) as eval_error:
evaluate_condition_lingo(
condition_lingo=condition_lingo,
providers={1: Mock(spec=BaseProvider)}, # fake provider
context={"key": "value"}, # fake context
)
assert eval_error.value.status_code == HTTPStatus.FORBIDDEN
def test_evaluate_condition_eval_returns_true():
condition_lingo = Mock()
condition_lingo.eval.return_value = True
with patch(
"nucypher.policy.conditions.lingo.ConditionLingo.from_dict"
) as mocked_from_dict:
mocked_from_dict.return_value = condition_lingo
evaluate_condition_lingo(
condition_lingo=condition_lingo,
providers={
1: Mock(spec=BaseProvider),
2: Mock(spec=BaseProvider),
}, # multiple fake provider
context={
"key1": "value1",
"key2": "value2",
}, # multiple values in fake context
)
@pytest.mark.parametrize(
"test_case",
(
("nounderscores", "nounderscores"),
("one_underscore", "oneUnderscore"),
("two_under_scores", "twoUnderScores"),
),
)
def test_to_from_camel_case(test_case: Tuple[str, str]):
# test to_camelcase()
snake_case, camel_case = test_case
result = to_camelcase(snake_case)
assert result == camel_case
# test camel_case_to_snake()
result = camel_case_to_snake(camel_case)
assert result == snake_case
def test_camel_case_schema():
# test CamelCaseSchema
@dataclass
class Function:
field_name_with_underscores: str
class FunctionSchema(CamelCaseSchema):
field_name_with_underscores = fields.Str()
value = "field_name_value"
function = Function(field_name_with_underscores=value)
schema = FunctionSchema()
output = schema.dump(function)
assert output == {"fieldNameWithUnderscores": f"{value}"}
reloaded_function = schema.load(output)
assert reloaded_function == {"field_name_with_underscores": f"{value}"}