use super::support::args;
use crate::cli::init::wizard::tests::Scripted;
use crate::cli::init::{InitArgs, describe, existing_config};
fn root(body: Option<&str>) -> tempfile::TempDir {
let dir = tempfile::tempdir().expect("tempdir");
if let Some(text) = body {
std::fs::write(dir.path().join("drep.toml"), text).expect("write config");
}
dir
}
const EXISTING: &str = r#"
[[llm]]
endpoint = "http://localhost:1234/v1"
model = "qwen3-30b-a3b"
"#;
#[test]
fn a_repository_with_no_config_proceeds_without_asking() {
let dir = root(None);
let mut console = Scripted::new(&[]);
let force = existing_config(dir.path(), &args(), true, &mut console).expect("decides");
assert_eq!(force, Some(false), "nothing to replace, so no force needed");
assert!(
console.transcript().is_empty(),
"and nothing was said: {}",
console.transcript()
);
}
#[test]
fn force_skips_the_question_entirely() {
let dir = root(Some(EXISTING));
let forced = InitArgs {
force: true,
..args()
};
let mut console = Scripted::new(&[]);
let force = existing_config(dir.path(), &forced, true, &mut console).expect("decides");
assert_eq!(force, Some(true));
assert!(console.transcript().is_empty(), "asked nothing");
}
#[test]
fn a_non_interactive_run_refuses_and_names_the_flag() {
let dir = root(Some(EXISTING));
let mut console = Scripted::new(&[]);
let err = existing_config(dir.path(), &args(), false, &mut console)
.expect_err("an existing config is fatal without a person to ask");
let message = err.to_string();
assert!(message.contains("already exists"), "got {message}");
assert!(message.contains("--force"), "got {message}");
}
#[test]
fn declining_stops_the_run_before_anything_is_asked_or_stored() {
let dir = root(Some(EXISTING));
let mut console = Scripted::new(&["n"]);
let decision = existing_config(dir.path(), &args(), true, &mut console).expect("decides");
assert_eq!(decision, None, "None is what stops the run");
assert!(console.is_drained());
assert!(
console.transcript().contains("drep auth login"),
"and points at the command that rotates a key: {}",
console.transcript()
);
}
#[test]
fn accepting_continues_with_force_so_the_write_can_replace_the_file() {
let dir = root(Some(EXISTING));
let mut console = Scripted::new(&["y"]);
let force = existing_config(dir.path(), &args(), true, &mut console).expect("decides");
assert_eq!(force, Some(true));
}
#[test]
fn the_default_answer_is_to_leave_the_config_alone() {
let dir = root(Some(EXISTING));
let mut console = Scripted::new(&[""]);
let decision = existing_config(dir.path(), &args(), true, &mut console).expect("decides");
assert_eq!(decision, None);
}
#[test]
fn the_prompt_shows_what_is_currently_configured() {
let dir = root(Some(EXISTING));
let mut console = Scripted::new(&["n"]);
existing_config(dir.path(), &args(), true, &mut console).expect("decides");
let transcript = console.transcript();
assert!(transcript.contains("qwen3-30b-a3b"), "got {transcript}");
assert!(
transcript.contains("http://localhost:1234/v1"),
"got {transcript}"
);
}
#[test]
fn every_provider_in_the_existing_chain_is_shown() {
let dir = root(Some(
r#"
[[llm]]
endpoint = "http://localhost:1234/v1"
model = "local-model"
[[llm]]
endpoint = "https://openrouter.ai/api/v1"
model = "cloud-model"
"#,
));
let lines = describe(&dir.path().join("drep.toml"));
assert_eq!(lines.len(), 2, "got {lines:?}");
assert!(lines[0].contains("local-model"), "got {lines:?}");
assert!(lines[1].contains("cloud-model"), "got {lines:?}");
}
#[test]
fn a_disabled_provider_is_shown_as_disabled() {
let dir = root(Some(
r#"
[[llm]]
enabled = false
endpoint = "http://localhost:1234/v1"
model = "parked"
"#,
));
let lines = describe(&dir.path().join("drep.toml"));
assert!(lines[0].contains("(disabled)"), "got {lines:?}");
}
#[test]
fn an_enabled_provider_is_not_labelled() {
let dir = root(Some(EXISTING));
let lines = describe(&dir.path().join("drep.toml"));
assert!(!lines[0].contains("disabled"), "got {lines:?}");
}
#[test]
fn a_missing_field_is_named_rather_than_omitted() {
let dir = root(Some("[[llm]]\nmodel = \"only-a-model\"\n"));
let lines = describe(&dir.path().join("drep.toml"));
assert!(lines[0].contains("only-a-model"), "got {lines:?}");
assert!(
lines[0].contains("(unset)"),
"the gap is visible: {lines:?}"
);
}
#[test]
fn an_unparseable_config_still_lets_the_user_answer() {
let dir = root(Some("this is not toml {{{"));
let lines = describe(&dir.path().join("drep.toml"));
assert_eq!(lines, vec!["(could not be parsed)".to_string()]);
}
#[test]
fn a_well_formed_document_is_not_reported_as_unparseable() {
let dir = root(Some(EXISTING));
let lines = describe(&dir.path().join("drep.toml"));
assert_ne!(lines, vec!["(could not be parsed)".to_string()]);
assert!(lines[0].contains("qwen3-30b-a3b"), "got {lines:?}");
}
#[test]
fn a_missing_file_is_described_rather_than_panicking() {
let dir = root(None);
let lines = describe(&dir.path().join("drep.toml"));
assert_eq!(lines, vec!["(could not be read)".to_string()]);
}
#[test]
fn a_config_declaring_no_provider_is_described_as_such() {
for body in ["# just a comment\n", "llm = []\n"] {
let dir = root(Some(body));
let lines = describe(&dir.path().join("drep.toml"));
assert_eq!(
lines,
vec!["(no [[llm]] provider)".to_string()],
"body {body:?}"
);
}
}
#[tokio::test]
async fn replacing_the_config_does_not_authorise_clobbering_a_foreign_hook() {
let dir = tempfile::tempdir().expect("tempdir");
crate::test_support::git_init(dir.path());
let hooks_dir = dir.path().join(".git").join("hooks");
std::fs::create_dir_all(&hooks_dir).expect("hooks dir");
let foreign = hooks_dir.join("pre-push");
std::fs::write(&foreign, "#!/bin/sh\necho someone elses hook\n").expect("write hook");
std::fs::write(dir.path().join("drep.toml"), EXISTING).expect("existing config");
let mut out = Vec::new();
let result = crate::cli::init::run_with(
&mut out,
&InitArgs {
path: dir.path().to_path_buf(),
provider: Some("local".to_string()),
no_gitignore: true,
non_interactive: true,
force: false,
..args()
},
&dir.path().join("auth.toml"),
)
.await;
assert!(
result.is_err(),
"an existing config is refused without --force"
);
assert_eq!(
std::fs::read_to_string(&foreign).expect("hook"),
"#!/bin/sh\necho someone elses hook\n",
"the foreign hook was not touched"
);
}