use crate::config::{LoadError, ModelsConfig};
use crate::harness_root;
use std::path::PathBuf;
use tempfile::TempDir;
const GLOBAL_MODELS: &str = "# adapter: /path/to/bz-compatible-binary\n";
const PER_REPO_ROLES: &str = r#"
roles:
worker:
provider: anthropic
model: claude-sonnet-5
tools: [bash, read_file]
compactor:
provider: anthropic
model: claude-haiku-4-5
"#;
struct Scratch {
_root: TempDir,
global_path: PathBuf,
per_repo_raw: String,
per_repo_origin: PathBuf,
}
impl Scratch {
fn load(&self) -> Result<ModelsConfig, LoadError> {
ModelsConfig::load_with_per_repo(
&self.global_path,
&self.per_repo_raw,
&self.per_repo_origin,
)
}
}
fn scratch(global: &str, per_repo: &str) -> Scratch {
let root = TempDir::new().unwrap();
let global_path = harness_root::models_path(root.path());
std::fs::write(&global_path, global).unwrap();
Scratch {
_root: root,
global_path,
per_repo_raw: per_repo.to_string(),
per_repo_origin: PathBuf::from("<commit>:providers.yaml"),
}
}
#[test]
fn loads_both_halves() {
let s = scratch(GLOBAL_MODELS, PER_REPO_ROLES);
let cfg = s.load().unwrap();
assert!(cfg.global.adapter.is_none());
assert_eq!(cfg.per_repo.roles.len(), 2);
assert_eq!(cfg.per_repo.roles["worker"].model, "claude-sonnet-5");
assert_eq!(
cfg.per_repo.roles["worker"].tools,
vec!["bash", "read_file"]
);
}
#[test]
fn adapter_override_rides_the_global_half() {
let s = scratch("adapter: /opt/bz\n", PER_REPO_ROLES);
let cfg = s.load().unwrap();
assert_eq!(
cfg.global.adapter.as_deref(),
Some(std::path::Path::new("/opt/bz"))
);
}
#[test]
fn legacy_blocks_in_per_repo_are_a_load_error() {
let per_repo_with_legacy = format!("models: {{}}\n{PER_REPO_ROLES}");
let s = scratch(GLOBAL_MODELS, &per_repo_with_legacy);
let err = s.load().unwrap_err();
match err {
LoadError::Invalid { key, .. } => assert_eq!(key, "models"),
other => panic!("expected Invalid, got {other:?}"),
}
}
#[test]
fn missing_global_is_a_load_error() {
let s = scratch(GLOBAL_MODELS, PER_REPO_ROLES);
std::fs::remove_file(&s.global_path).unwrap();
let err = s.load().unwrap_err();
assert!(matches!(err, LoadError::Io { .. }));
}