testing
Test conventions, harness API, and readability rules for the Ghost codebase. MUST READ before writing or modifying any test — unit tests, integration tests, or live tests. Covers: test helper functions (test_config, test_workspace, test_database), MockProvider, LiveTestEnv, live test isolation rules, and the non-negotiable readability requirements. For step-based end-to-end tests, also read the `e2e-testing` skill. Failure to read this will produce duplicated boilerplate and broken test isolation.
- License 许可
- MIT
- 所属仓库
- mrtolkien/GHOST
- 最近更新
- 2026-04-24
- Stars
- ★ 5
- Forks
- 0
01 前言 Front Matter
- name
- testing
- description
- Test conventions, harness API, and readability rules for the Ghost codebase. MUST READ before writing or modifying any test — unit tests, integration tests, or live tests. Covers: test helper functions (test_config, test_workspace, test_database), MockProvider, LiveTestEnv, live test isolation rules, and the non-negotiable readability requirements. For step-based end-to-end tests, also read the `e2e-testing` skill. Failure to read this will produce duplicated boilerplate and broken test isolation.
03 SKILL.md 正文
Testing Conventions
Strategy
- Integration tests with a
live-testsfeature flag for all major features. - Maintain robust, reusable test fixtures shared across tests.
- Unit tests only for genuinely complex behaviors or external crate behaviors.
- Live tests (
--features live-tests) are human-run only. Ask the user before running them.
Live Test Assertions (NON-NEGOTIABLE)
Live test assertions encode real quality requirements. NEVER weaken, remove, or relax assertions to make a flaky test pass. If the test fails, fix the agent behavior (prompt, nudges, tool loop, context management) — not the test.
See memory/deep-research-agent.md for the full iteration history, known failure modes,
and nudge architecture for the deep research agent specifically.
For step-based e2e fixture/log tooling, use uv run scripts/e2e and select the
operation (refresh, render-log, diff, analyze-request).
Live Test Isolation (NON-NEGOTIABLE)
Live tests must never load data from the user's real workspace (~/GHOST/). Always
use LiveTestEnv which provides a fresh temp workspace with repo-current agent
definitions (via include_str! + install_default_agents()). The real workspace may
contain stale agent prompts referencing deleted tools.
Test Readability (NON-NEGOTIABLE)
Tests must be readable first. A reader should understand what a test does in seconds, not minutes.
- Extract ALL setup boilerplate into helper functions. A test body should be setup + action + assert, each in 1–3 lines.
- Prefer
let workspace = test_workspace()over 15 lines ofTempDir+fs::write+env::set_varinline. - Name helpers after what they produce, not what they do:
test_config()notsetup_config_dir_and_write_toml_and_set_env(). - Never duplicate setup across tests — if two tests need similar state, extract a helper.
- Extend existing helpers with parameters rather than creating new ones. If
test_config()works for most tests but one needs a custom model, add an optional parameter — don't writetest_config_with_custom_model(). - Every test should read like a specification.
Test Harness API
All reusable infrastructure lives in tests/common.rs.
Non-Live Helpers (always available)
| Helper | Purpose |
|---|---|
test_config() |
Temp config + workspace dirs, returns (Config, TempDir, TempDir) |
test_workspace() |
test_config() + bootstrapped workspace |
test_database() |
test_workspace() + connected SQLite |
write_test_note(workspace, title, body) |
Write a note file with frontmatter |
write_test_reference(workspace, topic, filename, content) |
Write a reference file |
Mock Types (always available)
| Type/Function | Purpose |
|---|---|
MockProvider |
Queued ChatResponse provider; records all ChatRequests |
MockProvider::requests() |
Access recorded requests for assertion |
EchoTool |
Simple tool that returns echo:{text} |
response(content, stop_reason) |
Build a ChatResponse |
respond_response(message, citations) |
Build a response that calls the respond tool |
Live Test Helpers (#[cfg(feature = "live-tests")])
Created via let env = live_test_database("test_name").await;
LiveTestEnv uses the real ~/.config/ghost/config.toml (with real API keys) but
creates a fresh temp workspace and database.
Session Helpers
| Method | Purpose |
|---|---|
env.create_session() |
Create a bare session, returns Thing |
env.session_with_messages(&[("role", "content")]) |
Session with pre-filled messages |
Chat Helpers
| Method | Purpose |
|---|---|
env.chat() |
SessionChat with real provider + chat tools |
Job Runners
| Method | Purpose |
|---|---|
env.run_reflection(&session_id, &agent) |
Load + run reflection agent, returns (String, RunMetadata) |
Completion / Background Helpers
| Method | Purpose |
|---|---|
env.chat_with_completion_watcher() |
SessionChat with completion watcher enabled |
env.wait_for_continuation_response(&session_id, since_count, secs) |
Wait for background task continuation response |
env.wait_for_agents(&session_id, timeout_secs) |
Wait for agent tasks to complete |
env.wait_for_system_message(&session_id, pattern, timeout_secs) |
Wait for system message matching pattern |
env.stop_all_agents() |
Stop all running agents, returns count |
env.stop_and_reset_agent(&agent_id) |
Stop and reset a specific agent |
Agent Helpers
| Method | Purpose |
|---|---|
env.load_agent("name") |
Load agent definition from temp workspace (repo-current copy) |
Assertion Helpers
| Method | Purpose |
|---|---|
env.workspace_file_exists("relative/path") |
Check file exists in temp workspace |
env.read_workspace_file("relative/path") |
Read file content from temp workspace |
env.list_notes() |
List all files under knowledge/notes/ |
env.list_references() |
List all files under knowledge/references/ |
env.find_file_containing("subdir", "needle") |
Recursive content search under a workspace subdir |
env.collect_tool_calls(&session_id) |
Collect all tool calls from session |
env.collect_web_fetch_metrics(&session_id) |
Gather web fetch statistics |
env.assert_research_quality(findings, metrics, domains, keywords) |
Assert research quality markers |
env.assert_notes_contain_any(&[needles], description) |
Assert workspace notes contain any of specified strings |
env.assert_diary_exists() |
Returns diary content if it exists |
env.workspace_path() |
Returns workspace directory path |
Diagnostic Logging
| Method | Purpose |
|---|---|
env.log_session_json("label", &session_id) |
Dump all messages from a session into the diagnostic log |
env.log_session_json_since("label", &session_id) |
Logs messages added since last log_session_json for session |
env.collect_session_json(&session_id) |
Get all session messages as JSON |
env.log("custom message") |
Add a note to the diagnostic log |
On drop, the diagnostic log is written to
e2e-output/<timestamp>_<test_name>/diagnostic.log and printed to stderr with
--nocapture.
Transcript / Snapshot Helpers
| Method | Purpose |
|---|---|
env.session_from_transcript(&[json_values]) |
Create session from transcript JSON array |
env.write_workspace_archive(path) |
Write workspace to tar.zst |
env.install_web_cache_fixture(&session_id, &fixture_dir) |
Install web cache fixture for a session |
Writing a New Live Test
#[tokio::test]
async fn my_live_test() {
let env = common::live_test_database("my_test").await;
let session = env.session_with_messages(&[
("user", "Hello"),
("assistant", "Hi!"),
]).await;
let chat = env.chat();
let (result, _metadata) = chat.chat(&session, "What is 2+2?", None).await.unwrap();
env.log_session_json("chat", &session).await;
assert!(!result.message.trim().is_empty());
}
04 同仓库更多 Skills
system-management
Use when reading or changing GHOST configuration (providers, models, timing, compaction, services, any config.toml key), adding or switching LLM providers, answering questions about GHOST's own features or capabilities, a CLI tool is missing or "not found", services need starting/stopping/debugging, updating GHOST to a newer version, managing the Nix shell environment, or troubleshooting GHOST setup.
writing-skills
Use when creating new skills, editing existing skills, or verifying skills work before deployment
writing-plans
Use when you have a spec or requirements for a multi-step task, before touching code. Write comprehensive implementation plans assuming the engineer has zero context for our codebase and questionable taste.
verification
Use when about to claim work is complete, fixed, or passing, before committing or creating PRs. Requires running verification commands and confirming output before making any success claims. Evidence before assertions, always.
tdd
Use when implementing any feature or bugfix, before writing implementation code. Write the test first. Watch it fail. Write minimal code to pass.
systematic-debugging
Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes