creating unit tests for schema_validation (mostly focusing on the Ship dataclass, as it has the most validation requirement)

This commit is contained in:
max_metz 2023-09-08 23:43:28 +02:00
parent 20f860586f
commit 4195655e4e
3 changed files with 253 additions and 7 deletions

View File

@ -35,11 +35,22 @@ def length_matches_exactly(query_value, length_value):
### Ship dataclass (BreCal.schema.model.Ship) ###
@validation_state_and_validation_name("ship_bollard_pull")
def ship_bollard_is_none_or_in_range(ship):
@validation_state_and_validation_name("ship_bollard_pull_none_or_value_in_range")
def ship_bollard_pull_is_none_or_in_range(ship):
"""a ship should either have its bollard_pull between 0 and 500, or have an undefined bollard_pull (when not a tug)"""
return (ship.bollard_pull is None) or (value_in_range(ship.bollard_pull, 0, 500))
@validation_state_and_validation_name("ship_max_draft_none_or_value_in_range")
def ship_max_draft_is_none_or_in_range(ship):
"""a ship should either have its max_draft between 0 and 20, or have an undefined max_draft (when not a tug)"""
return (ship.max_draft is None) or (value_in_range(ship.max_draft, 0, 20))
@validation_state_and_validation_name("ship_participant_id_none_or_int")
def ship_participant_id_is_none_or_int(ship):
"""a ship should either have its participant_id defined (integer when ship is a tug), or have an undefined participant_id (when not a tug)"""
return isinstance(ship.participant_id, int) or (ship.participant_id is None)
@validation_state_and_validation_name("ship_length")
def ship_length_in_range(ship):
"""ship length-values should be valid. between 0 and 500 meters is plausible. returns bool"""
@ -50,11 +61,6 @@ def ship_width_in_range(ship):
"""ship length-values should be valid. between 0 and 500 meters is plausible. returns bool"""
return value_in_range(ship.width, 0, 500)
@validation_state_and_validation_name("ship_max_draft")
def ship_max_draft_in_range(ship):
"""ship max_draft-values should be valid. between 0 and 500 meters is plausible. returns bool"""
return value_in_range(ship.max_draft, 0, 20)
@validation_state_and_validation_name("ship_eni_length")
def ship_eni_len_is_eight(ship):
"""eni-no. are standardized. They should have exactly eight characters. returns bool"""

View File

@ -0,0 +1,240 @@
import pytest
from brecal_utils.stubs.ship import get_ship_simple
from brecal_utils.validators.schema_validation import ship_length_in_range, ship_width_in_range, ship_bollard_pull_is_none_or_in_range, ship_participant_id_is_none_or_int, ship_max_draft_is_none_or_in_range, ship_eni_len_is_eight, ship_callsign_len_is_seven_at_maximum, ship_imo_len_is_seven, ship_bollard_pull_is_defined_or_is_not_tug, ship_max_draft_is_defined_or_is_not_tug, ship_participant_id_is_defined_or_is_not_tug
def test_ship_length_valid_range_234_is_valid():
ship = get_ship_simple()
ship.length = 234
assert ship_length_in_range(ship)[0], f"ship length must be between 0 and 500 meters"
return
def test_ship_length_maximum_not_valid_range():
ship = get_ship_simple()
ship.length = 500
with pytest.raises(AssertionError):
assert ship_length_in_range(ship)[0], f"ship length must be between 0 and 500 meters, but is 500"
return
def test_ship_length_minimum_not_valid_range():
ship = get_ship_simple()
ship.length = 0
with pytest.raises(AssertionError):
assert ship_length_in_range(ship)[0], f"ship length must be between 0 and 500 meters, but is 0"
return
def test_ship_width_valid_range_137_is_valid():
ship = get_ship_simple()
ship.width = 137
assert ship_width_in_range(ship)[0], f"ship width must be between 0 and 500 meters"
return
def test_ship_width_maximum_not_valid_range():
ship = get_ship_simple()
ship.width = 500
with pytest.raises(AssertionError):
assert ship_width_in_range(ship)[0], f"ship width must be between 0 and 500 meters, but is 500"
return
def test_ship_width_minimum_not_valid_range():
ship = get_ship_simple()
ship.width = 0
with pytest.raises(AssertionError):
assert ship_width_in_range(ship)[0], f"ship width must be between 0 and 500 meters, but is 0"
return
# not tug: values can be None and raise no error
def test_ship_bollard_pull_is_none_and_not_tug():
ship = get_ship_simple()
ship.is_tug = False
ship.bollard_pull = None
assert ship_bollard_pull_is_none_or_in_range(ship)[0], f"the bollard_pull should either be undefined or between 0 and 500 meters"
return
def test_ship_participant_id_is_none_and_not_tug():
ship = get_ship_simple()
ship.is_tug = False
ship.participant_id = None
assert ship_participant_id_is_none_or_int(ship)[0], f"the participant_id should either be undefined or an integer id"
return
def test_ship_max_draft_is_none_and_not_tug():
ship = get_ship_simple()
ship.is_tug = False
ship.max_draft = None
assert ship_max_draft_is_none_or_in_range(ship)[0], f"the max_draft should either be undefined or between 0 and 20 meters"
return
# tug: values must be set, and are set. all tests should be accepted without assertion
def test_ship_is_tug_bollard_pull_is_not_none():
ship = get_ship_simple()
ship.is_tug = True
ship.bollard_pull = 311
assert ship_bollard_pull_is_none_or_in_range(ship)[0], f"the bollard_pull should either be undefined or between 0 and 500 meters"
return
def test_ship_is_tug_max_draft_is_not_none():
ship = get_ship_simple()
ship.is_tug = True
ship.max_draft = 17
assert ship_max_draft_is_none_or_in_range(ship)[0], f"the max_draft should either be undefined or between 0 and 20 meters"
return
def test_ship_is_tug_participant_id_is_not_none():
from brecal_utils.stubs import generate_uuid1_int
ship = get_ship_simple()
ship.is_tug = True
ship.participant_id = generate_uuid1_int()
assert ship_participant_id_is_none_or_int(ship)[0], f"the participant_id should either be undefined or an integer id"
return
def test_ship_is_tug_participant_id_is_str_and_fails():
# note: this is an artificial test case. However, it ensures that operators using the backend cannot create an id incorrectly
from brecal_utils.stubs import generate_uuid1_int
ship = get_ship_simple()
ship.is_tug = True
ship.participant_id = str(generate_uuid1_int())
with pytest.raises(AssertionError):
assert ship_participant_id_is_none_or_int(ship)[0], f"the participant_id should either be None or int, but is str"
return
# tug: values must be set, but are not. all tests should raise AssertionError
def test_ship_is_tug_bollard_pull_but_is_none_fails():
ship = get_ship_simple()
ship.is_tug = True
ship.bollard_pull = None
with pytest.raises(AssertionError):
assert ship_bollard_pull_is_defined_or_is_not_tug(ship)[0], f"the bollard_pull cannot be None, if the ship is a tug"
return
def test_ship_is_tug_max_draft_but_is_none_fails():
ship = get_ship_simple()
ship.is_tug = True
ship.max_draft = None
with pytest.raises(AssertionError):
assert ship_max_draft_is_defined_or_is_not_tug(ship)[0], f"the max_draft cannot be None, if the ship is a tug"
return
def test_ship_is_tug_participant_id_but_is_none_fails():
ship = get_ship_simple()
ship.is_tug = True
ship.participant_id = None
with pytest.raises(AssertionError):
assert ship_participant_id_is_defined_or_is_not_tug(ship)[0], f"the participant_id cannot be None, if the ship is a tug"
return
# tug: values must be in valid range
# # sequence: 1.) is valid, 2.) is too small, 3.) is too large
def test_ship_is_tug_bollard_pull_in_range_311_valid():
ship = get_ship_simple()
ship.is_tug = True
ship.bollard_pull = 311
assert ship_bollard_pull_is_none_or_in_range(ship)[0], f"the bollard_pull must be 0<value<500"
return
def test_ship_is_tug_bollard_pull_in_range_minimum_not_valid():
ship = get_ship_simple()
ship.is_tug = True
ship.bollard_pull = 0
with pytest.raises(AssertionError):
assert ship_bollard_pull_is_none_or_in_range(ship)[0], f"the bollard_pull must be 0<value<500, but is 0"
return
def test_ship_is_tug_bollard_pull_in_range_maximum_not_valid():
ship = get_ship_simple()
ship.is_tug = True
ship.bollard_pull = 500
with pytest.raises(AssertionError):
assert ship_bollard_pull_is_none_or_in_range(ship)[0], f"the bollard_pull must be 0<value<500, but is 500"
return
def test_ship_is_tug_max_draft_in_range_11_valid():
ship = get_ship_simple()
ship.is_tug = True
ship.max_draft = 11
assert ship_max_draft_is_none_or_in_range(ship)[0], f"the max_draft must be 0<value<20"
return
def test_ship_is_tug_max_draft_in_range_minimum_not_valid():
ship = get_ship_simple()
ship.is_tug = True
ship.max_draft = 0
with pytest.raises(AssertionError):
assert ship_max_draft_is_none_or_in_range(ship)[0], f"the max_draft must be 0<value<20, but is 0"
return
def test_ship_is_tug_max_draft_in_range_maximum_not_valid():
ship = get_ship_simple()
ship.is_tug = True
ship.max_draft = 20
with pytest.raises(AssertionError):
assert ship_max_draft_is_none_or_in_range(ship)[0], f"the max_draft must be 0<value<20, but is 20"
return
# Length tests
def test_ship_eni_len_is_eight_and_passes():
ship = get_ship_simple()
ship.eni = "01234567" # 8 character example
assert ship_eni_len_is_eight(ship)[0], f"the eni-no. should have exactly 8 characters"
return
def test_ship_eni_len_is_eight_but_has_nine():
ship = get_ship_simple()
ship.eni = "012345678" # 9 character example
with pytest.raises(AssertionError):
assert ship_eni_len_is_eight(ship)[0], f"the eni-no. should have exactly 8 characters, but has 9 characters"
return
def test_ship_callsign_len_is_seven_at_maximum_seven_passes():
ship = get_ship_simple()
ship.callsign = "0123456" # 7 character example
assert ship_callsign_len_is_seven_at_maximum(ship)[0], f"the callsign no. should have at maximum 7 characters"
return
def test_ship_callsign_len_is_seven_at_maximum_six_passes():
ship = get_ship_simple()
ship.callsign = "012345" # 6 character example
assert ship_callsign_len_is_seven_at_maximum(ship)[0], f"the callsign no. should have at maximum 7 characters"
return
def test_ship_callsign_len_is_seven_at_maximum_eight_fails():
ship = get_ship_simple()
ship.callsign = "01234567" # 8 character example
with pytest.raises(AssertionError):
assert ship_callsign_len_is_seven_at_maximum(ship)[0], f"the callsign no. should have at maximum 7 characters, but has 8"
return
def test_ship_callsign_len_is_seven_at_maximum_zero_passes():
ship = get_ship_simple()
ship.callsign = "" # 0 character example
assert ship_callsign_len_is_seven_at_maximum(ship)[0], f"the callsign no. should have at maximum 7 characters"
return
def test_imo_len_is_seven_and_seven_passes():
ship = get_ship_simple()
ship.imo = 1234567 # integer required
assert ship_imo_len_is_seven(ship)[0], f"a ship's IMO no. should have exactly 7 characters"
return
def test_imo_len_is_seven_and_eight_fails():
ship = get_ship_simple()
ship.imo = 12345678 # integer required
with pytest.raises(AssertionError):
assert ship_imo_len_is_seven(ship)[0], f"a ship's IMO no. should have exactly 7 characters, but it has 8 character"
return
def test_imo_len_is_seven_and_one_fails():
ship = get_ship_simple()
ship.imo = 1 # integer required
with pytest.raises(AssertionError):
assert ship_imo_len_is_seven(ship)[0], f"a ship's IMO no. should have exactly 7 characters, but is has 1 character"
return
if __name__=="__main__":
pass