use super::super::*;
use crate::config::LlmConfig;
fn config_with(endpoints: &[&str]) -> Config {
Config {
max_review_rounds: crate::config::DEFAULT_MAX_REVIEW_ROUNDS,
llm: endpoints
.iter()
.map(|endpoint| LlmConfig {
endpoint: Some((*endpoint).to_string()),
model: Some("m".to_string()),
..LlmConfig::default()
})
.collect(),
}
}
#[test]
fn a_stored_key_fills_in_an_entry_that_named_none() {
let mut config = config_with(&["https://api.kimi.com/coding/v1"]);
let mut store = AuthStore::new();
store
.set("https://api.kimi.com/coding/v1", "k-1")
.expect("set");
let sources = resolve(&mut config, &store);
assert_eq!(config.llm[0].api_key.as_deref(), Some("k-1"));
assert_eq!(sources, vec![KeySource::Store]);
}
#[test]
fn an_explicit_key_in_the_config_wins_over_a_stored_one() {
let mut config = config_with(&["https://e/v1"]);
config.llm[0].api_key = Some("from-config".to_string());
let mut store = AuthStore::new();
store.set("https://e/v1", "from-store").expect("set");
let sources = resolve(&mut config, &store);
assert_eq!(config.llm[0].api_key.as_deref(), Some("from-config"));
assert_eq!(sources, vec![KeySource::Config]);
}
#[test]
fn an_entry_with_no_stored_key_is_left_unset_and_reported_missing() {
let mut config = config_with(&["https://e/v1"]);
let sources = resolve(&mut config, &AuthStore::new());
assert_eq!(config.llm[0].api_key, None);
assert_eq!(sources, vec![KeySource::Missing]);
}
#[test]
fn a_disabled_entry_is_skipped_rather_than_resolved() {
let mut config = config_with(&["https://e/v1"]);
config.llm[0].enabled = false;
let mut store = AuthStore::new();
store.set("https://e/v1", "k").expect("set");
let sources = resolve(&mut config, &store);
assert_eq!(
config.llm[0].api_key, None,
"a parked entry is inert, so nothing was filled in"
);
assert_eq!(sources, vec![KeySource::Missing]);
}
#[test]
fn sources_are_positional_including_the_disabled_entries() {
let mut config = config_with(&["https://a/v1", "https://b/v1", "https://c/v1"]);
config.llm[1].enabled = false;
config.llm[2].api_key = Some("explicit".to_string());
let mut store = AuthStore::new();
store.set("https://a/v1", "k").expect("set");
let sources = resolve(&mut config, &store);
assert_eq!(
sources,
vec![KeySource::Store, KeySource::Missing, KeySource::Config]
);
assert_eq!(sources.len(), config.llm.len());
}
#[test]
fn an_entry_with_no_endpoint_resolves_to_missing_rather_than_panicking() {
let mut config = Config {
max_review_rounds: crate::config::DEFAULT_MAX_REVIEW_ROUNDS,
llm: vec![LlmConfig {
model: Some("m".to_string()),
..LlmConfig::default()
}],
};
assert_eq!(
resolve(&mut config, &AuthStore::new()),
vec![KeySource::Missing]
);
}
#[test]
fn resolution_matches_the_endpoint_regardless_of_spelling() {
let mut config = config_with(&["https://API.Z.AI/api/coding/paas/v4/"]);
let mut store = AuthStore::new();
store
.set("https://api.z.ai/api/coding/paas/v4", "k")
.expect("set");
let sources = resolve(&mut config, &store);
assert_eq!(config.llm[0].api_key.as_deref(), Some("k"));
assert_eq!(sources, vec![KeySource::Store]);
}
#[test]
fn each_source_has_its_own_label() {
let labels = [
KeySource::Config.label(),
KeySource::Store.label(),
KeySource::Missing.label(),
];
let mut unique = labels.to_vec();
unique.sort_unstable();
unique.dedup();
assert_eq!(unique.len(), 3, "labels collide: {labels:?}");
}