Skip to main content

Crate wabot_testing

Crate wabot_testing 

Source
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 of wabot-ts/src/testing/asyncHarness.ts.
chat_bot
Drive a real ChatBot in a test. Port of wabot-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 rest feature (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 of wabot-ts/src/testing/restHarness.ts.
ui
The UI harness — pages, islands, boosted navigation and actions. Behind the ui feature (on by default). Drive UI controllers in a test. Port of wabot-ts/src/testing/uiHarness.ts.