Technical Spec: Honcho Mock Server
Status: Draft Author: Claude (agent-assisted) Date: 2026-03-25 Target SDK Version: v2.1
Problem Statement
Developers building with the Honcho SDK currently need a running PostgreSQL database, Redis instance, vector store, and LLM API keys to validate their integrations. This creates significant friction for:
- SDK developers writing and running tests against the Honcho API.
- Application developers validating request/response shapes without a full Honcho deployment.
- CI pipelines that need a lightweight, deterministic server for integration tests.
- Offline development where external service dependencies are unavailable.
The Honcho test suite already contains a comprehensive mock infrastructure in tests/conftest.py and a working TestServer pattern in tests/sdk_typescript/conftest.py, but these are tightly coupled to pytest and not accessible to end users.
Goals
- Provide a mock Honcho server that runs the real FastAPI app with mocked external dependencies (LLM, embeddings, vector store, cache).
- Integrate into the Honcho CLI as
honcho mockso developers can start a mock server with a single command. - Use in-memory SQLite (via aiosqlite) so no PostgreSQL installation is required.
- Validate API contract — request schemas are validated against the real Pydantic models; responses match the OpenAPI spec exactly.
- Support stateful CRUD — workspaces, peers, sessions, messages, and conclusions are stored in-memory and persist for the lifetime of the server process.
- Deterministic behavior — same input produces same output (embeddings are content hashes, dialectic returns canned responses).
- Support test data seeding via JSON files or programmatic API calls.
- Design for future extraction into a standalone
honcho-mockpackage installable viauv tool install.
Non-Goals
- Realistic LLM responses — the mock server does not call any LLM. Dialectic chat returns canned/configurable strings.
- Background processing — the deriver, dreamer, and reconciler are disabled. No queue processing occurs.
- Production parity — the mock server is not intended to replicate production behavior for load testing, performance benchmarking, or concurrency testing.
- PostgreSQL-specific features — JSONB operators, pgvector HNSW indexes, GIN indexes, and full-text search via
tsvectorare not available in SQLite. Metadata filtering and vector search return simplified results. - Redis/cache behavior — caching is disabled; all reads hit the in-memory database.
- Webhook delivery — webhook endpoints can be registered but no HTTP delivery occurs.
- Authentication enforcement by default — auth is disabled unless explicitly enabled via flag.
Design
Architecture Overview
The mock server reuses the real Honcho FastAPI application (src/main.py) with dependency injection overrides for:
| Dependency | Production | Mock |
|---|---|---|
| Database | PostgreSQL + psycopg | In-memory SQLite + aiosqlite |
| Vector store | Turbopuffer / LanceDB / pgvector | In-memory dict (from tests/conftest.py) |
| Embeddings | OpenAI / Gemini API | Deterministic SHA-256 hash (from tests/conftest.py) |
| LLM calls | Anthropic / Google / OpenAI | Canned string responses |
| Cache | Redis via cashews | Disabled (no-op) |
| Deriver | Background queue processing | Disabled (DERIVER.ENABLED = False) |
| Dreamer | Background consolidation | Disabled (DREAM.ENABLED = False) |
| Sentry | Error tracking | Disabled |
| Telemetry | CloudEvents emission | Disabled |
| Langfuse | Observability | No-op decorator |
+--------------------------------------------------+
| honcho mock (CLI) |
| |
| +-----------+ +------------+ +-------------+ |
| | FastAPI | | SQLite | | Mock Vector | |
| | App | | (in-mem) | | Store | |
| | (real | | via | | (in-mem | |
| | routers) | | aiosqlite | | dict) | |
| +-----------+ +------------+ +-------------+ |
| |
| +-----------+ +------------+ +-------------+ |
| | Mock | | Mock LLM | | Disabled: | |
| | Embeddings| | (canned) | | Deriver, | |
| | (SHA-256) | | | | Dreamer, | |
| +-----------+ +------------+ | Sentry, | |
| | Telemetry | |
| +-------------+ |
+--------------------------------------------------+
|
| HTTP :8000 (configurable)
v
SDK / curl / tests
Mock Behavior Table
| API Endpoint | Mock Behavior |
|---|---|
POST /v3/workspaces | Creates/returns workspace in SQLite. Fully functional. |
POST /v3/workspaces/list | Lists workspaces with pagination. Metadata filters use simple equality (no JSONB operators). |
PUT /v3/workspaces/{id} | Updates workspace metadata/configuration. |
DELETE /v3/workspaces/{id} | Marks as deleted immediately (no background queue). |
POST /v3/workspaces/{id}/peers | Creates/returns peer. Fully functional. |
POST /v3/workspaces/{id}/peers/list | Lists peers with pagination. |
PUT /v3/workspaces/{id}/peers/{id} | Updates peer metadata/configuration. |
POST /v3/workspaces/{id}/peers/{id}/chat | Returns canned response: {"content": "Mock dialectic response"}. Streaming returns 3 SSE chunks. |
POST /v3/workspaces/{id}/peers/{id}/representation | Returns {"representation": "No representation available (mock server)"}. |
GET /v3/workspaces/{id}/peers/{id}/card | Returns stored peer card or null. |
PUT /v3/workspaces/{id}/peers/{id}/card | Stores and returns peer card. |
GET /v3/workspaces/{id}/peers/{id}/context | Returns peer card + empty representation. |
POST /v3/workspaces/{id}/sessions | Creates/returns session. Fully functional. |
POST /v3/workspaces/{id}/sessions/list | Lists sessions with pagination. |
PUT /v3/workspaces/{id}/sessions/{id} | Updates session metadata/configuration. |
DELETE /v3/workspaces/{id}/sessions/{id} | Marks inactive immediately. |
POST /v3/workspaces/{id}/sessions/{id}/clone | Clones session and messages. |
POST /v3/workspaces/{id}/sessions/{id}/peers | Adds peers to session. |
PUT /v3/workspaces/{id}/sessions/{id}/peers | Sets session peers. |
DELETE /v3/workspaces/{id}/sessions/{id}/peers | Removes peers from session. |
GET /v3/workspaces/{id}/sessions/{id}/context | Returns messages (within token limit) + summary if available. No representation. |
POST /v3/workspaces/{id}/sessions/{id}/messages | Creates messages. Does NOT enqueue for deriver processing. |
POST /v3/workspaces/{id}/sessions/{id}/messages/list | Lists messages with pagination. |
GET /v3/workspaces/{id}/sessions/{id}/messages/{id} | Returns single message. |
PUT /v3/workspaces/{id}/sessions/{id}/messages/{id} | Updates message metadata. |
POST /v3/workspaces/{id}/conclusions | Creates conclusions (documents) in SQLite. Embeddings stored as deterministic hashes. |
POST /v3/workspaces/{id}/conclusions/list | Lists conclusions with pagination. |
POST /v3/workspaces/{id}/conclusions/query | Returns all conclusions for the observer/observed pair (no real vector similarity). |
DELETE /v3/workspaces/{id}/conclusions/{id} | Soft-deletes conclusion. |
POST /v3/workspaces/{id}/search | Returns empty list (full-text search not supported in SQLite mock). |
POST /v3/workspaces/{id}/peers/{id}/search | Returns empty list. |
POST /v3/workspaces/{id}/sessions/{id}/search | Returns empty list. |
GET /v3/workspaces/{id}/queue/status | Returns all zeros (no queue processing). |
POST /v3/workspaces/{id}/schedule_dream | Returns 400 (“Dreams are not enabled”). |
POST /v3/keys | Returns 400 (“Auth is disabled”) unless --auth flag is used. |
POST /v3/workspaces/{id}/webhooks | Creates webhook endpoint. No delivery occurs. |
SQLite Compatibility Layer
The production models in src/models.py use PostgreSQL-specific features that require adaptation for SQLite:
Problem areas:
JSONBcolumns — SQLite has no native JSONB. UseJSONtype (SQLAlchemy maps this to TEXT with JSON serialization).Vector(1536)columns (pgvector) — SQLite has no vector type. Store as JSON-serialized list or omit.CheckConstraintwith regex (id ~ '^[A-Za-z0-9_-]+$') — SQLite does not support regex in CHECK. Remove these constraints for mock.server_default=text("'{}'::jsonb")— PostgreSQL cast syntax. Useserver_default=text("'{}'").Identity()for auto-increment — SQLite usesAUTOINCREMENT. Needs adaptation.Indexwithpostgresql_using,postgresql_ops,postgresql_with— Skip these indexes entirely.text("to_tsvector('english', content)")— Skip GIN full-text index.- Partial unique indexes (
postgresql_where) — SQLite supports partial indexes with different syntax but thepostgresql_wherekwarg won’t work. Skip these.
Approach: Create a mock_models.py module (or a function create_mock_metadata()) that clones Base.metadata and strips PostgreSQL-specific constructs before calling create_all(). Alternatively, iterate over Base.metadata.sorted_tables and remove incompatible constraints/indexes at table creation time. The cleanest approach is:
def sanitize_metadata_for_sqlite(metadata: MetaData) -> None:
"""Remove PostgreSQL-specific constructs from metadata for SQLite compatibility."""
for table in metadata.sorted_tables:
# Remove PostgreSQL-specific indexes
table.indexes = {
idx for idx in table.indexes
if not any(
hasattr(idx, k) for k in ['postgresql_using', 'postgresql_ops']
)
}
# Remove CHECK constraints with PostgreSQL regex
table.constraints = {
c for c in table.constraints
if not (isinstance(c, CheckConstraint) and '~' in str(c.sqltext))
}This function runs once during mock server startup before Base.metadata.create_all().
CLI Integration
The mock server is exposed as a subcommand of the Honcho CLI (as specified in the CLI spec):
# Start mock server with defaults (port 8000, no auth, no seed data)
honcho mock
# Custom port
honcho mock --port 9000
# Pre-populate with test data from JSON file
honcho mock --seed data.json
# Enable authentication (generates and prints an admin JWT)
honcho mock --auth
# Custom JWT secret (implies --auth)
honcho mock --auth --jwt-secret "my-test-secret"
# Verbose logging
honcho mock --log-level debug
# Combined
honcho mock --port 9000 --seed data.json --auth --log-level debugCLI Output:
Honcho Mock Server v3.0.3
Mode: mock (in-memory SQLite, no external dependencies)
Address: http://127.0.0.1:8000
Auth: disabled
Seed: none
Disabled: deriver, dreamer, sentry, telemetry, cache
Mocked: embeddings (SHA-256), LLM (canned), vector store (in-memory)
Press Ctrl+C to stop.
When --auth is used:
Honcho Mock Server v3.0.3
Mode: mock (in-memory SQLite, no external dependencies)
Address: http://127.0.0.1:8000
Auth: enabled (JWT)
Admin JWT: eyJhbGciOiJIUzI1NiIs...
Seed: data.json (3 workspaces, 5 peers, 12 sessions, 48 messages)
Disabled: deriver, dreamer, sentry, telemetry, cache
Mocked: embeddings (SHA-256), LLM (canned), vector store (in-memory)
Press Ctrl+C to stop.
CLI Implementation
The CLI command handler lives in the CLI package and imports mock server setup utilities from a new module:
New file: src/mock/__init__.py
Exposes: create_mock_app() -> FastAPI, run_mock_server(port, seed_file, auth, jwt_secret, log_level)
New file: src/mock/server.py
Core logic:
import threading
import uvicorn
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
from src.main import app
from src.db import Base
from src.dependencies import get_db
from src.mock.overrides import apply_mock_overrides, sanitize_metadata_for_sqlite
async def create_mock_engine():
"""Create an in-memory SQLite engine with all tables."""
engine = create_async_engine(
"sqlite+aiosqlite:///:memory:",
echo=False,
)
# Strip PG-specific constructs
sanitize_metadata_for_sqlite(Base.metadata)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
return engine
def run_mock_server(
port: int = 8000,
seed_file: str | None = None,
auth: bool = False,
jwt_secret: str | None = None,
log_level: str = "info",
):
"""Start the mock server (blocking)."""
import asyncio
engine = asyncio.run(create_mock_engine())
session_factory = async_sessionmaker(bind=engine, expire_on_commit=False)
# Apply all mock overrides to the real FastAPI app
apply_mock_overrides(app, session_factory, auth=auth, jwt_secret=jwt_secret)
# Seed data if provided
if seed_file:
asyncio.run(seed_from_file(engine, session_factory, seed_file))
# Print banner
print_banner(port, auth, jwt_secret, seed_file)
# Run uvicorn
uvicorn.run(app, host="127.0.0.1", port=port, log_level=log_level)New file: src/mock/overrides.py
Centralizes all mock dependency overrides, extracted from tests/conftest.py:
def apply_mock_overrides(
app: FastAPI,
session_factory: async_sessionmaker,
auth: bool = False,
jwt_secret: str | None = None,
) -> None:
"""Apply all mock overrides to the FastAPI app."""
# 1. Database override
async def mock_get_db():
async with session_factory() as session:
yield session
app.dependency_overrides[get_db] = mock_get_db
# 2. tracked_db override (same pattern as tests/sdk_typescript/conftest.py)
# Patches all tracked_db import sites
# 3. Settings overrides
settings.DERIVER.ENABLED = False
settings.DREAM.ENABLED = False
settings.SENTRY.ENABLED = False
settings.CACHE.ENABLED = False
settings.METRICS.ENABLED = False
settings.TELEMETRY.ENABLED = False
settings.AUTH.USE_AUTH = auth
if auth:
settings.AUTH.JWT_SECRET = jwt_secret or "honcho-mock-secret"
# 4. Mock embeddings (reuse _content_to_embedding from tests/conftest.py)
# 5. Mock vector store (reuse mock_vector_store from tests/conftest.py)
# 6. Mock LLM calls (reuse mock_llm_call_functions from tests/conftest.py)
# 7. Mock langfuse decorator
# 8. Disable background task enqueue (make enqueue a no-op)New file: src/mock/seed.py
Handles JSON seed file loading:
async def seed_from_file(
engine: AsyncEngine,
session_factory: async_sessionmaker,
seed_file: str,
) -> SeedSummary:
"""Load seed data from a JSON file into the mock database."""
...Test Data Seeding
JSON Seed Format:
{
"workspaces": [
{
"id": "my-workspace",
"metadata": {"env": "test"}
}
],
"peers": [
{
"workspace_id": "my-workspace",
"id": "user-alice",
"metadata": {"role": "user"}
},
{
"workspace_id": "my-workspace",
"id": "agent-bot",
"metadata": {"role": "agent"}
}
],
"sessions": [
{
"workspace_id": "my-workspace",
"id": "session-1",
"peers": {
"user-alice": {"observe": true},
"agent-bot": {"observe": true}
}
}
],
"messages": [
{
"workspace_id": "my-workspace",
"session_id": "session-1",
"peer_id": "user-alice",
"content": "Hello, how are you?"
},
{
"workspace_id": "my-workspace",
"session_id": "session-1",
"peer_id": "agent-bot",
"content": "I'm doing well, thanks for asking!"
}
],
"conclusions": [
{
"workspace_id": "my-workspace",
"observer_id": "agent-bot",
"observed_id": "user-alice",
"content": "Alice is a friendly user who uses greetings."
}
]
}The seeder creates resources in dependency order: workspaces → peers → sessions (with peer associations) → messages → conclusions.
TestServer Reuse Pattern
The existing TestServer class from tests/sdk_typescript/conftest.py provides the exact pattern needed. The mock server module can either:
Option A (preferred): Extract TestServer into src/mock/server.py and reuse it from both tests and CLI.
Option B: Keep TestServer in tests and have the CLI use uvicorn.run() directly (since CLI is blocking, no need for background thread).
For the CLI, Option B is simpler — uvicorn.run() blocks the main thread, which is the desired behavior. The TestServer threading pattern is only needed when embedding a server inside a test process.
For programmatic use (future standalone package), the TestServer pattern should be exposed:
from honcho.mock import MockServer
server = MockServer(port=8000, seed_file="data.json")
server.start() # Non-blocking, starts in background thread
# ... run tests ...
server.stop()Standalone Package (Future Phase)
The src/mock/ module is designed so it can be extracted into a standalone package:
honcho-mock/
pyproject.toml
src/
honcho_mock/
__init__.py # MockServer class
__main__.py # CLI entry: python -m honcho_mock
server.py # Core server logic
overrides.py # Mock dependency overrides
seed.py # JSON seed loading
The standalone package would depend on honcho (the server package) and add only aiosqlite as an additional dependency.
Installation and usage:
uv tool install honcho-mock
honcho-mock --port 8000 --seed data.json
# Or as a Python module
python -m honcho_mock --port 8000Handling the enqueue Background Task
The message creation endpoint (POST .../messages) calls background_tasks.add_task(enqueue, payloads) to trigger deriver processing. In the mock server, this must be neutralized. Two approaches:
Approach 1 (preferred): Patch enqueue to be a no-op.
async def mock_enqueue(payload: list[dict]) -> None:
"""No-op: mock server does not process the deriver queue."""
pass
# In apply_mock_overrides:
patch("src.deriver.enqueue.enqueue", mock_enqueue)This is simple and matches the existing test approach.
Approach 2: Patch enqueue_deletion and enqueue_dream as well. Since workspace/session deletion and dream scheduling also go through the queue, these also become no-ops. Deletion endpoints will return 202 but no actual background cleanup occurs (acceptable for mock).
Dialectic Chat Mock
The dialectic chat endpoint (POST /v3/workspaces/{id}/peers/{id}/chat) is the most complex mock because it involves an agentic loop. The mock replaces agentic_chat and agentic_chat_stream:
Non-streaming:
{"content": "Mock dialectic response"}Streaming (SSE):
data: {"delta": {"content": "Mock "}, "done": false}
data: {"delta": {"content": "dialectic "}, "done": false}
data: {"delta": {"content": "response"}, "done": false}
data: {"done": true}
This matches the existing mock in tests/conftest.py (mock_llm_call_functions).
Configurable responses (stretch goal): Allow users to configure dialectic responses via a config file or environment variable:
honcho mock --dialectic-response "This is my custom mock response"Dependencies
The mock server introduces one new dependency:
aiosqlite— async SQLite driver for SQLAlchemy. Already widely used, minimal footprint.
This should be added as an optional dependency in pyproject.toml:
[project.optional-dependencies]
mock = ["aiosqlite>=0.20.0"]Implementation Phases
Phase 1: Core Mock Module (MVP)
Goal: honcho mock starts a working server that handles all CRUD operations.
Tasks:
- Create
src/mock/__init__.py,src/mock/server.py,src/mock/overrides.py. - Implement
sanitize_metadata_for_sqlite()to strip PostgreSQL-specific constructs from SQLAlchemy metadata. - Implement
apply_mock_overrides()extracting mock logic fromtests/conftest.py:- Database override (SQLite in-memory).
tracked_dbpatching (all 11 import sites fromtests/sdk_typescript/conftest.py).- Embedding mock (
_content_to_embedding). - Vector store mock (in-memory dict).
- LLM call mocks (canned responses).
- Langfuse no-op.
- Deriver
enqueueno-op. - Settings overrides (disable deriver, dreamer, sentry, cache, metrics, telemetry).
- Implement
run_mock_server()with uvicorn. - Add
honcho mockCLI command with--portand--log-levelflags. - Add
aiosqliteto optional dependencies. - Verify: all CRUD operations work via
curlor SDK.
Estimated effort: 3-4 days.
Phase 2: Seeding and Auth
Goal: Support --seed, --auth, and --jwt-secret flags.
Tasks:
- Implement
src/mock/seed.pywith JSON schema validation and dependency-ordered insertion. - Implement
--authflag (enable JWT auth, print admin token). - Implement
--jwt-secretflag. - Write example seed file (
examples/mock-seed.json). - Add banner output with server status summary.
Estimated effort: 2 days.
Phase 3: Testing and Hardening
Goal: Verify the mock server works with both Python and TypeScript SDKs.
Tasks:
- Write integration tests that start mock server and run SDK operations against it.
- Verify pagination works correctly with SQLite.
- Verify metadata filtering works for simple equality cases.
- Test seed file loading with various data shapes.
- Test error cases (invalid seed file, port already in use, etc.).
- Document known limitations (no full-text search, no vector similarity, simplified metadata filtering).
Estimated effort: 2-3 days.
Phase 4: Standalone Package (Future)
Goal: Extract src/mock/ into a standalone honcho-mock package.
Tasks:
- Create
honcho-mock/package with its ownpyproject.toml. - Add
__main__.pyforpython -m honcho_mockusage. - Add
MockServerclass for programmatic use. - Publish to PyPI.
- Add
uv tool install honcho-mockdocumentation.
Estimated effort: 1-2 days.
Files to Create
| File | Purpose |
|---|---|
src/mock/__init__.py | Package init, exports create_mock_app, run_mock_server, MockServer |
src/mock/server.py | Core server logic: engine creation, SQLite metadata sanitization, uvicorn runner |
src/mock/overrides.py | All mock dependency overrides extracted from tests/conftest.py |
src/mock/seed.py | JSON seed file loading and database population |
examples/mock-seed.json | Example seed file for documentation |
Files to Modify
| File | Change |
|---|---|
pyproject.toml | Add aiosqlite to [project.optional-dependencies] under mock group |
| CLI entry point (per CLI spec) | Add honcho mock subcommand with --port, --seed, --auth, --jwt-secret, --log-level flags |
tests/conftest.py | Extract _content_to_embedding and mock factory functions into importable locations (or import from src/mock/overrides.py in tests) |
Key Files Referenced
| File | Relevance |
|---|---|
tests/conftest.py | Source of all mock implementations to extract |
tests/sdk_typescript/conftest.py | TestServer pattern and tracked_db patching sites |
src/main.py | FastAPI app definition, lifespan, middleware, routers |
src/db.py | Database engine, Base, SessionLocal, request_context |
src/dependencies.py | get_db and tracked_db — primary override targets |
src/config.py | AppSettings and all nested settings to override |
src/models.py | ORM models with PostgreSQL-specific constructs to sanitize |
src/schemas/api.py | Pydantic schemas defining the API contract |
src/security.py | JWT creation/verification, require_auth dependency |
src/routers/*.py | All API route handlers |
src/deriver/enqueue.py | enqueue() function to no-op |
src/vector_store/__init__.py | VectorStore ABC, get_external_vector_store |
src/embedding_client.py | _EmbeddingClient to mock |
Risk Assessment
High Risk
SQLite compatibility with SQLAlchemy models. The production models use extensive PostgreSQL-specific features (JSONB, pgvector Vector type, regex CHECK constraints, partial unique indexes, Identity columns, ::jsonb casts in server_default). The sanitize_metadata_for_sqlite() function must handle all of these correctly, and edge cases will likely emerge during implementation.
Mitigation: Start with a spike to verify all tables can be created in SQLite. If too many incompatibilities arise, fall back to using a “mock-specific” metadata/model set that mirrors production schemas without PG-specific constructs. Alternatively, use DuckDB with its JSONB support as the in-memory backend instead of SQLite.
Medium Risk
tracked_db patching fragility. The mock must patch tracked_db at 11 import sites (and this list may grow). If a new module imports tracked_db and the mock doesn’t patch it, that code path will try to use the production PostgreSQL connection and fail.
Mitigation: Maintain a central list of tracked_db import sites (already done in both tests/conftest.py and tests/sdk_typescript/conftest.py). Add a startup check that verifies all known import sites are patched. Consider refactoring tracked_db to use a factory pattern that can be globally overridden in one place.
Medium Risk
Metadata filtering limitations. Production uses PostgreSQL JSONB operators for metadata filtering (@>, ?, path queries). SQLite has json_extract() but the query builder in src/crud/ likely generates PG-specific SQL for metadata filters.
Mitigation: Document that metadata filtering in the mock server only supports simple top-level key equality. Complex JSONB queries will return unfiltered results or raise an error with a clear message.
Low Risk
Pagination behavior differences. fastapi-pagination should work identically with SQLite since it operates at the SQLAlchemy query level, not the database dialect level.
Low Risk
Port conflicts. The mock server may fail to bind to the requested port.
Mitigation: Catch OSError on bind and print a clear error message suggesting an alternative port.
Verification Plan
Unit Tests
- SQLite metadata sanitization: Verify
sanitize_metadata_for_sqlite()produces a metadata object that cancreate_all()without errors on an SQLite engine. - Mock overrides: Verify all dependency overrides are correctly applied and the original app state is not permanently mutated.
- Seed loading: Verify seed files are parsed and inserted in the correct dependency order.
Integration Tests
- Full CRUD lifecycle: Start mock server, create workspace → peer → session → messages → conclusions, read them back, update, delete. Verify all responses match expected schemas.
- Python SDK compatibility: Run a subset of SDK tests against the mock server.
- TypeScript SDK compatibility: Run a subset of TypeScript SDK tests against the mock server.
- Pagination: Verify paginated endpoints return correct
items,total,page,pages,sizefields. - Dialectic mock: Verify both streaming and non-streaming dialectic responses.
- Auth flow: Start with
--auth, verify unauthenticated requests are rejected, authenticated requests succeed. - Seed file: Start with
--seed, verify seeded data is queryable via API.
Manual Smoke Tests
- Start
honcho mock, point Python SDK athttp://localhost:8000, run through the quickstart tutorial. - Start
honcho mock --port 9000 --auth, use the printed JWT to authenticate SDK calls.
Open Questions
-
DuckDB vs SQLite? DuckDB supports JSONB natively and has better PostgreSQL compatibility. It would reduce the SQLite adaptation burden. However, it adds a heavier dependency. Decision: Start with SQLite (lighter weight, more widely available), upgrade to DuckDB only if SQLite compatibility becomes unmanageable.
-
Should the mock server expose an OpenAPI spec endpoint? The real FastAPI app already serves
/openapi.jsonand/docs. These would be automatically available in the mock server. This is a feature, not a question — just confirming it works. -
Should search endpoints return something useful? Currently specced to return empty lists. An alternative is to implement basic substring matching on message content using SQLite
LIKE. This would make the mock more useful for testing search flows without requiring pgvector or full-text search. -
Should the mock persist to disk optionally? An
--persist /path/to/db.sqliteflag could write the SQLite database to disk, allowing data to survive server restarts. This would be useful for longer development sessions. Low priority but worth considering. -
How should the mock handle file upload endpoints? The
POST .../messages/uploadendpoint processes files (PDF, etc.) into message content. The mock could either reject file uploads with a clear error or accept them with simplified text extraction. Decision: accept files, use the existingprocess_file_uploads_for_messagesutility if possible, or return the raw filename as content if not. -
Should
tests/conftest.pyimport fromsrc/mock/overrides.py? This would reduce duplication but create a dependency from test infrastructure on the mock module. Alternative: keep them separate and accept the duplication, or extract shared utilities intosrc/mock/shared.pythat both consume.