mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-11 22:53:00 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
b0dfda617b
3 changed files with 202 additions and 43 deletions
84
Cargo.lock
generated
84
Cargo.lock
generated
|
|
@ -1514,7 +1514,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-agent"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -1553,7 +1553,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-api"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fabro-config",
|
||||
|
|
@ -1573,7 +1573,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-auth"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -1596,7 +1596,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-checkpoint"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fabro-config",
|
||||
|
|
@ -1612,7 +1612,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-cli"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
|
|
@ -1705,7 +1705,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-client"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
|
|
@ -1734,7 +1734,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-config"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
|
@ -1760,7 +1760,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-core"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"fabro-types",
|
||||
|
|
@ -1775,7 +1775,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-dev"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
|
|
@ -1793,7 +1793,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-devcontainer"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-http",
|
||||
"fabro-static",
|
||||
|
|
@ -1810,7 +1810,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-github"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"chrono",
|
||||
|
|
@ -1831,7 +1831,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-graphviz"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"fabro-types",
|
||||
|
|
@ -1845,7 +1845,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-hooks"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"fabro-agent",
|
||||
|
|
@ -1869,7 +1869,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-http"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-static",
|
||||
"http",
|
||||
|
|
@ -1879,7 +1879,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-install"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
|
@ -1894,7 +1894,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-interview"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"dialoguer",
|
||||
|
|
@ -1908,7 +1908,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-llm"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -1940,7 +1940,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-macros"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"fabro-options-metadata",
|
||||
|
|
@ -1951,7 +1951,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-mcp"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"fabro-config",
|
||||
|
|
@ -1967,7 +1967,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-model"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-static",
|
||||
"insta",
|
||||
|
|
@ -1978,7 +1978,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-oauth"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"base64",
|
||||
|
|
@ -1999,7 +1999,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-options-metadata"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -2007,7 +2007,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-proc"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
|
@ -2016,7 +2016,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-redact"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"ref-cast",
|
||||
|
|
@ -2032,7 +2032,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-retro"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
|
@ -2050,7 +2050,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-sandbox"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -2083,7 +2083,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-server"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -2163,7 +2163,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-slack"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-http",
|
||||
"fabro-interview",
|
||||
|
|
@ -2183,18 +2183,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-spa"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"rust-embed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fabro-static"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
|
||||
[[package]]
|
||||
name = "fabro-store"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
|
|
@ -2220,7 +2220,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-telemetry"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
|
@ -2246,7 +2246,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-template"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"fabro-util",
|
||||
|
|
@ -2258,7 +2258,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-test"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"axum",
|
||||
|
|
@ -2281,7 +2281,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-tracker"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"fabro-github",
|
||||
|
|
@ -2294,7 +2294,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-types"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
|
|
@ -2314,7 +2314,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-util"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"console 0.15.11",
|
||||
|
|
@ -2334,7 +2334,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-validate"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-graphviz",
|
||||
"fabro-model",
|
||||
|
|
@ -2344,7 +2344,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-vault"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
|
|
@ -2355,7 +2355,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-workflow"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
|
|
@ -7086,7 +7086,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "twin-github"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"base64",
|
||||
|
|
@ -7105,7 +7105,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "twin-openai"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ resolver = "2"
|
|||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
version = "0.213.0-nightly.2"
|
||||
version = "0.214.0-nightly.0"
|
||||
license = "MIT"
|
||||
|
||||
[workspace.dependencies]
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ static INSTA_FILTERS: &[(&str, &str)] = &[
|
|||
|
||||
const MANAGED_STORAGE_MARKER: &str = "# fabro-test managed storage_dir";
|
||||
const SESSION_LOCK_TIMEOUT: Duration = Duration::from_secs(20);
|
||||
const STALE_TMP_DAEMON_THRESHOLD: Duration = Duration::from_mins(30);
|
||||
const TMP_DAEMON_REAPER_COOLDOWN: Duration = Duration::from_mins(5);
|
||||
const TEST_SESSION_SECRET: &str =
|
||||
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
|
||||
const TEST_DEV_TOKEN: &str =
|
||||
|
|
@ -310,6 +312,7 @@ fn session_refs() -> &'static Mutex<HashMap<PathBuf, usize>> {
|
|||
// the existing call structure without introducing cross-mode coupling.
|
||||
static NEXTEST_REAPED: OnceLock<()> = OnceLock::new();
|
||||
static PROCESS_REAPED: OnceLock<()> = OnceLock::new();
|
||||
static TMP_DAEMONS_REAPED: OnceLock<()> = OnceLock::new();
|
||||
|
||||
// Advisory-lock-based peer-presence marker. Each test process opens
|
||||
// `<session_root>/clients/<pid>` once, holds a shared (LOCK_SH) flock
|
||||
|
|
@ -1083,6 +1086,113 @@ fn reap_stale_session_roots(mode: SessionMode) {
|
|||
}
|
||||
}
|
||||
|
||||
fn maybe_reap_stale_tmp_daemons() {
|
||||
let (lock_path, stamp_path) = tmp_daemon_reaper_paths();
|
||||
if stamp_is_recent(&stamp_path, TMP_DAEMON_REAPER_COOLDOWN) {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(parent) = lock_path.parent() else {
|
||||
return;
|
||||
};
|
||||
let _ = std::fs::create_dir_all(parent);
|
||||
let Ok(lock_file) = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(false)
|
||||
.open(&lock_path)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Ok(true) = fabro_proc::try_flock_exclusive(&lock_file) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !stamp_is_recent(&stamp_path, TMP_DAEMON_REAPER_COOLDOWN) {
|
||||
reap_stale_tmp_daemons();
|
||||
let _ = File::create(&stamp_path);
|
||||
}
|
||||
let _ = fabro_proc::flock_unlock(&lock_file);
|
||||
}
|
||||
|
||||
fn reap_stale_tmp_daemons() {
|
||||
let Ok(output) = std::process::Command::new("ps")
|
||||
.args(["-ww", "-axo", "pid=,etime=,command="])
|
||||
.output()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if !output.status.success() {
|
||||
return;
|
||||
}
|
||||
|
||||
for line in String::from_utf8_lossy(&output.stdout).lines() {
|
||||
let Some((pid, elapsed_secs, command)) = parse_tmp_daemon_ps_line(line) else {
|
||||
continue;
|
||||
};
|
||||
if Duration::from_secs(elapsed_secs) <= STALE_TMP_DAEMON_THRESHOLD {
|
||||
continue;
|
||||
}
|
||||
if tmp_daemon_socket_re().is_match(command) {
|
||||
fabro_proc::sigkill(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tmp_daemon_reaper_paths() -> (PathBuf, PathBuf) {
|
||||
let base = short_session_base_dir();
|
||||
(
|
||||
base.join("tmp-daemon-reaper.lock"),
|
||||
base.join("tmp-daemon-reaper.stamp"),
|
||||
)
|
||||
}
|
||||
|
||||
fn stamp_is_recent(path: &Path, cooldown: Duration) -> bool {
|
||||
let Ok(metadata) = std::fs::metadata(path) else {
|
||||
return false;
|
||||
};
|
||||
let Ok(modified) = metadata.modified() else {
|
||||
return false;
|
||||
};
|
||||
modified.elapsed().is_ok_and(|elapsed| elapsed < cooldown)
|
||||
}
|
||||
|
||||
fn parse_tmp_daemon_ps_line(line: &str) -> Option<(u32, u64, &str)> {
|
||||
static PS_ROW_RE: OnceLock<Regex> = OnceLock::new();
|
||||
let captures = PS_ROW_RE
|
||||
.get_or_init(|| Regex::new(r"^\s*(\d+)\s+(\S+)\s+(.*)$").expect("static regex"))
|
||||
.captures(line)?;
|
||||
let pid = captures.get(1)?.as_str().parse::<u32>().ok()?;
|
||||
let elapsed_secs = parse_etime(captures.get(2)?.as_str())?;
|
||||
let command = captures.get(3)?.as_str();
|
||||
Some((pid, elapsed_secs, command))
|
||||
}
|
||||
|
||||
fn tmp_daemon_socket_re() -> &'static Regex {
|
||||
static TMP_DAEMON_SOCKET_RE: OnceLock<Regex> = OnceLock::new();
|
||||
TMP_DAEMON_SOCKET_RE.get_or_init(|| {
|
||||
Regex::new(r"^fabro server unix:/tmp/\.tmp[^/]+/[^/]+\.sock\s*$").expect("static regex")
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_etime(s: &str) -> Option<u64> {
|
||||
let (days, rest) = match s.split_once('-') {
|
||||
Some((days, rest)) => (days.parse::<u64>().ok()?, rest),
|
||||
None => (0, s),
|
||||
};
|
||||
let nums = rest
|
||||
.split(':')
|
||||
.map(|part| part.parse::<u64>().ok())
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
let (hours, minutes, seconds) = match nums.as_slice() {
|
||||
[minutes, seconds] => (0, *minutes, *seconds),
|
||||
[hours, minutes, seconds] => (*hours, *minutes, *seconds),
|
||||
_ => return None,
|
||||
};
|
||||
Some(days * 86_400 + hours * 3_600 + minutes * 60 + seconds)
|
||||
}
|
||||
|
||||
impl TestContext {
|
||||
/// Create a new isolated test context.
|
||||
///
|
||||
|
|
@ -1108,6 +1218,7 @@ impl TestContext {
|
|||
let (_, test_run_id, session_paths) = session_paths();
|
||||
NEXTEST_REAPED.get_or_init(|| reap_stale_session_roots(SessionMode::Nextest));
|
||||
PROCESS_REAPED.get_or_init(|| reap_stale_session_roots(SessionMode::Process));
|
||||
TMP_DAEMONS_REAPED.get_or_init(maybe_reap_stale_tmp_daemons);
|
||||
with_session_lock(&session_paths.root, || {
|
||||
std::fs::create_dir_all(session_clients_dir(&session_paths.root)).unwrap_or_else(
|
||||
|err| {
|
||||
|
|
@ -2375,6 +2486,54 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_etime_accepts_bsd_elapsed_time_formats() {
|
||||
assert_eq!(parse_etime("00:42"), Some(42));
|
||||
assert_eq!(parse_etime("01:23:45"), Some(5_025));
|
||||
assert_eq!(parse_etime("2-03:04:05"), Some(183_845));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_etime_rejects_malformed_elapsed_time() {
|
||||
assert_eq!(parse_etime(""), None);
|
||||
assert_eq!(parse_etime("abc"), None);
|
||||
assert_eq!(parse_etime("01:02:03:04"), None);
|
||||
assert_eq!(parse_etime("2-not-time"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_tmp_daemon_ps_line_handles_padded_rows() {
|
||||
let line = "43640 12:14 fabro server unix:/tmp/.tmppL8cKh/fabro.sock";
|
||||
|
||||
assert_eq!(
|
||||
parse_tmp_daemon_ps_line(line),
|
||||
Some((43_640, 734, "fabro server unix:/tmp/.tmppL8cKh/fabro.sock"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tmp_daemon_socket_regex_matches_only_test_tmp_unix_daemons() {
|
||||
let re = tmp_daemon_socket_re();
|
||||
|
||||
assert!(re.is_match("fabro server unix:/tmp/.tmpAbC/fabro.sock"));
|
||||
assert!(!re.is_match("fabro server unix:/tmp/.ft-foo-XYZ/test.sock"));
|
||||
assert!(!re.is_match("sh -c fabro server unix:/tmp/.tmpAbC/fabro.sock"));
|
||||
assert!(!re.is_match("fabro server unix:/Users/me/.fabro/fabro.sock"));
|
||||
assert!(!re.is_match("fabro server unix:/tmp/notmatching/fabro.sock"));
|
||||
assert!(!re.is_match("fabro server tcp:127.0.0.1:32276"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stamp_is_recent_checks_missing_and_fresh_stamps() {
|
||||
let temp = tempfile::tempdir().expect("temp dir should be created");
|
||||
let missing = temp.path().join("missing.stamp");
|
||||
assert!(!stamp_is_recent(&missing, Duration::from_mins(1)));
|
||||
|
||||
let fresh = temp.path().join("fresh.stamp");
|
||||
File::create(&fresh).expect("stamp should be created");
|
||||
assert!(stamp_is_recent(&fresh, Duration::from_mins(1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_and_create_commands_include_test_labels() {
|
||||
let context_root = tempfile::tempdir().expect("failed to create temp dir");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue