From 3b22800e37f8cc5dcafa191a2c3d3d4f4251ca43 Mon Sep 17 00:00:00 2001 From: Robert DeLanghe <1240090+bdelanghe@users.noreply.github.com> Date: Thu, 23 Jul 2026 14:21:53 -0400 Subject: [PATCH] test(resume): pin XDG_DATA_HOME in ScopedHome to sandbox opencode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The opencode `PathResolver` prefers `$XDG_DATA_HOME` over `$HOME` when locating `opencode.db`. `ScopedHome` only pinned `$HOME`, so on a machine that sets `$XDG_DATA_HOME` (common on Linux; set on this dev box) the opencode resume test escaped its sandbox: it seeded — and the projector wrote into — the *real* user database. The seed's `CREATE TABLE` then hit the already-populated real DB and failed with "table project already exists" (green in CI where XDG is unset, red locally), and a fresh-DB run would silently mutate the user's live opencode data. Pin `$XDG_DATA_HOME` to `/.local/share` in ScopedHome (restored on drop) so every harness — opencode included — stays sandboxed. Co-Authored-By: Claude Opus 4.8 --- crates/path-cli/tests/support/mod.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/crates/path-cli/tests/support/mod.rs b/crates/path-cli/tests/support/mod.rs index 578b5af1..b3ae4ae4 100644 --- a/crates/path-cli/tests/support/mod.rs +++ b/crates/path-cli/tests/support/mod.rs @@ -26,11 +26,20 @@ pub fn env_lock() -> std::sync::MutexGuard<'static, ()> { .unwrap_or_else(|e| e.into_inner()) } -/// RAII guard that pins `$HOME` and `$TOOLPATH_CONFIG_DIR` to a tempdir. +/// RAII guard that pins `$HOME`, `$TOOLPATH_CONFIG_DIR`, and +/// `$XDG_DATA_HOME` to a tempdir. +/// +/// `$XDG_DATA_HOME` matters because the opencode `PathResolver` prefers it +/// over `$HOME` when locating `opencode.db`. Without pinning it, a machine +/// that sets `$XDG_DATA_HOME` (common on Linux; also set on this dev box) +/// would send the opencode projector to the *real* user database — the +/// test would fail with "table … already exists" and, worse, mutate the +/// user's live opencode data. Pinning it keeps every harness sandboxed. pub struct ScopedHome { _td: tempfile::TempDir, prev_home: Option, prev_config: Option, + prev_xdg_data: Option, } impl ScopedHome { @@ -38,14 +47,17 @@ impl ScopedHome { let td = tempfile::tempdir().unwrap(); let prev_home = std::env::var_os("HOME"); let prev_config = std::env::var_os("TOOLPATH_CONFIG_DIR"); + let prev_xdg_data = std::env::var_os("XDG_DATA_HOME"); unsafe { std::env::set_var("HOME", td.path()); std::env::set_var("TOOLPATH_CONFIG_DIR", td.path().join(".toolpath")); + std::env::set_var("XDG_DATA_HOME", td.path().join(".local/share")); } Self { _td: td, prev_home, prev_config, + prev_xdg_data, } } @@ -65,6 +77,10 @@ impl Drop for ScopedHome { Some(v) => std::env::set_var("TOOLPATH_CONFIG_DIR", v), None => std::env::remove_var("TOOLPATH_CONFIG_DIR"), } + match &self.prev_xdg_data { + Some(v) => std::env::set_var("XDG_DATA_HOME", v), + None => std::env::remove_var("XDG_DATA_HOME"), + } } } }