opencode_rs/http/
config.rs1use crate::error::Result;
6use crate::http::HttpClient;
7use crate::types::config::Config;
8use crate::types::config::ConfigProviders;
9use reqwest::Method;
10
11#[derive(Clone)]
13pub struct ConfigApi {
14 http: HttpClient,
15}
16
17impl ConfigApi {
18 pub fn new(http: HttpClient) -> Self {
20 Self { http }
21 }
22
23 pub async fn get(&self) -> Result<Config> {
29 self.http.request_json(Method::GET, "/config", None).await
30 }
31
32 pub async fn update(&self, req: &Config) -> Result<Config> {
38 let body = serde_json::to_value(req)?;
39 self.http
40 .request_json(Method::PATCH, "/config", Some(body))
41 .await
42 }
43
44 pub async fn providers(&self) -> Result<ConfigProviders> {
50 self.http
51 .request_json(Method::GET, "/config/providers", None)
52 .await
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use crate::http::HttpConfig;
60 use std::time::Duration;
61 use wiremock::Mock;
62 use wiremock::MockServer;
63 use wiremock::ResponseTemplate;
64 use wiremock::matchers::method;
65 use wiremock::matchers::path;
66
67 #[tokio::test]
68 async fn test_get_config_success() {
69 let mock_server = MockServer::start().await;
70
71 Mock::given(method("GET"))
72 .and(path("/config"))
73 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
74 "provider": "anthropic",
75 "model": "claude-sonnet-4-20250514"
76 })))
77 .mount(&mock_server)
78 .await;
79
80 let http = HttpClient::new(HttpConfig {
81 base_url: mock_server.uri(),
82 directory: None,
83 workspace: None,
84 timeout: Duration::from_secs(30),
85 })
86 .unwrap();
87
88 let config = ConfigApi::new(http);
89 let result = config.get().await;
90 assert!(result.is_ok());
91 }
92
93 #[tokio::test]
94 async fn test_update_config_success() {
95 let mock_server = MockServer::start().await;
96
97 Mock::given(method("PATCH"))
98 .and(path("/config"))
99 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
100 "provider": "openai",
101 "model": "gpt-4"
102 })))
103 .mount(&mock_server)
104 .await;
105
106 let http = HttpClient::new(HttpConfig {
107 base_url: mock_server.uri(),
108 directory: None,
109 workspace: None,
110 timeout: Duration::from_secs(30),
111 })
112 .unwrap();
113
114 let config = ConfigApi::new(http);
115 let result = config
116 .update(&Config {
117 provider: Some(serde_json::json!("openai")),
118 model: Some(serde_json::json!("gpt-4")),
119 agent: None,
120 auto_compact: None,
121 mcp: None,
122 extra: serde_json::Value::Null,
123 })
124 .await;
125 assert!(result.is_ok());
126 }
127
128 #[tokio::test]
129 async fn test_providers_config_success() {
130 let mock_server = MockServer::start().await;
131
132 Mock::given(method("GET"))
133 .and(path("/config/providers"))
134 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
135 "providers": [
136 {"id": "anthropic", "name": "Anthropic"},
137 {"id": "openai", "name": "OpenAI"}
138 ]
139 })))
140 .mount(&mock_server)
141 .await;
142
143 let http = HttpClient::new(HttpConfig {
144 base_url: mock_server.uri(),
145 directory: None,
146 workspace: None,
147 timeout: Duration::from_secs(30),
148 })
149 .unwrap();
150
151 let config = ConfigApi::new(http);
152 let result = config.providers().await;
153 assert!(result.is_ok());
154 }
155
156 #[tokio::test]
157 async fn test_update_config_validation_error() {
158 let mock_server = MockServer::start().await;
159
160 Mock::given(method("PATCH"))
161 .and(path("/config"))
162 .respond_with(ResponseTemplate::new(400).set_body_json(serde_json::json!({
163 "name": "ValidationError",
164 "message": "Invalid provider"
165 })))
166 .mount(&mock_server)
167 .await;
168
169 let http = HttpClient::new(HttpConfig {
170 base_url: mock_server.uri(),
171 directory: None,
172 workspace: None,
173 timeout: Duration::from_secs(30),
174 })
175 .unwrap();
176
177 let config = ConfigApi::new(http);
178 let result = config
179 .update(&Config {
180 provider: Some(serde_json::json!("invalid")),
181 model: None,
182 agent: None,
183 auto_compact: None,
184 mcp: None,
185 extra: serde_json::Value::Null,
186 })
187 .await;
188 assert!(result.is_err());
189 assert!(result.unwrap_err().is_validation_error());
190 }
191}