Skip to content

Commit

Permalink
feat: Codable support (#19)
Browse files Browse the repository at this point in the history
Co-authored-by: danthorpe <[email protected]>
  • Loading branch information
danthorpe and danthorpe committed Sep 14, 2023
1 parent 104a30a commit 14fb846
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 19 deletions.
23 changes: 23 additions & 0 deletions Sources/HTTPNetworking/Core/NetworkingComponent.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Combine
import Dependencies
import Foundation
import Helpers
Expand Down Expand Up @@ -56,3 +57,25 @@ extension NetworkingComponent {
try await data(request, progress: updateProgress, timeout: .seconds(request.requestTimeoutInSeconds))
}
}

// MARK: - Codable Support

extension NetworkingComponent {

public func value<Body: Decodable, Decoder: TopLevelDecoder>(
_ request: HTTPRequestData,
as bodyType: Body.Type,
decoder specializedDecoder: Decoder
) async throws -> (body: Body, response: HTTPResponseData) where Decoder.Input == Data {
try await value(Request<Body>(http: request, decoder: specializedDecoder))
}

public func value<Body>(
_ request: Request<Body>
) async throws -> (body: Body, response: HTTPResponseData) {
let response = try await data(request.http)
try Task.checkCancellation()
let body = try request.decode(response)
return (body, response)
}
}
4 changes: 3 additions & 1 deletion Sources/TestSupport/TerminalNetworkingComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import XCTestDynamicOverlay
public struct TerminalNetworkingComponent: NetworkingComponent {
public struct TestFailure: Equatable, Error {
public let request: HTTPRequestData
public init(request: HTTPRequestData) {
self.request = request
}
}
let isFailingTerminal: Bool
public init(
Expand All @@ -14,7 +17,6 @@ public struct TerminalNetworkingComponent: NetworkingComponent {
public func send(_ request: HTTPRequestData) -> ResponseStream<HTTPResponseData> {
ResponseStream { continuation in
if isFailingTerminal {
XCTFail("\(request.debugDescription) reached the Failing Network Component")
continuation.finish(throwing: TestFailure(request: request))
} else {
continuation.finish()
Expand Down
2 changes: 1 addition & 1 deletion Tests/HTTPNetworkingTests/Components/NumberedTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import XCTest

final class NumberedTests: XCTestCase {

actor RequestSequenceReporter: NetworkReportingComponent {
actor RequestSequenceReporter: NetworkReportingComponent {
var numbers: [(identifier: String, number: Int)] = []
func didStart(request: HTTPRequestData) {
numbers.append((request.identifier, RequestSequence.number))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import XCTest

@testable import HTTPNetworking

final class AutomaticRetryTests: XCTestCase {
final class RetryTests: XCTestCase {

final class RetryingMock {
var stubs: [StubbedResponseStream]
Expand Down Expand Up @@ -60,6 +60,7 @@ final class AutomaticRetryTests: XCTestCase {
XCTAssertTrue(retryingMock.stubs.isEmpty)
}
}

func test__given_no_retry_strategy() async {
withDependencies {
$0.shortID = .incrementing
Expand All @@ -69,6 +70,7 @@ final class AutomaticRetryTests: XCTestCase {
request.retryingStrategy = nil
}
}

func test__default_behaviour__constant_backoff() async {
let request = HTTPRequestData(id: .init("1"), authority: "example.com")
XCTAssertTrue(request.supportsRetryingRequests)
Expand All @@ -95,7 +97,6 @@ final class AutomaticRetryTests: XCTestCase {
)
XCTAssertNil(delay)
}

}

final class RetryStrategyTests: XCTestCase {
Expand Down Expand Up @@ -124,4 +125,80 @@ final class RetryStrategyTests: XCTestCase {
)
XCTAssertNil(delay)
}

func test_exponential_backoff() async {
let request = HTTPRequestData(id: .init("1"), authority: "example.com")
let strategy = BackoffRetryStrategy.exponential(maxDelay: .seconds(20), maxAttemptCount: 6)
var delay = await strategy.retryDelay(
request: request,
after: [.failure("Some Error")],
date: Date(),
calendar: .current
)
XCTAssertEqual(delay?.components.seconds, 2)
delay = await strategy.retryDelay(
request: request,
after: [.failure("Some Error"), .failure("Some Error")],
date: Date(),
calendar: .current
)
XCTAssertEqual(delay?.components.seconds, 4)
delay = await strategy.retryDelay(
request: request,
after: [.failure("Some Error"), .failure("Some Error"), .failure("Some Error")],
date: Date(),
calendar: .current
)
XCTAssertEqual(delay?.components.seconds, 8)
delay = await strategy.retryDelay(
request: request,
after: [.failure("Some Error"), .failure("Some Error"), .failure("Some Error"), .failure("Some Error")],
date: Date(),
calendar: .current
)
XCTAssertEqual(delay?.components.seconds, 16)
delay = await strategy.retryDelay(
request: request,
after: [.failure("Some Error"), .failure("Some Error"), .failure("Some Error"), .failure("Some Error"),
.failure("Some Error")],
date: Date(),
calendar: .current
)
XCTAssertEqual(delay?.components.seconds, 20)
delay = await strategy.retryDelay(
request: request,
after: [.failure("Some Error"), .failure("Some Error"), .failure("Some Error"), .failure("Some Error"),
.failure("Some Error"), .failure("Some Error")],
date: Date(),
calendar: .current
)
XCTAssertNil(delay)
}

func test_immediate_backoff() async {
let request = HTTPRequestData(id: .init("1"), authority: "example.com")
let strategy = BackoffRetryStrategy.immediate(maxAttemptCount: 3)
var delay = await strategy.retryDelay(
request: request,
after: [.failure("Some Error")],
date: Date(),
calendar: .current
)
XCTAssertEqual(delay, .zero)
delay = await strategy.retryDelay(
request: request,
after: [.failure("Some Error"), .failure("Some Error")],
date: Date(),
calendar: .current
)
XCTAssertEqual(delay, .zero)
delay = await strategy.retryDelay(
request: request,
after: [.failure("Some Error"), .failure("Some Error"), .failure("Some Error")],
date: Date(),
calendar: .current
)
XCTAssertNil(delay)

}
}
34 changes: 19 additions & 15 deletions Tests/HTTPNetworkingTests/Core/RequestTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,35 @@ import Dependencies
import Foundation
import HTTPNetworking
import ShortID
import Tagged
import TestSupport
import XCTest

final class RequestTests: XCTestCase {
/*
func test__decoder_basics() throws {

func test__decoder_basics() async throws {
let json =
"""
{"value":"Hello World"}
"""
let http = HTTPRequestData(id: "some-id", authority: "example.com")
let request = Request<Message>(http: http)
let data = try XCTUnwrap(json.data(using: .utf8))

try await withDependencies {
$0.shortID = .incrementing
$0.continuousClock = TestClock()
} operation: {
let http = HTTPRequestData(authority: "example.com")
let network = TerminalNetworkingComponent()
.mocked(http, stub: .ok(data: data))

let httpResponseData = try HTTPResponseData(
request: http,
data: json.data(using: .utf8)!,
urlResponse: HTTPURLResponse(
httpResponse: .init(status: .ok),
url: URL(string: "example.com")!
)
)
var (message, response) = try await network.value(Request<Message>(http: http))
XCTAssertEqual(message.value, "Hello World")
XCTAssertEqual(response.status, .ok)

// ???
(message, response) = try await network.value(http, as: Message.self, decoder: JSONDecoder())
XCTAssertEqual(message.value, "Hello World")
XCTAssertEqual(response.status, .ok)
}
}
*/
}

private struct Message: Decodable, Equatable {
Expand Down

0 comments on commit 14fb846

Please sign in to comment.