import Foundation import Testing @testable import gitbay private func makeClient() throws -> (GitbayClient, StubProtocol.Box) { let box = StubProtocol.box() let client = GitbayClient( instance: try GitbayInstance(url: "https://gitbay.org"), token: "test-token", session: box.session() ) return (client, box) } private func argv(_ url: URL) -> [String] { URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.compactMap(\.value) ?? [] } struct DefinitionLinkTests { private let defs: [String: Definition] = [ "Parse": Definition(name: "Parse", count: 1, path: "lib.go", line: 3), "Run": Definition(name: "Run", count: 2, path: "lib.go", line: 4), ] @Test func definedNamesLinkAndOthersStayPlain() { let line = Definition.link(AttributedString("\tParse(); Parser(); Run(x)"), definitions: defs) let linked = line.runs.compactMap { run -> String? in run.link.map { _ in String(line[run.range].characters) } } // Parser is a different name, not Parse with a tail. #expect(linked == ["Parse", "Run"]) #expect(line.runs.compactMap(\.link).map { Definition.name(in: $0) } == ["Parse", "Run"]) } @Test func aLineWithNoDefinitionsIsUnchanged() { let text = AttributedString("// nothing here") #expect(Definition.link(text, definitions: defs) == text) #expect(Definition.link(AttributedString("Parse"), definitions: [:]) == AttributedString("Parse")) } @Test func aDefinitionURLIsOnlyOurs() { #expect(Definition.name(in: URL(string: "https://gitbay.org/x")!) == nil) } } @MainActor struct FileDefinitionsTests { @Test func theFileReadsTheDefinitionsItUses() async throws { let (client, stub) = try makeClient() stub.enqueue(.init(status: 200, json: """ {"protocol_version":1,"data":{"path":"krz/app","ref":"main","file":"use.go","size":10,\ "binary":false,"content":"Parse()\\n"},"exit_code":0} """, match: "argv=cat")) stub.enqueue(.init(status: 200, json: """ {"protocol_version":1,"data":[{"name":"Parse","count":1,"path":"lib.go","line":3}],"exit_code":0} """, match: "argv=definitions")) let model = FileViewModel(client: client, repoPath: "krz/app", filePath: "use.go", ref: "main") await model.load() let read = try #require(stub.seen.first { $0.url.absoluteString.contains("argv=definitions") }) #expect(argv(read.url) == ["repo", "definitions", "krz/app", "use.go", "--ref", "main"]) #expect(model.definitions["Parse"]?.line == 3) } /// A server without the command, or a ref that is not indexed, leaves /// the file plain rather than failing it. @Test func noDefinitionsLeaveTheFileReadable() async throws { let (client, stub) = try makeClient() stub.enqueue(.init(status: 200, json: """ {"protocol_version":1,"data":{"path":"krz/app","ref":"dev","file":"use.go","size":10,\ "binary":false,"content":"Parse()\\n"},"exit_code":0} """, match: "argv=cat")) stub.enqueue(.init(status: 404, json: #"{"protocol_version":1,"error":"only the default branch is indexed","exit_code":3}"#, match: "argv=definitions")) let model = FileViewModel(client: client, repoPath: "krz/app", filePath: "use.go", ref: "dev") await model.load() #expect(model.state.value?.content == "Parse()\n") #expect(model.definitions.isEmpty) } @Test func proseFilesAreNotLinked() async throws { let (client, stub) = try makeClient() stub.enqueue(.init(status: 200, json: """ {"protocol_version":1,"data":{"path":"krz/app","ref":"main","file":"README.md","size":10,\ "binary":false,"content":"# Parse\\n"},"exit_code":0} """, match: "argv=cat")) let model = FileViewModel(client: client, repoPath: "krz/app", filePath: "README.md", ref: "main") await model.load() #expect(model.state.value != nil) #expect(!stub.seen.contains { $0.url.absoluteString.contains("argv=definitions") }) } }