use super::*;
fn parse(raw: &str) -> Result<PerRepoProviders, LoadError> {
PerRepoProviders::parse(raw, Path::new("<commit>:providers.yaml"))
}
const ROLES_ONLY: &str = r#"
roles:
worker:
provider: anthropic
model: claude-sonnet-5
tools: [bash, read_file]
compactor:
provider: anthropic
model: claude-haiku-4-5
"#;
#[test]
fn parses_roles_only() {
let p = parse(ROLES_ONLY).unwrap();
assert_eq!(p.roles.len(), 2);
assert_eq!(p.roles["worker"].provider, "anthropic");
assert_eq!(p.roles["worker"].model, "claude-sonnet-5");
assert_eq!(p.roles["worker"].tools, vec!["bash", "read_file"]);
assert!(p.roles["compactor"].tools.is_empty());
assert!(p.roles["worker"].effort.is_none());
assert!(p.roles["worker"].priority.is_none());
}
#[test]
fn parses_the_role_effort_level() {
let yaml = r#"
roles:
worker:
provider: anthropic
model: claude-sonnet-5
effort: high
"#;
let p = parse(yaml).unwrap();
assert_eq!(p.roles["worker"].effort, Some(Effort::High));
}
#[test]
fn parses_the_role_priority_lane_request() {
let yaml = r#"
roles:
worker:
provider: anthropic
model: claude-sonnet-5
priority: true
"#;
let p = parse(yaml).unwrap();
assert_eq!(p.roles["worker"].priority, Some(true));
}
#[test]
fn rejects_a_priority_that_is_not_a_checkbox() {
let yaml = r#"
roles:
worker:
provider: anthropic
model: claude-sonnet-5
priority: fastest
"#;
let err = parse(yaml).unwrap_err();
assert!(matches!(err, LoadError::Yaml { .. }));
}
#[test]
fn rejects_an_effort_outside_the_vocabulary() {
let yaml = r#"
roles:
worker:
provider: anthropic
model: claude-sonnet-5
effort: maximal
"#;
let err = parse(yaml).unwrap_err();
assert!(matches!(err, LoadError::Yaml { .. }));
}
#[test]
fn missing_roles_section_loads_empty() {
let p = parse("# nothing yet\n").unwrap();
assert!(p.roles.is_empty());
}
#[test]
fn rejects_legacy_providers_block() {
let yaml = r#"
providers:
anthropic:
endpoint: https://api.anthropic.com
auth: { type: api_key, env: ANTHROPIC_API_KEY }
roles:
worker:
provider: anthropic
model: claude-sonnet-5
"#;
let err = parse(yaml).unwrap_err();
match err {
LoadError::Invalid { key, message, .. } => {
assert_eq!(key, "providers");
assert!(message.contains("retired"));
}
other => panic!("expected Invalid, got {other:?}"),
}
}
#[test]
fn rejects_legacy_models_block() {
let yaml = r#"
models:
claude-sonnet-5:
provider: anthropic
model_id: claude-sonnet-5
capabilities: []
context_window: 1000
"#;
let err = parse(yaml).unwrap_err();
match err {
LoadError::Invalid { key, .. } => assert_eq!(key, "models"),
other => panic!("expected Invalid, got {other:?}"),
}
}
#[test]
fn rejects_first_legacy_block_seen() {
let yaml = r#"
providers:
anthropic:
endpoint: x
auth: { type: api_key, env: K }
models:
m: { provider: anthropic, model_id: m, capabilities: [], context_window: 1 }
roles: {}
"#;
let err = parse(yaml).unwrap_err();
match err {
LoadError::Invalid { key, .. } => assert_eq!(key, "providers"),
other => panic!("expected Invalid, got {other:?}"),
}
}
#[test]
fn surfaces_yaml_parse_errors() {
let err = parse("not: [valid: yaml").unwrap_err();
assert!(matches!(err, LoadError::Yaml { .. }));
}
#[test]
fn non_map_roles_section_surfaces_yaml_error() {
let err = parse("roles: [not, a, map]\n").unwrap_err();
assert!(matches!(err, LoadError::Yaml { .. }));
}
#[test]
fn malformed_role_entry_surfaces_yaml_error() {
let yaml = r#"
roles:
worker:
provider: anthropic
"#;
let err = parse(yaml).unwrap_err();
assert!(matches!(err, LoadError::Yaml { .. }));
}
#[test]
fn top_level_non_mapping_loads_empty_roles() {
let p = parse("\"a string\"\n").unwrap();
assert!(p.roles.is_empty());
}