Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ and say so explicitly.
`voro-core` requires tests; state-machine transitions and scheduler ordering are
the highest-value targets. TUI code is tested where practical, not dogmatically.

A test that needs a scratch directory names it one way, with `tempfile`:
`tempfile::Builder::new().prefix("voro-<area>-").tempdir().unwrap().keep()`.
Never build the name from the process id and a clock stamp — the clock does not
advance a nanosecond at a time, so two threads reading inside one step read the
same number and land on the same directory. `keep` leaves the directory behind
deliberately, so a failing test's scratch state survives for inspection; nothing
cleans the temp dir up yet.

## Git conventions

- Feature branches, squash-merged to `main`. One logical change per PR.
Expand Down
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ toml = "0.8"
toml_edit = "0.22"
ratatui = { version = "0.30", features = ["unstable-rendered-line-info"] }
uuid = { version = "1", features = ["v4"] }
tempfile = "3"

# The profile that 'dist' will build with
[profile.dist]
Expand Down
3 changes: 3 additions & 0 deletions crates/voro-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ thiserror.workspace = true
toml.workspace = true
toml_edit.workspace = true
uuid.workspace = true

[dev-dependencies]
tempfile.workspace = true
13 changes: 5 additions & 8 deletions crates/voro-core/src/config_edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,14 +253,11 @@ mod tests {

/// A unique scratch path per test, cleaned up by the caller.
fn scratch(tag: &str) -> std::path::PathBuf {
std::env::temp_dir().join(format!(
"voro-config-edit-{tag}-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
))
tempfile::Builder::new()
.prefix(&format!("voro-config-edit-{tag}-"))
.tempdir()
.unwrap()
.keep()
}

#[test]
Expand Down
24 changes: 11 additions & 13 deletions crates/voro-core/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2049,16 +2049,11 @@ mod schema_guard_tests {

/// A unique scratch directory per test, cleaned up by the caller.
fn scratch(tag: &str) -> PathBuf {
let dir = std::env::temp_dir().join(format!(
"voro-store-{tag}-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
std::fs::create_dir_all(&dir).unwrap();
dir
tempfile::Builder::new()
.prefix(&format!("voro-store-{tag}-"))
.tempdir()
.unwrap()
.keep()
}

#[test]
Expand Down Expand Up @@ -4869,9 +4864,12 @@ mod tests {

/// A unique scratch database path under the OS temp dir.
fn scratch_db() -> PathBuf {
static COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let n = COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
std::env::temp_dir().join(format!("voro-dataversion-{}-{n}.db", std::process::id()))
tempfile::Builder::new()
.prefix("voro-dataversion-")
.tempdir()
.unwrap()
.keep()
.join("voro.db")
}

#[test]
Expand Down
3 changes: 3 additions & 0 deletions crates/voro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ categories = ["command-line-utilities"]
voro-core = { path = "../voro-core", version = "0.1.0" }
ratatui.workspace = true
clap = { version = "4.6.1", features = ["derive"] }

[dev-dependencies]
tempfile.workspace = true
54 changes: 20 additions & 34 deletions crates/voro/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4145,14 +4145,11 @@ mod tests {
) -> (Store, crate::dispatch::DispatchCtx, std::path::PathBuf) {
use std::process::{Command, Stdio};

let root = std::env::temp_dir().join(format!(
"voro-app-{name}-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
let root = tempfile::Builder::new()
.prefix(&format!("voro-app-{name}-"))
.tempdir()
.unwrap()
.keep();
let project_path = root.join("project");
std::fs::create_dir_all(&project_path).unwrap();
let status = Command::new("git")
Expand Down Expand Up @@ -4189,14 +4186,11 @@ mod tests {
fn resuming_a_task_with_a_live_session_spawns_no_continuation() {
use std::process::{Command, Stdio};

let root = std::env::temp_dir().join(format!(
"voro-app-resume-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
let root = tempfile::Builder::new()
.prefix("voro-app-resume-")
.tempdir()
.unwrap()
.keep();
let project_path = root.join("project");
std::fs::create_dir_all(&project_path).unwrap();
let status = Command::new("git")
Expand Down Expand Up @@ -7334,15 +7328,11 @@ mod tests {
/// decides whether that directory is a git repository at all, which is the
/// whole of what the press-time gate reads (DESIGN.md §8).
fn pr_ready_app(with_repo: bool) -> (App, i64, std::path::PathBuf) {
let dir = std::env::temp_dir().join(format!(
"voro-review-key-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
std::fs::create_dir_all(&dir).unwrap();
let dir = tempfile::Builder::new()
.prefix("voro-review-key-")
.tempdir()
.unwrap()
.keep();
if with_repo {
let status = std::process::Command::new("git")
.arg("-C")
Expand Down Expand Up @@ -7661,15 +7651,11 @@ mod tests {
/// config and PATH.
fn app_with_agents(agents_toml: &str) -> App {
let mut app = app_with(&[TaskState::Ready]);
let dir = std::env::temp_dir().join(format!(
"voro-plan-key-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
std::fs::create_dir_all(&dir).unwrap();
let dir = tempfile::Builder::new()
.prefix("voro-plan-key-")
.tempdir()
.unwrap()
.keep();
let agents_path = dir.join("voro.toml");
std::fs::write(&agents_path, agents_toml).unwrap();
app.dispatch_ctx = crate::dispatch::DispatchCtx {
Expand Down
80 changes: 30 additions & 50 deletions crates/voro/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2601,14 +2601,11 @@ mod tests {

#[test]
fn agent_init_then_list_through_the_cli() {
let dir = std::env::temp_dir().join(format!(
"voro-cli-agents-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
let dir = tempfile::Builder::new()
.prefix("voro-cli-agents-")
.tempdir()
.unwrap()
.keep();
let agents_path = dir.join("voro/voro.toml");
let ctx = DispatchCtx {
db_path: dir.join("voro.db"),
Expand Down Expand Up @@ -2653,14 +2650,11 @@ mod tests {

#[test]
fn viewer_add_remove_round_trip_through_the_cli() {
let dir = std::env::temp_dir().join(format!(
"voro-cli-viewers-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
let dir = tempfile::Builder::new()
.prefix("voro-cli-viewers-")
.tempdir()
.unwrap()
.keep();
let agents_path = dir.join("voro/voro.toml");
let ctx = DispatchCtx {
db_path: dir.join("voro.db"),
Expand Down Expand Up @@ -3672,15 +3666,11 @@ mod tests {
/// A throwaway checkout with no remotes — the shape of a first project
/// (DESIGN.md §8), which advertises `open` rather than `pr`.
fn remoteless_checkout(tag: &str) -> std::path::PathBuf {
let path = std::env::temp_dir().join(format!(
"voro-cli-{tag}-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
std::fs::create_dir_all(&path).unwrap();
let path = tempfile::Builder::new()
.prefix(&format!("voro-cli-{tag}-"))
.tempdir()
.unwrap()
.keep();
git_in(&path, &["init", "-q"]);
path
}
Expand Down Expand Up @@ -4622,15 +4612,11 @@ mod tests {
/// viewers. The default `ctx()` points at the developer's real config,
/// which these tests must not depend on (or launch viewers from).
fn ctx_with_toml(toml: &str) -> DispatchCtx {
let root = std::env::temp_dir().join(format!(
"voro-cli-viewer-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
std::fs::create_dir_all(&root).unwrap();
let root = tempfile::Builder::new()
.prefix("voro-cli-viewer-")
.tempdir()
.unwrap()
.keep();
let agents_path = root.join("voro.toml");
std::fs::write(&agents_path, toml).unwrap();
DispatchCtx {
Expand Down Expand Up @@ -5104,14 +5090,11 @@ mod tests {
fn a_dead_dispatched_session_is_finalised_and_stalled_on_read() {
use std::process::{Command, Stdio};

let root = std::env::temp_dir().join(format!(
"voro-cli-reconcile-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
let root = tempfile::Builder::new()
.prefix("voro-cli-reconcile-")
.tempdir()
.unwrap()
.keep();
let project = root.join("project");
std::fs::create_dir_all(&project).unwrap();
let git = |args: &[&str]| {
Expand Down Expand Up @@ -5188,14 +5171,11 @@ mod tests {
fn scratch_env(cmd: &str) -> (Store, DispatchCtx, std::path::PathBuf) {
use std::process::{Command, Stdio};

let root = std::env::temp_dir().join(format!(
"voro-cli-answer-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
let root = tempfile::Builder::new()
.prefix("voro-cli-answer-")
.tempdir()
.unwrap()
.keep();
let project = root.join("project");
std::fs::create_dir_all(&project).unwrap();
let status = Command::new("git")
Expand Down
Loading