open-standards/wai/wai-sink-swift/Tests/WaiSinkTests/CorpusTests.swift
Transaction Science aa607a60a5 wai-sink-swift: TestHost, an application the tests run inside on a phone or tablet
A device runs no tool-hosted test bundle, so the package's tests run on one hosted in
TestHost/: a minimal application (an empty window) and an Xcode project whose unit-test
target compiles the same test files against this package by its path, with the corpus in
its resources and local networking allowed for the loopback origin and relay. The package
itself is unchanged for consumers. The tests import WaiSink plainly (they use only its
public interface) and read the corpus from Bundle.module in the package, from their own
bundle in the host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 09:59:34 -04:00

79 lines
4.3 KiB
Swift

// The Swift wrapper gives every call of the native sink corpus the answer the corpus states
// (native-sink §10), through the same C interface every native integration links.
import XCTest
import WaiSink
final class CorpusTests: XCTestCase {
func testEveryCaseGetsItsAnswers() throws {
let doc = try corpus()
let objects = doc["objects"] as! [String: String]
func body(_ v: Any?) -> Data? { (v as? String).map { hexData(objects[$0]!) } }
let cases = doc["cases"] as! [[String: Any]]
XCTAssertGreaterThan(cases.count, 10)
var calls = 0
for c in cases {
let name = c["name"] as! String
let sink = try WaiSink(configJSON: c["config"] as! [String: Any], seed: hexData(c["seed"] as! String), startUs: (c["start_us"] as! NSNumber).uint64Value)
for (i, call) in (c["calls"] as! [[String: Any]]).enumerated() {
let d = call["do"] as! [String: Any]
let want = call["answer"]!
let got: Any
do {
switch d["call"] as! String {
case "playlist":
got = try sink.playlist(url: d["url"] as! String, bytes: body(d["body"])!, receipt: body(d["receipt"]))
case "parent":
try sink.playlistParent(url: d["url"] as! String, name: d["name"] as! String, bytes: body(d["body"]))
got = ["ok": true]
case "object":
got = try sink.object(meta: d["meta"] as! [String: Any], bytes: body(d["body"])!)
case "event":
try sink.event(d["name"] as! String, atUs: (d["t_us"] as! NSNumber).uint64Value)
got = ["ok": true]
case "observe":
let e = try sink.observe(d["obs"] as! [String: Any])
got = ["end": e?.rawValue ?? NSNull()] as [String: Any]
case "energy":
try sink.energy(d["entry"] as! [String: Any])
got = ["ok": true]
case "attach":
try sink.attach(name: d["name"] as! String, bytes: body(d["body"])!)
got = ["ok": true]
case "cmcd":
got = ["cmcd": try sink.takeCMCD()]
case "end":
let b = try sink.end(WaiSink.Ended(rawValue: d["ended"] as! String)!, atUs: (d["t_us"] as! NSNumber).uint64Value)
got = ["bundle": String(decoding: b, as: UTF8.self)]
default:
XCTFail("\(name): an unknown call"); continue
}
} catch let e as WaiSinkError {
got = ["error": e.reason]
}
XCTAssertEqual(canonical(got), canonical(want), "\(name): call \(i)")
calls += 1
}
}
print("WAI-SINK corpus: \(cases.count) cases, \(calls) calls through the C interface, ABI \(WaiSink.abiVersion)")
}
func testABundleVerifiesAsTheCollectorVerifiesIt() throws {
let doc = try corpus()
let c = (doc["cases"] as! [[String: Any]]).first { $0["name"] as? String == "signed-vod" }!
let end = (c["calls"] as! [[String: Any]]).compactMap { ($0["answer"] as? [String: Any])?["bundle"] as? String }.first!
let out = try WaiSink.verifyBundle(Data(end.utf8), owner: hexData(doc["owner"] as! String), channels: doc["channels"] as! [String])
XCTAssertEqual(canonical(out), canonical(c["outcome"]!))
let other = try WaiSink.verifyBundle(Data(end.utf8), owner: Data(repeating: 7, count: 32), channels: doc["channels"] as! [String])
XCTAssertEqual(other["reason"] as? String, "audit_wrong_owner")
}
func testAFreshKeyPerSession() throws {
let cfg = WaiSink.Config(channel: "film-library", owner: String(repeating: "0", count: 64))
let a = try WaiSink(config: cfg).info()["player"] as! String
let b = try WaiSink(config: cfg).info()["player"] as! String
XCTAssertNotEqual(a, b)
XCTAssertThrowsError(try WaiSink(configJSON: ["channel": "film library", "owner": String(repeating: "0", count: 64)])) { e in
XCTAssertEqual((e as? WaiSinkError)?.reason, "native_config_invalid")
}
}
}