api_testing_core/websocket/
expect.rs1use anyhow::Context;
2
3use crate::Result;
4use crate::websocket::runner::WebsocketExecutedRequest;
5use crate::websocket::schema::{WebsocketExpect, WebsocketRequest};
6
7pub fn evaluate_text_expect(expect: &WebsocketExpect, text: &str, label: &str) -> Result<()> {
8 if let Some(contains) = expect.text_contains.as_deref()
9 && !text.contains(contains)
10 {
11 anyhow::bail!("{label} textContains failed: expected to contain '{contains}'");
12 }
13
14 if let Some(expr) = expect.jq.as_deref() {
15 let json: serde_json::Value = serde_json::from_str(text)
16 .with_context(|| format!("{label} jq requires a JSON response text"))?;
17 if !crate::jq::eval_exit_status(&json, expr).unwrap_or(false) {
18 anyhow::bail!("{label} jq failed: {expr}");
19 }
20 }
21
22 Ok(())
23}
24
25pub fn evaluate_main_response(
26 request: &WebsocketRequest,
27 executed: &WebsocketExecutedRequest,
28) -> Result<()> {
29 let Some(expect) = request.expect.as_ref() else {
30 return Ok(());
31 };
32
33 let last = executed.last_received.as_deref().ok_or_else(|| {
34 anyhow::anyhow!("websocket expect requires at least one received message")
35 })?;
36
37 evaluate_text_expect(expect, last, "websocket expect")
38}
39
40#[cfg(test)]
41mod tests {
42 use pretty_assertions::assert_eq;
43
44 use super::*;
45
46 fn request_with_expect(expect: Option<WebsocketExpect>) -> WebsocketRequest {
47 WebsocketRequest {
48 url: None,
49 headers: Vec::new(),
50 connect_timeout_seconds: None,
51 steps: Vec::new(),
52 expect,
53 raw: serde_json::json!({}),
54 }
55 }
56
57 fn executed_with_text(text: Option<&str>) -> WebsocketExecutedRequest {
58 WebsocketExecutedRequest {
59 target: "ws://127.0.0.1:9001/ws".to_string(),
60 transcript: Vec::new(),
61 last_received: text.map(ToString::to_string),
62 }
63 }
64
65 #[test]
66 fn websocket_expect_accepts_matching_text_contains_and_jq() {
67 let expect = WebsocketExpect {
68 jq: Some(".ok == true".to_string()),
69 text_contains: Some("ok".to_string()),
70 };
71 let request = request_with_expect(Some(expect));
72 let executed = executed_with_text(Some("{\"ok\":true}"));
73 evaluate_main_response(&request, &executed).unwrap();
74 }
75
76 #[test]
77 fn websocket_expect_rejects_missing_receive_text() {
78 let expect = WebsocketExpect {
79 jq: None,
80 text_contains: Some("ok".to_string()),
81 };
82 let request = request_with_expect(Some(expect));
83 let executed = executed_with_text(None);
84 let err = evaluate_main_response(&request, &executed).unwrap_err();
85 assert!(format!("{err:#}").contains("at least one received message"));
86 }
87
88 #[test]
89 fn websocket_expect_rejects_failed_jq() {
90 let err = evaluate_text_expect(
91 &WebsocketExpect {
92 jq: Some(".ok == false".to_string()),
93 text_contains: None,
94 },
95 "{\"ok\":true}",
96 "test",
97 )
98 .unwrap_err();
99 assert!(format!("{err:#}").contains("jq failed"));
100 }
101
102 #[test]
103 fn websocket_expect_rejects_missing_text_contains() {
104 let err = evaluate_text_expect(
105 &WebsocketExpect {
106 jq: None,
107 text_contains: Some("needle".to_string()),
108 },
109 "haystack",
110 "test",
111 )
112 .unwrap_err();
113 assert!(format!("{err:#}").contains("textContains failed"));
114 }
115
116 #[test]
117 fn websocket_expect_accepts_empty_when_no_expect() {
118 let request = request_with_expect(None);
119 let executed = executed_with_text(None);
120 let result = evaluate_main_response(&request, &executed);
121 assert_eq!(result.is_ok(), true);
122 }
123}