package renderer import ( "strings" "testing" "git.flo.fo/FLO/dirmd/internal/config" "git.flo.fo/FLO/dirmd/internal/tree" ) func TestCountLines_empty(t *testing.T) { if got := countLines(""); got != 0 { t.Errorf("countLines(\"\") = %d; want 0", got) } } func TestCountLines_basic(t *testing.T) { tests := []struct { name string input string want int }{ {"one_line_no_newline", "hello", 1}, {"one_line_with_newline", "hello\n", 1}, {"two_lines", "hello\nworld", 2}, {"two_lines_trailing", "hello\nworld\n", 2}, {"three_lines", "a\nb\nc", 3}, {"empty_lines", "a\n\nb", 3}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := countLines(tt.input); got != tt.want { t.Errorf("countLines(%q) = %d; want %d", tt.input, got, tt.want) } }) } } func TestStripTestBlocks_rust(t *testing.T) { tests := []struct { name string input string want string }{ {"no_test_block", "fn main() {}\nfn helper() {}", "fn main() {}\nfn helper() {}"}, {"cfg_test_block", "#[cfg(test)]\nmod tests {\n #[test]\n fn test_one() {}\n}", ""}, {"mixed_content", "pub fn main() {}\n#[cfg(test)]\nmod tests {\n #[test]\n fn t() {}\n}\npub fn end() {}", "pub fn main() {}\npub fn end() {}"}, {"nested_braces", "#[cfg(test)]\nmod tests {\n mod inner {\n #[test]\n fn t() {}\n }\n}", ""}, {"multiple_blocks", "#[cfg(test)]\nmod t1 {}\n#[cfg(test)]\nmod t2 {}", ""}, {"cfg_all_test", "#[cfg(all(test, unix))]\nmod tests {}", ""}, {"cfg_any_test", "#[cfg(any(test, feature = \"foo\"))]\nmod tests {}", ""}, {"empty_after_strip", "#[cfg(test)]\nmod tests {}", ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := stripTestBlocks(tt.input); got != tt.want { t.Errorf("stripTestBlocks(%q)\ngot:\n%s\nwant:\n%s", tt.input, got, tt.want) } }) } } func TestExtToLang(t *testing.T) { tests := []struct { name string ext string want string }{ {"go", ".go", "go"}, {"markdown", ".md", "markdown"}, {"yaml_upper", ".YAML", "yaml"}, {"yaml_yml", ".yml", "yaml"}, {"javascript", ".js", "javascript"}, {"typescript", ".ts", "typescript"}, {"css", ".css", "css"}, {"html", ".html", "html"}, {"json", ".json", "json"}, {"xml", ".xml", "xml"}, {"sql", ".sql", "sql"}, {"bash", ".sh", "bash"}, {"python", ".py", "python"}, {"rust", ".rs", "rust"}, {"c", ".c", "c"}, {"cpp", ".cpp", "cpp"}, {"txt", ".txt", "text"}, {"toml", ".toml", "toml"}, {"unknown", ".xyz", ""}, {"empty", "", ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := extToLang(tt.ext); got != tt.want { t.Errorf("extToLang(%q) = %q; want %q", tt.ext, got, tt.want) } }) } } func TestHasAnyAnnotation_none(t *testing.T) { root := &tree.Node{Name: "root", IsDir: true, Children: map[string]*tree.Node{ "file.go": {Name: "file.go", IsDir: false}, }} if hasAnyAnnotation(root) { t.Error("hasAnyAnnotation should be false for clean tree") } } func TestHasAnyAnnotation_present(t *testing.T) { root := &tree.Node{Name: "root", IsDir: true, Children: map[string]*tree.Node{ ".git": {Name: ".git", IsDir: true, Annotation: "ignored (default)"}, }} if !hasAnyAnnotation(root) { t.Error("hasAnyAnnotation should be true with annotation") } } func TestHasAnyAnnotation_deep(t *testing.T) { root := &tree.Node{Name: "root", IsDir: true, Children: map[string]*tree.Node{ "internal": {Name: "internal", IsDir: true, Children: map[string]*tree.Node{ "deep": {Name: "deep", IsDir: true, Annotation: "tests omitted (--omit-test)"}, }}, }} if !hasAnyAnnotation(root) { t.Error("hasAnyAnnotation should be true for deep annotation") } } func TestFormatFlags_nil(t *testing.T) { if FormatFlags(nil) != "" { t.Error("FormatFlags(nil) should be empty") } } func TestFormatFlags_empty(t *testing.T) { cfg := &config.Config{ MaxDepth: config.DefaultMaxDepth, MaxSize: config.DefaultMaxSize, } if out := FormatFlags(cfg); out != "" { t.Errorf("FormatFlags(default config) = %q; want empty", out) } } func TestFormatFlags_set(t *testing.T) { cfg := &config.Config{ OmitTest: true, OmitMd: true, MaxDepth: 10, } out := FormatFlags(cfg) if !strings.Contains(out, "--omit-test") { t.Error("missing --omit-test") } if !strings.Contains(out, "--omit-md") { t.Error("missing --omit-md") } if !strings.Contains(out, "--max-depth 10") { t.Error("missing --max-depth 10") } } func TestFormatFlags_extra_ignores(t *testing.T) { cfg := &config.Config{ Ignores: append(config.DefaultIgnores, "mycustomdir"), } out := FormatFlags(cfg) if !strings.Contains(out, "mycustomdir") { t.Error("missing custom ignore in output") } if strings.Contains(out, "node_modules") { t.Error("default ignore leaked into Ignored line") } } func TestSetMinus(t *testing.T) { tests := []struct { name string haystack []string defaults []string want []string }{ {"empty", nil, []string{"a"}, nil}, {"all_default", []string{"a", "b"}, []string{"a", "b"}, nil}, {"one_extra", []string{"a", "c"}, []string{"a"}, []string{"c"}}, {"duplicates_deduped", []string{"c", "c", "a"}, []string{"a"}, []string{"c"}}, {"no_defaults", []string{"x"}, nil, []string{"x"}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := setMinus(tt.haystack, tt.defaults) if len(got) != len(tt.want) { t.Errorf("setMinus() length = %d; want %d", len(got), len(tt.want)) return } for i := range got { if got[i] != tt.want[i] { t.Errorf("setMinus()[%d] = %q; want %q", i, got[i], tt.want[i]) } } }) } }