f7a720204a
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用 - 添加所有子项目的完整源代码 - 保留原始 .git 为 .git.bak 备份
330 lines
10 KiB
Rust
330 lines
10 KiB
Rust
//! Provider CRUD, model fetch, and protocol detection tests with auth.
|
|
|
|
mod common;
|
|
|
|
use axum::http::StatusCode;
|
|
use serde_json::json;
|
|
use tower::ServiceExt;
|
|
use wiremock::matchers::{header as match_header, method, path};
|
|
use wiremock::{Mock, MockServer, ResponseTemplate};
|
|
|
|
use common::{body_json, build_app, delete_with_token, get_with_token, json_with_token, setup_and_login};
|
|
|
|
// ===========================================================================
|
|
// Provider CRUD
|
|
// ===========================================================================
|
|
|
|
#[tokio::test]
|
|
async fn provider_full_crud_with_auth() {
|
|
let (mut app, services) = build_app().await;
|
|
let (token, csrf) = setup_and_login(&mut app, &services, "admin", "StrongP@ss1").await;
|
|
|
|
// 1. List — empty
|
|
let resp = app
|
|
.clone()
|
|
.oneshot(get_with_token("/api/providers", &token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
let json = body_json(resp).await;
|
|
assert_eq!(json["data"], json!([]));
|
|
|
|
// 2. Create
|
|
let req = json_with_token(
|
|
"POST",
|
|
"/api/providers",
|
|
json!({
|
|
"platform": "anthropic",
|
|
"name": "Anthropic",
|
|
"base_url": "https://api.anthropic.com",
|
|
"api_key": "sk-ant-api03-test1234"
|
|
}),
|
|
&token,
|
|
&csrf,
|
|
);
|
|
let resp = app.clone().oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::CREATED);
|
|
let json = body_json(resp).await;
|
|
let id = json["data"]["id"].as_str().unwrap().to_string();
|
|
assert_eq!(json["data"]["platform"], "anthropic");
|
|
assert_eq!(json["data"]["name"], "Anthropic");
|
|
let api_key = json["data"]["api_key"].as_str().unwrap();
|
|
assert_eq!(
|
|
api_key, "sk-ant-api03-test1234",
|
|
"API key should be plaintext on the wire (pre-launch)"
|
|
);
|
|
|
|
// 3. List — should contain one
|
|
let resp = app
|
|
.clone()
|
|
.oneshot(get_with_token("/api/providers", &token))
|
|
.await
|
|
.unwrap();
|
|
let json = body_json(resp).await;
|
|
assert_eq!(json["data"].as_array().unwrap().len(), 1);
|
|
|
|
// 4. Update
|
|
let req = json_with_token(
|
|
"PUT",
|
|
&format!("/api/providers/{id}"),
|
|
json!({"name": "Updated Name", "enabled": false}),
|
|
&token,
|
|
&csrf,
|
|
);
|
|
let resp = app.clone().oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
let json = body_json(resp).await;
|
|
assert_eq!(json["data"]["name"], "Updated Name");
|
|
assert!(!json["data"]["enabled"].as_bool().unwrap());
|
|
|
|
// 5. Delete
|
|
let resp = app
|
|
.clone()
|
|
.oneshot(delete_with_token(&format!("/api/providers/{id}"), &token, &csrf))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
|
|
// 6. Verify deleted
|
|
let resp = app.oneshot(get_with_token("/api/providers", &token)).await.unwrap();
|
|
let json = body_json(resp).await;
|
|
assert_eq!(json["data"], json!([]));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn provider_create_validation_with_auth() {
|
|
let (mut app, services) = build_app().await;
|
|
let (token, csrf) = setup_and_login(&mut app, &services, "admin", "StrongP@ss1").await;
|
|
|
|
// Missing platform
|
|
let req = json_with_token(
|
|
"POST",
|
|
"/api/providers",
|
|
json!({"name": "Test", "base_url": "https://api.example.com", "api_key": "sk-test"}),
|
|
&token,
|
|
&csrf,
|
|
);
|
|
let resp = app.clone().oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
|
|
|
// Invalid URL
|
|
let req = json_with_token(
|
|
"POST",
|
|
"/api/providers",
|
|
json!({"platform": "openai", "name": "Test", "base_url": "not-a-url", "api_key": "sk-test"}),
|
|
&token,
|
|
&csrf,
|
|
);
|
|
let resp = app.oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn provider_update_nonexistent_with_auth() {
|
|
let (mut app, services) = build_app().await;
|
|
let (token, csrf) = setup_and_login(&mut app, &services, "admin", "StrongP@ss1").await;
|
|
|
|
let req = json_with_token("PUT", "/api/providers/nonexistent", json!({"name": "X"}), &token, &csrf);
|
|
let resp = app.oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn provider_delete_nonexistent_with_auth() {
|
|
let (mut app, services) = build_app().await;
|
|
let (token, csrf) = setup_and_login(&mut app, &services, "admin", "StrongP@ss1").await;
|
|
|
|
let resp = app
|
|
.oneshot(delete_with_token("/api/providers/nonexistent", &token, &csrf))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Model fetch
|
|
// ===========================================================================
|
|
|
|
#[tokio::test]
|
|
async fn model_fetch_openai_with_auth() {
|
|
let mock_server = MockServer::start().await;
|
|
Mock::given(method("GET"))
|
|
.and(path("/models"))
|
|
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
|
|
"data": [{"id": "gpt-4o"}, {"id": "gpt-4o-mini"}]
|
|
})))
|
|
.mount(&mock_server)
|
|
.await;
|
|
|
|
let (mut app, services) = build_app().await;
|
|
let (token, csrf) = setup_and_login(&mut app, &services, "admin", "StrongP@ss1").await;
|
|
|
|
let req = json_with_token(
|
|
"POST",
|
|
"/api/providers",
|
|
json!({
|
|
"platform": "openai",
|
|
"name": "OpenAI Mock",
|
|
"base_url": mock_server.uri(),
|
|
"api_key": "test-api-key"
|
|
}),
|
|
&token,
|
|
&csrf,
|
|
);
|
|
let resp = app.clone().oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::CREATED);
|
|
let json = body_json(resp).await;
|
|
let id = json["data"]["id"].as_str().unwrap().to_string();
|
|
|
|
let req = json_with_token(
|
|
"POST",
|
|
&format!("/api/providers/{id}/models"),
|
|
json!({"try_fix": false}),
|
|
&token,
|
|
&csrf,
|
|
);
|
|
let resp = app.oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
let json = body_json(resp).await;
|
|
let models = json["data"]["models"].as_array().unwrap();
|
|
assert_eq!(models.len(), 2);
|
|
assert_eq!(models[0], "gpt-4o");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn model_fetch_nonexistent_provider_with_auth() {
|
|
let (mut app, services) = build_app().await;
|
|
let (token, csrf) = setup_and_login(&mut app, &services, "admin", "StrongP@ss1").await;
|
|
|
|
let req = json_with_token("POST", "/api/providers/nonexistent/models", json!({}), &token, &csrf);
|
|
let resp = app.oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Protocol detection
|
|
// ===========================================================================
|
|
|
|
#[tokio::test]
|
|
async fn protocol_detect_openai_with_auth() {
|
|
let mock_server = MockServer::start().await;
|
|
Mock::given(method("GET"))
|
|
.and(path("/models"))
|
|
.and(match_header("Authorization", "Bearer sk-test-key"))
|
|
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
|
|
"data": [{"id": "gpt-4"}, {"id": "gpt-3.5-turbo"}]
|
|
})))
|
|
.mount(&mock_server)
|
|
.await;
|
|
|
|
let (mut app, services) = build_app().await;
|
|
let (token, csrf) = setup_and_login(&mut app, &services, "admin", "StrongP@ss1").await;
|
|
|
|
let req = json_with_token(
|
|
"POST",
|
|
"/api/providers/detect-protocol",
|
|
json!({
|
|
"base_url": mock_server.uri(),
|
|
"api_key": "sk-test-key"
|
|
}),
|
|
&token,
|
|
&csrf,
|
|
);
|
|
let resp = app.oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
let json = body_json(resp).await;
|
|
assert_eq!(json["data"]["protocol"], "openai");
|
|
assert!(json["data"]["confidence"].as_u64().unwrap() > 0);
|
|
let models = json["data"]["models"].as_array().unwrap();
|
|
assert!(!models.is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn protocol_detect_all_fail_with_auth() {
|
|
let mock_server = MockServer::start().await;
|
|
Mock::given(method("GET"))
|
|
.respond_with(ResponseTemplate::new(404))
|
|
.mount(&mock_server)
|
|
.await;
|
|
|
|
let (mut app, services) = build_app().await;
|
|
let (token, csrf) = setup_and_login(&mut app, &services, "admin", "StrongP@ss1").await;
|
|
|
|
let req = json_with_token(
|
|
"POST",
|
|
"/api/providers/detect-protocol",
|
|
json!({
|
|
"base_url": mock_server.uri(),
|
|
"api_key": "sk-unknown"
|
|
}),
|
|
&token,
|
|
&csrf,
|
|
);
|
|
let resp = app.oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
let json = body_json(resp).await;
|
|
assert_eq!(json["data"]["protocol"], "unknown");
|
|
assert_eq!(json["data"]["confidence"], 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn protocol_detect_validation_with_auth() {
|
|
let (mut app, services) = build_app().await;
|
|
let (token, csrf) = setup_and_login(&mut app, &services, "admin", "StrongP@ss1").await;
|
|
|
|
// Missing baseUrl
|
|
let req = json_with_token(
|
|
"POST",
|
|
"/api/providers/detect-protocol",
|
|
json!({"api_key": "sk-test"}),
|
|
&token,
|
|
&csrf,
|
|
);
|
|
let resp = app.clone().oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
|
|
|
// Missing apiKey
|
|
let req = json_with_token(
|
|
"POST",
|
|
"/api/providers/detect-protocol",
|
|
json!({"base_url": "https://api.example.com"}),
|
|
&token,
|
|
&csrf,
|
|
);
|
|
let resp = app.oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn protocol_detect_switch_platform_suggestion_with_auth() {
|
|
let mock_server = MockServer::start().await;
|
|
|
|
Mock::given(method("GET"))
|
|
.and(path("/models"))
|
|
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
|
|
"data": [{"id": "gpt-4"}]
|
|
})))
|
|
.mount(&mock_server)
|
|
.await;
|
|
|
|
let (mut app, services) = build_app().await;
|
|
let (token, csrf) = setup_and_login(&mut app, &services, "admin", "StrongP@ss1").await;
|
|
|
|
let req = json_with_token(
|
|
"POST",
|
|
"/api/providers/detect-protocol",
|
|
json!({
|
|
"base_url": mock_server.uri(),
|
|
"api_key": "sk-test",
|
|
"preferred_protocol": "anthropic"
|
|
}),
|
|
&token,
|
|
&csrf,
|
|
);
|
|
let resp = app.oneshot(req).await.unwrap();
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
let json = body_json(resp).await;
|
|
assert_eq!(json["data"]["protocol"], "openai");
|
|
assert_eq!(json["data"]["suggestion"]["type"], "switch_platform");
|
|
}
|