Files
MyAiDesk/nomifun-tauri/crates/backend/nomifun-app/tests/acp_e2e.rs
T
freedak f7a720204a Update: 将子项目从 submodule 转为完整内容
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
2026-07-04 19:20:46 +08:00

167 lines
5.7 KiB
Rust

//! E2E integration tests for ACP management routes.
//!
//! Tests cover: agents list, agents/refresh, agents/test, health-check,
//! and session-bound routes (mode/model).
mod common;
use axum::http::StatusCode;
use serde_json::json;
use tower::ServiceExt;
use common::{body_json, build_app, get_with_token, json_with_token, setup_and_login};
// ── Global ACP routes ────────────────────────────────────────────
#[tokio::test]
async fn list_agents_returns_array() {
let (mut app, services) = build_app().await;
let (token, _csrf) = setup_and_login(&mut app, &services, "user1", "pass123").await;
let req = get_with_token("/api/agents", &token);
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let body = body_json(resp).await;
assert_eq!(body["success"], true);
assert!(body["data"].is_array());
let agents = body["data"].as_array().unwrap();
assert!(agents.iter().any(|a| a["agent_type"] == "nomi"));
}
#[tokio::test]
async fn refresh_agents_returns_array() {
let (mut app, services) = build_app().await;
let (token, csrf) = setup_and_login(&mut app, &services, "user1", "pass123").await;
let req = json_with_token("POST", "/api/agents/refresh", json!({}), &token, &csrf);
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let body = body_json(resp).await;
assert_eq!(body["success"], true);
assert!(body["data"].is_array());
}
#[tokio::test]
async fn test_custom_agent_nonexistent_command() {
let (mut app, services) = build_app().await;
let (token, csrf) = setup_and_login(&mut app, &services, "user1", "pass123").await;
// Endpoint was renamed from /api/agents/test to /api/agents/custom/try-connect
// when the custom-agent CRUD routes were introduced. The new endpoint always
// returns HTTP 200 and encodes failure in the JSON body (step = "fail_cli" or
// "fail_acp"), so we assert on the body rather than the HTTP status.
let req = json_with_token(
"POST",
"/api/agents/custom/try-connect",
json!({ "command": "/nonexistent/path/to/agent" }),
&token,
&csrf,
);
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let json = common::body_json(resp).await;
assert_eq!(json["success"], true);
assert_eq!(json["data"]["step"], "fail_cli");
}
#[tokio::test]
async fn health_check_returns_status() {
let (mut app, services) = build_app().await;
let (token, csrf) = setup_and_login(&mut app, &services, "user1", "pass123").await;
let req = json_with_token(
"POST",
"/api/agents/health-check",
json!({ "backend": "claude" }),
&token,
&csrf,
);
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let body = body_json(resp).await;
assert_eq!(body["success"], true);
// available is a boolean
assert!(body["data"]["available"].is_boolean());
// latency should be present
assert!(body["data"]["latency"].is_number());
}
#[tokio::test]
async fn health_check_unknown_backend_reports_unavailable() {
let (mut app, services) = build_app().await;
let (token, csrf) = setup_and_login(&mut app, &services, "user1", "pass123").await;
// Same rationale as `detect_cli_unknown_backend_returns_null_path`:
// unknown backends are valid at the request layer and surface as
// `available: false` with an error string.
let req = json_with_token(
"POST",
"/api/agents/health-check",
json!({ "backend": "iFlow" }),
&token,
&csrf,
);
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let body = body_json(resp).await;
assert_eq!(body["success"], true);
assert_eq!(body["data"]["available"], false);
}
// ── Session-bound ACP routes (no active task → 404) ──────────────
#[tokio::test]
async fn get_mode_no_active_task() {
let (mut app, services) = build_app().await;
let (token, _csrf) = setup_and_login(&mut app, &services, "user1", "pass123").await;
let req = get_with_token("/api/conversations/nonexistent/mode", &token);
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn set_mode_no_active_task() {
let (mut app, services) = build_app().await;
let (token, csrf) = setup_and_login(&mut app, &services, "user1", "pass123").await;
let req = json_with_token(
"PUT",
"/api/conversations/nonexistent/mode",
json!({ "mode": "code" }),
&token,
&csrf,
);
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn get_model_no_active_task() {
let (mut app, services) = build_app().await;
let (token, _csrf) = setup_and_login(&mut app, &services, "user1", "pass123").await;
let req = get_with_token("/api/conversations/nonexistent/model", &token);
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn set_model_no_active_task() {
let (mut app, services) = build_app().await;
let (token, csrf) = setup_and_login(&mut app, &services, "user1", "pass123").await;
let req = json_with_token(
"PUT",
"/api/conversations/nonexistent/model",
json!({ "model_id": "claude-sonnet-4" }),
&token,
&csrf,
);
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
}