use super::{ACME, applying, brazen_file, deps_at, fire, quiet, script};
use crate::actions::verbs::Outcome;
use crate::boundary::config::{ConfigFile, write};
use crate::boundary::dispatch::Deps;
use crate::boundary::reply::Reply;
use crate::config_edit::branch::edit::EditOrigin;
use crate::config_edit::brazen::Applied;
use crate::config_edit::litany_global::Saved;
use crate::test_support::world_under;
use std::fs;
use std::path::Path;
use tempfile::tempdir;
#[test]
fn a_brazen_apply_lands_only_what_bz_accepts() {
let root = tempdir().unwrap();
let deps = quiet(root.path());
let good = ACME.replace("acme", "zinc");
let dest = crate::test_support::wall_paths(root.path()).config;
assert_eq!(
fire(&deps, &applying(brazen_file(), &good)),
Ok(Reply::Applied)
);
assert_eq!(fs::read_to_string(&dest).unwrap(), good);
let err = fire(&deps, &applying(brazen_file(), "not toml = = =\n")).unwrap_err();
assert!(err.contains("bz refused the draft"), "{err}");
assert_eq!(fs::read_to_string(&dest).unwrap(), good, "left untouched");
}
#[test]
fn a_brazen_apply_that_cannot_write_says_so() {
let root = tempdir().unwrap();
let deps = Deps {
world: world_under(&root.path().join("gone")),
..quiet(root.path())
};
let err = fire(
&deps,
&applying(brazen_file(), &ACME.replace("acme", "zinc")),
)
.unwrap_err();
assert!(!err.is_empty(), "the io error rides back verbatim");
}
#[test]
fn a_litany_global_apply_lands_the_text_it_is_handed() {
let root = tempdir().unwrap();
let deps = quiet(root.path());
let text = "models:\n m-1:\n context_window: 200000\n";
assert_eq!(
fire(&deps, &applying(ConfigFile::LitanyModels, text)),
Ok(Reply::Applied)
);
assert_eq!(
fs::read_to_string(root.path().join("litany/models.yaml")).unwrap(),
text
);
let legacy = "models:\n m-2:\n provider: nope\n context_window: 400000\n";
assert_eq!(
fire(&deps, &applying(ConfigFile::LitanyModels, legacy)),
Ok(Reply::Applied)
);
assert_eq!(
fs::read_to_string(root.path().join("litany/models.yaml")).unwrap(),
legacy
);
}
#[test]
fn a_workflow_destination_must_name_one_file_and_lands_under_workflows() {
let root = tempdir().unwrap();
let deps = quiet(root.path());
let err = fire(
&deps,
&applying(
ConfigFile::LitanyWorkflow {
name: "a/b".to_owned(),
},
"events: {}\n",
),
)
.unwrap_err();
assert_eq!(err, "name must be a single file, no path");
assert_eq!(
fire(
&deps,
&applying(
ConfigFile::LitanyWorkflow {
name: "review".to_owned(),
},
"events: {}\n",
),
),
Ok(Reply::Applied)
);
assert_eq!(
fs::read_to_string(root.path().join("litany/workflows/review.yaml")).unwrap(),
"events: {}\n"
);
}
#[test]
fn a_cadence_apply_writes_the_clocks_own_file() {
let root = tempdir().unwrap();
let deps = quiet(root.path());
let text = "sweep_secs: 3\n";
let dest = write::cadence_path(&deps.world);
assert_eq!(
fire(&deps, &applying(ConfigFile::Cadence, text)),
Ok(Reply::Applied)
);
assert_eq!(fs::read_to_string(&dest).unwrap(), text);
}
#[test]
fn an_unreadable_file_refuses_before_anything_is_staged() {
let root = tempdir().unwrap();
let deps = quiet(root.path());
fs::create_dir_all(root.path().join("litany/models.yaml")).unwrap();
assert!(fire(&deps, &applying(ConfigFile::LitanyModels, "x: 1\n")).is_err());
}
#[test]
fn a_lineage_apply_stages_the_text_and_drives_litany_config() {
let root = tempdir().unwrap();
let bin = tempdir().unwrap();
let log = bin.path().join("log");
let litany = script(
bin.path(),
"litany",
&format!(
"cat \"$YOG_EDIT_SRC/providers.yaml\" > {}\nprintf 'ok'\nexit 0\n",
log.display()
),
);
let ws = root.path().join("sphere");
let deps = super::seeing(
&deps_at(root.path(), &litany, Path::new("/no/bl")),
&[ws.as_path()],
);
let text = "roles:\n worker:\n provider: acme\n";
let reply = fire(
&deps,
&applying(
ConfigFile::Branch {
workspace: crate::naming::leaf(&ws),
lineage: "default".to_owned(),
origin: EditOrigin::Advance,
path: "providers.yaml".to_owned(),
},
text,
),
);
assert_eq!(
reply,
Ok(Reply::Outcome(Outcome {
exit: 0,
stdout: "ok".to_owned(),
stderr: String::new(),
}))
);
assert_eq!(fs::read_to_string(&log).unwrap(), text);
let logged = crate::opslog::tail(&deps.state_root, 10);
assert_eq!(logged.len(), 1);
assert_eq!(
&logged[0].argv[1..],
["config", &ws.display().to_string(), "default"]
);
}
#[test]
fn a_lineage_apply_that_cannot_stage_says_so() {
let root = tempdir().unwrap();
let ws = root.path().join("sphere");
let deps = super::seeing(&quiet(root.path()), &[ws.as_path()]);
fs::write(deps.world.yog_stage_root(), b"in the way").unwrap();
let err = fire(
&deps,
&applying(
ConfigFile::Branch {
workspace: crate::naming::leaf(&ws),
lineage: "default".to_owned(),
origin: EditOrigin::Orphan,
path: "workflow.yaml".to_owned(),
},
"events: {}\n",
),
)
.unwrap_err();
assert!(!err.is_empty(), "{err}");
}
#[test]
fn the_two_verdict_folds_name_every_refusal() {
assert_eq!(write::applied(Applied::Ok), Ok(()));
assert_eq!(
write::applied(Applied::Conflict),
Err(write::CONFLICT.to_owned())
);
assert_eq!(
write::applied(Applied::Io {
error: "disk".to_owned()
}),
Err("disk".to_owned())
);
assert_eq!(
write::saved(Saved::Conflict),
Err(write::CONFLICT.to_owned())
);
assert_eq!(
write::saved(Saved::Io {
error: "disk".to_owned()
}),
Err("disk".to_owned())
);
}