use super::{World, write_exec};
use crate::cli_outbound::Cli;
use crate::opslog::Origin;
use crate::start::{DETACHED_EXIT, Prepared, execute_prompt};
use std::path::Path;
pub(super) fn fire(
ws: &Path,
name: &str,
binding: Option<&Path>,
goal: &str,
) -> crate::start::Fire {
crate::start::Fire {
workspace: ws.to_path_buf(),
prepared: prepared(name, binding),
goal: goal.to_owned(),
}
}
pub(super) fn prepared(name: &str, binding: Option<&Path>) -> Prepared {
Prepared {
workspace: name.to_owned(),
binding: binding.map(Path::to_path_buf),
goal: String::new(),
origin: Origin::Conversation,
lineage: None,
}
}
pub(super) fn workspace(w: &World) -> std::path::PathBuf {
let ws = w.yog.path().join("ws");
std::fs::create_dir_all(&ws).expect("workspace dir");
ws
}
pub(super) fn make_fifo(path: &Path) {
let status = crate::git_env::status(
crate::git_env::command(Path::new("mkfifo"))
.args(["-m", "600"])
.arg(path),
)
.expect("spawn mkfifo");
assert!(status.success(), "mkfifo");
}
#[test]
fn prompt_fires_the_goal_verbatim_layers_yog_name_and_logs_the_sentinel() {
let w = World::new();
let fifo = w.bin.path().join("report");
make_fifo(&fifo);
let body = format!(
"#!/bin/sh\nprintf '%s\\037%s\\037%s\\037%s\\037%s\\037%s' \"$1\" \"$2\" \"$3\" \"$4\" \"$5\" \"$YOG_NAME\" > '{}'\n",
fifo.display()
);
let litany = Cli::new(write_exec(w.bin.path(), "litany", &body));
let ws = workspace(&w);
let conversation = execute_prompt(
&litany,
w.state.path(),
"TS",
&fire(&ws, "cobalt-gecko", None, "do it"),
&[],
&super::rng(),
)
.unwrap();
let recorded = std::fs::read_to_string(&fifo).unwrap();
let fields: Vec<&str> = recorded.split('\u{1f}').collect();
assert_eq!(fields[0], "prompt");
assert_eq!(fields[1], "--name");
assert_eq!(fields[3], ws.to_string_lossy());
assert_eq!(fields[4], "do it", "the payload is unmutated");
assert_eq!(fields[2], conversation, "--name carries the fired mint");
assert_ne!(conversation, "cobalt-gecko");
assert_eq!(fields[5], "cobalt-gecko", "YOG_NAME layered (§8)");
let e = &w.ops()[0];
assert_eq!(e.argv[1], "prompt");
assert_eq!(e.exit, DETACHED_EXIT);
assert_eq!(e.cwd, ws.display().to_string());
assert!(e.stderr.is_empty(), "a clean launch logs no error");
let row = crate::opslog::OpRow::from(e);
assert!(!row.failed());
assert_eq!(
row.exit_label(),
"detached — handed off, no exit to observe"
);
assert_eq!(&e.argv[2..4], ["--name", conversation.as_str()]);
assert_eq!(
e.argv[5], "do it",
"the logged goal is the verbatim payload"
);
}
#[test]
fn prompt_passes_the_typed_target_as_cwd_with_the_goal_still_last() {
let w = World::new();
let fifo = w.bin.path().join("report");
make_fifo(&fifo);
let body = format!(
"#!/bin/sh\nprintf '%s\\037%s\\037%s\\037%s\\037%s\\037%s\\037%s' \"$1\" \"$2\" \"$3\" \"$4\" \"$5\" \"$6\" \"$7\" > '{}'\n",
fifo.display()
);
let litany = Cli::new(write_exec(w.bin.path(), "litany", &body));
let ws = workspace(&w);
let target = w.balls.path().join("work");
std::fs::create_dir_all(&target).unwrap();
let conversation = execute_prompt(
&litany,
w.state.path(),
"TS",
&fire(&ws, "cobalt-gecko", Some(&target), "do it"),
&[],
&super::rng(),
)
.unwrap();
let recorded = std::fs::read_to_string(&fifo).unwrap();
let fields: Vec<&str> = recorded.split('\u{1f}').collect();
assert_eq!(fields[0], "prompt");
assert_eq!(fields[1], "--name");
assert_eq!(fields[2], conversation);
assert_eq!(fields[3], "--cwd", "the binding is a parameter, not prose");
assert_eq!(fields[4], target.to_string_lossy());
assert_eq!(fields[5], ws.to_string_lossy());
assert_eq!(fields[6], "do it", "the goal is still the last argument");
let e = &w.ops()[0];
assert_eq!(&e.argv[4..6], ["--cwd", target.to_string_lossy().as_ref()]);
assert_eq!(e.argv[7], "do it");
assert_eq!(e.cwd, ws.display().to_string());
}