use super::{TEMPLATE_PROVIDERS, table};
use crate::model_pick::grammar::{GrammarError, RoleModel, roles};
use crate::model_pick::{
Pick, PickError, WORKER_ROLE, WRITE_NOTE, birth_sentence, default_row, plan, role_fault,
scope_sentence,
};
fn pick(model: &str) -> Pick {
Pick {
role: WORKER_ROLE.to_string(),
provider: "codex".to_string(),
model: model.to_string(),
}
}
fn rows() -> Vec<crate::config_edit::brazen::ProviderRow> {
table(&["codex", "google"])
}
#[test]
fn a_pick_writes_the_role_assignment_and_only_that() {
let assigned = plan(TEMPLATE_PROVIDERS, &rows(), &pick("gpt-5.6-sol")).unwrap();
assert_eq!(
roles(&assigned)
.into_iter()
.find(|r| r.role == WORKER_ROLE)
.map(|r| (r.provider, r.model)),
Some(("codex".to_string(), "gpt-5.6-sol".to_string()))
);
assert!(!assigned.contains("models:"), "{assigned}");
assert!(!assigned.contains("context_window"), "{assigned}");
assert!(plan(TEMPLATE_PROVIDERS, &rows(), &pick("my-local-tag")).is_ok());
}
#[test]
fn an_unreadable_providers_yaml_refuses_the_plan() {
let refused = plan("roles: {}\n", &rows(), &pick("gpt-5.6-sol"));
let expected = GrammarError::Inline {
file: crate::model_pick::grammar::PROVIDERS_YAML,
key: "roles".into(),
};
assert_eq!(refused, Err(PickError::Grammar(expected.clone())));
assert_eq!(refused.unwrap_err().to_string(), expected.to_string());
}
#[test]
fn a_pick_on_a_row_brazen_lacks_is_refused_before_the_file_is_touched() {
let live = table(&["openai-chatgpt"]);
let refused = plan(TEMPLATE_PROVIDERS, &live, &pick("gpt-5.4"));
assert_eq!(
refused,
Err(PickError::UnknownProvider {
provider: "codex".to_string(),
})
);
let said = refused.unwrap_err().to_string();
assert!(said.contains("no provider row `codex`"), "{said}");
assert!(said.contains("pick a live row here"), "{said}");
}
#[test]
fn a_model_id_the_block_grammar_cannot_hold_is_refused() {
for bad in ["", " ", "gpt 5", "gpt:5", "gpt#5", "a\tb"] {
let mut broken = pick(bad);
broken.model = bad.to_string();
assert_eq!(
plan(TEMPLATE_PROVIDERS, &rows(), &broken),
Err(PickError::NotAnId {
model: bad.to_string(),
}),
"{bad:?} must not reach the file"
);
}
let said = PickError::NotAnId {
model: "gpt 5".into(),
}
.to_string();
assert!(said.contains("not a plain model id"), "{said}");
assert!(said.contains("providers.yaml"), "{said}");
assert!(!said.contains("models.yaml"), "{said}");
}
#[test]
fn an_unanswerable_brazen_gates_no_pick() {
assert!(plan(TEMPLATE_PROVIDERS, &[], &pick("gpt-5.4")).is_ok());
}
#[test]
fn the_default_row_leaves_a_row_brazen_dropped_and_says_so() {
let live = vec!["openai-chatgpt".to_string(), "google".to_string()];
let steered = default_row("codex", &live);
assert_eq!(steered.row, "openai-chatgpt");
assert_eq!(steered.stranded.as_deref(), Some("codex"));
let note = steered.strand_note().expect("a strand is named");
assert!(note.contains("codex"), "{note}");
assert!(note.contains("openai-chatgpt"), "{note}");
assert!(note.contains("config.toml"), "{note}");
for scoped in [default_row("google", &live), default_row("codex", &[])] {
assert_eq!(scoped.stranded, None);
assert_eq!(scoped.strand_note(), None);
}
assert_eq!(default_row("google", &live).row, "google");
assert_eq!(default_row("codex", &[]).row, "codex");
}
#[test]
fn the_scope_sentence_states_branch_workspace_and_the_reach_into_this_conversation() {
let s = scope_sentence("mauve-tapir", "default", "1a2b3c4d");
assert!(s.contains("config/default"));
assert!(s.contains("mauve-tapir workspace"));
assert!(s.contains("this one included"));
assert!(s.contains("at its next step"));
assert!(s.contains("advances from 1a2b3c4d"));
assert!(!s.contains("NEXT conversation"), "{s}");
assert!(!s.contains("frozen"), "{s}");
}
#[test]
fn the_birth_sentence_admits_it_moves_the_workspace_default() {
let s = birth_sentence("mauve-tapir", "default");
assert!(s.contains("mauve-tapir workspace default"));
assert!(s.contains("config/default"));
assert!(s.contains("about to start"));
assert!(!s.contains("this one included"));
assert!(!s.contains("frozen"));
}
#[test]
fn the_write_note_names_the_one_file_and_no_second_one() {
assert!(WRITE_NOTE.contains("writes"));
assert!(WRITE_NOTE.contains("providers.yaml"));
assert!(WRITE_NOTE.contains("litany config"));
assert!(!WRITE_NOTE.contains("models.yaml"), "{WRITE_NOTE}");
assert!(!WRITE_NOTE.contains("first"), "{WRITE_NOTE}");
}
#[test]
fn a_role_on_a_row_brazen_lacks_is_marked_in_the_pick_gates_words() {
let stranded = RoleModel {
role: WORKER_ROLE.to_string(),
provider: "codex".to_string(),
model: "gpt-5.4".to_string(),
effort: None,
priority: false,
};
let live = vec!["openai-chatgpt".to_string()];
assert_eq!(
role_fault(&live, &stranded),
Some(
PickError::UnknownProvider {
provider: "codex".to_string(),
}
.to_string()
)
);
let mut alive = stranded.clone();
alive.provider = "openai-chatgpt".to_string();
assert_eq!(role_fault(&live, &alive), None);
assert_eq!(role_fault(&[], &stranded), None);
}