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

158 lines
5.5 KiB
Rust

use nomifun_ai_agent::cc_switch::{
CcSwitchPaths, build_model_info_from_env, read_claude_model_info_with_paths, read_claude_provider_env_with_paths,
};
use rusqlite::Connection;
use std::collections::HashMap;
use std::fs;
use tempfile::TempDir;
fn create_test_db(dir: &std::path::Path, provider_id: &str, settings_config: &str) {
let db_path = dir.join("cc-switch.db");
let conn = Connection::open(&db_path).unwrap();
conn.execute_batch(
"CREATE TABLE IF NOT EXISTS providers (
id TEXT NOT NULL,
app_type TEXT NOT NULL,
name TEXT NOT NULL,
settings_config TEXT NOT NULL,
PRIMARY KEY (id, app_type)
);
CREATE TABLE IF NOT EXISTS model_pricing (
model_id TEXT PRIMARY KEY,
display_name TEXT NOT NULL
);",
)
.unwrap();
conn.execute(
"INSERT INTO providers (id, app_type, name, settings_config) VALUES (?1, 'claude', 'Test Provider', ?2)",
[provider_id, settings_config],
)
.unwrap();
conn.execute(
"INSERT INTO model_pricing (model_id, display_name) VALUES (?1, ?2)",
["deepseek-v4-pro", "DeepSeek V4 Pro"],
)
.unwrap();
conn.execute(
"INSERT INTO model_pricing (model_id, display_name) VALUES (?1, ?2)",
["deepseek-v4-max", "DeepSeek V4 Max"],
)
.unwrap();
}
#[test]
fn reads_provider_env_from_fixture_db() {
let tmp = TempDir::new().unwrap();
let cc_switch_dir = tmp.path().join(".cc-switch");
fs::create_dir_all(&cc_switch_dir).unwrap();
let settings = r#"{"currentProviderClaude": "deepseek-relay"}"#;
fs::write(cc_switch_dir.join("settings.json"), settings).unwrap();
let config = serde_json::json!({
"env": {
"ANTHROPIC_BASE_URL": "https://relay.example.com/v1",
"ANTHROPIC_API_KEY": "sk-relay-test-key",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-v4-pro",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek-v4-max"
},
"model": "default"
});
create_test_db(&cc_switch_dir, "deepseek-relay", &config.to_string());
let paths = CcSwitchPaths::from_home(tmp.path());
let env = read_claude_provider_env_with_paths(&paths);
assert_eq!(env.get("ANTHROPIC_BASE_URL").unwrap(), "https://relay.example.com/v1");
assert_eq!(env.get("ANTHROPIC_API_KEY").unwrap(), "sk-relay-test-key");
assert_eq!(env.get("ANTHROPIC_DEFAULT_SONNET_MODEL").unwrap(), "deepseek-v4-pro");
assert_eq!(env.get("ANTHROPIC_DEFAULT_OPUS_MODEL").unwrap(), "deepseek-v4-max");
}
#[test]
fn reads_model_info_from_fixture_db() {
let tmp = TempDir::new().unwrap();
let cc_switch_dir = tmp.path().join(".cc-switch");
fs::create_dir_all(&cc_switch_dir).unwrap();
let settings = r#"{"currentProviderClaude": "deepseek-relay"}"#;
fs::write(cc_switch_dir.join("settings.json"), settings).unwrap();
let config = serde_json::json!({
"env": {
"ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-v4-pro",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek-v4-max"
},
"model": "default"
});
create_test_db(&cc_switch_dir, "deepseek-relay", &config.to_string());
let paths = CcSwitchPaths::from_home(tmp.path());
let info = read_claude_model_info_with_paths(&paths);
assert!(info.is_some());
let payload = info.unwrap();
assert_eq!(payload.available_models.len(), 2);
assert_eq!(payload.current_model_id.as_deref(), Some("default"));
assert_eq!(payload.current_model_label.as_deref(), Some("DeepSeek V4 Pro"));
assert_eq!(payload.available_models[0].label, "DeepSeek V4 Pro");
assert_eq!(payload.available_models[1].label, "DeepSeek V4 Max");
}
#[test]
fn gracefully_handles_missing_cc_switch() {
let tmp = TempDir::new().unwrap();
let paths = CcSwitchPaths::from_home(tmp.path());
let env = read_claude_provider_env_with_paths(&paths);
assert!(env.is_empty());
let info = read_claude_model_info_with_paths(&paths);
assert!(info.is_none());
}
#[test]
fn gracefully_handles_empty_provider_id() {
let tmp = TempDir::new().unwrap();
let cc_switch_dir = tmp.path().join(".cc-switch");
fs::create_dir_all(&cc_switch_dir).unwrap();
fs::write(cc_switch_dir.join("settings.json"), r#"{"currentProviderClaude": ""}"#).unwrap();
let paths = CcSwitchPaths::from_home(tmp.path());
let env = read_claude_provider_env_with_paths(&paths);
assert!(env.is_empty());
}
#[test]
fn default_provider_returns_empty_env_when_no_env_configured() {
let tmp = TempDir::new().unwrap();
let cc_switch_dir = tmp.path().join(".cc-switch");
fs::create_dir_all(&cc_switch_dir).unwrap();
let settings = r#"{"currentProviderClaude": "default"}"#;
fs::write(cc_switch_dir.join("settings.json"), settings).unwrap();
let config = serde_json::json!({
"env": {}
});
create_test_db(&cc_switch_dir, "default", &config.to_string());
let paths = CcSwitchPaths::from_home(tmp.path());
let env = read_claude_provider_env_with_paths(&paths);
assert!(env.is_empty());
}
#[test]
fn build_model_info_from_env_works_standalone() {
let mut env = HashMap::new();
env.insert("ANTHROPIC_DEFAULT_SONNET_MODEL".into(), "test-model".into());
let labels = HashMap::from([("test-model".to_owned(), "Test Model Display".to_owned())]);
let info = build_model_info_from_env(&env, &labels, None);
assert!(info.is_some());
let payload = info.unwrap();
assert_eq!(payload.available_models[0].label, "Test Model Display");
}