diff --git a/Tests/KumoTests/Fixtures/XML/XMLRoundtripTests.swift b/Tests/KumoTests/Fixtures/XML/XMLRoundtripTests.swift
new file mode 100644
index 0000000..7ac2059
--- /dev/null
+++ b/Tests/KumoTests/Fixtures/XML/XMLRoundtripTests.swift
@@ -0,0 +1,219 @@
+import Foundation
+import XCTest
+@testable import Kumo
+@testable import KumoCoding
+
+/// Tests that verify XMLEncoder and XMLDecoder work together for common
+/// XML patterns: flat structures, nested objects, SOAP envelopes, and
+/// PascalCase key conversion — the typical patterns used by enterprise
+/// healthcare XML services.
+class XMLRoundtripTests: XCTestCase {
+
+ // MARK: - Models (generic, not tied to any specific vendor)
+
+ struct PatientRecord: Codable, Equatable {
+ let recordId: String
+ let firstName: String
+ let lastName: String
+ let dateOfBirth: String
+ let gender: String
+ }
+
+ struct Appointment: Codable, Equatable {
+ let appointmentId: String
+ let recordId: String
+ let provider: String
+ let dateTime: String
+ let status: String
+ }
+
+ struct ResponseEnvelope: Codable, Equatable {
+ let statusCode: String
+ let message: String
+ let patient: PatientRecord
+ }
+
+ // MARK: - Plain XML Encode → Decode Roundtrip
+
+ func testFlatStructureRoundtrip() {
+ let encoder = XMLEncoder()
+ encoder.keyEncodingStrategy = .convertToPascalCase
+ encoder.userInfo[.rootNamespace] = XMLNamespace(prefix: "", uri: "urn:test:record:v1")
+
+ let decoder = XMLDecoder()
+ decoder.keyDecodingStrategy = .convertFromPascalCase
+
+ let original = PatientRecord(
+ recordId: "R12345",
+ firstName: "Jane",
+ lastName: "Smith",
+ dateOfBirth: "1985-12-25",
+ gender: "F"
+ )
+
+ do {
+ let data = try encoder.encode(original)
+ let decoded = try decoder.decode(PatientRecord.self, from: data)
+ XCTAssertEqual(original, decoded)
+ } catch {
+ XCTFail("Flat structure roundtrip failed: \(error)")
+ }
+ }
+
+ func testNestedStructureRoundtrip() {
+ let encoder = XMLEncoder()
+ encoder.keyEncodingStrategy = .convertToPascalCase
+ encoder.userInfo[.rootNamespace] = XMLNamespace(prefix: "", uri: "urn:test:response:v1")
+
+ let decoder = XMLDecoder()
+ decoder.keyDecodingStrategy = .convertFromPascalCase
+
+ let original = ResponseEnvelope(
+ statusCode: "200",
+ message: "OK",
+ patient: PatientRecord(
+ recordId: "R99999",
+ firstName: "John",
+ lastName: "Doe",
+ dateOfBirth: "1990-01-15",
+ gender: "M"
+ )
+ )
+
+ do {
+ let data = try encoder.encode(original)
+ let decoded = try decoder.decode(ResponseEnvelope.self, from: data)
+ XCTAssertEqual(original, decoded)
+ } catch {
+ XCTFail("Nested structure roundtrip failed: \(error)")
+ }
+ }
+
+ // MARK: - Plain XML Decoding (no SOAP)
+
+ func testDecodingFlatXML() {
+ let decoder = XMLDecoder()
+ decoder.keyDecodingStrategy = .convertFromPascalCase
+
+ let xml = """
+
+ R12345
+ Jane
+ Smith
+ 1985-12-25
+ F
+
+ """.data(using: .utf8)!
+
+ do {
+ let record = try decoder.decode(PatientRecord.self, from: xml)
+ XCTAssertEqual(record.recordId, "R12345")
+ XCTAssertEqual(record.firstName, "Jane")
+ XCTAssertEqual(record.lastName, "Smith")
+ XCTAssertEqual(record.dateOfBirth, "1985-12-25")
+ XCTAssertEqual(record.gender, "F")
+ } catch {
+ XCTFail("Flat XML decoding failed: \(error)")
+ }
+ }
+
+ func testDecodingNestedXML() {
+ let decoder = XMLDecoder()
+ decoder.keyDecodingStrategy = .convertFromPascalCase
+
+ let xml = """
+
+ 200
+ OK
+
+ R12345
+ Jane
+ Smith
+ 1985-12-25
+ F
+
+
+ """.data(using: .utf8)!
+
+ do {
+ let response = try decoder.decode(ResponseEnvelope.self, from: xml)
+ XCTAssertEqual(response.statusCode, "200")
+ XCTAssertEqual(response.message, "OK")
+ XCTAssertEqual(response.patient.recordId, "R12345")
+ XCTAssertEqual(response.patient.firstName, "Jane")
+ } catch {
+ XCTFail("Nested XML decoding failed: \(error)")
+ }
+ }
+
+ // MARK: - SOAP Roundtrip
+
+ func testSOAPRoundtrip() {
+ let encoder = SOAPEncoder()
+ encoder.keyEncodingStrategy = .convertToPascalCase
+ encoder.soapNamespaceUsage = .define(
+ using: XMLNamespace(prefix: "soap", uri: "http://www.w3.org/2003/05/soap-envelope/"),
+ including: []
+ )
+ encoder.requestPayloadNamespaceUsage = .defineBeneath(
+ XMLNamespace(prefix: "ns", uri: "urn:test:record:v1")
+ )
+
+ let original = PatientRecord(
+ recordId: "R77777",
+ firstName: "Alice",
+ lastName: "Wong",
+ dateOfBirth: "2000-06-15",
+ gender: "F"
+ )
+
+ do {
+ let data = try encoder.encode(original)
+ let xmlString = String(data: data, encoding: .utf8)!
+
+ // Verify SOAP structure
+ XCTAssertTrue(xmlString.contains("soap:Envelope"))
+ XCTAssertTrue(xmlString.contains("soap:Body"))
+ XCTAssertTrue(xmlString.contains("ns:PatientRecord"))
+ XCTAssertTrue(xmlString.contains("ns:RecordId"))
+ XCTAssertTrue(xmlString.contains(">R77777<"))
+
+ // Decode back via SOAPDecoder
+ let decoder = SOAPDecoder()
+ decoder.keyDecodingStrategy = .convertFromPascalCase
+ let decoded: PatientRecord = try decoder.decode(from: data)
+ XCTAssertEqual(original, decoded)
+ } catch {
+ XCTFail("SOAP roundtrip failed: \(error)")
+ }
+ }
+
+ // MARK: - Encoding Verification
+
+ func testEncodingProducesExpectedXML() {
+ let encoder = XMLEncoder()
+ encoder.keyEncodingStrategy = .convertToPascalCase
+ encoder.userInfo[.rootNamespace] = XMLNamespace(prefix: "", uri: "urn:test:appt:v1")
+
+ let appointment = Appointment(
+ appointmentId: "A001",
+ recordId: "R12345",
+ provider: "Dr. Smith",
+ dateTime: "2026-04-01T10:30:00",
+ status: "Confirmed"
+ )
+
+ do {
+ let data = try encoder.encode(appointment)
+ let xml = String(data: data, encoding: .utf8)!
+ XCTAssertTrue(xml.contains("A001"))
+ XCTAssertTrue(xml.contains("R12345"))
+ XCTAssertTrue(xml.contains("Dr. Smith"))
+ XCTAssertTrue(xml.contains("2026-04-01T10:30:00"))
+ XCTAssertTrue(xml.contains("Confirmed"))
+ XCTAssertTrue(xml.contains("xmlns=\"urn:test:appt:v1\""))
+ } catch {
+ XCTFail("Encoding failed: \(error)")
+ }
+ }
+}