test: refactor async context management in approval and dependency tests
This commit is contained in:
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user