use std::collections::HashMap; use std::io::Write; use std::path::Path; use super::*; use crate::launch_configs::launch_config::PaneTemplateType; use crate::tab_configs::render_tab_config; use crate::tab_configs::tab_config::{generated_worktree_repo_dir, TabConfigPaneType}; #[cfg(feature = "local_fs")] #[test] fn test_default_tab_configs_dir_uses_underscores() { assert!(default_tab_configs_dir().ends_with("default_tab_configs")); } #[cfg(feature = "local_fs")] #[test] fn test_is_tab_config_toml_matches_user_tab_configs() { let path = tab_configs_dir().join("my_tab_config.toml"); assert!(is_tab_config_toml(&path)); } #[cfg(feature = "local_fs")] #[test] fn test_is_tab_config_toml_matches_default_tab_configs() { let path = default_tab_configs_dir().join("worktree.toml"); assert!(is_tab_config_toml(&path)); } #[cfg(feature = "local_fs")] #[test] fn test_is_tab_config_toml_rejects_non_toml_paths() { let path = tab_configs_dir().join("my_tab_config.yaml"); assert!(!is_tab_config_toml(&path)); } #[cfg(feature = "local_fs")] #[test] fn test_is_tab_config_toml_rejects_tomls_outside_tab_config_dirs() { let path = launch_configs_dir().join("workspace.toml"); assert!(!is_tab_config_toml(&path)); } #[cfg(feature = "local_fs")] #[test] fn test_materialize_default_worktree_config_bakes_repo_and_pane_type_only() { let template = include_str!("../../resources/tab_configs/default_worktree.toml"); let repo_path = "/tmp/example-repo"; let (toml_content, tab_config) = materialize_default_worktree_config(template, "Worktree: example-repo", repo_path, "agent") .expect("expected template materialization to succeed"); assert!(toml_content.contains("name = \"Worktree: example-repo\"")); assert!(toml_content.contains(repo_path)); assert!(toml_content.contains("type = \"agent\"")); assert!(toml_content.contains("{{autogenerated_branch_name}}")); assert!(toml_content.contains( &generated_worktree_repo_dir(Path::new(repo_path)) .display() .to_string() )); assert!(!toml_content.contains("{{repo}}")); assert!(!toml_content.contains("{{pane_type}}")); assert!(!toml_content.contains("{{worktree_path_prefix}}")); assert!(!toml_content.contains("[params.repo]")); assert!(!toml_content.contains("[params.pane_type]")); assert!(tab_config.params.is_empty()); assert_eq!(tab_config.name, "Worktree: example-repo"); assert_eq!(tab_config.panes.len(), 1); assert_eq!(tab_config.panes[0].directory.as_deref(), Some(repo_path)); assert_eq!( tab_config.panes[0].pane_type, Some(TabConfigPaneType::Agent) ); } #[cfg(feature = "local_fs")] #[test] fn test_materialized_default_worktree_config_renders_full_worktree_path() { let template = include_str!("../../resources/tab_configs/default_worktree.toml"); let repo_path = "/tmp/example-repo"; let (_, tab_config) = materialize_default_worktree_config(template, "Worktree: example-repo", repo_path, "agent") .expect("expected template materialization to succeed"); let (_, pane_template) = render_tab_config(&tab_config, &HashMap::new(), Some("my-feature")); if let PaneTemplateType::PaneTemplate { commands, .. } = pane_template { let expected_worktree_path = generated_worktree_repo_dir(Path::new(repo_path)) .join("my-feature") .display() .to_string(); assert_eq!(commands.len(), 2); assert_eq!( commands[0].exec, format!("git worktree add -b my-feature {expected_worktree_path}") ); assert_eq!(commands[1].exec, format!("cd {expected_worktree_path}")); } else { panic!("expected terminal pane template"); } } #[cfg(feature = "local_fs")] #[test] fn test_sanitize_toml_base_name_replaces_spaces_and_dots() { assert_eq!(sanitize_toml_base_name("My Project.v2"), "my_project_v2"); } #[cfg(feature = "local_fs")] #[test] fn test_sanitize_toml_base_name_falls_back_for_empty_result() { assert_eq!(sanitize_toml_base_name("..."), "worktree"); } fn write_tab_config_toml(dir: &Path, file_name: &str, config_name: &str) { let path = dir.join(file_name); let mut f = std::fs::File::create(path).unwrap(); write!(f, "name = \"{}\"", config_name).unwrap(); } #[cfg(feature = "local_fs")] #[test] fn test_load_tab_configs_sorts_case_insensitive() { let dir = tempfile::tempdir().unwrap(); write_tab_config_toml(dir.path(), "zebra.toml", "Zebra"); write_tab_config_toml(dir.path(), "alpha.toml", "alpha"); write_tab_config_toml(dir.path(), "beta.toml", "Beta"); let (configs, errors) = load_tab_configs(dir.path()); assert!(errors.is_empty()); let names: Vec<&str> = configs.iter().map(|c| c.name.as_str()).collect(); assert_eq!(names, vec!["alpha", "Beta", "Zebra"]); } #[cfg(feature = "local_fs")] #[test] fn test_load_tab_configs_deterministic_tie_breaking() { let dir = tempfile::tempdir().unwrap(); write_tab_config_toml(dir.path(), "upper.toml", "Alpha"); write_tab_config_toml(dir.path(), "lower.toml", "alpha"); let (configs, errors) = load_tab_configs(dir.path()); assert!(errors.is_empty()); let names: Vec<&str> = configs.iter().map(|c| c.name.as_str()).collect(); assert_eq!(names, vec!["Alpha", "alpha"]); } #[cfg(feature = "local_fs")] #[test] fn test_load_tab_configs_empty_directory() { let dir = tempfile::tempdir().unwrap(); let (configs, errors) = load_tab_configs(dir.path()); assert!(configs.is_empty()); assert!(errors.is_empty()); } #[cfg(feature = "local_fs")] #[test] fn test_load_tab_configs_skips_non_toml_files() { let dir = tempfile::tempdir().unwrap(); write_tab_config_toml(dir.path(), "real.toml", "Real"); std::fs::write(dir.path().join("readme.md"), "not a config").unwrap(); std::fs::write(dir.path().join("data.json"), "{}").unwrap(); let (configs, errors) = load_tab_configs(dir.path()); assert!(errors.is_empty()); assert_eq!(configs.len(), 1); assert_eq!(configs[0].name, "Real"); }