blackopsrepl/quickstart-template
2
1"""2Constraint verification tests.3 4These tests verify that each constraint behaves correctly in isolation.5Use ConstraintVerifier to test individual constraints without running the full solver.6"""7 8import pytest9from solverforge_legacy.solver.test import ConstraintVerifier10from my_quickstart.domain import Resource, Task, Schedule11from my_quickstart.constraints import define_constraints12 13 14@pytest.fixture15def constraint_verifier():16 """Create a constraint verifier for testing."""17 return ConstraintVerifier.build(18 define_constraints,19 Schedule,20 Task,21 )22 23 24# =============================================================================25# TEST DATA FIXTURES26# =============================================================================27 28@pytest.fixture29def alice():30 return Resource(name="Alice", capacity=100, skills={"python", "sql"})31 32 33@pytest.fixture34def bob():35 return Resource(name="Bob", capacity=50, skills={"java"})36 37 38# =============================================================================39# HARD CONSTRAINT TESTS40# =============================================================================41 42class TestRequiredSkill:43 """Tests for the 'Required skill missing' constraint."""44 45 def test_no_penalty_when_skill_matches(self, constraint_verifier, alice):46 """Task with matching skill should not be penalized."""47 task = Task(id="1", name="Python Task", duration=30, required_skill="python", resource=alice)48 49 constraint_verifier.verify_that("Required skill missing") \50 .given(task) \51 .penalizes_by(0)52 53 def test_penalty_when_skill_missing(self, constraint_verifier, bob):54 """Task assigned to resource without required skill should be penalized."""55 task = Task(id="1", name="Python Task", duration=30, required_skill="python", resource=bob)56 57 constraint_verifier.verify_that("Required skill missing") \58 .given(task) \59 .penalizes_by(1)60 61 def test_no_penalty_when_no_skill_required(self, constraint_verifier, alice):62 """Task with no skill requirement should not be penalized."""63 task = Task(id="1", name="Any Task", duration=30, required_skill="", resource=alice)64 65 constraint_verifier.verify_that("Required skill missing") \66 .given(task) \67 .penalizes_by(0)68 69 70class TestResourceCapacity:71 """Tests for the 'Resource capacity exceeded' constraint."""72 73 def test_no_penalty_under_capacity(self, constraint_verifier, alice):74 """Tasks under capacity should not be penalized."""75 task1 = Task(id="1", name="Task 1", duration=30, resource=alice)76 task2 = Task(id="2", name="Task 2", duration=40, resource=alice)77 # Total: 70, Capacity: 10078 79 constraint_verifier.verify_that("Resource capacity exceeded") \80 .given(task1, task2) \81 .penalizes_by(0)82 83 def test_penalty_over_capacity(self, constraint_verifier, bob):84 """Tasks exceeding capacity should be penalized by the overflow amount."""85 task1 = Task(id="1", name="Task 1", duration=30, resource=bob)86 task2 = Task(id="2", name="Task 2", duration=40, resource=bob)87 # Total: 70, Capacity: 50, Overflow: 2088 89 constraint_verifier.verify_that("Resource capacity exceeded") \90 .given(task1, task2) \91 .penalizes_by(20)92 93 94# =============================================================================95# SOFT CONSTRAINT TESTS96# =============================================================================97 98class TestMinimizeDuration:99 """Tests for the 'Minimize total duration' constraint."""100 101 def test_penalizes_by_duration(self, constraint_verifier, alice):102 """Each assigned task should be penalized by its duration."""103 task = Task(id="1", name="Task", duration=45, resource=alice)104 105 constraint_verifier.verify_that("Minimize total duration") \106 .given(task) \107 .penalizes_by(45)108 109 def test_unassigned_not_penalized(self, constraint_verifier):110 """Unassigned tasks should not be penalized."""111 task = Task(id="1", name="Task", duration=45, resource=None)112 113 constraint_verifier.verify_that("Minimize total duration") \114 .given(task) \115 .penalizes_by(0)116 117 118# =============================================================================119# INTEGRATION TEST120# =============================================================================121 122class TestFullSolution:123 """Test the full constraint set on a complete solution."""124 125 def test_feasible_solution(self, constraint_verifier, alice, bob):126 """A feasible solution should have no hard constraint violations."""127 tasks = [128 Task(id="1", name="Python Task", duration=30, required_skill="python", resource=alice),129 Task(id="2", name="SQL Task", duration=20, required_skill="sql", resource=alice),130 Task(id="3", name="Java Task", duration=40, required_skill="java", resource=bob),131 ]132 133 # Verify no hard violations134 constraint_verifier.verify_that("Required skill missing") \135 .given(*tasks) \136 .penalizes_by(0)137 138 constraint_verifier.verify_that("Resource capacity exceeded") \139 .given(*tasks) \140 .penalizes_by(0)141 