import XCTest import ForgeColor @testable import ForgeGrade final class SerializationTests: XCTestCase { func makeStack() -> GradeStack { var set = CurveSet.identity set.master = Curve(points: [.init(x: 0, y: 0.05), .init(x: 1, y: 0.95)]) return GradeStack(nodes: [ GradeNode(kind: .inputTransform(.arriLogC4AWG4)), GradeNode(kind: .cdl(CDL( slope: SIMD3(1.1, 0.95, 1.02), offset: SIMD3(0.01, -0.005, 0), power: SIMD3(0.98, 1.03, 1.0), saturation: 1.15))), GradeNode(kind: .curves(set)), GradeNode(kind: .saturation(0.9), isEnabled: false), GradeNode(kind: .lut3d(Lut3D.build(size: 2) { $0 * 0.9 })), GradeNode(kind: .outputTransform(.rec709Display)), ]) } // Round trip: encode -> decode -> same stack (ids, kinds, params, enabled flags). func testRoundTrip() throws { let stack = makeStack() let data = try GradeSnapshot.encode(stack) let back = try GradeSnapshot.decode(data) XCTAssertEqual(back.nodes.count, stack.nodes.count) for (a, b) in zip(stack.nodes, back.nodes) { XCTAssertEqual(a.id, b.id) XCTAssertEqual(a.isEnabled, b.isEnabled) } // Behavioral equality: same eval on sample pixels. for px in [SIMD3(0.2, 0.4, 0.6), SIMD3(0.8, 0.1, 0.5)] { let ea = stack.evaluate(px) let eb = back.evaluate(px) XCTAssertEqual(ea.x, eb.x, accuracy: 1e-6) XCTAssertEqual(ea.y, eb.y, accuracy: 1e-6) XCTAssertEqual(ea.z, eb.z, accuracy: 1e-6) } } // Deterministic: re-encoding a decoded stack is byte-identical. func testDeterministicEncoding() throws { let stack = makeStack() let data1 = try GradeSnapshot.encode(stack) let back = try GradeSnapshot.decode(data1) let data2 = try GradeSnapshot.encode(back) XCTAssertEqual(data1, data2) } // Unknown node kind fails loudly, not silently dropped. func testUnknownNodeKindFails() { let json = """ {"nodes":[{"id":"6EC0BD7F-11C0-43DA-975E-2A8AD9EBAE0B","isEnabled":true,"kind":{"type":"warpDrive"}}],"version":1} """ XCTAssertThrowsError(try GradeSnapshot.decode(Data(json.utf8))) } // Version field present; future-proofing. func testVersionField() throws { let data = try GradeSnapshot.encode(GradeStack(nodes: [])) let obj = try JSONSerialization.jsonObject(with: data) as? [String: Any] XCTAssertEqual(obj?["version"] as? Int, 1) } // Wrong version rejected. func testWrongVersionRejected() { let json = #"{"nodes":[],"version":99}"# XCTAssertThrowsError(try GradeSnapshot.decode(Data(json.utf8))) } }