1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! # 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.)
//!
//! ```ignore
//! 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
//!
//! ```ignore
//! 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
//!
//! ```ignore
//! 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
//!
//! ```ignore
//! 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
//!
//! ```toml
//! [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.
/// The async/cron harness. Behind `async-jobs` (on by default).
/// 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.
/// The UI harness — pages, islands, boosted navigation and actions.
/// Behind the `ui` feature (on by default).
pub use ;
pub use ;
pub use ;
pub use ;
pub use TestChatMemory;
pub use ;
pub use ;
pub use ;