mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-08-01 19:06:01 +00:00
Replaces the pre-hexagonal scheduled-task implementation with a slice built to the layering spec: a pure domain (two aggregates, two state machines, the policy value object), output ports it declares itself, SQL/launcher/thread adapters implementing them under `app/adapters/`, and a composition root that is the one place any of them is instantiated. The old implementation mixed all of that into `app/scheduler/service.py` and a router that reached straight into repositories, so the rules that matter -- overlap policy, lease handling, which write owns which timestamp -- were only reachable through a live database. They are now unit-assertable on in-memory fakes, with the contract suite running each port against both the fake and real sqlite, and the concurrency invariants pinned by dedicated race tests. Two bugs the old shape hid are fixed on the way: a completion hook that replayed a stale snapshot and rolled back the launch write, and a corrupt stored row surfacing to the client as a 4xx. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
72 lines
3.1 KiB
Python
72 lines
3.1 KiB
Python
"""Contract tests for the thread-lookup anti-corruption layer.
|
|
|
|
The port asks one question -- "does this thread exist AND may this user use
|
|
it?" -- and deliberately answers both halves with a single bool, so a caller
|
|
cannot probe for the existence of threads they cannot see. These tests pin that
|
|
the adapter does not accidentally widen it back into two answers.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from app.adapters.schedule.thread_lookup import ThreadStoreThreadLookup
|
|
|
|
|
|
class _RecordingThreadStore:
|
|
"""Stands in for `ThreadMetaStore`, recording how it was asked."""
|
|
|
|
def __init__(self, owners: dict[str, str] | None = None) -> None:
|
|
self._owners = dict(owners or {})
|
|
self.calls: list[tuple[str, str, bool]] = []
|
|
|
|
async def check_access(self, thread_id: str, user_id: str, *, require_existing: bool = False) -> bool:
|
|
self.calls.append((thread_id, user_id, require_existing))
|
|
owner = self._owners.get(thread_id)
|
|
if owner is None:
|
|
# Mirrors the real store: absent rows pass a non-strict check.
|
|
return not require_existing
|
|
return owner == user_id
|
|
|
|
|
|
class TestTheOneQuestion:
|
|
@pytest.mark.asyncio
|
|
async def test_an_owned_thread_exists_for_its_user(self):
|
|
lookup = ThreadStoreThreadLookup(_RecordingThreadStore({"thread-1": "user-1"}))
|
|
assert await lookup.exists_for_user("thread-1", "user-1") is True
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_someone_elses_thread_does_not(self):
|
|
lookup = ThreadStoreThreadLookup(_RecordingThreadStore({"thread-1": "user-1"}))
|
|
assert await lookup.exists_for_user("thread-1", "user-2") is False
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_missing_thread_does_not(self):
|
|
"""This is the half `require_existing` buys: without it the store
|
|
treats an absent row as accessible, and a task could be bound to a
|
|
thread that does not exist."""
|
|
lookup = ThreadStoreThreadLookup(_RecordingThreadStore())
|
|
assert await lookup.exists_for_user("thread-nope", "user-1") is False
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_missing_and_forbidden_are_indistinguishable(self):
|
|
lookup = ThreadStoreThreadLookup(_RecordingThreadStore({"thread-1": "user-1"}))
|
|
missing = await lookup.exists_for_user("thread-nope", "user-2")
|
|
forbidden = await lookup.exists_for_user("thread-1", "user-2")
|
|
assert missing == forbidden is False
|
|
|
|
|
|
class TestHowTheStoreIsAsked:
|
|
@pytest.mark.asyncio
|
|
async def test_require_existing_is_always_set(self):
|
|
store = _RecordingThreadStore({"thread-1": "user-1"})
|
|
await ThreadStoreThreadLookup(store).exists_for_user("thread-1", "user-1")
|
|
assert store.calls == [("thread-1", "user-1", True)]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_the_result_is_a_real_bool(self):
|
|
"""The port is typed `bool`; a truthy row object leaking through would
|
|
satisfy the domain's `if` and still be the wrong contract."""
|
|
store = _RecordingThreadStore({"thread-1": "user-1"})
|
|
assert await ThreadStoreThreadLookup(store).exists_for_user("thread-1", "user-1") is True
|