test: refactor async context management in approval and dependency tests

This commit is contained in:
Abhimanyu Saharan
2026-02-12 14:14:29 +05:30
parent 3116262a57
commit cfd62abe66
2 changed files with 189 additions and 160 deletions

View File

@@ -68,80 +68,86 @@ def test_normalize_task_ids_dedupes_and_merges_sources() -> None:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_task_counts_for_board_supports_multi_task_links_and_legacy_rows() -> None: async def test_task_counts_for_board_supports_multi_task_links_and_legacy_rows() -> None:
engine = await _make_engine() engine = await _make_engine()
async with await _make_session(engine) as session: try:
board_id, task_a, task_b, task_c = await _seed_board(session) async with await _make_session(engine) as session:
board_id, task_a, task_b, task_c = await _seed_board(session)
approval_pending_multi = Approval( approval_pending_multi = Approval(
board_id=board_id, board_id=board_id,
task_id=task_a, task_id=task_a,
action_type="task.update", action_type="task.update",
confidence=80, confidence=80,
status="pending", status="pending",
) )
approval_approved = Approval( approval_approved = Approval(
board_id=board_id, board_id=board_id,
task_id=task_a, task_id=task_a,
action_type="task.complete", action_type="task.complete",
confidence=90, confidence=90,
status="approved", status="approved",
) )
approval_pending_two = Approval( approval_pending_two = Approval(
board_id=board_id, board_id=board_id,
task_id=task_b, task_id=task_b,
action_type="task.assign", action_type="task.assign",
confidence=75, confidence=75,
status="pending", status="pending",
) )
approval_legacy = Approval( approval_legacy = Approval(
board_id=board_id, board_id=board_id,
task_id=task_c, task_id=task_c,
action_type="task.comment", action_type="task.comment",
confidence=65, confidence=65,
status="pending", status="pending",
) )
session.add(approval_pending_multi) session.add(approval_pending_multi)
session.add(approval_approved) session.add(approval_approved)
session.add(approval_pending_two) session.add(approval_pending_two)
session.add(approval_legacy) session.add(approval_legacy)
await session.flush() await session.flush()
session.add( session.add(
ApprovalTaskLink(approval_id=approval_pending_multi.id, task_id=task_a), ApprovalTaskLink(approval_id=approval_pending_multi.id, task_id=task_a),
) )
session.add( session.add(
ApprovalTaskLink(approval_id=approval_pending_multi.id, task_id=task_b), 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_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_b))
session.add(ApprovalTaskLink(approval_id=approval_pending_two.id, task_id=task_c)) session.add(ApprovalTaskLink(approval_id=approval_pending_two.id, task_id=task_c))
await session.commit() 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_a] == (2, 1)
assert counts[task_b] == (2, 2) assert counts[task_b] == (2, 2)
assert counts[task_c] == (2, 2) assert counts[task_c] == (2, 2)
finally:
await engine.dispose()
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_load_task_ids_by_approval_preserves_insert_order() -> None: async def test_load_task_ids_by_approval_preserves_insert_order() -> None:
engine = await _make_engine() engine = await _make_engine()
async with await _make_session(engine) as session: try:
board_id, task_a, task_b, task_c = await _seed_board(session) async with await _make_session(engine) as session:
board_id, task_a, task_b, task_c = await _seed_board(session)
approval = Approval( approval = Approval(
board_id=board_id, board_id=board_id,
task_id=task_a, task_id=task_a,
action_type="task.update", action_type="task.update",
confidence=88, confidence=88,
status="pending", status="pending",
) )
session.add(approval) session.add(approval)
await session.flush() await session.flush()
session.add(ApprovalTaskLink(approval_id=approval.id, task_id=task_a)) 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_b))
session.add(ApprovalTaskLink(approval_id=approval.id, task_id=task_c)) session.add(ApprovalTaskLink(approval_id=approval.id, task_id=task_c))
await session.commit() await session.commit()
mapping = await load_task_ids_by_approval(session, approval_ids=[approval.id]) mapping = await load_task_ids_by_approval(session, approval_ids=[approval.id])
assert mapping[approval.id] == [task_a, task_b, task_c] assert mapping[approval.id] == [task_a, task_b, task_c]
finally:
await engine.dispose()

View File

@@ -43,128 +43,151 @@ async def _seed_board_and_tasks(
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_validate_dependency_update_rejects_self_dependency() -> None: async def test_validate_dependency_update_rejects_self_dependency() -> None:
engine = await _make_engine() engine = await _make_engine()
async with await _make_session(engine) as session: try:
board_id = uuid4() async with await _make_session(engine) as session:
task_id = uuid4() board_id = uuid4()
await _seed_board_and_tasks(session, board_id=board_id, task_ids=[task_id]) task_id = uuid4()
await _seed_board_and_tasks(session, board_id=board_id, task_ids=[task_id])
with pytest.raises(HTTPException) as exc: with pytest.raises(HTTPException) as exc:
await td.validate_dependency_update( await td.validate_dependency_update(
session, session,
board_id=board_id, board_id=board_id,
task_id=task_id, task_id=task_id,
depends_on_task_ids=[task_id], depends_on_task_ids=[task_id],
) )
assert exc.value.status_code == 422 assert exc.value.status_code == 422
finally:
await engine.dispose()
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_validate_dependency_update_404s_when_dependency_missing() -> None: async def test_validate_dependency_update_404s_when_dependency_missing() -> None:
engine = await _make_engine() engine = await _make_engine()
async with await _make_session(engine) as session: try:
board_id = uuid4() async with await _make_session(engine) as session:
task_id = uuid4() board_id = uuid4()
dep_id = uuid4() task_id = uuid4()
await _seed_board_and_tasks(session, board_id=board_id, task_ids=[task_id]) dep_id = uuid4()
await _seed_board_and_tasks(session, board_id=board_id, task_ids=[task_id])
with pytest.raises(HTTPException) as exc: with pytest.raises(HTTPException) as exc:
await td.validate_dependency_update( await td.validate_dependency_update(
session, session,
board_id=board_id, board_id=board_id,
task_id=task_id, task_id=task_id,
depends_on_task_ids=[dep_id], depends_on_task_ids=[dep_id],
) )
assert exc.value.status_code == 404 assert exc.value.status_code == 404
detail = exc.value.detail detail = exc.value.detail
assert isinstance(detail, dict) assert isinstance(detail, dict)
assert detail["missing_task_ids"] == [str(dep_id)] assert detail["missing_task_ids"] == [str(dep_id)]
finally:
await engine.dispose()
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_validate_dependency_update_detects_cycle() -> None: async def test_validate_dependency_update_detects_cycle() -> None:
engine = await _make_engine() engine = await _make_engine()
async with await _make_session(engine) as session: try:
board_id = uuid4() async with await _make_session(engine) as session:
a, b = uuid4(), uuid4() board_id = uuid4()
await _seed_board_and_tasks(session, board_id=board_id, task_ids=[a, b]) a, b = uuid4(), uuid4()
await _seed_board_and_tasks(session, board_id=board_id, task_ids=[a, b])
# existing edge a -> b # existing edge a -> b
session.add(TaskDependency(board_id=board_id, task_id=a, depends_on_task_id=b)) session.add(TaskDependency(board_id=board_id, task_id=a, depends_on_task_id=b))
await session.commit() await session.commit()
# update b -> a introduces cycle # update b -> a introduces cycle
with pytest.raises(HTTPException) as exc: with pytest.raises(HTTPException) as exc:
await td.validate_dependency_update( await td.validate_dependency_update(
session, session,
board_id=board_id, board_id=board_id,
task_id=b, task_id=b,
depends_on_task_ids=[a], depends_on_task_ids=[a],
) )
assert exc.value.status_code == 409 assert exc.value.status_code == 409
finally:
await engine.dispose()
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_dependency_queries_and_replace_and_dependents() -> None: async def test_dependency_queries_and_replace_and_dependents() -> None:
engine = await _make_engine() engine = await _make_engine()
async with await _make_session(engine) as session: try:
board_id = uuid4() async with await _make_session(engine) as session:
t1, t2, t3 = uuid4(), uuid4(), uuid4() board_id = uuid4()
await _seed_board_and_tasks(session, board_id=board_id, task_ids=[t1, t2, t3]) 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 # 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=t2))
session.add(TaskDependency(board_id=board_id, task_id=t1, depends_on_task_id=t3)) session.add(TaskDependency(board_id=board_id, task_id=t1, depends_on_task_id=t3))
await session.commit() await session.commit()
# cover empty input short-circuit # cover empty input short-circuit
assert await td.dependency_ids_by_task_id(session, board_id=board_id, task_ids=[]) == {} 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]) deps_map = await td.dependency_ids_by_task_id(
assert deps_map[t1] == [t2, t3] session, board_id=board_id, task_ids=[t1, t2]
assert deps_map.get(t2, []) == [] )
assert deps_map[t1] == [t2, t3]
assert deps_map.get(t2, []) == []
# mark t2 done, t3 not # mark t2 done, t3 not
task2 = (await session.exec(select(Task).where(col(Task.id) == t2))).first() task2 = (await session.exec(select(Task).where(col(Task.id) == t2))).first()
assert task2 is not None assert task2 is not None
task2.status = td.DONE_STATUS task2.status = td.DONE_STATUS
await session.commit() await session.commit()
# cover empty input short-circuit # cover empty input short-circuit
assert await td.dependency_status_by_id(session, board_id=board_id, dependency_ids=[]) == {} assert (
await td.dependency_status_by_id(session, board_id=board_id, dependency_ids=[])
== {}
)
status_map = await td.dependency_status_by_id( status_map = await td.dependency_status_by_id(
session, board_id=board_id, dependency_ids=[t2, t3] session, board_id=board_id, dependency_ids=[t2, t3]
) )
assert status_map[t2] == td.DONE_STATUS assert status_map[t2] == td.DONE_STATUS
assert status_map[t3] != 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) blocked = await td.blocked_by_for_task(session, board_id=board_id, task_id=t1)
assert blocked == [t3] assert blocked == [t3]
# cover early return when no deps provided # cover early return when no deps provided
assert ( assert (
await td.blocked_by_for_task(session, board_id=board_id, task_id=t1, dependency_ids=[]) await td.blocked_by_for_task(
== [] session, board_id=board_id, task_id=t1, dependency_ids=[]
) )
== []
)
# replace deps with duplicates (deduped) -> [t3] # replace deps with duplicates (deduped) -> [t3]
out = await td.replace_task_dependencies( out = await td.replace_task_dependencies(
session, session,
board_id=board_id, board_id=board_id,
task_id=t1, task_id=t1,
depends_on_task_ids=[t3, t3], depends_on_task_ids=[t3, t3],
) )
await session.commit() await session.commit()
assert out == [t3] assert out == [t3]
deps_map2 = await td.dependency_ids_by_task_id(session, board_id=board_id, task_ids=[t1]) deps_map2 = await td.dependency_ids_by_task_id(
assert deps_map2[t1] == [t3] 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) dependents = await td.dependent_task_ids(
assert dependents == [t1] session, board_id=board_id, dependency_task_id=t3
)
assert dependents == [t1]
# also exercise explicit dependency_ids passed # also exercise explicit dependency_ids passed
blocked2 = await td.blocked_by_for_task( blocked2 = await td.blocked_by_for_task(
session, board_id=board_id, task_id=t1, dependency_ids=[t3] session, board_id=board_id, task_id=t1, dependency_ids=[t3]
) )
assert blocked2 == [t3] assert blocked2 == [t3]
finally:
await engine.dispose()