#[cfg(test)]
mod parser_tests {
use flk::flake::parsers::{
commands::parse_shell_hook_section,
env::parse_env_vars_section,
overlays::{parse_overlay_section, parse_sources_section},
packages::parse_packages_section,
};
const PROFILE_CONTENT: &str = include_str!("profile_tests.nix");
const PINS_CONTENT: &str = include_str!("pins_tests.nix");
#[test]
fn test_package_exists() {
let section = parse_packages_section(PROFILE_CONTENT).unwrap();
let exists = section.package_exists("git");
assert!(exists);
let not_exists = section.package_exists("nonexistent");
assert!(!not_exists);
}
#[test]
fn test_package_exists_with_pkgs_prefix() {
let content = r#"
packages = [
pkgs.git
pkgs.curl
];
"#;
let section = parse_packages_section(content).unwrap();
let exists = section.package_exists("git");
assert!(exists);
}
#[test]
fn test_add_package_to_empty_list() {
let content = r#"
pkgs = import nixpkgs {
inherit system overlays;
};
profileLib = profile-lib. lib {inherit pkgs;};
profileDefinitions = {
default = {
packages = with pkgs; [
];
};
};
"#;
let section = parse_packages_section(content).unwrap();
let result = section.add_package(content, "ripgrep", None);
assert!(result.contains("ripgrep"));
}
#[test]
fn test_add_package_to_existing_list() {
let section = parse_packages_section(PROFILE_CONTENT).unwrap();
let result = section.add_package(PROFILE_CONTENT, "ripgrep", None);
assert!(result.contains("ripgrep"));
assert!(result.contains("git"));
assert!(result.contains("curl"));
}
#[test]
fn test_add_package_preserves_formatting() {
let section = parse_packages_section(PROFILE_CONTENT).unwrap();
let result = section.add_package(PROFILE_CONTENT, "ripgrep", None);
assert!(result.contains(" ") || result.contains(" "));
}
#[test]
fn test_remove_package() {
let section = parse_packages_section(PROFILE_CONTENT).unwrap();
let result = section.remove_package(PROFILE_CONTENT, "curl").unwrap();
println!("{}", result);
assert!(result.contains("git"));
assert!(!result.contains("curl"));
}
#[test]
fn test_remove_package_from_middle() {
let content = r#"
packages = [
pkgs.git
pkgs.curl
pkgs.wget
];
"#;
let section = parse_packages_section(content).unwrap();
let result = section.remove_package(content, "curl").unwrap();
assert!(result.contains("git"));
assert!(result.contains("wget"));
assert!(!result.contains("curl"));
}
#[test]
fn test_remove_nonexistent_package() {
let section = parse_packages_section(PROFILE_CONTENT).unwrap();
let result = section.remove_package(PROFILE_CONTENT, "nonexistent");
assert!(result.is_err());
}
#[test]
fn test_command_exists() {
let section = parse_shell_hook_section(PROFILE_CONTENT).unwrap();
let exists = section.command_exists("test");
assert!(exists);
let not_exists = section.command_exists("nonexistent");
assert!(!not_exists);
}
#[test]
fn test_add_command() {
let mut section = parse_shell_hook_section(PROFILE_CONTENT).unwrap();
if (section.add_command("test_add", "echo 'test command'")).is_err() {
panic!("Failed to add command");
}
assert!(section.command_exists("test_add"));
}
#[test]
fn test_add_command_with_multiline() {
let mut section = parse_shell_hook_section(PROFILE_CONTENT).unwrap();
let multiline_cmd = "echo 'line 1'\necho 'line 2'\necho 'line 3'";
section.add_command("multiline", multiline_cmd).unwrap();
assert!(section.command_exists("multiline"));
let entry = section
.entries
.iter()
.find(|e| e.name == "multiline")
.unwrap();
assert!(entry.script.contains("echo 'line 1'"));
assert!(entry.script.contains("echo 'line 2'"));
assert!(entry.script.contains("echo 'line 3'"));
}
#[test]
fn test_add_command_with_special_chars() {
let cmd = "cargo build --release && echo 'Done!'";
let mut section = parse_shell_hook_section(PROFILE_CONTENT).unwrap();
if (section.add_command("build", cmd)).is_err() {
panic!("Failed to add command with special characters");
}
assert!(section.command_exists("build"));
let entry = section.entries.iter().find(|e| e.name == "build").unwrap();
assert!(entry.script.contains("cargo build --release"));
}
#[test]
fn test_remove_command() {
let mut section = parse_shell_hook_section(PROFILE_CONTENT).unwrap();
section.remove_command("test").unwrap();
assert!(!section.command_exists("test"));
}
#[test]
fn test_remove_nonexistent_command() {
let mut section = parse_shell_hook_section(PROFILE_CONTENT).unwrap();
let result = section.remove_command("nonexistent");
assert!(result.is_err());
}
#[test]
fn test_env_var_exists() {
let section = parse_env_vars_section(PROFILE_CONTENT).unwrap();
let exists = section.env_var_exists("VAR2").unwrap();
assert!(exists);
let not_exists = section.env_var_exists("NONEXISTENT").unwrap();
assert!(!not_exists);
}
#[test]
fn test_add_env_var() {
let section = parse_env_vars_section(PROFILE_CONTENT).unwrap();
let result = section.add_env_var(PROFILE_CONTENT, "MY_VAR", "test_value");
assert!(result.contains(" MY_VAR = \"test_value\""));
}
#[test]
fn test_add_env_var_with_quotes() {
let section = parse_env_vars_section(PROFILE_CONTENT).unwrap();
let result = section.add_env_var(PROFILE_CONTENT, "QUOTED", r#"value"with"quotes"#);
assert!(result.contains("QUOTED"));
assert!(result.contains(r#"value"with"quotes"#));
}
#[test]
fn test_add_env_var_with_special_chars() {
let section = parse_env_vars_section(PROFILE_CONTENT).unwrap();
let result = section.add_env_var(PROFILE_CONTENT, "SPECIAL", "value with $pecial ch@rs!");
assert!(result.contains("SPECIAL"));
assert!(result.contains("value with $pecial ch@rs!"));
}
#[test]
fn test_remove_env_var() {
let section = parse_env_vars_section(PROFILE_CONTENT).unwrap();
let result = section.remove_env_var(PROFILE_CONTENT, "VAR1").unwrap();
assert!(!result.contains("VAR1"));
}
#[test]
fn test_remove_env_var_middle() {
let section = parse_env_vars_section(PROFILE_CONTENT).unwrap();
let result = section.remove_env_var(PROFILE_CONTENT, "VAR2").unwrap();
assert!(result.contains("VAR1"));
assert!(result.contains("VAR3"));
assert!(!result.contains("VAR2"));
}
#[test]
fn test_parse_env_vars() {
let section = parse_env_vars_section(PROFILE_CONTENT).unwrap();
assert_eq!(section.entries.len(), 3);
let vars: Vec<(String, String)> = section
.entries
.iter()
.map(|e| (e.name.clone(), e.value.clone()))
.collect();
assert!(vars.contains(&("VAR1".to_string(), "value1".to_string())));
assert!(vars.contains(&("VAR2".to_string(), "value2".to_string())));
assert!(vars.contains(&("VAR3".to_string(), "value3".to_string())));
}
#[test]
fn test_parse_env_vars_empty() {
let content = r#"
envVars = {
};
"#;
let section = parse_env_vars_section(content).unwrap();
assert_eq!(section.entries.len(), 0);
}
#[test]
fn test_parse_shell_hook() {
let section = parse_shell_hook_section(PROFILE_CONTENT).unwrap();
assert_eq!(section.entries.len(), 1);
let entry = §ion.entries[0];
assert_eq!(entry.name, "test");
assert!(entry.script.contains("echo \"This is a test command\""));
}
#[test]
fn test_parse_packages_ignores_comments() {
let content = r#"
packages = [
pkgs.git
# This is a comment
pkgs.curl
];
"#;
let section = parse_packages_section(content).unwrap();
let packages: Vec<_> = section.entries.iter().collect();
assert_eq!(packages.len(), 2);
assert!(packages.iter().any(|p| p.name == "git"));
assert!(packages.iter().any(|p| p.name == "curl"));
}
#[test]
fn test_source_exists_with_existing_source() {
let section = parse_sources_section(PINS_CONTENT).unwrap();
let result = section.source_exists("pkgs-abc1234");
assert!(result);
}
#[test]
fn test_source_exists_with_nonexistent_source() {
let section = parse_sources_section(PINS_CONTENT).unwrap();
let result = section.source_exists("pkgs-nonexistent");
assert!(!result);
}
#[test]
fn test_source_exists_with_empty_content() {
let content = r#"{
sources = {
};
pinnedPackages = {
};
}"#;
let section = parse_sources_section(content).unwrap();
let result = section.source_exists("my-source");
assert!(!result);
}
#[test]
fn test_add_source() {
let mut section = parse_sources_section(PINS_CONTENT).unwrap();
section
.add_source("pkgs-new123", "github:NixOS/nixpkgs/new123")
.unwrap();
assert!(section.entries.iter().any(|s| s.name == "pkgs-new123"));
assert!(section
.entries
.iter()
.any(|s| s.reference == "github:NixOS/nixpkgs/new123"));
}
#[test]
fn test_remove_source() {
let mut section = parse_sources_section(PINS_CONTENT).unwrap();
section.remove_source("pkgs-abc1234").unwrap();
assert!(!section.source_exists("pkgs-abc1234"));
assert!(section.source_exists("pkgs-def5678")); }
#[test]
fn test_remove_nonexistent_source() {
let mut section = parse_sources_section(PINS_CONTENT).unwrap();
let result = section.remove_source("pkgs-nonexistent");
assert!(result.is_err());
}
#[test]
fn test_pin_entry_exists_with_existing_entry() {
let section = parse_overlay_section(PINS_CONTENT).unwrap();
assert!(section.pin_entry_exists("pkgs-abc1234"));
}
#[test]
fn test_pin_entry_exists_with_nonexistent_entry() {
let section = parse_overlay_section(PINS_CONTENT).unwrap();
assert!(!section.pin_entry_exists("pkgs-nonexistent"));
}
#[test]
fn test_pin_entry_exists_with_empty_content() {
let content = r#"{
sources = {
};
pinnedPackages = {
};
}"#;
let section = parse_overlay_section(content).unwrap();
assert!(!section.pin_entry_exists("my-pin"));
}
#[test]
fn test_add_pin_entry() {
let mut section = parse_overlay_section(PINS_CONTENT).unwrap();
section.add_pin_entry("pkgs-new456").unwrap();
assert!(section.pin_entry_exists("pkgs-new456"));
}
#[test]
fn test_remove_pin_entry() {
let mut section = parse_overlay_section(PINS_CONTENT).unwrap();
section.remove_pin_entry("pkgs-abc1234").unwrap();
assert!(!section.pin_entry_exists("pkgs-abc1234"));
assert!(section.pin_entry_exists("pkgs-def5678")); }
#[test]
fn test_remove_nonexistent_pin_entry() {
let mut section = parse_overlay_section(PINS_CONTENT).unwrap();
let result = section.remove_pin_entry("pkgs-nonexistent");
assert!(result.is_err());
}
#[test]
fn test_package_in_pin_exists_with_existing_package() {
let section = parse_overlay_section(PINS_CONTENT).unwrap();
let result = section.package_in_pin_exists("pkgs-abc1234", "git");
assert!(result);
}
#[test]
fn test_package_in_pin_exists_with_nonexistent_package() {
let section = parse_overlay_section(PINS_CONTENT).unwrap();
let result = section.package_in_pin_exists("pkgs-abc1234", "python");
assert!(!result);
}
#[test]
fn test_add_package_to_pin() {
let mut section = parse_overlay_section(PINS_CONTENT).unwrap();
section
.add_package_to_pin("pkgs-abc1234", "wget", "wget@1.21.0")
.unwrap();
assert!(section.package_in_pin_exists("pkgs-abc1234", "git")); assert!(section.package_in_pin_exists("pkgs-abc1234", "curl"));
assert!(section.package_in_pin_exists("pkgs-abc1234", "wget")); }
#[test]
fn test_remove_package_from_pin() {
let mut section = parse_overlay_section(PINS_CONTENT).unwrap();
section
.remove_package_from_pin("pkgs-abc1234", "git")
.unwrap();
assert!(!section.package_in_pin_exists("pkgs-abc1234", "git"));
assert!(section.package_in_pin_exists("pkgs-abc1234", "curl"));
}
#[test]
fn test_remove_nonexistent_package_from_pin() {
let mut section = parse_overlay_section(PINS_CONTENT).unwrap();
let result = section.remove_package_from_pin("pkgs-abc1234", "nonexistent@1.0");
assert!(result.is_err());
}
#[test]
fn test_indent_consistency() {
let section = parse_packages_section(PROFILE_CONTENT).unwrap();
let result = section.add_package(PROFILE_CONTENT, "test", None);
let lines: Vec<&str> = result.lines().collect();
for line in lines {
if !line.trim().is_empty() {
let leading_spaces = line.len() - line.trim_start().len();
assert!(leading_spaces % 2 == 0);
}
}
}
}
#[cfg(test)]
mod generator_tests {
use flk::flake;
#[test]
fn test_generate_generic_flake() {
let flake = flake::generator::generate_flake("generic").unwrap();
assert!(flake.contains("Generic Development Environment"));
}
#[test]
fn test_generate_rust_flake() {
let flake = flake::generator::generate_flake("rust").unwrap();
assert!(flake.contains("Rust development environment"));
assert!(flake.contains("rust-bin.stable.latest.default"));
}
#[test]
fn test_generate_python_flake() {
let flake = flake::generator::generate_flake("python").unwrap();
assert!(flake.contains("Python development environment"));
assert!(flake.contains("python3"));
}
#[test]
fn test_generate_node_flake() {
let flake = flake::generator::generate_flake("node").unwrap();
assert!(flake.contains("Node.js development environment"));
assert!(flake.contains("nodejs"));
}
#[test]
fn test_generate_go_flake() {
let flake = flake::generator::generate_flake("go").unwrap();
assert!(flake.contains("Go development environment"));
assert!(flake.contains("go"));
}
#[test]
fn test_unknown_template_defaults_to_generic() {
let flake = flake::generator::generate_flake("unknown").unwrap();
assert!(flake.contains("Generic Development Environment"));
}
#[test]
fn test_generate_root_flake() {
let flake = flake::generator::generate_root_flake().unwrap();
assert!(!flake.is_empty());
assert!(flake.contains("inputs"));
assert!(flake.contains("outputs"));
}
#[test]
fn test_generate_helper_module() {
let helper = flake::generator::generate_helper_module().unwrap();
assert!(!helper.is_empty());
}
#[test]
fn test_generate_importer_module() {
let importer = flake::generator::generate_importer_module().unwrap();
assert!(!importer.is_empty());
}
#[test]
fn test_generate_overlays() {
let overlays = flake::generator::generate_overlays().unwrap();
assert!(!overlays.is_empty());
}
#[test]
fn test_generate_pins() {
let pins = flake::generator::generate_pins().unwrap();
assert!(!pins.is_empty());
}
#[test]
fn test_all_templates_are_valid_nix() {
let templates = vec!["rust", "python", "node", "go", "generic"];
for template in templates {
let flake = flake::generator::generate_flake(template).unwrap();
assert!(flake.contains("packages"));
assert!(flake.contains("="));
}
}
#[test]
fn test_all_templates_have_env_vars_section() {
let templates = vec!["rust", "python", "node", "go", "generic"];
for template in templates {
let flake = flake::generator::generate_flake(template).unwrap();
assert!(flake.contains("envVars"));
}
}
#[test]
fn test_all_templates_have_shell_hook() {
let templates = vec!["rust", "python", "node", "go", "generic"];
for template in templates {
let flake = flake::generator::generate_flake(template).unwrap();
assert!(flake.contains("shellHook"));
}
}
}
#[cfg(test)]
mod interface_tests {
use flk::flake::interfaces::profiles::{EnvVar, FlakeConfig, Package, Profile};
#[test]
fn test_package_creation() {
let pkg = Package::new("ripgrep".to_string());
assert_eq!(pkg.name, "ripgrep");
assert_eq!(pkg.version.unwrap(), "latest");
}
#[test]
fn test_package_display() {
let pkg = Package::new("test-pkg".to_string());
let display = format!("{}", pkg);
assert!(display.contains("test-pkg"));
}
#[test]
fn test_env_var_creation() {
let env = EnvVar::new("TEST_VAR".to_string(), "test_value".to_string());
assert_eq!(env.name, "TEST_VAR");
assert_eq!(env.value, "test_value");
}
#[test]
fn test_env_var_display() {
let env = EnvVar::new("MY_VAR".to_string(), "my_value".to_string());
let display = format!("{}", env);
assert!(display.contains("MY_VAR"));
assert!(display.contains("my_value"));
}
#[test]
fn test_profile_creation() {
let profile = Profile::new("test-profile".to_string());
assert_eq!(profile.name, "test-profile");
assert_eq!(profile.packages.len(), 0);
assert_eq!(profile.env_vars.len(), 0);
}
#[test]
fn test_profile_with_data() {
let mut profile = Profile::new("dev".to_string());
profile.packages.push(Package::new("git".to_string()));
profile
.env_vars
.push(EnvVar::new("VAR1".to_string(), "value1".to_string()));
assert_eq!(profile.packages.len(), 1);
assert_eq!(profile.env_vars.len(), 1);
}
#[test]
fn test_flake_config_default() {
let config = FlakeConfig::default();
assert!(config.inputs.is_empty());
assert!(config.profiles.is_empty());
}
#[test]
fn test_flake_config_with_profiles() {
let mut config = FlakeConfig::default();
config.profiles.push(Profile::new("default".to_string()));
config.profiles.push(Profile::new("dev".to_string()));
assert_eq!(config.profiles.len(), 2);
}
#[test]
fn test_flake_config_display() {
let mut config = FlakeConfig::default();
config.inputs.push("nixpkgs".to_string());
let mut profile = Profile::new("default".to_string());
profile.packages.push(Package::new("git".to_string()));
config.profiles.push(profile);
let display = format!("{}", config);
assert!(display.contains("nixpkgs"));
assert!(display.contains("default"));
}
}
#[cfg(test)]
mod profile_display_and_hooks_tests {
use flk::flake::interfaces::profiles::{EnvVar, FlakeConfig, Package, Profile};
use flk::flake::interfaces::shellhooks::{ShellHookEntry, ShellHookSection};
fn populated_profile(name: &str) -> Profile {
let mut p = Profile::new(name.to_string());
p.packages.push(Package::new("git".to_string()));
p.env_vars
.push(EnvVar::new("KEY".to_string(), "val".to_string()));
p.shell_hook.entries.push(ShellHookEntry {
name: "dev".to_string(),
script: "echo dev".to_string(),
});
p
}
#[test]
fn test_package_display_unpinned_omits_version() {
let pkg = Package {
name: "ripgrep".to_string(),
version: None,
};
let display = format!("{}", pkg);
assert!(display.contains("ripgrep"));
assert!(
!display.contains('('),
"unpinned package must not render parens: {display:?}"
);
assert!(!display.contains(')'));
}
#[test]
fn test_package_display_pinned_shows_version() {
let pkg = Package {
name: "ripgrep".to_string(),
version: Some("15.1.0".to_string()),
};
let display = format!("{}", pkg);
assert!(display.contains("ripgrep"));
assert!(display.contains("15.1.0"));
}
#[test]
fn test_profile_display_full() {
let p = populated_profile("dev");
let display = format!("{}", p);
assert!(display.contains("dev"));
assert!(display.contains("git"));
assert!(display.contains("KEY"));
assert!(display.contains("Packages:"));
assert!(display.contains("Environment Variables:"));
assert!(display.contains("Commands:"));
}
#[test]
fn test_profile_display_empty_skips_sections() {
let p = Profile::new("empty".to_string());
let display = format!("{}", p);
assert!(display.contains("empty"));
assert!(!display.contains("Packages:"));
assert!(!display.contains("Environment Variables:"));
assert!(!display.contains("Commands:"));
}
#[test]
fn test_flake_config_display_helpers_empty_and_populated() {
let empty = FlakeConfig::default();
empty.display_env_vars();
empty.display_shell_hooks();
let mut populated = FlakeConfig::default();
populated.profiles.push(populated_profile("dev"));
populated.display_env_vars();
populated.display_shell_hooks();
}
fn empty_section() -> ShellHookSection {
ShellHookSection {
entries: Vec::new(),
indentation: " ".to_string(),
section_start: 0,
section_end: 0,
}
}
#[test]
fn test_add_command_succeeds_then_duplicate_fails() {
let mut s = empty_section();
assert!(s.add_command("build", "cargo build").is_ok());
assert_eq!(s.entries.len(), 1);
let dup = s.add_command("build", "cargo build --release");
assert!(dup.is_err(), "duplicate add_command must fail");
assert_eq!(s.entries.len(), 1, "no entry should have been appended");
assert_eq!(
s.entries[0].script, "cargo build",
"original script must be preserved on duplicate-add"
);
}
#[test]
fn test_remove_command_missing_errors_and_existing_succeeds() {
let mut s = empty_section();
assert!(s.remove_command("nope").is_err());
s.add_command("test", "cargo test").unwrap();
assert!(s.remove_command("test").is_ok());
assert!(s.entries.is_empty());
}
#[test]
fn test_display_list_empty_is_empty_string() {
use flk::utils::visual::display_list;
assert!(display_list(&[]).is_empty());
}
#[test]
fn test_display_list_includes_index_name_and_version() {
use flk::utils::visual::display_list;
let pkgs = vec![
Package::new("ripgrep".to_string()),
Package {
name: "fd".to_string(),
version: None,
},
];
let out = display_list(&pkgs);
assert!(out.contains("[1]"));
assert!(out.contains("ripgrep"));
assert!(out.contains("Version:"));
assert!(out.contains("latest"));
assert!(out.contains("[2]"));
assert!(out.contains("fd"));
}
#[test]
fn test_display_table_empty_is_empty_string() {
use flk::utils::visual::display_table;
assert!(display_table(&[]).is_empty());
}
#[test]
fn test_display_table_header_and_rows() {
use flk::utils::visual::display_table;
let pkgs = vec![
Package {
name: "ripgrep".to_string(),
version: Some("15.1.0".to_string()),
},
Package {
name: "fd".to_string(),
version: None,
},
];
let out = display_table(&pkgs);
assert!(out.contains("Name"));
assert!(out.contains("Version"));
assert!(out.contains("ripgrep"));
assert!(out.contains("15.1.0"));
assert!(out.contains("fd"));
}
#[test]
fn test_apply_to_content_splices_section() {
let mut s = empty_section();
let original = "PREFIX[old section]SUFFIX";
s.section_start = "PREFIX".len();
s.section_end = "PREFIX[old section]".len();
let result = s.apply_to_content(original, "[NEW]");
assert_eq!(result, "PREFIX[NEW]SUFFIX");
}
}