import uuid from datetime import date from types import SimpleNamespace import pytest from app.scheduling_service import calculate_schedule, check_for_cycle def test_cycle_detection(): id_a = uuid.uuid4() id_b = uuid.uuid4() id_c = uuid.uuid4() edges = [ (id_a, id_b), (id_b, id_c), ] # Adding C -> A should detect cycle assert check_for_cycle(edges, id_c, id_a) is True # Adding A -> A (self-loop) should detect cycle assert check_for_cycle(edges, id_a, id_a) is True # Adding C -> D (new node) should not detect cycle id_d = uuid.uuid4() assert check_for_cycle(edges, id_c, id_d) is False def test_cpm_forward_and_backward_pass(): id_a = uuid.uuid4() id_b = uuid.uuid4() id_c = uuid.uuid4() t_a = SimpleNamespace(id=id_a, duration=3, planned_start=date(2026, 1, 1), parent_id=None, progress=0) t_b = SimpleNamespace(id=id_b, duration=4, planned_start=None, parent_id=None, progress=0) t_c = SimpleNamespace(id=id_c, duration=2, planned_start=None, parent_id=None, progress=0) # A (3d) -[FS + 1 lag]-> B (4d) -[FS + 0 lag]-> C (2d) rel_ab = SimpleNamespace(from_task_id=id_a, to_task_id=id_b, relation_type="FS", lag_days=1) rel_bc = SimpleNamespace(from_task_id=id_b, to_task_id=id_c, relation_type="FS", lag_days=0) res = calculate_schedule([t_a, t_b, t_c], [rel_ab, rel_bc], project_start=date(2026, 1, 1)) sched = res["tasks_schedule"] # Task A: 2026-01-01, duration 3 is a gap -> ends 2026-01-04 assert sched[id_a]["planned_start"] == date(2026, 1, 1) assert sched[id_a]["planned_end"] == date(2026, 1, 4) # Task B starts right after A finishes (Jan 4) plus 1 lag day = Jan 5 assert sched[id_b]["planned_start"] == date(2026, 1, 5) # B: duration 4 gap -> Jan 5 + 4 = Jan 9 assert sched[id_b]["planned_end"] == date(2026, 1, 9) # Task C starts Jan 9, duration 2 gap -> ends Jan 11 assert sched[id_c]["planned_start"] == date(2026, 1, 9) assert sched[id_c]["planned_end"] == date(2026, 1, 11) # All three are on critical path assert id_a in res["critical_path"] assert id_b in res["critical_path"] assert id_c in res["critical_path"]