Skip to main content

api_testing_core/grpc/
expect.rs

1use anyhow::Context;
2
3use crate::Result;
4use crate::grpc::runner::GrpcExecutedRequest;
5use crate::grpc::schema::GrpcRequest;
6
7pub fn evaluate_main_response(request: &GrpcRequest, executed: &GrpcExecutedRequest) -> Result<()> {
8    let Some(expect) = request.expect.as_ref() else {
9        return Ok(());
10    };
11
12    if let Some(status) = expect.status
13        && executed.grpc_status != status
14    {
15        anyhow::bail!(
16            "gRPC expect.status failed: expected {}, got {}",
17            status,
18            executed.grpc_status
19        );
20    }
21
22    if let Some(expr) = expect.jq.as_deref() {
23        let json: serde_json::Value = serde_json::from_slice(&executed.response_body)
24            .context("gRPC expect.jq requires a JSON response body")?;
25        if !crate::jq::eval_exit_status(&json, expr).unwrap_or(false) {
26            anyhow::bail!("gRPC expect.jq failed: {expr}");
27        }
28    }
29
30    Ok(())
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36    use crate::grpc::schema::{GrpcExpect, GrpcRequest};
37
38    fn base_request(expect: Option<GrpcExpect>) -> GrpcRequest {
39        GrpcRequest {
40            method: "health.HealthService/Check".to_string(),
41            body: serde_json::json!({}),
42            metadata: Vec::new(),
43            proto: None,
44            import_paths: Vec::new(),
45            plaintext: true,
46            authority: None,
47            timeout_seconds: None,
48            expect,
49            raw: serde_json::json!({}),
50        }
51    }
52
53    fn base_executed(body: serde_json::Value) -> GrpcExecutedRequest {
54        GrpcExecutedRequest {
55            target: "127.0.0.1:50051".to_string(),
56            method: "health.HealthService/Check".to_string(),
57            grpc_status: 0,
58            response_body: serde_json::to_vec(&body).unwrap(),
59            stderr: String::new(),
60        }
61    }
62
63    #[test]
64    fn grpc_expect_accepts_matching_status_and_jq() {
65        let request = base_request(Some(GrpcExpect {
66            status: Some(0),
67            jq: Some(".ok == true".to_string()),
68        }));
69        let executed = base_executed(serde_json::json!({"ok": true}));
70        evaluate_main_response(&request, &executed).unwrap();
71    }
72
73    #[test]
74    fn grpc_expect_rejects_mismatched_status() {
75        let request = base_request(Some(GrpcExpect {
76            status: Some(2),
77            jq: None,
78        }));
79        let executed = base_executed(serde_json::json!({"ok": true}));
80        let err = evaluate_main_response(&request, &executed).unwrap_err();
81        assert!(format!("{err:#}").contains("expect.status failed"));
82    }
83
84    #[test]
85    fn grpc_expect_rejects_failed_jq() {
86        let request = base_request(Some(GrpcExpect {
87            status: Some(0),
88            jq: Some(".ok == false".to_string()),
89        }));
90        let executed = base_executed(serde_json::json!({"ok": true}));
91        let err = evaluate_main_response(&request, &executed).unwrap_err();
92        assert!(format!("{err:#}").contains("expect.jq failed"));
93    }
94}