Restrict Quire::new and Default impls to test builds; inline config loading
GlobalConfig::load is inlined into Quire::load, removing the separate
method. Default impls for GlobalConfig and Quire, and Quire::new, are
now #[cfg(test)] since they exist only to support test construction.
dev.rs switches to Quire::load (returns built-in defaults when the
seeded tempdir has no config file).
diff --git a/quire-server/src/bin/quire/commands/dev.rs b/quire-server/src/bin/quire/commands/dev.rs
index 8442719..ccc566b 100644
--- a/quire-server/src/bin/quire/commands/dev.rs
+++ b/quire-server/src/bin/quire/commands/dev.rs
@@ -67,7 +67,7 @@ impl Seeder {
let base_dir = dir.keep();
tracing::info!(path = %base_dir.display(), "seeded tempdir");
- let quire = Quire::new(base_dir, quire::GlobalConfig::default());
+ let quire = Quire::load(base_dir)?;
// Create the repos dir + a bare repo so the web view resolves the repo.
let bare_repo = quire.repos_dir().join("example.git");
diff --git a/quire-server/src/quire/mod.rs b/quire-server/src/quire/mod.rs
index e5e6794..b107707 100644
--- a/quire-server/src/quire/mod.rs
+++ b/quire-server/src/quire/mod.rs
@@ -36,6 +36,7 @@ pub struct GlobalConfig {
pub github: GlobalGithubConfig,
}
+#[cfg(test)]
impl Default for GlobalConfig {
fn default() -> Self {
Self {
@@ -48,17 +49,6 @@ impl Default for GlobalConfig {
}
}
-impl GlobalConfig {
- /// Load config from `path`. Returns `Self::default()` if the file is absent;
- /// propagates parse errors.
- pub fn load(path: &Path) -> Result<Self> {
- if !path.exists() {
- return Ok(Self::default());
- }
- Ok(Fennel::load_config(path)?)
- }
-}
-
/// Global GitHub integration configuration.
#[derive(serde::Deserialize, Debug, Default, Clone)]
#[serde(rename_all = "kebab-case")]
@@ -236,24 +226,27 @@ pub struct Quire {
db_pool: Arc<OnceLock<Mutex<rusqlite::Connection>>>,
}
-impl Default for Quire {
- fn default() -> Self {
- Self::new(PathBuf::from("/var/quire"), GlobalConfig::default())
- }
-}
-
impl Quire {
/// Load config from `base_dir/config.fnl` and create a `Quire` rooted there.
///
- /// Returns default config if the file is absent; propagates parse errors.
+ /// Returns built-in defaults if the file is absent; propagates parse errors.
pub fn load(base_dir: PathBuf) -> Result<Self> {
- let config = GlobalConfig::load(&base_dir.join("config.fnl"))?;
- Ok(Self::new(base_dir, config))
+ let config_path = base_dir.join("config.fnl");
+ let config = if config_path.exists() {
+ Fennel::load_config(&config_path)?
+ } else {
+ GlobalConfig {
+ sentry: None,
+ secrets: HashMap::new(),
+ port: default_port(),
+ ci: CiConfig::default(),
+ github: GlobalGithubConfig::default(),
+ }
+ };
+ Ok(Self::init(base_dir, config))
}
- /// Create a `Quire` with an explicit config. Prefer `Quire::load` in production;
- /// use this in tests that need direct control over config.
- pub fn new(base_dir: PathBuf, config: GlobalConfig) -> Self {
+ fn init(base_dir: PathBuf, config: GlobalConfig) -> Self {
Self {
base_dir,
config,
@@ -261,6 +254,11 @@ impl Quire {
}
}
+ #[cfg(test)]
+ pub fn new(base_dir: PathBuf, config: GlobalConfig) -> Self {
+ Self::init(base_dir, config)
+ }
+
pub fn base_dir(&self) -> &Path {
&self.base_dir
}
@@ -335,6 +333,13 @@ impl Quire {
}
}
+#[cfg(test)]
+impl Default for Quire {
+ fn default() -> Self {
+ Self::new(PathBuf::from("/var/quire"), GlobalConfig::default())
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -468,88 +473,80 @@ mod tests {
#[test]
fn global_config_ci_defaults() {
let dir = tempfile::tempdir().expect("tempdir");
- let config_path = dir.path().join("config.fnl");
- fs_err::write(&config_path, "{}").expect("write");
+ fs_err::write(dir.path().join("config.fnl"), "{}").expect("write");
- let config = GlobalConfig::load(&config_path).expect("should load");
- assert_eq!(config.ci.executor, Executor::Process);
- assert_eq!(config.port, 3000);
+ let q = Quire::load(dir.path().to_path_buf()).expect("should load");
+ assert_eq!(q.config.ci.executor, Executor::Process);
+ assert_eq!(q.config.port, 3000);
}
#[test]
fn global_config_parses_custom_port() {
let dir = tempfile::tempdir().expect("tempdir");
- let config_path = dir.path().join("config.fnl");
- fs_err::write(&config_path, r#"{:port 4000}"#).expect("write");
+ fs_err::write(dir.path().join("config.fnl"), r#"{:port 4000}"#).expect("write");
- let config = GlobalConfig::load(&config_path).expect("should load");
- assert_eq!(config.port, 4000);
+ let q = Quire::load(dir.path().to_path_buf()).expect("should load");
+ assert_eq!(q.config.port, 4000);
}
#[test]
fn global_config_loads_from_fennel_file() {
let dir = tempfile::tempdir().expect("tempdir");
- let config_path = dir.path().join("config.fnl");
- fs_err::write(&config_path, "{}").expect("write");
+ fs_err::write(dir.path().join("config.fnl"), "{}").expect("write");
- let config = GlobalConfig::load(&config_path).expect("should load");
- assert!(config.secrets.is_empty());
+ let q = Quire::load(dir.path().to_path_buf()).expect("should load");
+ assert!(q.config.secrets.is_empty());
}
#[test]
fn global_config_missing_file_uses_defaults() {
let dir = tempfile::tempdir().expect("tempdir");
- let config_path = dir.path().join("config.fnl");
- let config = GlobalConfig::load(&config_path).expect("missing file should use defaults");
- assert_eq!(config.port, 3000);
- assert!(config.sentry.is_none());
- assert!(config.secrets.is_empty());
+ let q = Quire::load(dir.path().to_path_buf()).expect("missing file should use defaults");
+ assert_eq!(q.config.port, 3000);
+ assert!(q.config.sentry.is_none());
+ assert!(q.config.secrets.is_empty());
}
#[test]
fn global_config_loads_with_sentry() {
let dir = tempfile::tempdir().expect("tempdir");
- let config_path = dir.path().join("config.fnl");
fs_err::write(
- &config_path,
+ dir.path().join("config.fnl"),
r#"{:sentry {:dsn "https://key@sentry.io/123"}}"#,
)
.expect("write");
- let config = GlobalConfig::load(&config_path).expect("should load");
- let sentry = config.sentry.expect("sentry should be present");
+ let q = Quire::load(dir.path().to_path_buf()).expect("should load");
+ let sentry = q.config.sentry.expect("sentry should be present");
assert_eq!(sentry.dsn.reveal().unwrap(), "https://key@sentry.io/123");
}
#[test]
fn global_config_sentry_is_optional() {
let dir = tempfile::tempdir().expect("tempdir");
- let config_path = dir.path().join("config.fnl");
- fs_err::write(&config_path, "{}").expect("write");
+ fs_err::write(dir.path().join("config.fnl"), "{}").expect("write");
- let config = GlobalConfig::load(&config_path).expect("should load");
- assert!(config.sentry.is_none());
+ let q = Quire::load(dir.path().to_path_buf()).expect("should load");
+ assert!(q.config.sentry.is_none());
}
#[test]
fn global_config_secrets_default_empty() {
let dir = tempfile::tempdir().expect("tempdir");
- let config_path = dir.path().join("config.fnl");
- fs_err::write(&config_path, "{}").expect("write");
+ fs_err::write(dir.path().join("config.fnl"), "{}").expect("write");
- let config = GlobalConfig::load(&config_path).expect("should load");
- assert!(config.secrets.is_empty());
+ let q = Quire::load(dir.path().to_path_buf()).expect("should load");
+ assert!(q.config.secrets.is_empty());
}
#[test]
fn global_config_loads_secrets_map() {
let dir = tempfile::tempdir().expect("tempdir");
- let config_path = dir.path().join("config.fnl");
let secret_file = dir.path().join("gh_token");
fs_err::write(&secret_file, "ghp_from_file\n").expect("write secret");
fs_err::write(
- &config_path,
+ dir.path().join("config.fnl"),
format!(
r#"{{:secrets {{:github_token {{:file "{}"}}
:slack_webhook "https://hooks.slack.com/abc"}}}}"#,
@@ -558,14 +555,14 @@ mod tests {
)
.expect("write");
- let config = GlobalConfig::load(&config_path).expect("should load");
- assert_eq!(config.secrets.len(), 2);
+ let q = Quire::load(dir.path().to_path_buf()).expect("should load");
+ assert_eq!(q.config.secrets.len(), 2);
assert_eq!(
- config.secrets["github_token"].reveal().unwrap(),
+ q.config.secrets["github_token"].reveal().unwrap(),
"ghp_from_file"
);
assert_eq!(
- config.secrets["slack_webhook"].reveal().unwrap(),
+ q.config.secrets["slack_webhook"].reveal().unwrap(),
"https://hooks.slack.com/abc"
);
}
diff --git a/quire-server/src/quire/web/api.rs b/quire-server/src/quire/web/api.rs
index c01f96a..c73557e 100644
--- a/quire-server/src/quire/web/api.rs
+++ b/quire-server/src/quire/web/api.rs
@@ -371,10 +371,14 @@ mod tests {
async fn secret_returns_plaintext_value() {
let config = {
let dir = tempfile::tempdir().expect("tempdir");
- let config_path = dir.path().join("config.fnl");
- fs_err::write(&config_path, r#"{:secrets {:my_token "hunter2"}}"#)
- .expect("write config");
- crate::quire::GlobalConfig::load(&config_path).expect("load config")
+ fs_err::write(
+ dir.path().join("config.fnl"),
+ r#"{:secrets {:my_token "hunter2"}}"#,
+ )
+ .expect("write config");
+ crate::Quire::load(dir.path().to_path_buf())
+ .expect("load config")
+ .config
};
let env = TestEnv::with_config(config);
let session = ApiSession::new(3000);