Testing #
Test Structure #
- Framework: pytest; coverage reporting comes from
make test-cov,make verify, ormake coverage, notmake testormake ci - Unit Tests: live under
tests/(and each app'stests/dir)- Fast, with mocked process/thread/clock/network/repository boundaries
- No external API calls, real browser, heavyweight build, or shared fixture writes
- Read
tests/fixtures/journal/mock data; usejournal_copyortmp_pathfor any scan, index rebuild, or mutation - Test individual functions and modules
- Integration Tests: marked
@pytest.mark.integration- Opt-in via
make test-integration; excluded frommake testandmake ci - Real local processes/builds and persisted-index contracts
- Still use disposable
tmp_pathstate and never the owner's journal
- Opt-in via
- Release Tests: marked
@pytest.mark.release- Run serially via
make test-release; excluded from developmentmake testandmake ci - Cover release transactions, release entrypoints, packaging/install probes, and release-host tool contracts
make release-checkscombines them with the real advisory-policy liveness and minisign gates; candidate construction runs that target automatically
- Run serially via
- Naming: Files
test_*.py, functionstest_* - Fixtures: Shared fixtures in
tests/conftest.py
Fixture Journal #
# The autouse set_test_journal_path fixture in tests/conftest.py does this
# for unit tests. Set it explicitly only when a test needs a different journal.
os.environ["SOLSTONE_JOURNAL"] = "tests/fixtures/journal"
# Now all journal operations work with test data
The tests/fixtures/journal/ directory contains immutable mock input with sample
facets, agents, transcripts, and indexed data. Tests may read it directly. Any
test that writes, scans, or rebuilds journal/index state must use the
journal_copy fixture or a smaller journal under tmp_path.
Running Tests #
make testruns all unit tests —tests/+ everysolstone/apps/*/tests/, in one parallel runmake test-cov— the same suite with coverage reportingmake test-integration— opt-in real local build/process and persisted-index contractsmake test-release— serial release transactions and release-host probesmake release-checks— complete candidate-host validation, including the release tests, advisory-policy liveness, and real minisign signingmake test-app APP=<name>andmake test-only TEST=pathare the focused development loopmake coverageto generate a coverage reportmake cionce on the settled final tree before merge or release (install checks plus the full unit suite)- Always run
journal restart-conveyafter editingsolstone/convey/orsolstone/apps/to reload code
OpenAPI Verification Lanes #
There are two API referees with different jobs:
tests/test_openapi_schemathesis.pyfuzzes a small allowlist from the committed native-client contract atdocs/openapi/convey-clients.jsonagainst the Flask WSGI app. It runs with the normal unit suite because it uses an isolated tmp journal and does not use HTTP transport sockets.make verify-apichecks SPA/API response baselines against a running sandbox. That lane verifies rendered baseline behavior, not the native-client OpenAPI contract.
Run the Schemathesis lane directly with:
make test-only TEST=tests/test_openapi_schemathesis.py
The operator live lane is:
make verify-schemathesis
verify-schemathesis starts a disposable sandbox, sets
SOLSTONE_SCHEMATHESIS_LIVE=1, and resolves the base URL through
solstone.think.convey_client.resolve_base_url(). Set
SOLSTONE_SCHEMATHESIS_BASE_URL to override the target. The live lane may grow to
include mutating routes because it targets disposable instances; do not run it as part
of ordinary unit CI.
The WSGI lane uses Schemathesis' default checks, but pins input generation to positive
cases only. That means checks such as status-code conformance, content-type
conformance, response headers, response schema, server-error rejection, and ignored-auth
behavior still run, while negative_data_rejection has no generated negative inputs in
this lane. The positive-only setting keeps the floor assertions meaningful: every
generated floor case must return 2xx.
Known findings are reported by this lane and triaged separately; the lane must not auto-fix them or widen the contract to pass. A contract-vs-implementation mismatch is triaged by asking which side is wrong. Current recorded findings:
GET /app/network/api/statusmaterializes link CA key material on a read path:ca_dir().exists()first createsjournal/link/ca, then_ca_fingerprint()callsload_or_generate_ca()and writesprivate.pempluscert.pem.GET /app/network/api/statusreaches_detect_lan_ip(), which opens a UDP socket to8.8.8.8:80to read the kernel-selected source address. The WSGI test harness reaches that syscall.
Worktree Development #
Run the full stack (supervisor + callosum + sense + cortex + convey) against test fixture data:
make dev # Start stack (Ctrl+C to stop)
In a second terminal, hit endpoints:
export SOLSTONE_JOURNAL=tests/fixtures/journal
export PATH=$(pwd)/.venv/bin:$PATH
curl -s http://localhost:$(cat tests/fixtures/journal/health/convey.port)/
Notes:
- Agents won't execute without API keys — this is expected in worktrees
- Output artifacts go in
scratch/(git-ignored) - Service logs:
tests/fixtures/journal/health/<service>.log make devwrites runtime artifacts (stats cache, health logs, task logs) into the fixtures journal — these are covered bytests/fixtures/journal/.gitignoreand should never be committed