chore: remove stale manifest cascade tests

This commit is contained in:
2026-05-30 12:49:43 +09:00
parent 2533a3d5d3
commit e9354b38d6
11 changed files with 21 additions and 974 deletions
-125
View File
@@ -1,125 +0,0 @@
//! Cascade-layer collection helpers.
//!
//! Pod manifests are assembled from up to three on-disk layers (see
//! `pod::PodFactory` for the full cascade story):
//!
//! 1. **User manifest** — Pod CLI uses
//! [`crate::paths::user_manifest_path_with_env_override`]
//! 2. **Project manifest** at the closest `.insomnia/manifest.toml`
//! found by walking up from a starting directory (typically `cwd`)
//! 3. **Programmatic overlay** supplied at the call site
//!
//! This module owns the project-layer discovery and the parser glue.
//! User-layer path resolution lives in [`crate::paths`].
//!
//! Cascade *merging* and final validation stay outside this module —
//! that's the data layer's responsibility (`PodManifestConfig::merge`
//! and `PodManifest::try_from`). This module only handles the I/O and
//! path-discovery glue around them.
use std::path::{Path, PathBuf};
use crate::PodManifestConfig;
/// Errors returned when reading a single manifest layer from disk.
#[derive(Debug, thiserror::Error)]
pub enum LayerLoadError {
#[error("failed to read manifest {}: {source}", .path.display())]
Io {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("failed to parse manifest {}: {source}", .path.display())]
Parse {
path: PathBuf,
#[source]
source: toml::de::Error,
},
}
/// Walk up from `start` looking for `.insomnia/manifest.toml`. Returns
/// the closest match, or `None` if none is found before reaching the
/// filesystem root.
pub fn find_project_manifest_from(start: &Path) -> Option<PathBuf> {
let start = start
.canonicalize()
.ok()
.unwrap_or_else(|| start.to_path_buf());
let mut cur: Option<&Path> = Some(start.as_path());
while let Some(dir) = cur {
let candidate = dir.join(".insomnia").join("manifest.toml");
if candidate.is_file() {
return Some(candidate);
}
cur = dir.parent();
}
None
}
/// Read a manifest file from `path` and parse it as a partial
/// [`PodManifestConfig`]. Path resolution against a base directory and
/// merging with other layers are the caller's responsibility.
pub fn load_layer(path: &Path) -> Result<PodManifestConfig, LayerLoadError> {
let toml = std::fs::read_to_string(path).map_err(|source| LayerLoadError::Io {
path: path.to_path_buf(),
source,
})?;
PodManifestConfig::from_toml(&toml).map_err(|source| LayerLoadError::Parse {
path: path.to_path_buf(),
source,
})
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn find_project_manifest_walks_up() {
let tmp = TempDir::new().unwrap();
let root = tmp.path().canonicalize().unwrap();
let manifest = root.join(".insomnia").join("manifest.toml");
std::fs::create_dir_all(manifest.parent().unwrap()).unwrap();
std::fs::write(&manifest, "").unwrap();
let nested = root.join("a").join("b");
std::fs::create_dir_all(&nested).unwrap();
let found = find_project_manifest_from(&nested).unwrap();
assert_eq!(found, manifest);
}
#[test]
fn find_project_manifest_returns_none_when_absent() {
let tmp = TempDir::new().unwrap();
assert!(find_project_manifest_from(tmp.path()).is_none());
}
#[test]
fn load_layer_round_trips_partial_config() {
let tmp = TempDir::new().unwrap();
let path = tmp.path().join("manifest.toml");
std::fs::write(
&path,
r#"
[pod]
name = "from-disk"
"#,
)
.unwrap();
let cfg = load_layer(&path).unwrap();
assert_eq!(cfg.pod.name.as_deref(), Some("from-disk"));
}
#[test]
fn load_layer_io_error_carries_path() {
let bogus = PathBuf::from("/definitely/does/not/exist/manifest.toml");
let err = load_layer(&bogus).unwrap_err();
match err {
LayerLoadError::Io { path, .. } => assert_eq!(path, bogus),
_ => panic!("expected Io variant"),
}
}
}
+3 -3
View File
@@ -210,9 +210,9 @@ impl PodManifestConfig {
})
}
/// Cascade layer populated with the in-code defaults listed in
/// [`crate::defaults`]. Used by [`PodFactory::resolve`] as the
/// bottom layer, so every per-field default lives at exactly one
/// Base config populated with the in-code defaults listed in
/// [`crate::defaults`]. Profile and one-file Manifest resolvers start
/// from this layer so every per-field default lives at exactly one
/// call site (the `defaults` module).
///
/// `TryFrom<PodManifestConfig>` also reads the same constants as a
+4 -11
View File
@@ -1,4 +1,3 @@
mod cascade;
mod config;
pub mod defaults;
mod model;
@@ -6,7 +5,6 @@ pub mod paths;
mod profile;
mod scope;
pub use cascade::{LayerLoadError, find_project_manifest_from, load_layer};
pub use config::{
CompactionConfigPartial, FileUploadLimitsPartial, PermissionConfigPartial, PodManifestConfig,
PodMetaConfig, ResolveError, SessionConfigPartial, ToolOutputLimitsPartial,
@@ -15,10 +13,7 @@ pub use config::{
pub use model::{
AuthRef, ModelCapability, ModelManifest, ReasoningControl, ReasoningEffort, SchemeKind,
};
pub use paths::{
user_manifest_path, user_manifest_path_from_env, user_manifest_path_with_env_override,
user_profiles_path,
};
pub use paths::user_profiles_path;
pub use profile::{
ProfileDiscovery, ProfileError, ProfileManifestSnapshot, ProfileMetadata, ProfileRegistry,
ProfileRegistryEntry, ProfileRegistrySource, ProfileResolveOptions, ProfileResolver,
@@ -76,17 +71,15 @@ pub struct PodManifest {
pub skills: Option<SkillsConfig>,
/// Optional profile provenance for manifests produced by profile resolution.
/// Stored only after profile resolution so Pod restore can prefer the
/// validated snapshot over ambient manifest cascade state.
/// validated snapshot over current profile files or one-file Manifest input.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub profile: Option<profile::ProfileManifestSnapshot>,
}
/// External Agent Skills (`SKILL.md`) ingest configuration. Skills are
/// loaded *only* from the directories listed here — there is no
/// implicit `$config_dir/skills/` or builtin probe. Cascade-merged
/// across manifest layers, so a user-level manifest can declare a
/// shared skill root once while a project manifest adds its own
/// `.claude/skills/` / `.cursor/skills/` paths on top.
/// implicit `$config_dir/skills/` or builtin probe. Profile and Manifest
/// resolution may compose these entries before validation.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SkillsConfig {
/// Skills *roots*. Children of each root must be individual
+3 -79
View File
@@ -2,7 +2,7 @@
//!
//! 用途別に三つの base directory を持つ:
//!
//! - **`config_dir`** — 人が手で書く / 編集する設定。`manifest.toml`,
//! - **`config_dir`** — 人が手で書く / 編集する設定。`profiles.toml`,
//! `providers.toml`, `models.toml`, `prompts/`, `prompts.toml` 等
//! - **`data_dir`** — プログラムが書く永続データ。`sessions/` 等
//! - **`runtime_dir`** — 再起動で消えてよいランタイム状態。socket,
@@ -23,20 +23,12 @@
//! 解決された各 base が存在するか / ディレクトリかは保証しない —
//! 呼び出し側がファイル操作の前に作成 / 検査する。
use std::ffi::OsString;
use std::path::PathBuf;
/// Environment variable that points at an explicit user manifest.
///
/// Pod CLI treats a non-empty value as an explicit manifest path. Empty values
/// are treated the same as an unset variable, so callers fall back to the
/// auto-discovered user manifest path.
pub const USER_MANIFEST_ENV: &str = "INSOMNIA_USER_MANIFEST";
/// Environment variable that points at installed project resources.
pub const RESOURCE_DIR_ENV: &str = "INSOMNIA_RESOURCE_DIR";
/// 設定ディレクトリ。`manifest.toml`, `providers.toml`, `models.toml`,
/// 設定ディレクトリ。`profiles.toml`, `providers.toml`, `models.toml`,
/// `prompts/` などが置かれる。
pub fn config_dir() -> Option<PathBuf> {
if let Some(p) = env_path("INSOMNIA_CONFIG_DIR") {
@@ -80,42 +72,10 @@ pub fn runtime_dir() -> Option<PathBuf> {
// ---- well-known file getters ------------------------------------------------
/// `<config_dir>/manifest.toml` — user manifest の既定位置。
///
/// This deliberately ignores [`USER_MANIFEST_ENV`]. Use
/// [`user_manifest_path_with_env_override`] when mirroring the Pod CLI cascade
/// resolution rules.
pub fn user_manifest_path() -> Option<PathBuf> {
Some(config_dir()?.join("manifest.toml"))
}
/// Resolve an explicit user manifest override from an env value.
///
/// Non-empty values are paths. `None` and empty strings are both treated as no
/// override, matching the Pod CLI's `INSOMNIA_USER_MANIFEST` handling.
pub fn user_manifest_path_from_env(value: Option<OsString>) -> Option<PathBuf> {
value.and_then(|value| {
if value.as_os_str().is_empty() {
None
} else {
Some(PathBuf::from(value))
}
})
}
/// User manifest path using the same env override rule as the Pod CLI cascade.
///
/// A non-empty [`USER_MANIFEST_ENV`] value wins. If the variable is unset or
/// empty, this falls back to [`user_manifest_path`]. The returned path is not
/// guaranteed to exist.
pub fn user_manifest_path_with_env_override() -> Option<PathBuf> {
user_manifest_path_from_env(std::env::var_os(USER_MANIFEST_ENV)).or_else(user_manifest_path)
}
/// `<config_dir>/profiles.toml` — user profile registry/default configuration.
///
/// This is application/profile selection configuration, not a Pod manifest
/// layer. It deliberately ignores [`USER_MANIFEST_ENV`].
/// layer.
pub fn user_profiles_path() -> Option<PathBuf> {
Some(config_dir()?.join("profiles.toml"))
}
@@ -228,7 +188,6 @@ mod tests {
"INSOMNIA_CONFIG_DIR",
"INSOMNIA_DATA_DIR",
"INSOMNIA_RUNTIME_DIR",
"INSOMNIA_USER_MANIFEST",
"INSOMNIA_RESOURCE_DIR",
"INSOMNIA_HOME",
"XDG_CONFIG_HOME",
@@ -355,44 +314,9 @@ mod tests {
assert!(runtime_dir().is_none());
}
#[test]
fn user_manifest_env_override_wins_when_non_empty() {
let _g = EnvGuard::new(&[
("HOME", Some("/h")),
("INSOMNIA_USER_MANIFEST", Some("/tmp/user.toml")),
]);
assert_eq!(
user_manifest_path_with_env_override().unwrap(),
PathBuf::from("/tmp/user.toml")
);
}
#[test]
fn empty_user_manifest_env_falls_back_to_default_path() {
let _g = EnvGuard::new(&[("HOME", Some("/h")), ("INSOMNIA_USER_MANIFEST", Some(""))]);
assert_eq!(
user_manifest_path_with_env_override().unwrap(),
PathBuf::from("/h/.config/insomnia/manifest.toml")
);
}
#[test]
fn user_manifest_path_from_env_treats_empty_as_unset() {
assert_eq!(user_manifest_path_from_env(None), None);
assert_eq!(user_manifest_path_from_env(Some(OsString::from(""))), None);
assert_eq!(
user_manifest_path_from_env(Some(OsString::from("/tmp/u.toml"))).unwrap(),
PathBuf::from("/tmp/u.toml")
);
}
#[test]
fn well_known_files_compose_off_base_dirs() {
let _g = EnvGuard::new(&[("INSOMNIA_HOME", Some("/sand"))]);
assert_eq!(
user_manifest_path().unwrap(),
PathBuf::from("/sand/config/manifest.toml")
);
assert_eq!(
user_profiles_path().unwrap(),
PathBuf::from("/sand/config/profiles.toml")
-56
View File
@@ -1186,7 +1186,6 @@ mod tests {
let lock = env_lock();
let names = [
"INSOMNIA_CONFIG_DIR",
"INSOMNIA_USER_MANIFEST",
"INSOMNIA_RESOURCE_DIR",
"INSOMNIA_HOME",
"XDG_CONFIG_HOME",
@@ -1456,61 +1455,6 @@ return profile {
assert!(err.to_string().contains("Lua profiles must end in .lua"));
}
#[test]
fn for_cwd_reads_profiles_toml_and_ignores_manifest_profiles() {
let tmp = TempDir::new().unwrap();
let config_dir = tmp.path().join("config");
std::fs::create_dir_all(&config_dir).unwrap();
let _env = EnvGuard::new(&[("INSOMNIA_CONFIG_DIR", Some(config_dir.to_str().unwrap()))]);
let project = tmp.path().join("project").join("nested");
let insomnia = tmp.path().join("project").join(".insomnia");
std::fs::create_dir_all(&project).unwrap();
std::fs::create_dir_all(&insomnia).unwrap();
std::fs::write(
insomnia.join("manifest.toml"),
"[profiles]\ndefault = \"wrong\"\n[profiles.profile]\nwrong = \"wrong.lua\"\n",
)
.unwrap();
std::fs::write(
insomnia.join("profiles.toml"),
"default = \"coder\"\n[profile]\ncoder = \"profiles/coder.lua\"\n",
)
.unwrap();
let registry = ProfileDiscovery::for_cwd(&project).discover().unwrap();
assert!(registry.select_named(None, "wrong").is_err());
let selected = registry.default_entry().unwrap();
assert_eq!(selected.source, ProfileRegistrySource::Project);
assert_eq!(selected.name, "coder");
}
#[test]
fn user_manifest_env_does_not_affect_profile_registry_discovery() {
let tmp = TempDir::new().unwrap();
let config_dir = tmp.path().join("config");
std::fs::create_dir_all(&config_dir).unwrap();
let env_manifest = tmp.path().join("env-manifest.toml");
std::fs::write(
&env_manifest,
"[profiles]\ndefault = \"wrong\"\n[profiles.profile]\nwrong = \"wrong.lua\"\n",
)
.unwrap();
std::fs::write(
config_dir.join("profiles.toml"),
"default = \"coder\"\n[profile]\ncoder = \"profiles/coder.lua\"\n",
)
.unwrap();
let _env = EnvGuard::new(&[
("INSOMNIA_CONFIG_DIR", Some(config_dir.to_str().unwrap())),
(
"INSOMNIA_USER_MANIFEST",
Some(env_manifest.to_str().unwrap()),
),
]);
let registry = ProfileDiscovery::for_cwd(tmp.path()).discover().unwrap();
assert!(registry.select_named(None, "wrong").is_err());
let selected = registry.default_entry().unwrap();
assert_eq!(selected.source, ProfileRegistrySource::User);
assert_eq!(selected.name, "coder");
}
#[test]
fn discovery_reads_user_and_project_registry_and_project_default_wins() {
let tmp = TempDir::new().unwrap();
let user_config = tmp.path().join("profiles.toml");