rig_core/client/
verify.rs1use crate::{http_client, provider_response, wasm_compat::WasmCompatSend};
2use thiserror::Error;
3
4#[derive(Debug, Error)]
14#[non_exhaustive]
15pub enum VerifyError {
16 #[error("invalid authentication")]
17 InvalidAuthentication,
18 #[error("provider error: {0}")]
19 ProviderError(String),
20 #[error("provider response error: {0}")]
22 ProviderResponse(provider_response::ProviderResponseError),
23 #[error("http error: {0}")]
24 HttpError(
25 #[from]
26 #[source]
27 http_client::Error,
28 ),
29}
30
31crate::provider_response::impl_provider_response_helpers!(VerifyError);
32
33pub trait VerifyClient {
36 fn verify(&self) -> impl Future<Output = Result<(), VerifyError>> + WasmCompatSend;
38}
39
40#[cfg(test)]
41mod provider_response_tests {
42 use super::*;
43 use http::StatusCode;
44
45 #[test]
46 fn verify_error_provider_response_helpers_with_preserved_json_body() {
47 let body = r#"{"error":{"message":"rate limited"}}"#;
48 let error = VerifyError::ProviderResponse(provider_response::ProviderResponseError {
49 status: None,
50 body: body.to_string(),
51 });
52
53 assert_eq!(error.provider_response_body(), Some(body));
54 assert_eq!(error.provider_response_status(), None);
55 assert_eq!(
56 error.provider_response_json().expect("valid JSON"),
57 Some(serde_json::json!({ "error": { "message": "rate limited" } }))
58 );
59 }
60
61 #[test]
62 fn verify_error_provider_response_helpers_with_http_non_success() {
63 let body = r#"{"error":{"message":"bad request"}}"#;
64 let error = VerifyError::HttpError(http_client::Error::InvalidStatusCodeWithMessage(
65 StatusCode::BAD_REQUEST,
66 body.to_string(),
67 ));
68
69 assert_eq!(error.provider_response_body(), Some(body));
70 assert_eq!(
71 error.provider_response_status(),
72 Some(StatusCode::BAD_REQUEST)
73 );
74 assert_eq!(
75 error.provider_response_json().expect("valid JSON"),
76 Some(serde_json::json!({ "error": { "message": "bad request" } }))
77 );
78 }
79
80 #[test]
81 fn verify_error_provider_response_helpers_with_preserved_plain_text_body() {
82 let error = VerifyError::ProviderResponse(provider_response::ProviderResponseError {
83 status: None,
84 body: "not json".to_string(),
85 });
86
87 assert_eq!(error.provider_response_body(), Some("not json"));
88 assert!(error.provider_response_json().is_err());
89 }
90
91 #[test]
92 fn verify_error_provider_error_is_not_a_provider_response() {
93 let error = VerifyError::ProviderError("internal diagnostic".to_string());
94
95 assert_eq!(error.provider_response_body(), None);
96 assert_eq!(error.provider_response_status(), None);
97 assert_eq!(error.provider_response_json().expect("no body"), None);
98 }
99
100 #[test]
101 fn verify_error_provider_response_helpers_with_unrelated_variant() {
102 let error = VerifyError::InvalidAuthentication;
103
104 assert_eq!(error.provider_response_body(), None);
105 assert_eq!(error.provider_response_status(), None);
106 assert_eq!(error.provider_response_json().expect("no body"), None);
107 }
108
109 #[tokio::test]
110 async fn verify_preserves_status_and_body_on_provider_error_response() {
111 use crate::client::VerifyClient;
112 use crate::providers::openai::Client;
113 use crate::test_utils::RecordingHttpClient;
114
115 let body = r#"{"error":{"message":"server exploded","type":"server_error"}}"#;
116 let http_client =
117 RecordingHttpClient::with_error_response(StatusCode::INTERNAL_SERVER_ERROR, body);
118 let client = Client::builder()
119 .api_key("test-key")
120 .http_client(http_client)
121 .build()
122 .expect("build client");
123
124 let error = client
125 .verify()
126 .await
127 .expect_err("verify should fail on a 500 response");
128
129 assert_eq!(
130 error.provider_response_status(),
131 Some(StatusCode::INTERNAL_SERVER_ERROR)
132 );
133 assert_eq!(error.provider_response_body(), Some(body));
134 let json = error
135 .provider_response_json()
136 .expect("raw body should be valid JSON")
137 .expect("parsed JSON should be present");
138 assert_eq!(json["error"]["type"], "server_error");
139 }
140}