diff --git a/backend/tests/test_approval_task_links.py b/backend/tests/test_approval_task_links.py index 084309b8..232baa38 100644 --- a/backend/tests/test_approval_task_links.py +++ b/backend/tests/test_approval_task_links.py @@ -68,80 +68,86 @@ def test_normalize_task_ids_dedupes_and_merges_sources() -> None: @pytest.mark.asyncio async def test_task_counts_for_board_supports_multi_task_links_and_legacy_rows() -> None: engine = await _make_engine() - async with await _make_session(engine) as session: - board_id, task_a, task_b, task_c = await _seed_board(session) + try: + async with await _make_session(engine) as session: + board_id, task_a, task_b, task_c = await _seed_board(session) - approval_pending_multi = Approval( - board_id=board_id, - task_id=task_a, - action_type="task.update", - confidence=80, - status="pending", - ) - approval_approved = Approval( - board_id=board_id, - task_id=task_a, - action_type="task.complete", - confidence=90, - status="approved", - ) - approval_pending_two = Approval( - board_id=board_id, - task_id=task_b, - action_type="task.assign", - confidence=75, - status="pending", - ) - approval_legacy = Approval( - board_id=board_id, - task_id=task_c, - action_type="task.comment", - confidence=65, - status="pending", - ) - session.add(approval_pending_multi) - session.add(approval_approved) - session.add(approval_pending_two) - session.add(approval_legacy) - await session.flush() + approval_pending_multi = Approval( + board_id=board_id, + task_id=task_a, + action_type="task.update", + confidence=80, + status="pending", + ) + approval_approved = Approval( + board_id=board_id, + task_id=task_a, + action_type="task.complete", + confidence=90, + status="approved", + ) + approval_pending_two = Approval( + board_id=board_id, + task_id=task_b, + action_type="task.assign", + confidence=75, + status="pending", + ) + approval_legacy = Approval( + board_id=board_id, + task_id=task_c, + action_type="task.comment", + confidence=65, + status="pending", + ) + session.add(approval_pending_multi) + session.add(approval_approved) + session.add(approval_pending_two) + session.add(approval_legacy) + await session.flush() - session.add( - ApprovalTaskLink(approval_id=approval_pending_multi.id, task_id=task_a), - ) - session.add( - ApprovalTaskLink(approval_id=approval_pending_multi.id, task_id=task_b), - ) - session.add(ApprovalTaskLink(approval_id=approval_approved.id, task_id=task_a)) - session.add(ApprovalTaskLink(approval_id=approval_pending_two.id, task_id=task_b)) - session.add(ApprovalTaskLink(approval_id=approval_pending_two.id, task_id=task_c)) - await session.commit() + session.add( + ApprovalTaskLink(approval_id=approval_pending_multi.id, task_id=task_a), + ) + session.add( + ApprovalTaskLink(approval_id=approval_pending_multi.id, task_id=task_b), + ) + session.add(ApprovalTaskLink(approval_id=approval_approved.id, task_id=task_a)) + session.add(ApprovalTaskLink(approval_id=approval_pending_two.id, task_id=task_b)) + session.add(ApprovalTaskLink(approval_id=approval_pending_two.id, task_id=task_c)) + await session.commit() - counts = await task_counts_for_board(session, board_id=board_id) + counts = await task_counts_for_board(session, board_id=board_id) - assert counts[task_a] == (2, 1) - assert counts[task_b] == (2, 2) - assert counts[task_c] == (2, 2) + assert counts[task_a] == (2, 1) + assert counts[task_b] == (2, 2) + assert counts[task_c] == (2, 2) + finally: + await engine.dispose() @pytest.mark.asyncio async def test_load_task_ids_by_approval_preserves_insert_order() -> None: engine = await _make_engine() - async with await _make_session(engine) as session: - board_id, task_a, task_b, task_c = await _seed_board(session) + try: + async with await _make_session(engine) as session: + board_id, task_a, task_b, task_c = await _seed_board(session) - approval = Approval( - board_id=board_id, - task_id=task_a, - action_type="task.update", - confidence=88, - status="pending", - ) - session.add(approval) - await session.flush() - session.add(ApprovalTaskLink(approval_id=approval.id, task_id=task_a)) - session.add(ApprovalTaskLink(approval_id=approval.id, task_id=task_b)) - session.add(ApprovalTaskLink(approval_id=approval.id, task_id=task_c)) - await session.commit() + approval = Approval( + board_id=board_id, + task_id=task_a, + action_type="task.update", + confidence=88, + status="pending", + ) + session.add(approval) + await session.flush() + session.add(ApprovalTaskLink(approval_id=approval.id, task_id=task_a)) + session.add(ApprovalTaskLink(approval_id=approval.id, task_id=task_b)) + session.add(ApprovalTaskLink(approval_id=approval.id, task_id=task_c)) + await session.commit() - mapping = await load_task_ids_by_approval(session, approval_ids=[approval.id]) - assert mapping[approval.id] == [task_a, task_b, task_c] + mapping = await load_task_ids_by_approval(session, approval_ids=[approval.id]) + assert mapping[approval.id] == [task_a, task_b, task_c] + finally: + await engine.dispose() diff --git a/backend/tests/test_task_dependencies_integration.py b/backend/tests/test_task_dependencies_integration.py index 31f414c1..77cecfbe 100644 --- a/backend/tests/test_task_dependencies_integration.py +++ b/backend/tests/test_task_dependencies_integration.py @@ -43,128 +43,151 @@ async def _seed_board_and_tasks( @pytest.mark.asyncio async def test_validate_dependency_update_rejects_self_dependency() -> None: engine = await _make_engine() - async with await _make_session(engine) as session: - board_id = uuid4() - task_id = uuid4() - await _seed_board_and_tasks(session, board_id=board_id, task_ids=[task_id]) + try: + async with await _make_session(engine) as session: + board_id = uuid4() + task_id = uuid4() + await _seed_board_and_tasks(session, board_id=board_id, task_ids=[task_id]) - with pytest.raises(HTTPException) as exc: - await td.validate_dependency_update( - session, - board_id=board_id, - task_id=task_id, - depends_on_task_ids=[task_id], - ) - assert exc.value.status_code == 422 + with pytest.raises(HTTPException) as exc: + await td.validate_dependency_update( + session, + board_id=board_id, + task_id=task_id, + depends_on_task_ids=[task_id], + ) + assert exc.value.status_code == 422 + finally: + await engine.dispose() @pytest.mark.asyncio async def test_validate_dependency_update_404s_when_dependency_missing() -> None: engine = await _make_engine() - async with await _make_session(engine) as session: - board_id = uuid4() - task_id = uuid4() - dep_id = uuid4() - await _seed_board_and_tasks(session, board_id=board_id, task_ids=[task_id]) + try: + async with await _make_session(engine) as session: + board_id = uuid4() + task_id = uuid4() + dep_id = uuid4() + await _seed_board_and_tasks(session, board_id=board_id, task_ids=[task_id]) - with pytest.raises(HTTPException) as exc: - await td.validate_dependency_update( - session, - board_id=board_id, - task_id=task_id, - depends_on_task_ids=[dep_id], - ) - assert exc.value.status_code == 404 - detail = exc.value.detail - assert isinstance(detail, dict) - assert detail["missing_task_ids"] == [str(dep_id)] + with pytest.raises(HTTPException) as exc: + await td.validate_dependency_update( + session, + board_id=board_id, + task_id=task_id, + depends_on_task_ids=[dep_id], + ) + assert exc.value.status_code == 404 + detail = exc.value.detail + assert isinstance(detail, dict) + assert detail["missing_task_ids"] == [str(dep_id)] + finally: + await engine.dispose() @pytest.mark.asyncio async def test_validate_dependency_update_detects_cycle() -> None: engine = await _make_engine() - async with await _make_session(engine) as session: - board_id = uuid4() - a, b = uuid4(), uuid4() - await _seed_board_and_tasks(session, board_id=board_id, task_ids=[a, b]) + try: + async with await _make_session(engine) as session: + board_id = uuid4() + a, b = uuid4(), uuid4() + await _seed_board_and_tasks(session, board_id=board_id, task_ids=[a, b]) - # existing edge a -> b - session.add(TaskDependency(board_id=board_id, task_id=a, depends_on_task_id=b)) - await session.commit() + # existing edge a -> b + session.add(TaskDependency(board_id=board_id, task_id=a, depends_on_task_id=b)) + await session.commit() - # update b -> a introduces cycle - with pytest.raises(HTTPException) as exc: - await td.validate_dependency_update( - session, - board_id=board_id, - task_id=b, - depends_on_task_ids=[a], - ) - assert exc.value.status_code == 409 + # update b -> a introduces cycle + with pytest.raises(HTTPException) as exc: + await td.validate_dependency_update( + session, + board_id=board_id, + task_id=b, + depends_on_task_ids=[a], + ) + assert exc.value.status_code == 409 + finally: + await engine.dispose() @pytest.mark.asyncio async def test_dependency_queries_and_replace_and_dependents() -> None: engine = await _make_engine() - async with await _make_session(engine) as session: - board_id = uuid4() - t1, t2, t3 = uuid4(), uuid4(), uuid4() - await _seed_board_and_tasks(session, board_id=board_id, task_ids=[t1, t2, t3]) + try: + async with await _make_session(engine) as session: + board_id = uuid4() + t1, t2, t3 = uuid4(), uuid4(), uuid4() + await _seed_board_and_tasks(session, board_id=board_id, task_ids=[t1, t2, t3]) - # seed deps: t1 depends on t2 then t3 - session.add(TaskDependency(board_id=board_id, task_id=t1, depends_on_task_id=t2)) - session.add(TaskDependency(board_id=board_id, task_id=t1, depends_on_task_id=t3)) - await session.commit() + # seed deps: t1 depends on t2 then t3 + session.add(TaskDependency(board_id=board_id, task_id=t1, depends_on_task_id=t2)) + session.add(TaskDependency(board_id=board_id, task_id=t1, depends_on_task_id=t3)) + await session.commit() - # cover empty input short-circuit - assert await td.dependency_ids_by_task_id(session, board_id=board_id, task_ids=[]) == {} + # cover empty input short-circuit + assert await td.dependency_ids_by_task_id(session, board_id=board_id, task_ids=[]) == {} - deps_map = await td.dependency_ids_by_task_id(session, board_id=board_id, task_ids=[t1, t2]) - assert deps_map[t1] == [t2, t3] - assert deps_map.get(t2, []) == [] + deps_map = await td.dependency_ids_by_task_id( + session, board_id=board_id, task_ids=[t1, t2] + ) + assert deps_map[t1] == [t2, t3] + assert deps_map.get(t2, []) == [] - # mark t2 done, t3 not - task2 = (await session.exec(select(Task).where(col(Task.id) == t2))).first() - assert task2 is not None - task2.status = td.DONE_STATUS - await session.commit() + # mark t2 done, t3 not + task2 = (await session.exec(select(Task).where(col(Task.id) == t2))).first() + assert task2 is not None + task2.status = td.DONE_STATUS + await session.commit() - # cover empty input short-circuit - assert await td.dependency_status_by_id(session, board_id=board_id, dependency_ids=[]) == {} + # cover empty input short-circuit + assert ( + await td.dependency_status_by_id(session, board_id=board_id, dependency_ids=[]) + == {} + ) - status_map = await td.dependency_status_by_id( - session, board_id=board_id, dependency_ids=[t2, t3] - ) - assert status_map[t2] == td.DONE_STATUS - assert status_map[t3] != td.DONE_STATUS + status_map = await td.dependency_status_by_id( + session, board_id=board_id, dependency_ids=[t2, t3] + ) + assert status_map[t2] == td.DONE_STATUS + assert status_map[t3] != td.DONE_STATUS - blocked = await td.blocked_by_for_task(session, board_id=board_id, task_id=t1) - assert blocked == [t3] + blocked = await td.blocked_by_for_task(session, board_id=board_id, task_id=t1) + assert blocked == [t3] - # cover early return when no deps provided - assert ( - await td.blocked_by_for_task(session, board_id=board_id, task_id=t1, dependency_ids=[]) - == [] - ) + # cover early return when no deps provided + assert ( + await td.blocked_by_for_task( + session, board_id=board_id, task_id=t1, dependency_ids=[] + ) + == [] + ) - # replace deps with duplicates (deduped) -> [t3] - out = await td.replace_task_dependencies( - session, - board_id=board_id, - task_id=t1, - depends_on_task_ids=[t3, t3], - ) - await session.commit() - assert out == [t3] + # replace deps with duplicates (deduped) -> [t3] + out = await td.replace_task_dependencies( + session, + board_id=board_id, + task_id=t1, + depends_on_task_ids=[t3, t3], + ) + await session.commit() + assert out == [t3] - deps_map2 = await td.dependency_ids_by_task_id(session, board_id=board_id, task_ids=[t1]) - assert deps_map2[t1] == [t3] + deps_map2 = await td.dependency_ids_by_task_id( + session, board_id=board_id, task_ids=[t1] + ) + assert deps_map2[t1] == [t3] - dependents = await td.dependent_task_ids(session, board_id=board_id, dependency_task_id=t3) - assert dependents == [t1] + dependents = await td.dependent_task_ids( + session, board_id=board_id, dependency_task_id=t3 + ) + assert dependents == [t1] - # also exercise explicit dependency_ids passed - blocked2 = await td.blocked_by_for_task( - session, board_id=board_id, task_id=t1, dependency_ids=[t3] - ) - assert blocked2 == [t3] + # also exercise explicit dependency_ids passed + blocked2 = await td.blocked_by_for_task( + session, board_id=board_id, task_id=t1, dependency_ids=[t3] + ) + assert blocked2 == [t3] + finally: + await engine.dispose()