import Foundation
import Testing
@testable import IrdAPI
func upload(_ status: String, token: String? = "grant-tok", cid: String? = nil, code: Int = 200) -> HTTPResponse {
json(code, #"{"upload":{"id":"u1","pin_id":"p1","url":"https://node.test/uploads/u1","token":\#(token.map { "\"\($0)\"" } ?? "null"),"max_bytes":1000,"expires_at":null,"status":"\#(status)","cid":\#(cid.map { "\"\($0)\"" } ?? "null"),"size_bytes":null,"error":null}}"#)
}
@Suite struct IpfsFlowTests {
@Test func addFileGrantsPostsBytesAndPolls() async throws {
let (client, http) = captureClient(script: [
upload("issued", code: 201), json(200, #"{"ok":true}"#), upload("issued"), upload("completed", token: nil, cid: "bafy123"),
])
let result = try await client.ipfs.addFile(data: Data("hello".utf8), name: "site")
#expect(result.cid == "bafy123")
#expect(result.pinId == "p1")
#expect(result.status == .completed)
#expect(http.trail == ["POST /ipfs/uploads", "POST /uploads/u1", "GET /ipfs/uploads/u1", "GET /ipfs/uploads/u1"])
#expect(bodyJSON(http.requests[0]) == ["name": "site"])
let post = http.requests[1]
#expect(post.url?.absoluteString == "https://node.test/uploads/u1")
#expect(post.value(forHTTPHeaderField: "Authorization") == "Bearer grant-tok")
#expect(post.value(forHTTPHeaderField: "Content-Type") == "application/octet-stream")
#expect(post.httpBody == Data("hello".utf8))
}
@Test func carFormatAppendsTheQueryAndANilTokenSendsNoBearer() async throws {
let (client, http) = captureClient(script: [upload("issued", token: nil, code: 201), json(200, "{}"), upload("completed", cid: "bafycar")])
_ = try await client.ipfs.addFile(data: Data([1]), format: .car)
#expect(bodyJSON(http.requests[0]) == ["format": "car"])
#expect(http.requests[1].url?.absoluteString == "https://node.test/uploads/u1?format=car")
#expect(http.requests[1].value(forHTTPHeaderField: "Authorization") == nil)
}
@Test func noOptionsMeansNoGrantBody() async throws {
let (client, http) = captureClient(script: [upload("issued", code: 201), json(200, "{}"), upload("completed", cid: "x")])
_ = try await client.ipfs.addFile(data: Data())
#expect(http.requests[0].httpBody == nil)
}
@Test func failedUploadThrowsUploadFailed() async throws {
let (client, _) = captureClient(script: [upload("issued", code: 201), json(200, "{}"), upload("expired")])
let e = await caught { _ = try await client.ipfs.addFile(data: Data([1])) }
guard case .uploadFailed(let u)? = e else { Issue.record("expected uploadFailed, got \(String(describing: e))"); return }
#expect(u.status == .expired)
}
@Test func nodeRejectionSurfacesAsAPIError() async throws {
let (client, _) = captureClient(script: [upload("issued", code: 201), json(413, #"{"error":"message_too_large"}"#)])
let e = await caught { _ = try await client.ipfs.addFile(data: Data([1])) }
#expect(e?.status == 413)
#expect(e?.code == "message_too_large")
}
@Test func addFileFromURLStreamsTheFile() async throws {
let dir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
let file = dir.appendingPathComponent("site.tar")
try Data("archive".utf8).write(to: file)
let (client, http) = captureClient(script: [upload("issued", code: 201), json(200, "{}"), upload("completed", cid: "bafyfile")])
let result = try await client.ipfs.addFile(url: file)
#expect(result.cid == "bafyfile")
#expect(bodyJSON(http.requests[0]) == ["name": "site.tar"])
#expect(http.requests[1].httpBody == Data("archive".utf8))
}
}