use super::*;
use crate::wire::{Binding, Command};
use std::fs;
use std::path::PathBuf;
use tempfile::TempDir;
fn clk() -> i64 {
0
}
fn pref(name: &str, bin: Option<PathBuf>) -> PluginRef {
PluginRef { name: name.into(), bin, source: None }
}
fn ctx() -> OpContext {
OpContext {
actor: "me".into(),
binding: Binding {
remote: None,
stealth: false,
tasks_branch: "balls/tasks".into(),
store: "/store".into(),
landing: "/landing".into(),
invocation_path: "/proj".into(),
},
command: Some(Command { op: "close".into(), id: Some("bl-1234".into()), body_change: None, message: None, target: None }),
before: None,
}
}
#[test]
fn a_missing_third_party_plugin_aborts_and_names_bl_install() {
let tmp = TempDir::new().unwrap();
let log = Log::new(tmp.path().join("log"), Level::Debug, Verb::Close, clk);
let err = Subprocess::new(ctx(), &log, 0)
.run(&pref("ghost", None), Verb::Close, Phase::Pre, tmp.path(), None)
.unwrap_err();
assert!(err.to_string().contains("ghost referenced but bin/ghost missing — run bl install"));
}
#[test]
fn a_missing_plugin_with_a_source_hint_names_the_acquisition() {
let tmp = TempDir::new().unwrap();
let log = Log::new(tmp.path().join("log"), Level::Debug, Verb::Close, clk);
let hinted = PluginRef { name: "ghost".into(), bin: None, source: Some("cargo install ghost".into()) };
let err = Subprocess::new(ctx(), &log, 0)
.run(&hinted, Verb::Close, Phase::Pre, tmp.path(), None)
.unwrap_err();
assert!(
err.to_string()
.contains("ghost referenced but bin/ghost missing — source: cargo install ghost — then bl install to bind"),
"{err}"
);
}
#[test]
fn a_renamed_plugins_notice_carries_the_new_names_hint_when_authored() {
let tmp = TempDir::new().unwrap();
let log_path = tmp.path().join("log");
let log = Log::new(log_path.clone(), Level::Debug, Verb::Sync, clk);
let hinted = PluginRef { name: "tracker".into(), bin: None, source: Some("make install".into()) };
Subprocess::new(ctx(), &log, 0).run(&hinted, Verb::Sync, Phase::Pre, tmp.path(), None).unwrap();
let log_body = fs::read_to_string(&log_path).unwrap();
assert!(
log_body.contains("then prime to resume — source: make install"),
"{log_body}"
);
}
#[test]
fn a_missing_renamed_first_party_plugin_is_skipped_with_a_notice() {
let tmp = TempDir::new().unwrap();
let log_path = tmp.path().join("log");
let log = Log::new(log_path.clone(), Level::Debug, Verb::Sync, clk);
Subprocess::new(ctx(), &log, 0)
.run(&pref("tracker", None), Verb::Sync, Phase::Pre, tmp.path(), None)
.unwrap(); let rec = fs::read_to_string(&log_path)
.unwrap()
.lines()
.map(|l| serde_json::from_str::<serde_json::Value>(l).unwrap())
.find(|r| r["src"] == "core" && r["msg"].as_str().is_some_and(|m| m.contains("was renamed")))
.expect("a rename notice was logged");
assert_eq!(rec["lvl"], "info");
let msg = rec["msg"].as_str().unwrap();
assert!(msg.contains("tracker was renamed bl-tracker"), "{msg}");
assert!(msg.contains("bl conf"), "points at the fix: {msg}");
}