huskies: merge 1238 bug worktree tests fail on macOS: tests clobber global HOME
This commit is contained in:
@@ -447,26 +447,35 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn init_git_repo(repo: &std::path::Path) {
|
fn init_git_repo(repo: &std::path::Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init"])
|
.args(["init"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.email", "test@test.com"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.name", "Test"])
|
.args(["config", "user.name", "Test"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.name",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "--allow-empty", "-m", "init"])
|
.args(["commit", "--allow-empty", "-m", "init"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── find_duplicate_module_files tests ────────────────────────
|
// ── find_duplicate_module_files tests ────────────────────────
|
||||||
|
|||||||
@@ -821,26 +821,35 @@ mod tests {
|
|||||||
// ── feature_branch_has_unmerged_changes tests ────────────────────────────
|
// ── feature_branch_has_unmerged_changes tests ────────────────────────────
|
||||||
|
|
||||||
fn init_git_repo(repo: &std::path::Path) {
|
fn init_git_repo(repo: &std::path::Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init"])
|
.args(["init"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.email", "test@test.com"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.name", "Test"])
|
.args(["config", "user.name", "Test"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.name",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "--allow-empty", "-m", "init"])
|
.args(["commit", "--allow-empty", "-m", "init"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bug 226: feature_branch_has_unmerged_changes returns true when the
|
/// Bug 226: feature_branch_has_unmerged_changes returns true when the
|
||||||
|
|||||||
@@ -3,26 +3,35 @@ use super::*;
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
fn init_git_repo(repo: &std::path::Path) {
|
fn init_git_repo(repo: &std::path::Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init"])
|
.args(["init"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.email", "test@test.com"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.name", "Test"])
|
.args(["config", "user.name", "Test"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.name",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "--allow-empty", "-m", "init"])
|
.args(["commit", "--allow-empty", "-m", "init"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
@@ -3,26 +3,35 @@ use super::*;
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
fn init_git_repo(repo: &std::path::Path) {
|
fn init_git_repo(repo: &std::path::Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init"])
|
.args(["init"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.email", "test@test.com"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.name", "Test"])
|
.args(["config", "user.name", "Test"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.name",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "--allow-empty", "-m", "init"])
|
.args(["commit", "--allow-empty", "-m", "init"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
@@ -6,26 +6,35 @@ use std::path::PathBuf;
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
fn init_git_repo(repo: &std::path::Path) {
|
fn init_git_repo(repo: &std::path::Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init"])
|
.args(["init"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.email", "test@test.com"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.name", "Test"])
|
.args(["config", "user.name", "Test"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.name",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "--allow-empty", "-m", "init"])
|
.args(["commit", "--allow-empty", "-m", "init"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── report_completion tests ────────────────────────────────────
|
// ── report_completion tests ────────────────────────────────────
|
||||||
|
|||||||
@@ -34,26 +34,35 @@ fn serial_test_lock() -> std::sync::MutexGuard<'static, ()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn init_git_repo(repo: &std::path::Path) {
|
fn init_git_repo(repo: &std::path::Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init"])
|
.args(["init"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.email", "test@test.com"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.name", "Test"])
|
.args(["config", "user.name", "Test"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.name",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "--allow-empty", "-m", "init"])
|
.args(["commit", "--allow-empty", "-m", "init"])
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── bug 498: stale Running job blocks retry ───────────────────────────────
|
// ── bug 498: stale Running job blocks retry ───────────────────────────────
|
||||||
|
|||||||
@@ -189,16 +189,32 @@ mod tests {
|
|||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
fn init_git_repo(dir: &Path) {
|
fn init_git_repo(dir: &Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
|
git_ok(
|
||||||
|
Command::new("git").args(["init"]).current_dir(dir).output(),
|
||||||
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.expect("git init");
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
|
Command::new("git")
|
||||||
|
.args(["config", "user.name", "Test"])
|
||||||
|
.current_dir(dir)
|
||||||
|
.output(),
|
||||||
|
"git config user.name",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "--allow-empty", "-m", "init"])
|
.args(["commit", "--allow-empty", "-m", "init"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.expect("git commit");
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_project(tmp: &TempDir) -> PathBuf {
|
fn setup_project(tmp: &TempDir) -> PathBuf {
|
||||||
|
|||||||
@@ -1161,34 +1161,45 @@ fn display_section_returns_closed_for_new_terminal_variants() {
|
|||||||
|
|
||||||
/// Initialise a bare-minimum git repo in `dir` with one commit.
|
/// Initialise a bare-minimum git repo in `dir` with one commit.
|
||||||
fn init_git_repo(dir: &std::path::Path) {
|
fn init_git_repo(dir: &std::path::Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init", "-b", "main"])
|
.args(["init", "-b", "main"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.email", "test@test.com"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["config", "user.name", "Test"])
|
.args(["config", "user.name", "Test"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.name",
|
||||||
|
);
|
||||||
// Create an initial commit so the repo has a HEAD.
|
// Create an initial commit so the repo has a HEAD.
|
||||||
std::fs::write(dir.join("README.md"), "# test").unwrap();
|
std::fs::write(dir.join("README.md"), "# test").unwrap();
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["add", "README.md"])
|
.args(["add", "README.md"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git add",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "-m", "init"])
|
.args(["commit", "-m", "init"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -566,33 +566,44 @@ mod tests {
|
|||||||
|
|
||||||
/// Initialise a bare-minimum git repo in `dir` with one commit.
|
/// Initialise a bare-minimum git repo in `dir` with one commit.
|
||||||
fn init_git_repo(dir: &std::path::Path) {
|
fn init_git_repo(dir: &std::path::Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
use std::process::Command as Cmd;
|
use std::process::Command as Cmd;
|
||||||
|
git_ok(
|
||||||
Cmd::new("git")
|
Cmd::new("git")
|
||||||
.args(["init", "-b", "main"])
|
.args(["init", "-b", "main"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Cmd::new("git")
|
Cmd::new("git")
|
||||||
.args(["config", "user.email", "test@test.com"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Cmd::new("git")
|
Cmd::new("git")
|
||||||
.args(["config", "user.name", "Test"])
|
.args(["config", "user.name", "Test"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git config user.name",
|
||||||
|
);
|
||||||
std::fs::write(dir.join("README.md"), "# test").unwrap();
|
std::fs::write(dir.join("README.md"), "# test").unwrap();
|
||||||
|
git_ok(
|
||||||
Cmd::new("git")
|
Cmd::new("git")
|
||||||
.args(["add", "README.md"])
|
.args(["add", "README.md"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git add",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Cmd::new("git")
|
Cmd::new("git")
|
||||||
.args(["commit", "-m", "init"])
|
.args(["commit", "-m", "init"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.unwrap();
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -10,10 +10,55 @@ use std::path::{Path, PathBuf};
|
|||||||
/// replaced by `-` (e.g. `/workspace/.huskies/worktrees/1186` becomes
|
/// replaced by `-` (e.g. `/workspace/.huskies/worktrees/1186` becomes
|
||||||
/// `-workspace--huskies-worktrees-1186`).
|
/// `-workspace--huskies-worktrees-1186`).
|
||||||
pub fn transcript_path(cwd: &Path, session_id: &str) -> PathBuf {
|
pub fn transcript_path(cwd: &Path, session_id: &str) -> PathBuf {
|
||||||
|
#[cfg(test)]
|
||||||
|
if let Some(home) = test_home::get() {
|
||||||
|
return transcript_path_under_home(&home, cwd, session_id);
|
||||||
|
}
|
||||||
let home = std::env::var("HOME").unwrap_or_else(|_| "/home/huskies".to_string());
|
let home = std::env::var("HOME").unwrap_or_else(|_| "/home/huskies".to_string());
|
||||||
transcript_path_under_home(Path::new(&home), cwd, session_id)
|
transcript_path_under_home(Path::new(&home), cwd, session_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Per-thread `$HOME` override for tests, so a test can sandbox where
|
||||||
|
/// [`transcript_path`] looks without mutating the process-global `$HOME`
|
||||||
|
/// env var (which every thread shares, including unrelated `git`
|
||||||
|
/// subprocesses spawned by other tests reading `$HOME` for
|
||||||
|
/// `~/.gitconfig`). Thread-local storage gives each test's thread its own
|
||||||
|
/// independent value — no lock, no serialization, no race, since
|
||||||
|
/// `#[tokio::test]` (current-thread flavor, used throughout this crate)
|
||||||
|
/// pins a test's whole async call graph to the one thread that set it.
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) mod test_home {
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static HOME: RefCell<Option<PathBuf>> = const { RefCell::new(None) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the current thread's `$HOME` override, if one is set.
|
||||||
|
pub(crate) fn get() -> Option<PathBuf> {
|
||||||
|
HOME.with(|h| h.borrow().clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RAII guard: while held, this thread's [`super::transcript_path`]
|
||||||
|
/// calls resolve under the overridden home instead of the real
|
||||||
|
/// `$HOME`. Clears the override on drop.
|
||||||
|
pub(crate) struct HomeGuard;
|
||||||
|
|
||||||
|
impl Drop for HomeGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
HOME.with(|h| *h.borrow_mut() = None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Override `$HOME` resolution for [`super::transcript_path`] calls
|
||||||
|
/// made on the current thread for the lifetime of the returned guard.
|
||||||
|
pub(crate) fn set(home: &Path) -> HomeGuard {
|
||||||
|
HOME.with(|h| *h.borrow_mut() = Some(home.to_path_buf()));
|
||||||
|
HomeGuard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Same as [`transcript_path`] but takes an explicit `$HOME` directory,
|
/// Same as [`transcript_path`] but takes an explicit `$HOME` directory,
|
||||||
/// keeping the path-joining logic testable without mutating process env vars.
|
/// keeping the path-joining logic testable without mutating process env vars.
|
||||||
fn transcript_path_under_home(home: &Path, cwd: &Path, session_id: &str) -> PathBuf {
|
fn transcript_path_under_home(home: &Path, cwd: &Path, session_id: &str) -> PathBuf {
|
||||||
|
|||||||
@@ -673,11 +673,7 @@ mod tests {
|
|||||||
let project_root = tmp.path().join("project");
|
let project_root = tmp.path().join("project");
|
||||||
std::fs::create_dir_all(&project_root).unwrap();
|
std::fs::create_dir_all(&project_root).unwrap();
|
||||||
let home = tempfile::tempdir().unwrap();
|
let home = tempfile::tempdir().unwrap();
|
||||||
// SAFETY: this test owns HOME for its duration; no other test in this
|
let _home_guard = crate::chat::compact::transcript::test_home::set(home.path());
|
||||||
// process reads HOME concurrently with this call.
|
|
||||||
unsafe {
|
|
||||||
std::env::set_var("HOME", home.path());
|
|
||||||
}
|
|
||||||
let transcript_dir =
|
let transcript_dir =
|
||||||
crate::chat::compact::transcript::transcript_path(&project_root, session_id)
|
crate::chat::compact::transcript::transcript_path(&project_root, session_id)
|
||||||
.parent()
|
.parent()
|
||||||
|
|||||||
@@ -2108,11 +2108,7 @@ mod tests {
|
|||||||
let project_root_dir = tempfile::tempdir().unwrap();
|
let project_root_dir = tempfile::tempdir().unwrap();
|
||||||
let project_root = project_root_dir.path().to_path_buf();
|
let project_root = project_root_dir.path().to_path_buf();
|
||||||
let home = tempfile::tempdir().unwrap();
|
let home = tempfile::tempdir().unwrap();
|
||||||
// SAFETY: this test owns HOME for its duration; no other test in this
|
let _home_guard = crate::chat::compact::transcript::test_home::set(home.path());
|
||||||
// process reads HOME concurrently with this call.
|
|
||||||
unsafe {
|
|
||||||
std::env::set_var("HOME", home.path());
|
|
||||||
}
|
|
||||||
let transcript_dir =
|
let transcript_dir =
|
||||||
crate::chat::compact::transcript::transcript_path(&project_root, session_id)
|
crate::chat::compact::transcript::transcript_path(&project_root, session_id)
|
||||||
.parent()
|
.parent()
|
||||||
|
|||||||
@@ -139,11 +139,7 @@ mod tests {
|
|||||||
let session_id = "sess-compact-1";
|
let session_id = "sess-compact-1";
|
||||||
let project_root = tempfile::tempdir().unwrap();
|
let project_root = tempfile::tempdir().unwrap();
|
||||||
let home = tempfile::tempdir().unwrap();
|
let home = tempfile::tempdir().unwrap();
|
||||||
// SAFETY: this test owns HOME for its duration; no other test in this
|
let _home_guard = crate::chat::compact::transcript::test_home::set(home.path());
|
||||||
// process reads HOME concurrently with this call.
|
|
||||||
unsafe {
|
|
||||||
std::env::set_var("HOME", home.path());
|
|
||||||
}
|
|
||||||
let transcript_dir = compact::transcript::transcript_path(project_root.path(), session_id)
|
let transcript_dir = compact::transcript::transcript_path(project_root.path(), session_id)
|
||||||
.parent()
|
.parent()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//! Shared test helper for driving `git` as a subprocess in unit tests.
|
||||||
|
//!
|
||||||
|
//! `Command::output()` resolves to `Ok` even when the spawned process exits
|
||||||
|
//! non-zero — a bare `.expect(...)`/`.unwrap()` on that `Output` only
|
||||||
|
//! checks that the process could be spawned, not that git itself
|
||||||
|
//! succeeded. A failed `git commit` (e.g. missing `user.name`/`user.email`
|
||||||
|
//! identity) then silently leaves the repo without the commit the rest of
|
||||||
|
//! the test assumes exists, surfacing later as a confusing, unrelated
|
||||||
|
//! assertion failure instead of the real git error.
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
use std::process::Output;
|
||||||
|
|
||||||
|
/// Unwrap a `git` subprocess result, panicking with `context` and git's
|
||||||
|
/// stderr if the process failed to spawn or exited non-zero.
|
||||||
|
pub(crate) fn git_ok(output: io::Result<Output>, context: &str) -> Output {
|
||||||
|
let output = output.unwrap_or_else(|e| panic!("{context}: failed to run git: {e}"));
|
||||||
|
if !output.status.success() {
|
||||||
|
panic!("{context}: {}", String::from_utf8_lossy(&output.stderr));
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
@@ -30,6 +30,10 @@ pub(crate) mod event_log;
|
|||||||
/// Gateway mode — multi-project reverse proxy that fronts multiple project containers.
|
/// Gateway mode — multi-project reverse proxy that fronts multiple project containers.
|
||||||
pub mod gateway;
|
pub mod gateway;
|
||||||
mod gateway_relay;
|
mod gateway_relay;
|
||||||
|
/// Shared test helper for driving `git` as a subprocess and checking its
|
||||||
|
/// exit status, used by `init_git_repo`-style helpers across the crate.
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) mod git_test_support;
|
||||||
/// History — subject-scoped, cursor-paged timeline over chat turns, agent
|
/// History — subject-scoped, cursor-paged timeline over chat turns, agent
|
||||||
/// runs, and pipeline transitions.
|
/// runs, and pipeline transitions.
|
||||||
pub(crate) mod history;
|
pub(crate) mod history;
|
||||||
|
|||||||
@@ -177,16 +177,32 @@ mod tests {
|
|||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
fn init_git_repo(dir: &std::path::Path) {
|
fn init_git_repo(dir: &std::path::Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
|
git_ok(
|
||||||
|
Command::new("git").args(["init"]).current_dir(dir).output(),
|
||||||
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.expect("git init");
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
|
Command::new("git")
|
||||||
|
.args(["config", "user.name", "Test"])
|
||||||
|
.current_dir(dir)
|
||||||
|
.output(),
|
||||||
|
"git config user.name",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "--allow-empty", "-m", "init"])
|
.args(["commit", "--allow-empty", "-m", "init"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.expect("git commit");
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty_config() -> ProjectConfig {
|
fn empty_config() -> ProjectConfig {
|
||||||
|
|||||||
@@ -216,16 +216,32 @@ mod tests {
|
|||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
fn init_git_repo(dir: &Path) {
|
fn init_git_repo(dir: &Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
|
git_ok(
|
||||||
|
Command::new("git").args(["init"]).current_dir(dir).output(),
|
||||||
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.expect("git init");
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
|
Command::new("git")
|
||||||
|
.args(["config", "user.name", "Test"])
|
||||||
|
.current_dir(dir)
|
||||||
|
.output(),
|
||||||
|
"git config user.name",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "--allow-empty", "-m", "init"])
|
.args(["commit", "--allow-empty", "-m", "init"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.expect("git commit");
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty_config() -> ProjectConfig {
|
fn empty_config() -> ProjectConfig {
|
||||||
|
|||||||
@@ -64,11 +64,20 @@ pub(crate) fn create_worktree_sync(
|
|||||||
.current_dir(project_root)
|
.current_dir(project_root)
|
||||||
.output();
|
.output();
|
||||||
|
|
||||||
// Try to create branch. If it already exists that's fine.
|
// Try to create branch. If it already exists that's fine; any other
|
||||||
let _ = Command::new("git")
|
// failure (e.g. unborn HEAD, invalid branch name) must surface here
|
||||||
|
// rather than resurface later as a confusing "git worktree add" error.
|
||||||
|
let output = Command::new("git")
|
||||||
.args(["branch", branch])
|
.args(["branch", branch])
|
||||||
.current_dir(project_root)
|
.current_dir(project_root)
|
||||||
.output();
|
.output()
|
||||||
|
.map_err(|e| format!("git branch: {e}"))?;
|
||||||
|
if !output.status.success() {
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
if !stderr.contains("already exists") {
|
||||||
|
return Err(format!("git branch failed: {stderr}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create worktree
|
// Create worktree
|
||||||
let output = Command::new("git")
|
let output = Command::new("git")
|
||||||
@@ -227,16 +236,32 @@ mod tests {
|
|||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
fn init_git_repo(dir: &Path) {
|
fn init_git_repo(dir: &Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
|
git_ok(
|
||||||
|
Command::new("git").args(["init"]).current_dir(dir).output(),
|
||||||
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.expect("git init");
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
|
Command::new("git")
|
||||||
|
.args(["config", "user.name", "Test"])
|
||||||
|
.current_dir(dir)
|
||||||
|
.output(),
|
||||||
|
"git config user.name",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "--allow-empty", "-m", "init"])
|
.args(["commit", "--allow-empty", "-m", "init"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.expect("git commit");
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -74,16 +74,32 @@ mod tests {
|
|||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
fn init_git_repo(dir: &std::path::Path) {
|
fn init_git_repo(dir: &std::path::Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
|
git_ok(
|
||||||
|
Command::new("git").args(["init"]).current_dir(dir).output(),
|
||||||
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.expect("git init");
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
|
Command::new("git")
|
||||||
|
.args(["config", "user.name", "Test"])
|
||||||
|
.current_dir(dir)
|
||||||
|
.output(),
|
||||||
|
"git config user.name",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "--allow-empty", "-m", "init"])
|
.args(["commit", "--allow-empty", "-m", "init"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.expect("git commit");
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty_config() -> ProjectConfig {
|
fn empty_config() -> ProjectConfig {
|
||||||
|
|||||||
@@ -98,16 +98,32 @@ mod tests {
|
|||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
fn init_git_repo(dir: &Path) {
|
fn init_git_repo(dir: &Path) {
|
||||||
|
use crate::git_test_support::git_ok;
|
||||||
|
git_ok(
|
||||||
|
Command::new("git").args(["init"]).current_dir(dir).output(),
|
||||||
|
"git init",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["init"])
|
.args(["config", "user.email", "test@test.com"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.expect("git init");
|
"git config user.email",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
|
Command::new("git")
|
||||||
|
.args(["config", "user.name", "Test"])
|
||||||
|
.current_dir(dir)
|
||||||
|
.output(),
|
||||||
|
"git config user.name",
|
||||||
|
);
|
||||||
|
git_ok(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["commit", "--allow-empty", "-m", "init"])
|
.args(["commit", "--allow-empty", "-m", "init"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
.output()
|
.output(),
|
||||||
.expect("git commit");
|
"git commit",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty_config() -> ProjectConfig {
|
fn empty_config() -> ProjectConfig {
|
||||||
|
|||||||
Reference in New Issue
Block a user