use super::{SeedError, ensure_seeded, seeded};
use crate::cli_outbound::Cli;
use crate::opslog::{self, OpEntry, Origin};
use crate::world::{Layout, layout_under};
use std::fs;
use std::path::Path;
use tempfile::{TempDir, tempdir};
struct World {
bin: TempDir,
state: TempDir,
yog: TempDir,
}
impl World {
fn new() -> Self {
Self {
bin: tempdir().unwrap(),
state: tempdir().unwrap(),
yog: tempdir().unwrap(),
}
}
fn layout(&self) -> Layout {
layout_under(self.yog.path())
}
fn seed_marker(&self) {
let litany = self.layout().litany;
fs::create_dir_all(&litany).unwrap();
fs::write(litany.join("models.yaml"), b"models: {}\n").unwrap();
}
fn ops(&self) -> Vec<OpEntry> {
opslog::tail(self.state.path(), 16)
}
}
fn fake_litany(dir: &Path, body: &str) -> std::path::PathBuf {
let path = dir.join("litany");
crate::test_support::write_exec(&path, body);
path
}
#[test]
fn seeded_is_true_iff_models_yaml_is_present() {
let w = World::new();
assert!(!seeded(&w.layout()), "a fresh world has no models.yaml");
w.seed_marker();
assert!(seeded(&w.layout()), "the founded marker reads as seeded");
}
#[test]
fn ensure_seeded_skips_a_seeded_world() {
let w = World::new();
w.seed_marker();
let litany = Cli::new("/definitely/not/a/real/litany");
let primed = ensure_seeded(
&litany,
w.state.path(),
"TS",
&w.layout(),
Origin::Conversation,
)
.unwrap();
assert!(!primed, "a seeded world is skipped");
assert!(w.ops().is_empty(), "the skip runs and logs nothing");
}
#[test]
fn ensure_seeded_primes_with_litany_home_and_logs() {
let w = World::new();
let report = w.bin.path().join("home");
let body = format!(
"#!/bin/sh\nprintf '%s' \"$LITANY_HOME\" > '{}'\n",
report.display()
);
let layout = w.layout();
let litany = Cli::new(fake_litany(w.bin.path(), &body)).with_env(vec![(
"LITANY_HOME".to_owned(),
layout.litany.to_string_lossy().into_owned(),
)]);
let primed =
ensure_seeded(&litany, w.state.path(), "TS", &layout, Origin::Conversation).unwrap();
assert!(primed, "an unseeded world is primed");
let got = fs::read_to_string(&report).unwrap();
assert_eq!(Path::new(&got), layout.litany);
let e = w.ops();
assert_eq!(e.len(), 1);
assert_eq!(&e[0].argv[1..], &["prime"]);
assert_eq!(e[0].exit, 0);
assert_eq!(e[0].cwd, "");
assert!(e[0].stderr.is_empty(), "a clean prime logs no error");
}
#[test]
fn ensure_seeded_errors_on_a_nonzero_prime() {
let w = World::new();
let body = "#!/bin/sh\nprintf '%s\\n' 'prime boom' 1>&2\nexit 3\n";
let litany = Cli::new(fake_litany(w.bin.path(), body));
let err = ensure_seeded(
&litany,
w.state.path(),
"TS",
&w.layout(),
Origin::Conversation,
)
.unwrap_err();
let SeedError::Prime(out) = err else {
panic!("expected Prime, got {err:?}");
};
assert_eq!(out.exit, 3);
assert!(out.stderr.contains("prime boom"));
assert_eq!(w.ops().len(), 1);
assert!(SeedError::Prime(out).to_string().contains("exit 3"));
}
#[test]
fn ensure_seeded_refuses_an_unmigrated_lernie_era_world() {
let w = World::new();
let era = w.layout().root.join("lernie");
fs::create_dir_all(&era).unwrap();
let litany = Cli::new("/definitely/not/a/real/litany");
let err = ensure_seeded(
&litany,
w.state.path(),
"TS",
&w.layout(),
Origin::Conversation,
)
.unwrap_err();
let SeedError::Unmigrated(got) = &err else {
panic!("expected Unmigrated, got {err:?}");
};
assert_eq!(got, &era);
let said = err.to_string();
assert!(
said.contains("refs/lernie/*"),
"the ref half is named: {said}"
);
assert!(
said.contains("rename that directory"),
"the remedy is stated: {said}"
);
assert!(w.ops().is_empty(), "the refusal runs no child");
let fresh = World::new();
assert!(!fresh.layout().root.join("lernie").is_dir());
}
#[test]
fn ensure_seeded_surfaces_a_spawn_failure() {
let w = World::new();
let litany = Cli::new("/definitely/not/a/real/litany");
let err = ensure_seeded(
&litany,
w.state.path(),
"TS",
&w.layout(),
Origin::Conversation,
)
.unwrap_err();
assert!(matches!(err, SeedError::Io(_)), "a missing binary is Io");
assert!(!err.to_string().is_empty(), "the Io error renders");
let ops = w.ops();
assert_eq!(ops.len(), 1, "a spawn failure appends a synthetic ops line");
assert_eq!(ops[0].exit, crate::opslog::SYNTHETIC_EXIT);
assert_eq!(ops[0].argv[0], "/definitely/not/a/real/litany");
assert!(!ops[0].stderr.is_empty());
}