#![allow(dead_code)]
use gy_ledger::{
DecisionScope, FileStore, FormatVersion, Node, NodeId, NodeKind, Repository, format, location,
};
use std::path::PathBuf;
use std::process::{Command, Output};
pub const DATE: &str = "2026-09-15T00:00:00Z";
pub fn node_id(kind: NodeKind, hash: &str) -> NodeId {
NodeId::from_hash(kind, hash).unwrap()
}
pub fn need(hash: &str, title: &str) -> Node {
Node::need(node_id(NodeKind::Need, hash), "a", DATE, title).unwrap()
}
pub fn criterion(hash: &str, title: &str) -> Node {
Node::criterion(node_id(NodeKind::Criterion, hash), "a", DATE, title).unwrap()
}
pub fn question(hash: &str, title: &str) -> Node {
Node::question(node_id(NodeKind::Question, hash), "a", DATE, title).unwrap()
}
pub fn decision(hash: &str, title: &str) -> Node {
Node::decision(
node_id(NodeKind::Decision, hash),
"a",
DATE,
title,
DecisionScope::recorded("scope").unwrap(),
)
.unwrap()
}
pub struct Fixture {
_temp: tempfile::TempDir,
pub root: PathBuf,
pub data: PathBuf,
}
pub fn fixture() -> Fixture {
let temp = tempfile::tempdir().unwrap();
let root = temp.path().join("repo");
std::fs::create_dir_all(&root).unwrap();
std::fs::write(root.join("gy.toml"), "[scopes.a]\n").unwrap();
let data = temp.path().join("data");
std::fs::create_dir_all(&data).unwrap();
Fixture {
_temp: temp,
root,
data,
}
}
pub fn stdout(output: &Output) -> String {
String::from_utf8(output.stdout.clone()).unwrap()
}
pub fn stderr(output: &Output) -> String {
String::from_utf8(output.stderr.clone()).unwrap()
}
pub fn id_of(output: &Output) -> String {
let text = stdout(output);
let line = text.lines().next().unwrap_or_default();
let id = line.strip_prefix("id: ").unwrap_or(line);
id.split_whitespace().next().unwrap_or_default().to_string()
}
pub fn cover(fx: &Fixture, criterion: &str) -> String {
let need = id_of(&fx.run(&["need", "add", "a need", "--targets", criterion]));
let request = id_of(&fx.run(&[
"req",
"add",
"a requirement",
"--need",
&need,
"--targets",
criterion,
]));
let approved = fx.run(&[
"req",
"approve",
&request,
"--design",
"d",
"--heard-by",
"master",
"--evidence",
"e",
]);
assert!(approved.status.success(), "{}", stderr(&approved));
request
}
impl Fixture {
pub fn ledger(&self) -> PathBuf {
location::dir_in(&self.data, &self.root)
}
pub fn seed(&self, nodes: &[Node]) {
let ledger = self.ledger();
format::write(&ledger, FormatVersion::CURRENT).unwrap();
let store = FileStore::open_with(&ledger, |_| Some("piko".into())).unwrap();
let mut repository = Repository::new(store);
repository
.transaction("seed", "test", |repository| {
for node in nodes {
repository.put(node)?;
}
Ok(())
})
.unwrap();
}
pub fn run(&self, args: &[&str]) -> Output {
self.command(args).output().unwrap()
}
pub fn run_without_actor(&self, args: &[&str]) -> Output {
self.command(args).env_remove("GY_ACTOR").output().unwrap()
}
fn command(&self, args: &[&str]) -> Command {
let mut command = Command::new(env!("CARGO_BIN_EXE_gy"));
command
.current_dir(&self.root)
.env("XDG_DATA_HOME", &self.data)
.env("GY_ACTOR", "piko")
.args(args);
command
}
}
pub fn ident() {
static ONCE: std::sync::Once = std::sync::Once::new();
ONCE.call_once(|| unsafe {
let path = std::env::temp_dir().join(format!("gy-ident-{}", std::process::id()));
std::fs::write(&path, "[user]\n\tname = piko\n\temail = piko@example.com\n").unwrap();
for (key, value) in [
("GIT_CONFIG_GLOBAL", path.display().to_string()),
("GIT_CONFIG_SYSTEM", "/dev/null".to_string()),
("GIT_CONFIG_NOSYSTEM", "1".to_string()),
("GIT_AUTHOR_NAME", "piko".to_string()),
("GIT_AUTHOR_EMAIL", "piko@example.com".to_string()),
("GIT_COMMITTER_NAME", "piko".to_string()),
("GIT_COMMITTER_EMAIL", "piko@example.com".to_string()),
] {
std::env::set_var(key, value);
}
});
}