Source code for camcops_server.cc_modules.tests.cc_policy_tests

#!/usr/bin/env python

"""
camcops_server/cc_modules/tests/cc_policy_tests.py

===============================================================================

    Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
    Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

    This file is part of CamCOPS.

    CamCOPS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CamCOPS 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.

===============================================================================
"""

import logging
from typing import Dict

from cardinal_pythonlib.logs import BraceStyleAdapter
from pendulum import Date

from camcops_server.cc_modules.cc_policy import TokenizedPolicy
from camcops_server.cc_modules.cc_simpleobjects import (
    BarePatientInfo,
    IdNumReference,
)
from camcops_server.cc_modules.cc_unittest import ExtendedTestCase

log = BraceStyleAdapter(logging.getLogger(__name__))


# =============================================================================
# Unit tests
# =============================================================================


[docs]class PolicyTests(ExtendedTestCase): """ Unit tests. """ def test_policies(self) -> None: self.announce("test_policies") class TestRig(object): def __init__( self, policy: str, syntactically_valid: bool = None, valid: bool = None, ptinfo_satisfies_id_policy: bool = None, test_critical_single_numerical_id: bool = False, critical_single_numerical_id: int = None, compatible_with_tablet_id_policy: bool = None, is_idnum_mandatory_in_policy: Dict[int, bool] = None, ) -> None: self.policy = policy self.syntactically_valid = syntactically_valid self.valid = valid self.ptinfo_satisfies_id_policy = ptinfo_satisfies_id_policy self.test_critical_single_numerical_id = ( test_critical_single_numerical_id ) self.critical_single_numerical_id = ( critical_single_numerical_id ) self.compatible_with_tablet_id_policy = ( compatible_with_tablet_id_policy ) self.is_idnum_mandatory_in_policy = ( is_idnum_mandatory_in_policy or {} ) # type: Dict[int, bool] valid_idnums = list(range(1, 10 + 1)) # noinspection PyTypeChecker bpi = BarePatientInfo( forename="forename", surname="surname", dob=Date.today(), # random value email="patient@example.com", sex="F", idnum_definitions=[IdNumReference(1, 1), IdNumReference(10, 3)], ) test_policies = [ TestRig("", syntactically_valid=False, valid=False), TestRig( "sex AND (failure", syntactically_valid=False, valid=False ), TestRig("sex AND NOT", syntactically_valid=False, valid=False), TestRig("OR OR", syntactically_valid=False, valid=False), TestRig("idnum99", syntactically_valid=True, valid=False), TestRig( "sex AND idnum1", syntactically_valid=True, valid=True, ptinfo_satisfies_id_policy=True, test_critical_single_numerical_id=True, critical_single_numerical_id=1, compatible_with_tablet_id_policy=True, is_idnum_mandatory_in_policy={1: True, 3: False}, ), TestRig( "sex AND NOT idnum1", syntactically_valid=True, valid=False, # not compatible with tablet policy ptinfo_satisfies_id_policy=False, test_critical_single_numerical_id=True, critical_single_numerical_id=None, compatible_with_tablet_id_policy=False, is_idnum_mandatory_in_policy={1: False, 3: False}, ), TestRig( "sex AND NOT idnum2", syntactically_valid=True, valid=False, # not compatible with tablet policy test_critical_single_numerical_id=True, critical_single_numerical_id=None, compatible_with_tablet_id_policy=False, is_idnum_mandatory_in_policy={1: False, 3: False}, ), TestRig( "sex AND NOT idnum1 AND idnum3", syntactically_valid=True, valid=True, test_critical_single_numerical_id=True, critical_single_numerical_id=3, compatible_with_tablet_id_policy=True, is_idnum_mandatory_in_policy={1: False, 3: True}, ), TestRig( "sex AND NOT idnum2 AND idnum10", syntactically_valid=True, valid=True, test_critical_single_numerical_id=True, critical_single_numerical_id=10, compatible_with_tablet_id_policy=True, is_idnum_mandatory_in_policy={1: False, 3: False}, ), TestRig( "sex AND NOT idnum1 AND NOT idnum2 AND NOT idnum3", syntactically_valid=True, valid=False, # not compatible with tablet policy test_critical_single_numerical_id=True, critical_single_numerical_id=None, compatible_with_tablet_id_policy=False, is_idnum_mandatory_in_policy={1: False, 3: False}, ), TestRig( "sex AND NOT (idnum1 OR idnum2 OR idnum3)", # same as previous syntactically_valid=True, valid=False, # not compatible with tablet policy test_critical_single_numerical_id=True, critical_single_numerical_id=None, compatible_with_tablet_id_policy=False, is_idnum_mandatory_in_policy={1: False, 3: False}, ), TestRig( "NOT (sex OR forename OR surname OR dob OR anyidnum)", syntactically_valid=True, valid=False, # not compatible with tablet policy test_critical_single_numerical_id=True, critical_single_numerical_id=None, compatible_with_tablet_id_policy=False, is_idnum_mandatory_in_policy={1: False, 3: False}, ), TestRig( """ NOT (sex OR forename OR surname OR dob OR idnum1 OR idnum2 OR idnum3) """, syntactically_valid=True, valid=False, # not compatible with tablet policy test_critical_single_numerical_id=True, critical_single_numerical_id=None, compatible_with_tablet_id_policy=False, is_idnum_mandatory_in_policy={1: False, 3: False}, ), TestRig( "sex AND idnum1 AND otheridnum AND NOT address", syntactically_valid=True, valid=True, test_critical_single_numerical_id=True, critical_single_numerical_id=None, compatible_with_tablet_id_policy=True, is_idnum_mandatory_in_policy={1: True, 3: False}, ), TestRig( "sex AND idnum1 AND NOT (otheridnum OR forename OR surname OR " "dob OR address OR gp OR otherdetails)", syntactically_valid=True, valid=True, test_critical_single_numerical_id=True, critical_single_numerical_id=1, compatible_with_tablet_id_policy=True, is_idnum_mandatory_in_policy={1: True, 3: False}, ), TestRig( "forename AND surname AND dob AND sex AND anyidnum", syntactically_valid=True, valid=True, test_critical_single_numerical_id=True, critical_single_numerical_id=None, compatible_with_tablet_id_policy=True, is_idnum_mandatory_in_policy={1: False, 3: False}, ), TestRig( "forename AND surname AND email AND sex AND idnum1", syntactically_valid=True, valid=True, test_critical_single_numerical_id=True, critical_single_numerical_id=1, compatible_with_tablet_id_policy=True, is_idnum_mandatory_in_policy={1: True, 3: False}, ), TestRig( "email AND sex AND idnum1", syntactically_valid=True, valid=True, ptinfo_satisfies_id_policy=True, test_critical_single_numerical_id=True, critical_single_numerical_id=1, compatible_with_tablet_id_policy=True, is_idnum_mandatory_in_policy={1: True, 3: False}, ), ] test_idnums = [None, -1, 1, 3] correct_msg = "... correct" for tp in test_policies: policy_string = tp.policy log.info("Testing {!r}", policy_string) p = TokenizedPolicy(policy_string) p.set_valid_idnums(valid_idnums) log.debug("Testing is_syntactically_valid()") x = p.is_syntactically_valid() self.assertIsInstance(x, bool) log.info("is_syntactically_valid() -> {!r}".format(x)) if tp.syntactically_valid is not None: self.assertEqual(x, tp.syntactically_valid) log.info(correct_msg) log.debug("Testing is_valid()") x = p.is_valid(verbose=True) self.assertIsInstance(x, bool) log.info( "is_valid(valid_idnums={!r}) -> {!r}".format(valid_idnums, x) ) if tp.valid is not None: self.assertEqual(x, tp.valid) log.info(correct_msg) log.debug("Testing is_idnum_mandatory_in_policy()") for which_idnum in test_idnums: log.debug("... for which_idnum = {}", which_idnum) x = p.is_idnum_mandatory_in_policy( which_idnum=which_idnum, valid_idnums=valid_idnums, verbose=True, ) self.assertIsInstance(x, bool) log.info( "is_idnum_mandatory_in_policy(which_idnum={!r}, " "valid_idnums={!r}) -> {!r}".format( which_idnum, valid_idnums, x ) ) if tp.is_idnum_mandatory_in_policy: if which_idnum in tp.is_idnum_mandatory_in_policy: self.assertEqual( x, tp.is_idnum_mandatory_in_policy[which_idnum] ) log.info(correct_msg) log.debug("Testing find_critical_single_numerical_id()") x = p.find_critical_single_numerical_id( valid_idnums=valid_idnums, verbose=True ) self.assertIsInstanceOrNone(x, int) log.info( "find_critical_single_numerical_id(valid_idnums={!r}) " "-> {!r}".format(valid_idnums, x) ) if tp.test_critical_single_numerical_id: self.assertEqual(x, tp.critical_single_numerical_id) log.info(correct_msg) log.debug("Testing compatible_with_tablet_id_policy()") x = p.compatible_with_tablet_id_policy( valid_idnums=valid_idnums, verbose=True ) self.assertIsInstance(x, bool) log.info( "compatible_with_tablet_id_policy(valid_idnums={!r}) " "-> {!r}".format(valid_idnums, x) ) if tp.compatible_with_tablet_id_policy is not None: self.assertEqual(x, tp.compatible_with_tablet_id_policy) log.info(correct_msg) log.debug("Testing satisfies_id_policy()") x = p.satisfies_id_policy(bpi) self.assertIsInstance(x, bool) log.info("satisfies_id_policy(bpi={}) -> {!r}".format(bpi, x)) if tp.ptinfo_satisfies_id_policy is not None: self.assertEqual(x, tp.ptinfo_satisfies_id_policy) log.info(correct_msg)