use super::TEMPLATE_PROVIDERS;
use crate::model_pick::grammar::{GrammarError, PROVIDERS_YAML, RoleModel, roles, set_role_model};
#[test]
fn reads_every_role_litanys_template_declares() {
assert_eq!(
roles(TEMPLATE_PROVIDERS),
vec![
RoleModel {
role: "worker".into(),
provider: "codex".into(),
model: "gpt-5.4".into(),
},
RoleModel {
role: "compactor".into(),
provider: "codex".into(),
model: "gpt-5.4-mini".into(),
},
]
);
}
#[test]
fn unrecognized_shapes_declare_no_roles() {
assert!(roles("").is_empty());
assert!(roles("version: 1\n").is_empty());
assert!(roles("roles: {}\n").is_empty());
assert!(roles("rolesx:\n worker:\n provider: p\n model: m\n").is_empty());
assert!(roles("roles:\n worker: { provider: codex, model: gpt-5.4 }\n").is_empty());
assert!(roles("roles:\n worker:\n provider: codex\n").is_empty());
assert!(roles("roles:\n worker:\n model: gpt-5.4\n").is_empty());
assert_eq!(
roles("roles:\n worker:\n provider: p\n compactor:\n provider: q\n model: m\n")
.len(),
1,
"the worker's missing `model:` is not read off the compactor"
);
}
#[test]
fn comments_and_blanks_ride_inside_a_block() {
let text = "roles:\n # the role that talks to you\n worker:\n\n provider: codex\n # a note mid-entry\n model: gpt-5.4\n";
assert_eq!(
roles(text),
vec![RoleModel {
role: "worker".into(),
provider: "codex".into(),
model: "gpt-5.4".into(),
}]
);
}
#[test]
fn a_later_top_level_key_ends_the_block() {
let text = "roles:\n worker:\n provider: codex\n model: gpt-5.4\nversion: 1\n";
assert_eq!(roles(text).len(), 1);
let out = set_role_model(text, "worker", "codex", "gpt-5.6-sol").unwrap();
assert!(out.ends_with("version: 1\n"));
assert_eq!(roles(&out).len(), 1);
assert!(matches!(
set_role_model(
"roles:\n worker:\n provider: codex\nversion: 1\n model: x\n",
"worker",
"codex",
"y"
),
Err(GrammarError::NoField { .. })
));
}
#[test]
fn set_role_model_moves_two_lines_and_nothing_else() {
let out = set_role_model(TEMPLATE_PROVIDERS, "worker", "codex", "gpt-5.6-sol").unwrap();
assert_eq!(
roles(&out),
vec![
RoleModel {
role: "worker".into(),
provider: "codex".into(),
model: "gpt-5.6-sol".into(),
},
RoleModel {
role: "compactor".into(),
provider: "codex".into(),
model: "gpt-5.4-mini".into(),
},
]
);
assert!(out.contains(" tools: [bash, read_file, load_skill]"));
assert!(out.contains(" compactor:\n provider: codex\n model: gpt-5.4-mini"));
}
#[test]
fn set_role_model_declines_an_absent_or_flow_role() {
let err = set_role_model(TEMPLATE_PROVIDERS, "auditor", "codex", "x").unwrap_err();
assert_eq!(
err,
GrammarError::NoEntry {
file: PROVIDERS_YAML,
entry: "auditor".into(),
}
);
assert!(err.to_string().contains("raw editor"));
assert!(matches!(
set_role_model("version: 1\n", "worker", "codex", "x"),
Err(GrammarError::NoEntry { .. })
));
}
#[test]
fn set_role_model_declines_an_inline_roles_key() {
let err = set_role_model("roles: {}\n", "worker", "codex", "x").unwrap_err();
assert_eq!(
err,
GrammarError::Inline {
file: PROVIDERS_YAML,
key: "roles".into(),
}
);
assert!(err.to_string().contains("inline value"));
}
#[test]
fn set_role_model_declines_a_role_missing_the_field_it_must_move() {
let err = set_role_model(
"roles:\n worker:\n provider: codex\n",
"worker",
"codex",
"x",
)
.unwrap_err();
assert_eq!(
err,
GrammarError::NoField {
file: PROVIDERS_YAML,
entry: "worker".into(),
field: "model",
}
);
assert!(err.to_string().contains("model"));
}