Phase 1: pure-logic tests requiring zero production code changes

27 tests across 4 files, all targeting code that was already testable
the moment the target existed -- no visibility bumps, no refactors:

- GitignoreParserTests: MCPService.GitignoreParser's glob-to-regex
  matching (wildcards, **, directory anchors, negation, comments).
- OpenRouterModelsTests: the string-vs-content-block-array decoders
  on both the request side (APIMessage.MessageContent/ContentItem)
  and response side (Choice.MessageContent, StreamChoice.Delta),
  including image extraction from content blocks.
- AIProviderTests: ChatResponse/Usage decoding, with an explicit
  regression guard that Usage.rawCostUSD always decodes to nil (it's
  only ever set programmatically, never from API JSON).
- MessageCodableTests: confirms Message's transient fields
  (isStreaming, isStarred, generatedImages, toolCalls,
  thinkingContent) don't survive an encode/decode round-trip, and
  documents that its custom == deliberately ignores role/timestamp/
  attachments/modelId -- a real but non-obvious behavior worth
  locking in with a test.

Removed the placeholder oAITests.swift example test now that there's
real coverage. Phase 1 of the test-suite rollout plan
(peaceful-baking-kurzweil).
This commit is contained in:
2026-07-23 13:37:49 +02:00
parent 8c7fb59d41
commit 1f4a2caf67
5 changed files with 389 additions and 18 deletions
+83
View File
@@ -0,0 +1,83 @@
//
// 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"))
}
}