mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-08-01 19:06:01 +00:00
The outer ring: SQL repositories, the run launcher, the thread lookup, and the run-completion listener implementing the ports the domain declared, plus the HTTP router and the poller driving them. All of it is instantiated in one composition root, so no route or lifespan hook builds an adapter of its own. With the ports filled, the pre-hexagonal implementation is deleted rather than left alongside: `app/scheduler/service.py` and its router mixed policy, persistence, and HTTP into one class, which is why its rules were only reachable through a live database. Two bugs that shape hid are fixed here: a completion hook that replayed a stale snapshot and rolled the launch write back, and a corrupt stored row surfacing to the client as a 4xx. The concurrency invariants are pinned by contract suites that run each port against both the fake and real sqlite, plus dedicated dispatch-race tests. 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
|