From 99dd7718c01e5a02c1665924ec01b4d24d0b88d0 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Mon, 27 Jul 2026 18:39:25 -0400 Subject: [PATCH] Keep server tests off the developer's real ~/.fabro/storage Test settings usually omit `[server.storage] root`, so it resolved to the production default. Handlers that walk that tree read whatever the machine happened to have. That is why all_spec_routes_are_routable was slow. Timing every request in it showed 91% of the runtime in two routes: 6304ms GET /api/v1/system/resources 4574ms GET /api/v1/system/df 583ms POST /api/v1/system/prune/runs ... the remaining 134 operations: 8ms combined Both size Fabro-managed storage. On this machine that meant 193MB and 90,795 entries under scratch/, so the test's duration tracked how long the developer had been running Fabro locally. Run-creating tests were writing there too. Redirect settings that still carry the production default to a `storage` directory beside the test vault, alongside the existing `server.env` and `settings.toml` siblings. A test that chose its own root keeps it. all_spec_routes_are_routable drops from ~15s to 0.6s, and the full workspace run from ~33s to ~21s. All 7402 tests pass. Co-Authored-By: Claude Opus 5 (1M context) --- lib/apps/fabro-server/src/test_support.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/apps/fabro-server/src/test_support.rs b/lib/apps/fabro-server/src/test_support.rs index 8fe368fff..178641298 100644 --- a/lib/apps/fabro-server/src/test_support.rs +++ b/lib/apps/fabro-server/src/test_support.rs @@ -13,6 +13,7 @@ use axum::middleware::Next; use axum::response::Response; use axum::{Router, middleware}; use chrono::Duration as ChronoDuration; +use fabro_config::user::default_storage_dir; use fabro_config::{RunLayer, ServerSettingsBuilder, Storage, envfile}; use fabro_db::DbPool; use fabro_interview::Interviewer; @@ -253,9 +254,10 @@ impl TestAppStateBuilder { self.try_build().expect("test app state should build") } - pub fn try_build(self) -> anyhow::Result> { + pub fn try_build(mut self) -> anyhow::Result> { let (store, artifact_store) = self.store_bundle.unwrap_or_else(test_store_bundle); let vault_path = self.vault_path.unwrap_or_else(test_secret_store_path); + redirect_default_storage_root(&mut self.server_settings, &vault_path); if !self.vault_entries.is_empty() { let mut vault = Vault::load(vault_path.clone()).expect("test vault should load"); for (name, value) in &self.vault_entries { @@ -672,6 +674,25 @@ pub fn test_secret_store_path() -> PathBuf { dir.join("secrets.json") } +/// Keeps tests off the developer's real `~/.fabro/storage`. +/// +/// Settings built for tests usually omit `[server.storage] root`, which +/// resolves to the production default. Handlers that walk that tree — `df`, +/// `system/resources`, `prune` — then read whatever runs and scratch +/// directories the machine happens to have, making tests slow and +/// machine-dependent, and letting run-creating tests write there. +/// +/// Only settings still carrying the production default are redirected; a test +/// that chose its own root keeps it. +fn redirect_default_storage_root(settings: &mut ServerSettings, vault_path: &Path) { + if Path::new(&settings.server.storage.root) != default_storage_dir() { + return; + } + let root = vault_path.with_file_name("storage"); + std::fs::create_dir_all(&root).expect("test storage root should be creatable"); + settings.server.storage.root = root.display().to_string(); +} + #[must_use] pub fn test_auth_mode() -> AuthMode { AuthMode::Enabled(ConfiguredAuth {