""" This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course. © Copyright Utrecht University (Department of Information and Computing Sciences) """ import uuid import pytest from pydantic import ValidationError from control_backend.schemas.program import ( BasicNorm, ConditionalNorm, Goal, InferredBelief, KeywordBelief, LogicalOperator, Phase, Plan, Program, SemanticBelief, Trigger, ) def base_norm() -> BasicNorm: return BasicNorm( id=uuid.uuid4(), name="testNormName", norm="testNormNorm", critical=False, ) def base_goal() -> Goal: return Goal( id=uuid.uuid4(), name="testGoalName", description="This description can be used to determine whether the goal has been achieved.", plan=Plan( id=uuid.uuid4(), name="testGoalPlanName", steps=[], ), can_fail=False, ) def base_trigger() -> Trigger: return Trigger( id=uuid.uuid4(), name="testTriggerName", condition=KeywordBelief( id=uuid.uuid4(), name="testTriggerKeywordBeliefTriggerName", keyword="Keyword", ), plan=Plan( id=uuid.uuid4(), name="testTriggerPlanName", steps=[], ), ) def base_phase() -> Phase: return Phase( id=uuid.uuid4(), norms=[base_norm()], goals=[base_goal()], triggers=[base_trigger()], ) def base_program() -> Program: return Program(phases=[base_phase()]) def invalid_program() -> dict: # wrong types inside phases list (not Phase objects) return { "phases": [ {"id": uuid.uuid4()}, # incomplete {"not_a_phase": True}, ] } def test_valid_program(): program = base_program() validated = Program.model_validate(program) assert isinstance(validated, Program) assert validated.phases[0].norms[0].norm == "testNormNorm" def test_valid_deepprogram(): program = base_program() validated = Program.model_validate(program) # validate nested components directly phase = validated.phases[0] assert isinstance(phase.goals[0], Goal) assert isinstance(phase.triggers[0], Trigger) assert isinstance(phase.norms[0], BasicNorm) def test_invalid_program(): bad = invalid_program() with pytest.raises(ValidationError): Program.model_validate(bad) def test_conditional_norm_parsing(): """ Check that pydantic is able to preserve the type of the norm, that it doesn't lose its "condition" field when serializing and deserializing. """ norm = ConditionalNorm( name="testNormName", id=uuid.uuid4(), norm="testNormNorm", critical=False, condition=KeywordBelief( name="testKeywordBelief", id=uuid.uuid4(), keyword="testKeywordBelief", ), ) program = Program( phases=[ Phase( name="Some phase", id=uuid.uuid4(), norms=[norm], goals=[], triggers=[], ), ], ) parsed_program = Program.model_validate_json(program.model_dump_json()) parsed_norm = parsed_program.phases[0].norms[0] assert hasattr(parsed_norm, "condition") assert isinstance(parsed_norm, ConditionalNorm) def test_belief_type_parsing(): """ Check that pydantic is able to discern between the different types of beliefs when serializing and deserializing. """ keyword_belief = KeywordBelief( name="testKeywordBelief", id=uuid.uuid4(), keyword="something", ) semantic_belief = SemanticBelief( name="testSemanticBelief", id=uuid.uuid4(), description="something", ) inferred_belief = InferredBelief( name="testInferredBelief", id=uuid.uuid4(), operator=LogicalOperator.OR, left=keyword_belief, right=semantic_belief, ) program = Program( phases=[ Phase( name="Some phase", id=uuid.uuid4(), norms=[], goals=[], triggers=[ Trigger( name="testTriggerKeywordTrigger", id=uuid.uuid4(), condition=keyword_belief, plan=Plan(name="testTriggerPlanName", id=uuid.uuid4(), steps=[]), ), Trigger( name="testTriggerSemanticTrigger", id=uuid.uuid4(), condition=semantic_belief, plan=Plan(name="testTriggerPlanName", id=uuid.uuid4(), steps=[]), ), Trigger( name="testTriggerInferredTrigger", id=uuid.uuid4(), condition=inferred_belief, plan=Plan(name="testTriggerPlanName", id=uuid.uuid4(), steps=[]), ), ], ), ], ) parsed_program = Program.model_validate_json(program.model_dump_json()) parsed_keyword_belief = parsed_program.phases[0].triggers[0].condition assert isinstance(parsed_keyword_belief, KeywordBelief) parsed_semantic_belief = parsed_program.phases[0].triggers[1].condition assert isinstance(parsed_semantic_belief, SemanticBelief) parsed_inferred_belief = parsed_program.phases[0].triggers[2].condition assert isinstance(parsed_inferred_belief, InferredBelief)