Update: 将子项目从 submodule 转为完整内容

- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
This commit is contained in:
freedak
2026-07-04 19:20:46 +08:00
parent 54d6465fa7
commit f7a720204a
3360 changed files with 802660 additions and 3 deletions
@@ -0,0 +1,329 @@
//! 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");
}