story-kit: merge 126_story_test_coverage_http_anthropic_rs
This commit is contained in:
@@ -85,6 +85,15 @@ impl AnthropicApi {
|
|||||||
/// List available Anthropic models.
|
/// List available Anthropic models.
|
||||||
#[oai(path = "/anthropic/models", method = "get")]
|
#[oai(path = "/anthropic/models", method = "get")]
|
||||||
async fn list_anthropic_models(&self) -> OpenApiResult<Json<Vec<String>>> {
|
async fn list_anthropic_models(&self) -> OpenApiResult<Json<Vec<String>>> {
|
||||||
|
self.list_anthropic_models_from(ANTHROPIC_MODELS_URL).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnthropicApi {
|
||||||
|
async fn list_anthropic_models_from(
|
||||||
|
&self,
|
||||||
|
url: &str,
|
||||||
|
) -> OpenApiResult<Json<Vec<String>>> {
|
||||||
let api_key = get_anthropic_api_key(self.ctx.as_ref()).map_err(bad_request)?;
|
let api_key = get_anthropic_api_key(self.ctx.as_ref()).map_err(bad_request)?;
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
@@ -98,7 +107,7 @@ impl AnthropicApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let response = client
|
let response = client
|
||||||
.get(ANTHROPIC_MODELS_URL)
|
.get(url)
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
@@ -124,3 +133,147 @@ impl AnthropicApi {
|
|||||||
Ok(Json(models))
|
Ok(Json(models))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::http::context::AppContext;
|
||||||
|
use serde_json::json;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
fn test_ctx(dir: &TempDir) -> AppContext {
|
||||||
|
AppContext::new_test(dir.path().to_path_buf())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_api(dir: &TempDir) -> AnthropicApi {
|
||||||
|
AnthropicApi::new(Arc::new(test_ctx(dir)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- get_anthropic_api_key (private helper) --
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_api_key_returns_err_when_not_set() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let ctx = test_ctx(&dir);
|
||||||
|
let result = get_anthropic_api_key(&ctx);
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(result.unwrap_err().contains("not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_api_key_returns_err_when_empty() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let ctx = test_ctx(&dir);
|
||||||
|
ctx.store.set(KEY_ANTHROPIC_API_KEY, json!(""));
|
||||||
|
let result = get_anthropic_api_key(&ctx);
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(result.unwrap_err().contains("empty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_api_key_returns_err_when_not_string() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let ctx = test_ctx(&dir);
|
||||||
|
ctx.store.set(KEY_ANTHROPIC_API_KEY, json!(12345));
|
||||||
|
let result = get_anthropic_api_key(&ctx);
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(result.unwrap_err().contains("not a string"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_api_key_returns_key_when_set() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let ctx = test_ctx(&dir);
|
||||||
|
ctx.store.set(KEY_ANTHROPIC_API_KEY, json!("sk-ant-test123"));
|
||||||
|
let result = get_anthropic_api_key(&ctx);
|
||||||
|
assert_eq!(result.unwrap(), "sk-ant-test123");
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- get_anthropic_api_key_exists endpoint --
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn key_exists_returns_false_when_not_set() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let api = make_api(&dir);
|
||||||
|
let result = api.get_anthropic_api_key_exists().await.unwrap();
|
||||||
|
assert!(!result.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn key_exists_returns_true_when_set() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let ctx = AppContext::new_test(dir.path().to_path_buf());
|
||||||
|
ctx.store.set(KEY_ANTHROPIC_API_KEY, json!("sk-ant-test123"));
|
||||||
|
let api = AnthropicApi::new(Arc::new(ctx));
|
||||||
|
let result = api.get_anthropic_api_key_exists().await.unwrap();
|
||||||
|
assert!(result.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- set_anthropic_api_key endpoint --
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn set_api_key_returns_true() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let api = make_api(&dir);
|
||||||
|
let payload = Json(ApiKeyPayload {
|
||||||
|
api_key: "sk-ant-test123".to_string(),
|
||||||
|
});
|
||||||
|
let result = api.set_anthropic_api_key(payload).await.unwrap();
|
||||||
|
assert!(result.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn set_then_exists_returns_true() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let ctx = Arc::new(AppContext::new_test(dir.path().to_path_buf()));
|
||||||
|
let api = AnthropicApi::new(ctx);
|
||||||
|
api.set_anthropic_api_key(Json(ApiKeyPayload {
|
||||||
|
api_key: "sk-ant-test123".to_string(),
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let result = api.get_anthropic_api_key_exists().await.unwrap();
|
||||||
|
assert!(result.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- list_anthropic_models endpoint --
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn list_models_fails_when_no_key() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let api = make_api(&dir);
|
||||||
|
let result = api.list_anthropic_models().await;
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn list_models_fails_with_invalid_header_value() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let ctx = AppContext::new_test(dir.path().to_path_buf());
|
||||||
|
// A header value containing a newline is invalid
|
||||||
|
ctx.store
|
||||||
|
.set(KEY_ANTHROPIC_API_KEY, json!("bad\nvalue"));
|
||||||
|
let api = AnthropicApi::new(Arc::new(ctx));
|
||||||
|
let result = api.list_anthropic_models_from("http://127.0.0.1:1").await;
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn list_models_fails_when_server_unreachable() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let ctx = AppContext::new_test(dir.path().to_path_buf());
|
||||||
|
ctx.store
|
||||||
|
.set(KEY_ANTHROPIC_API_KEY, json!("sk-ant-test123"));
|
||||||
|
let api = AnthropicApi::new(Arc::new(ctx));
|
||||||
|
// Port 1 is reserved and should immediately refuse the connection
|
||||||
|
let result = api.list_anthropic_models_from("http://127.0.0.1:1").await;
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_creates_api_instance() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let _api = make_api(&dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user