test(integration): add cross-crate boundary tests

This commit is contained in:
Joseph O'Brien
2026-04-01 06:32:07 -04:00
parent eb1807a8b1
commit 02c84c32f7
2 changed files with 116 additions and 0 deletions

View File

@@ -9,6 +9,10 @@ publish = false
name = "bootstrap"
path = "tests/bootstrap.rs"
[[test]]
name = "cross_crate"
path = "tests/cross_crate.rs"
[dev-dependencies]
notstrap = { path = "../../crates/notstrap" }
notfiles = { path = "../../crates/notfiles" }

View File

@@ -0,0 +1,112 @@
use std::fs;
use std::path::Path;
use tempfile::TempDir;
use notcore::{HookPhase, HookSpec};
use nothooks::{HookResult, HookRunner};
use notsecrets::{resolve_age_key, AgeKeySource, FileSource};
use notfiles::{link, LinkOptions};
/// Test that notsecrets and nothooks can each be used independently
/// in the same integration boundary — resolving an age key from a file
/// and running a dot-phase hook both succeed without coupling.
#[test]
fn test_nothooks_notsecrets_independent() {
let dir = TempDir::new().unwrap();
// Write a fake age key via FileSource
let key_path = dir.path().join("age.key");
fs::write(&key_path, "AGE-SECRET-KEY-1CROSSCRATE\n").unwrap();
let sources: Vec<Box<dyn AgeKeySource>> = vec![Box::new(FileSource::new(key_path))];
let key = resolve_age_key(sources).unwrap();
assert!(
key.trim().starts_with("AGE-SECRET-KEY-"),
"expected age key prefix, got: {key:?}"
);
// Write a hook that just prints
let script = dir.path().join("chain.nu");
fs::write(&script, "print chain-ok\n").unwrap();
let spec = HookSpec {
name: "chain".to_string(),
script: script.to_str().unwrap().to_string(),
phase: HookPhase::Dot,
};
let runner = HookRunner::new(dir.path().to_path_buf());
let result = runner.run_hook(&spec);
assert!(
matches!(result, HookResult::Ok),
"expected HookResult::Ok, got: {result:?}"
);
}
/// Test that notfiles ignores .notfiles-state.toml and .nothooks-state.toml
/// by default — they must not be symlinked into the target directory and must
/// not appear in the returned State.
#[test]
fn test_notfiles_respects_default_ignore() {
let dotfiles = TempDir::new().unwrap();
let home = TempDir::new().unwrap();
let d = dotfiles.path();
// notfiles.toml — package "pkg" targeting home tempdir
fs::write(
d.join("notfiles.toml"),
format!(
"[defaults]\nmethod = \"symlink\"\ntarget = \"{}\"\n",
home.path().display()
),
)
.unwrap();
// Package dir with a normal file and two state files that should be ignored
let pkg = d.join("pkg");
fs::create_dir_all(&pkg).unwrap();
fs::write(pkg.join("foo.txt"), "hello\n").unwrap();
fs::write(pkg.join(".notfiles-state.toml"), "# state\n").unwrap();
fs::write(pkg.join(".nothooks-state.toml"), "# state\n").unwrap();
let opts = LinkOptions { force: false, no_backup: false, dry_run: false, verbose: false };
let state = link(d, &[], &opts).unwrap();
// foo.txt linked
assert!(
home.path().join("foo.txt").exists(),
"foo.txt should be linked"
);
// state files NOT linked
assert!(
!home.path().join(".notfiles-state.toml").exists(),
".notfiles-state.toml must not be linked"
);
assert!(
!home.path().join(".nothooks-state.toml").exists(),
".nothooks-state.toml must not be linked"
);
// state doesn't record them either
let names: Vec<_> = state
.entries
.iter()
.map(|e| {
Path::new(&e.source)
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_string()
})
.collect();
assert!(
!names.contains(&".notfiles-state.toml".to_string()),
".notfiles-state.toml must not be in state entries"
);
assert!(
!names.contains(&".nothooks-state.toml".to_string()),
".nothooks-state.toml must not be in state entries"
);
}