Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 0 additions & 164 deletions apps/backend/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from fastapi.testclient import TestClient
from main import app
from services.trace_service import TraceService
from api.routes.chat import _serialize_value
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
from langchain_core.messages import AIMessage, AIMessageChunk
from langgraph.types import Command
Expand Down Expand Up @@ -655,161 +654,6 @@ def head_supervisor(state: BaseAgentState):
assert second_checkpoint["message_count"] > first_checkpoint["message_count"]


def test_chat_resume_discards_speculative_supervisor_text_before_finalizer(monkeypatch):
class MockTuple:
tasks = ["dummy_task"]

class MockSaver:
async def setup(self):
pass

async def __aenter__(self):
return self

async def __aexit__(self, *args):
pass

async def aget_tuple(self, config):
return MockTuple()

monkeypatch.setattr(AsyncPostgresSaver, "from_conn_string", lambda x: MockSaver())

canonical_answer = "resume canonical answer"

class Snapshot:
config = {
"configurable": {
"thread_id": "resume_dup_1",
"checkpoint_id": "cp-resume-1",
"checkpoint_ns": "",
}
}
values = {
"messages": [StructuredMessage(content=canonical_answer, name="assistant")],
"route_history": [],
"streaming_status": "completed",
}
next = ()
created_at = "2026-03-11T00:00:00+00:00"

class ResumeGraph:
async def astream_events(self, *args, **kwargs):
yield {
"event": "on_chat_model_stream",
"name": "ChatOpenAI",
"metadata": {"langgraph_node": "head_supervisor"},
"data": {
"chunk": StructuredChunk(
content='{"reasoning":"resume","next":"FINISH","content":"resume speculative draft"}'
)
},
"run_id": "resume-head-run",
}
yield {
"event": "on_chain_end",
"name": "head_supervisor",
"data": {
"output": Command(
update={
"active_team": None,
"active_worker": None,
"response_mode": "finalizer",
"streaming_status": "running",
"route_history": [
build_route_entry(
layer="head",
node="head_supervisor",
next_node="finalizer",
status="running",
)
],
},
goto="finalizer",
)
},
}
final_json = f'{{"content":"{canonical_answer}"}}'
for i in range(0, len(final_json), 5):
yield {
"event": "on_chat_model_stream",
"name": "ChatOpenAI",
"metadata": {"langgraph_node": "finalizer"},
"data": {"chunk": StructuredChunk(content=final_json[i : i + 5])},
"run_id": "resume-finalizer-run",
}
yield {
"event": "on_chain_end",
"name": "finalizer",
"data": {
"output": Command(
update={
"streaming_status": "completed",
"messages": [
StructuredMessage(content=canonical_answer, name="assistant")
],
"route_history": [
build_route_entry(
layer="head",
node="finalizer",
next_node="FINISH",
status="completed",
)
],
},
goto="__end__",
)
},
}

async def aget_state(self, config, subgraphs=False):
return Snapshot()

monkeypatch.setattr(
"services.orchestration_service.OrchestrationService.get_graph",
lambda: type("B", (), {"compile": lambda self, checkpointer: ResumeGraph()})(),
)

persisted_logs = []

async def mock_create_events(*args, **kwargs):
return args[1]

async def mock_log_message(*args, **kwargs):
persisted_logs.append(
{
"thread_id": args[1],
"role": kwargs.get("role", args[2] if len(args) > 2 else None),
"content": kwargs.get("content", args[3] if len(args) > 3 else None),
}
)

monkeypatch.setattr(TraceService, "create_events", mock_create_events)

from services.logging_service import LoggingService

monkeypatch.setattr(LoggingService, "log_message", mock_log_message)

with client.stream(
"POST",
"/api/chat/resume",
json={"action": "approve", "thread_id": "resume_dup_1", "feedback": "ok"},
) as response:
payloads = _sse_payloads(response)

text_payloads = [
payload["content"] for payload in payloads if payload["event_type"] == "text"
]
final_text = "".join(text_payloads)

assert response.status_code == 200
assert "resume speculative draft" not in final_text
assert final_text == canonical_answer

assistant_logs = [entry for entry in persisted_logs if entry["role"] == "assistant"]
assert len(assistant_logs) == 1
assert assistant_logs[0]["content"] == canonical_answer


def test_chat_stream_interrupt_and_resume(monkeypatch):
from langgraph.errors import GraphInterrupt
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
Expand Down Expand Up @@ -1039,11 +883,3 @@ async def mock_log_message(*args, **kwargs):
)


def test_serialize_value_strips_nul_bytes_and_truncates_large_strings():
raw = "abc\x00def" + ("x" * 25000)

serialized = _serialize_value(raw)

assert "\x00" not in serialized
assert serialized.startswith("abcdef")
assert "(truncated " in serialized
27 changes: 11 additions & 16 deletions apps/backend/tests/test_auth_protected_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,20 @@ async def _override_get_db():


@pytest.mark.no_auth_override
def test_threads_requires_auth():
@pytest.mark.parametrize(
"method,path,json_body",
[
("GET", "/api/threads", None),
("POST", "/api/chat", {"message": "hello", "thread_id": "thread-1"}),
],
ids=["threads_list", "chat_post"],
)
def test_protected_route_requires_auth(method, path, json_body):
"""Any caller missing auth cookies must receive 401 from protected routes."""
app.dependency_overrides.clear()
app.dependency_overrides[get_db] = _override_get_db
try:
response = client.get("/api/threads")
finally:
app.dependency_overrides.clear()
app.dependency_overrides.pop(get_db, None)

assert response.status_code == 401
assert response.json()["detail"] == "Authentication required"


@pytest.mark.no_auth_override
def test_chat_requires_auth():
app.dependency_overrides.clear()
app.dependency_overrides[get_db] = _override_get_db
try:
response = client.post("/api/chat", json={"message": "hello", "thread_id": "thread-1"})
response = client.request(method, path, json=json_body)
finally:
app.dependency_overrides.clear()
app.dependency_overrides.pop(get_db, None)
Expand Down
25 changes: 0 additions & 25 deletions apps/backend/tests/test_chat_analytics_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,31 +42,6 @@ async def fake_refresh(instance):
mock_db.commit.assert_awaited_once()


@pytest.mark.asyncio
async def test_mark_first_token_sets_ttft():
started_at = datetime(2026, 3, 24, 8, 0, tzinfo=UTC)
first_token_at = started_at + timedelta(milliseconds=320)
turn = ChatTurn(
id=uuid4(),
thread_id="thread-1",
user_id="user-1",
turn_index=1,
request_kind="chat",
status="running",
started_at=started_at,
trace_id="trace-1",
)
mock_db = AsyncMock()
mock_db.get.return_value = turn

updated = await ChatAnalyticsService.mark_first_token(
mock_db, turn.id, first_token_at
)

assert updated is turn
assert turn.ttft_ms == 320


@pytest.mark.asyncio
async def test_finalize_turn_sets_completed_latency_and_summary_fields():
"""Completion path must compute latency and persist summary fields."""
Expand Down
40 changes: 0 additions & 40 deletions apps/backend/tests/test_dynamic_tools.py

This file was deleted.

Loading
Loading