Mechanical only, and separated from the judgment calls that follow so the reviewable changes are not buried in a 98-file whitespace diff. 227 automatic fixes: 60 blank lines carrying whitespace, 60 unsorted import blocks, 34 Optional[X] to X | None, 28 unused imports, 16 deprecated typing imports, 12 datetime.timezone.utc to datetime.UTC, and assorted smaller modernisations. Then `ruff format` over src and tests: 98 files reformatted, 35 already conforming. No file among the unused-import findings defines __all__ or is an __init__.py, so nothing here removes a re-export. `make test`: 658 passed, unchanged from HEAD. Two things observed while verifying, neither addressed here: `pytest tests/` cannot collect — tests/e2e/test_orchestration_e2e.py uses an `e2e` marker that is not registered, and the config is strict about markers. This fails identically at HEAD, so it predates this change; `make test` passes because it ignores tests/e2e, tests/integration and tests/contracts. test_tatlock_tool_call_logging_calculator is flaky. It failed once in a full run with these changes and passed on the next, passes in isolation with them, and fails in isolation at HEAD. It is order- or timing-dependent, not a regression from this commit — established by running the full suite both ways rather than by reasoning about which change could have caused it. Co-Authored-By: Claude <noreply@anthropic.com>
End-to-End API Tests
These tests make real HTTP requests to the running Tatlock API server to verify the complete stack works correctly.
Prerequisites
- Server must be running on
http://localhost:8777(usemake run) - Ollama must be running with the
gemma4:e2bmodel - Redis must be running (for benchmarking)
- Qdrant must be running on
http://localhost:6333(for memory tests)
Running the Tests
Start the server first:
# Terminal 1: Start the server (auto-reload enabled)
make run
# Logs are written to build/logs/server.log - tail them in another terminal:
tail -f logs/server.log
Run the E2E tests:
# Run all E2E tests
pytest tests/e2e/ -v -m e2e
# Run orchestration tests specifically
pytest tests/e2e/test_orchestration_e2e.py -v
# Run API endpoint tests
pytest tests/e2e/test_api_endpoints.py -v
Run specific test categories:
# Memory system tests
pytest tests/e2e/test_orchestration_e2e.py::TestMemoryStorage -v
pytest tests/e2e/test_orchestration_e2e.py::TestMemoryRecall -v
# Steward delegation tests
pytest tests/e2e/test_orchestration_e2e.py::TestStewardDelegation -v
# Direct delegation bypass tests (new feature)
pytest tests/e2e/test_orchestration_e2e.py::TestDirectDelegationBypass -v
# User isolation tests
pytest tests/e2e/test_orchestration_e2e.py::TestUserContextIsolation -v
# Orchestration scenario tests
pytest tests/e2e/test_orchestration_e2e.py::TestScenario1WeatherWithMemory -v
pytest tests/e2e/test_orchestration_e2e.py::TestScenario4SimpleExpertDelegation -v
pytest tests/e2e/test_orchestration_e2e.py::TestScenario6WikiCreation -v
# Generate evaluation report
pytest tests/e2e/test_orchestration_e2e.py::TestEvaluationReport -v -s
Test Organization
test_api_endpoints.py - Core API Tests
- Chat Completions endpoint (
/v1/chat/completions) - Responses API endpoint (
/v1/responses) - Streaming responses
- Error handling
- OpenAI format compliance
test_orchestration_e2e.py - Orchestration Scenario Tests
Based on ORCHESTRATION_SCENARIOS.md:
| Class | Scenario | What it Tests |
|---|---|---|
TestMemoryStorage |
Memory storage | Store -> Qdrant verification |
TestMemoryRecall |
Memory recall | Store -> Recall flow |
TestStewardDelegation |
Steward routing | Capability recommendations |
TestDirectDelegation |
Direct bypass | Pure memory/librarian requests |
TestScenario1WeatherWithMemory |
Weather check | Multi-step with memory lookup |
TestScenario4SimpleExpertDelegation |
Calculator/datetime | Simple tool use |
TestScenario6WikiCreation |
Wiki operations | Librarian delegation |
TestScenario8MultiExpertCoordination |
Complex requests | Multiple capabilities |
TestUserContextIsolation |
User isolation | llm_tester vs production |
TestDataVerification |
Data presence | Qdrant structure verification |
TestIntegrationHealth |
System health | API/Qdrant reachability |
TestEvaluationReport |
Diagnostic | Generates behavior reports |
User Isolation
Tests use the llm_tester user (development environment default) to isolate test data from production:
- Test memories:
memories_llm_tester(Qdrant collection) - Production memories:
memories_jpmschweitzer(never modified by tests)
Handling LLM Non-Determinism
LLM outputs are non-deterministic. Tests handle this by:
- Flexible assertions - Check for behavior patterns, not exact text
assert_llm_behavior()- Helper for pattern matching with confidence levels- Soft failures (
pytest.xfail) - Some tests may fail due to LLM variance without failing the suite - Evaluation reports - Generate diagnostic reports for human review
Example:
result = assert_llm_behavior(
message_text,
expected_patterns=[r"(remember|noted|stored)", r"purple"],
min_matches=1,
)
if not result.passed:
pytest.xfail(f"LLM response unclear: {result.evidence}")
Data Verification
Tests verify data presence in Qdrant:
# QdrantVerifier helper
qdrant = QdrantVerifier()
points = await qdrant.scroll_points("memories_llm_tester")
memory = await qdrant.find_memory_by_key("memories_llm_tester", "favorite_color")
Troubleshooting
Tests fail with connection error
Make sure the server is running:
make run
curl http://localhost:8777/health # Should return 200
Tests timeout
- Check Ollama is running:
curl http://localhost:11434/api/tags - Increase timeout if needed (default: 120s for LLM calls)
Memory tests fail
- Check Qdrant is running:
curl http://localhost:6333/collections - Verify
memories_llm_testercollection exists
Inconsistent results
- LLM responses vary - this is expected
- Check the evaluation report for detailed diagnostics:
pytest tests/e2e/test_orchestration_e2e.py::TestEvaluationReport -v -s
Tests pollute production data
- This shouldn't happen - tests use
llm_testeruser - If it does, check
ENVIRONMENTis set todevelopmentin.env
Adding New Tests
- Use existing fixtures (
client,qdrant,clean_test_memories) - Use
assert_llm_behavior()for flexible LLM output checking - Add
@pytest.mark.e2edecorator - Consider adding soft failures for non-deterministic checks
- Add test keys to
clean_test_memoriesfixture if storing new memories
Example:
@pytest.mark.e2e
@pytest.mark.asyncio
class TestNewScenario:
async def test_something(
self,
client: httpx.AsyncClient,
qdrant: QdrantVerifier,
clean_test_memories,
):
response = await client.post("/v1/responses", json={...})
# Use assert_llm_behavior for flexible checking
result = assert_llm_behavior(response_text, expected_patterns=[...])