Expand description
§wabot-testing
Test support for the LLM half of the framework. Port of the
harnesses in wabot-ts/src/testing.
Without these, testing a mindset means hand-rolling a fake adapter, an in-RAM memory and the container wiring — which is enough work that the tests don’t get written, and enough duplication that each one drifts from what production does. (This crate exists because the port’s own tests kept rebuilding exactly that scaffolding.)
let harness = ChatBotHarness::builder(Arc::new(SupportMindset))
.tools(OrderTools::register_tools(&container))
.container(container)
.build();
harness.adapter().call_tool("read_order", json!({ "id": 7 }));
harness.adapter().reply("It shipped yesterday.");
let turn = harness.send("where is my order?").await?;
assert_eq!(turn.text(), "It shipped yesterday.");
assert!(turn.called("read_order"));§The one design rule
A harness wires production types together; it never
reimplements them. ChatBotHarness holds a real ChatBot and
a real MindsetOperator; AgentHarness::for_agent returns the
very AgentBuilder an application uses. A harness that
reimplemented the loop would be a second implementation, free to
drift from the one that ships — and a test passing against the
drifted copy is worse than no test.
Only two things are substitutes, and both are deliberate: the model
(MockChatAdapter, because a real one is neither deterministic
nor free) and storage (TestChatMemory, which is the in-memory
implementation plus the ability to read it back).
§REST
let harness = RestHarness::new(UserController::register_routes(&container, Router::new()));
harness.post("/users").json(&body).send().await.assert_status(StatusCode::CREATED);No port is bound — axum’s router is a tower::Service, so the
request is driven straight through it. The stack it drives is built
by the same function run_rest_controllers uses, so the harness
can’t accidentally test an application the deployment doesn’t have.
§UI
let page = harness.get("/notes").await;
page.assert_contains("<h1>Notes</h1>");
assert_eq!(page.island_props("notes-form"), Some(json!({ "count": 2 })));Islands aren’t hydrated — there is no browser. What is checked is the server’s half of the contract: the host element, its id and its props. A mismatch there is what “the island silently never appeared” looks like from the server side.
§Async jobs
let harness = AsyncHarness::builder().command(entry).build();
harness.execute(&SendEmail { … }).await.assert_succeeded();No polling workers and no database, but the real JobRunner —
so the state transitions, the retry decision and the restored audit
actor are the production ones. TS calls the handler directly and
skips all of that.
§Add it under dev-dependencies
[dev-dependencies]
wabot-testing = "0.1"Through the umbrella it is wabot::testing, behind the testing
feature — off by default, so nothing ships a mock adapter to
production by accident.
Re-exports§
pub use agent::AgentHarness;pub use agent::AgentHarnessBuilder;pub use async_jobs::AsyncHarness;pub use async_jobs::AsyncHarnessBuilder;pub use async_jobs::FinishedJob;pub use chat_bot::ChatBotHarness;pub use chat_bot::ChatBotHarnessBuilder;pub use chat_bot::ChatTurn;pub use chat_bot::IntoChatMessage;pub use llm_judge::render_transcript;pub use llm_judge::JudgeError;pub use llm_judge::LlmJudge;pub use llm_judge::Transcript;pub use llm_judge::Verdict;pub use memory::TestChatMemory;pub use mock_adapter::MockChatAdapter;pub use mock_adapter::NoArgs;pub use mock_adapter::RecordedRequest;pub use mock_adapter::ScriptedTurn;pub use mock_adapter::ToArguments;pub use rest::RequestBuilder;pub use rest::RestHarness;pub use rest::TestResponse;pub use ui::Fragment;pub use ui::Page;pub use ui::UiHarness;
Modules§
- agent
- Drive a real agent in a test. Port of
wabot-ts/src/testing/agentHarness.ts. - async_
jobs - The async/cron harness. Behind
async-jobs(on by default). Run command and cron handlers in a test. Port ofwabot-ts/src/testing/asyncHarness.ts. - chat_
bot - Drive a real
ChatBotin a test. Port ofwabot-ts/src/testing/chatBotHarness.ts. - conformance
- A suite every chat adapter must pass. Port of
wabot-ts/src/testing/conformance/chatAdapterConformanceCases.ts. - llm_
judge - Grade a conversation with a real LLM. Port of
wabot-ts/src/testing/LlmJudge.ts. - memory
- An in-RAM chat memory that a test can read back. Port of
wabot-ts/src/testing/TestChatMemory.ts. - mock_
adapter - A scriptable stand-in for an LLM. Port of
wabot-ts/src/testing/MockChatAdapter.ts. - rest
- The REST harness. Behind the
restfeature (on by default) so a project testing only its chat stack doesn’t compile axum and tower into its test binaries. Drive REST controllers in a test. Port ofwabot-ts/src/testing/restHarness.ts. - ui
- The UI harness — pages, islands, boosted navigation and actions.
Behind the
uifeature (on by default). Drive UI controllers in a test. Port ofwabot-ts/src/testing/uiHarness.ts.