// // GitignoreParserTests.swift // oAITests // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen import Testing @testable import oAI @Suite("GitignoreParser") struct GitignoreParserTests { private func parser(for content: String) -> GitignoreParser { var parser = GitignoreParser(rootPath: "/tmp/fake-root") parser.parseContent(content) return parser } @Test("Simple extension wildcard matches at any depth") func extensionWildcard() { let p = parser(for: "*.log") #expect(p.isIgnored("debug.log")) #expect(p.isIgnored("nested/debug.log")) #expect(!p.isIgnored("debug.txt")) } @Test("Directory pattern matches the directory and everything under it") func directoryPattern() { let p = parser(for: "build/") #expect(p.isIgnored("build")) #expect(p.isIgnored("build/output.txt")) #expect(p.isIgnored("foo/build/output.txt")) #expect(!p.isIgnored("rebuild")) } @Test("Path-anchored pattern only matches that exact relative path, not nested occurrences") func pathAnchoredWildcard() { let p = parser(for: "src/*.tmp") #expect(p.isIgnored("src/foo.tmp")) #expect(!p.isIgnored("other/src/foo.tmp")) #expect(!p.isIgnored("src/sub/foo.tmp")) } @Test("Double-star matches across any number of intermediate directories") func doubleStarPattern() { let p = parser(for: "**/logs") #expect(p.isIgnored("logs")) #expect(p.isIgnored("a/logs")) #expect(p.isIgnored("a/b/logs")) #expect(p.isIgnored("a/logs/file.txt")) #expect(!p.isIgnored("logsfile")) } @Test("Negation un-ignores a path matched by an earlier rule") func negation() { let p = parser(for: "*.log\n!important.log") #expect(p.isIgnored("debug.log")) #expect(!p.isIgnored("important.log")) } @Test("Comments and blank lines are skipped, not treated as patterns") func commentsAndBlankLines() { let p = parser(for: "# a comment\n\n*.log\n \n") #expect(p.isIgnored("x.log")) #expect(!p.isIgnored("# a comment")) } @Test("Leading slash still matches (root-anchoring is not enforced by this implementation)") func leadingSlashPattern() { // Documents actual current behavior: the leading "/" is stripped without // truly restricting the match to the root level, unlike real git. let p = parser(for: "/dist") #expect(p.isIgnored("dist")) #expect(p.isIgnored("dist/file.txt")) } @Test("No rules means nothing is ignored") func noRules() { let p = parser(for: "") #expect(!p.isIgnored("anything.txt")) } }