Osiris/Tests/OsirisTests/CodableTests.swift
2025-06-15 08:45:02 -07:00

273 lines
No EOL
9.4 KiB
Swift

//
// CodableTests.swift
// OsirisTests
//
// Created by Sami Samhuri on 2025-06-15.
//
@testable import Osiris
import XCTest
class CodableTests: XCTestCase {
let baseURL = URL(string: "https://api.example.net")!
// Test models
struct Person: Codable, Sendable, Equatable {
let name: String
let email: String
let age: Int?
}
struct APIResponse: Codable, Sendable, Equatable {
let success: Bool
let data: Person?
let message: String?
}
// MARK: - HTTPRequest Codable Body Tests
func testHTTPRequestWithCodableBody() throws {
let person = Person(name: "Jane Doe", email: "jane@example.net", age: 30)
let request = HTTPRequest(method: .post, url: baseURL, body: person)
XCTAssertEqual(request.method, .post)
XCTAssertEqual(request.url, baseURL)
XCTAssertNotNil(request.body)
XCTAssertNil(request.parameters)
}
func testPostJSONConvenience() throws {
let person = Person(name: "John Doe", email: "john@example.net", age: 25)
let request = HTTPRequest.postJSON(baseURL, body: person)
XCTAssertEqual(request.method, .post)
XCTAssertEqual(request.url, baseURL)
XCTAssertEqual(request.contentType, .json)
XCTAssertNotNil(request.body)
XCTAssertNil(request.parameters)
}
func testPutJSONConvenience() throws {
let person = Person(name: "John Doe", email: "john@example.net", age: 26)
let request = HTTPRequest.putJSON(baseURL, body: person)
XCTAssertEqual(request.method, .put)
XCTAssertEqual(request.url, baseURL)
XCTAssertEqual(request.contentType, .json)
XCTAssertNotNil(request.body)
XCTAssertNil(request.parameters)
}
// MARK: - RequestBuilder Codable Encoding Tests
func testRequestBuilderWithCodableBody() throws {
let person = Person(name: "Jane Doe", email: "jane@example.net", age: 30)
let httpRequest = HTTPRequest.postJSON(baseURL, body: person)
let urlRequest = try RequestBuilder.build(request: httpRequest)
XCTAssertEqual(urlRequest.url, baseURL)
XCTAssertEqual(urlRequest.httpMethod, "POST")
XCTAssertEqual(urlRequest.value(forHTTPHeaderField: "Content-Type"), "application/json")
XCTAssertNotNil(urlRequest.httpBody)
// Verify the JSON was encoded correctly
let decodedPerson = try JSONDecoder().decode(Person.self, from: urlRequest.httpBody!)
XCTAssertEqual(decodedPerson, person)
}
func testRequestBuilderPrefersCodableOverParameters() throws {
let person = Person(name: "Jane Doe", email: "jane@example.net", age: 30)
let params = ["name": "Different Name", "email": "different@example.net"]
let httpRequest = HTTPRequest(
method: .post,
url: baseURL,
contentType: .json,
parameters: params,
body: person
)
let urlRequest = try RequestBuilder.build(request: httpRequest)
// Should use the Codable body, not the parameters
let decodedPerson = try JSONDecoder().decode(Person.self, from: urlRequest.httpBody!)
XCTAssertEqual(decodedPerson, person)
XCTAssertEqual(decodedPerson.name, "Jane Doe") // Not "Different Name"
}
// MARK: - HTTPResponse Decoding Tests
func testHTTPResponseDecodeSuccess() throws {
let person = Person(name: "Jane Doe", email: "jane@example.net", age: 30)
let jsonData = try JSONEncoder().encode(person)
let httpURLResponse = HTTPURLResponse(
url: baseURL,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/json"]
)!
let response = HTTPResponse.success(httpURLResponse, jsonData)
let decodedPerson = try response.decode(Person.self)
XCTAssertEqual(decodedPerson, person)
}
func testHTTPResponseDecodeFailureCase() throws {
let person = Person(name: "Jane Doe", email: "jane@example.net", age: 30)
let jsonData = try JSONEncoder().encode(person)
let httpURLResponse = HTTPURLResponse(
url: baseURL,
statusCode: 400,
httpVersion: nil,
headerFields: ["Content-Type": "application/json"]
)!
let response = HTTPResponse.failure(HTTPRequestError.http, httpURLResponse, jsonData)
// Should still be able to decode data from failure responses
let decodedPerson = try response.decode(Person.self)
XCTAssertEqual(decodedPerson, person)
}
func testHTTPResponseDecodeNoData() throws {
let httpURLResponse = HTTPURLResponse(
url: baseURL,
statusCode: 200,
httpVersion: nil,
headerFields: nil
)!
let response = HTTPResponse.success(httpURLResponse, nil)
XCTAssertThrowsError(try response.decode(Person.self)) { error in
XCTAssertTrue(error is DecodingError)
if case DecodingError.dataCorrupted(let context) = error {
XCTAssertEqual(context.debugDescription, "No data found in response")
} else {
XCTFail("Expected DecodingError.dataCorrupted")
}
}
}
func testHTTPResponseDecodeInvalidJSON() throws {
let invalidJSON = "{ invalid json }".data(using: .utf8)!
let httpURLResponse = HTTPURLResponse(
url: baseURL,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/json"]
)!
let response = HTTPResponse.success(httpURLResponse, invalidJSON)
XCTAssertThrowsError(try response.decode(Person.self)) { error in
XCTAssertTrue(error is DecodingError)
}
}
func testHTTPResponseTryDecodeSuccess() throws {
let person = Person(name: "Jane Doe", email: "jane@example.net", age: 30)
let jsonData = try JSONEncoder().encode(person)
let httpURLResponse = HTTPURLResponse(
url: baseURL,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/json"]
)!
let response = HTTPResponse.success(httpURLResponse, jsonData)
let decodedPerson = response.tryDecode(Person.self)
XCTAssertNotNil(decodedPerson)
XCTAssertEqual(decodedPerson, person)
}
func testHTTPResponseTryDecodeFailure() throws {
let invalidJSON = "{ invalid json }".data(using: .utf8)!
let httpURLResponse = HTTPURLResponse(
url: baseURL,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/json"]
)!
let response = HTTPResponse.success(httpURLResponse, invalidJSON)
let decodedPerson = response.tryDecode(Person.self)
XCTAssertNil(decodedPerson)
}
func testHTTPResponseTryDecodeNoData() throws {
let httpURLResponse = HTTPURLResponse(
url: baseURL,
statusCode: 200,
httpVersion: nil,
headerFields: nil
)!
let response = HTTPResponse.success(httpURLResponse, nil)
let decodedPerson = response.tryDecode(Person.self)
XCTAssertNil(decodedPerson)
}
// MARK: - Custom JSONDecoder Tests
func testHTTPResponseDecodeWithCustomDecoder() throws {
// Create a Person with an ISO8601 date as a string (for testing custom decoder)
let apiResponse = APIResponse(
success: true,
data: Person(name: "Jane Doe", email: "jane@example.net", age: 30),
message: "Success"
)
let jsonData = try JSONEncoder().encode(apiResponse)
let httpURLResponse = HTTPURLResponse(
url: baseURL,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/json"]
)!
let response = HTTPResponse.success(httpURLResponse, jsonData)
// Test with custom decoder (just using default for this test)
let customDecoder = JSONDecoder()
let decodedResponse = try response.decode(APIResponse.self, using: customDecoder)
XCTAssertEqual(decodedResponse, apiResponse)
}
// MARK: - Integration Tests
func testEndToEndCodableFlow() throws {
// Create request with Codable body
let person = Person(name: "Jane Doe", email: "jane@example.net", age: 30)
let httpRequest = HTTPRequest.postJSON(baseURL, body: person)
// Build URLRequest
let urlRequest = try RequestBuilder.build(request: httpRequest)
// Simulate successful response with the same data
let responseData = urlRequest.httpBody!
let httpURLResponse = HTTPURLResponse(
url: baseURL,
statusCode: 201,
httpVersion: nil,
headerFields: ["Content-Type": "application/json"]
)!
let httpResponse = HTTPResponse.success(httpURLResponse, responseData)
// Decode response
let decodedPerson = try httpResponse.decode(Person.self)
XCTAssertEqual(decodedPerson, person)
}
}