apple_kit

Apple-native services for Radroots iOS and macOS apps
git clone https://radroots.dev/git/apple_kit.git
Log | Files | Refs | README | LICENSE

RadrootsNativeResponseTests.swift (3631B)


      1 #if os(iOS) && targetEnvironment(simulator)
      2     import Foundation
      3     @testable import RadrootsKit
      4     import Testing
      5 
      6     @Test(arguments: ["valid", "declared", "chunked", "missing", "status", "type", "shape", "encoded"])
      7     func nativeResponseEnforcesActualTransportBounds(_ name: String) async throws {
      8         let server = try NativeRedirectServer()
      9         defer { server.stop() }
     10         let port = try await server.start()
     11         server.respond(with: nativeResponseWire(name))
     12         let roots = try appleTransferRoots()
     13         defer { try? FileManager.default.removeItem(at: roots.dataRoot.deletingLastPathComponent()) }
     14         let source = RadrootsFileReference(scope: .cache, relativePath: "response_body")
     15         try RadrootsAppleFileAccess(roots: roots).write(.inline(Data("isolated fixture body".utf8)), to: source)
     16         let store = RadrootsAppleBackgroundTransferStore(roots: roots)
     17         let adapters = try RadrootsAppleBackgroundTransferAdapters.live(
     18             sessionIdentifier: "org.radroots.response.\(UUID().uuidString.lowercased())", store: store,
     19             fileResolver: RadrootsAppleBackgroundTransferFileResolver(roots: roots),
     20             downloadStagingRoot: roots.temporaryRoot
     21         )
     22         let transfer = RadrootsAppleBackgroundTransfer(store: store, adapters: adapters)
     23         let request = try RadrootsBackgroundTransferRequest(
     24             identifier: RadrootsBackgroundTransferIdentifier("response.\(name)"),
     25             remoteURL: #require(URL(string: "http://127.0.0.1:\(port)/upload")), method: .put,
     26             operation: .upload(source: .file(source)), networkPolicy: .simulatorLoopbackHTTP,
     27             responsePolicy: .boundedJSON(maximumBodyBytes: 32)
     28         )
     29         _ = try await transfer.enqueue(request)
     30         let deadline = Date().addingTimeInterval(15)
     31         var snapshot = try await store.loadSnapshots().first
     32         while snapshot?.state == .queued || snapshot?.state == .running {
     33             guard Date() < deadline else { throw RadrootsBackgroundTransferError.transferFailure }
     34             try await Task.sleep(for: .milliseconds(25))
     35             snapshot = try await store.loadSnapshots().first
     36         }
     37         let failures: [String: RadrootsBackgroundTransferFailure] = [
     38             "declared": .responseTooLarge, "chunked": .responseTooLarge, "missing": .responseMissing,
     39             "status": .httpStatus, "type": .responseMediaType, "shape": .responseInvalid,
     40             "encoded": .responseContentEncoding
     41         ]
     42         #expect(snapshot?.state == (name == "valid" ? .awaitingVerification : .failed))
     43         #expect(snapshot?.failure == failures[name])
     44         #expect(server.requestCount == 1)
     45         if name == "valid" {
     46             #expect(snapshot?.response?.body == Data("{}".utf8))
     47         }
     48     }
     49 
     50     private func nativeResponseWire(_ name: String) -> Data {
     51         let status = name == "status" ? "503 Unavailable" : "200 OK"
     52         let type = name == "type" ? "image/jpeg" : "application/json"
     53         var headers = "HTTP/1.1 \(status)\r\nContent-Type: \(type)\r\nConnection: close\r\n"
     54         if name == "encoded" {
     55             headers += "Content-Encoding: gzip\r\n"
     56         }
     57         if name == "chunked" {
     58             return Data((headers + "Transfer-Encoding: chunked\r\n\r\n20\r\n" + String(repeating: "x", count: 32)
     59                     + "\r\n1\r\nx\r\n0\r\n\r\n").utf8)
     60         }
     61         let body = name == "missing" ? "" : name == "shape" ? "[" : "{}"
     62         let length = name == "declared" ? 1024 : body.utf8.count
     63         return Data((headers + "Content-Length: \(length)\r\n\r\n" + body).utf8)
     64     }
     65 #endif