// 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") } } }