From 97a6c5f626513bbc0ff74ece693efe120a9abc9c Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Thu, 15 Jun 2023 14:03:44 -0700 Subject: [PATCH] `LoadShedderIntegrationTests`: verify requests are actually handled by load shedder --- Sources/Logging/Strings/NetworkStrings.swift | 4 ++ .../Networking/HTTPClient/HTTPClient.swift | 10 ++++ .../LoadShedderIntegrationTests.swift | 21 +++++++++ .../OtherIntegrationTests.swift | 8 ++++ .../Networking/HTTPClientTests.swift | 47 +++++++++++++++++++ 5 files changed, 90 insertions(+) diff --git a/Sources/Logging/Strings/NetworkStrings.swift b/Sources/Logging/Strings/NetworkStrings.swift index 2d04a2227a..4db29e49ab 100644 --- a/Sources/Logging/Strings/NetworkStrings.swift +++ b/Sources/Logging/Strings/NetworkStrings.swift @@ -34,6 +34,7 @@ enum NetworkStrings { case blocked_network(url: URL, newHost: String?) case api_request_redirect(from: URL, to: URL) case operation_state(NetworkOperation.Type, state: String) + case request_handled_by_load_shedder(HTTPRequest.Path) #if DEBUG case api_request_forcing_server_error(HTTPRequest) @@ -103,6 +104,9 @@ extension NetworkStrings: LogMessage { case let .operation_state(operation, state): return "\(operation): \(state)" + case let .request_handled_by_load_shedder(path): + return "Request was handled by load shedder: \(path.description)" + #if DEBUG case let .api_request_forcing_server_error(request): return "Returning fake HTTP 500 error for '\(request.description)'" diff --git a/Sources/Networking/HTTPClient/HTTPClient.swift b/Sources/Networking/HTTPClient/HTTPClient.swift index 793d1bcf87..1a8ab8405e 100644 --- a/Sources/Networking/HTTPClient/HTTPClient.swift +++ b/Sources/Networking/HTTPClient/HTTPClient.swift @@ -112,6 +112,7 @@ extension HTTPClient { case signature = "X-Signature" case requestDate = "X-RevenueCat-Request-Time" case contentType = "Content-Type" + case isLoadShedder = "X-RevenueCat-Fortress" } @@ -319,6 +320,11 @@ private extension HTTPClient { // If that can't be extracted, get status code from the parsed response. httpCode: urlResponse?.httpStatusCode ?? response.statusCode )) + + if response.isLoadShedder { + Logger.debug(Strings.network.request_handled_by_load_shedder(request.httpRequest.path)) + } + case let .failure(error): Logger.debug(Strings.network.api_request_failed(request.httpRequest, httpCode: urlResponse?.httpStatusCode, @@ -530,6 +536,10 @@ private extension HTTPResponse { } } + var isLoadShedder: Bool { + return self.value(forHeaderField: HTTPClient.ResponseHeader.isLoadShedder.rawValue) == "true" + } + } private extension HTTPResponse where Body == Data { diff --git a/Tests/BackendIntegrationTests/LoadShedderIntegrationTests.swift b/Tests/BackendIntegrationTests/LoadShedderIntegrationTests.swift index 32ee7e438b..9f1657a15c 100644 --- a/Tests/BackendIntegrationTests/LoadShedderIntegrationTests.swift +++ b/Tests/BackendIntegrationTests/LoadShedderIntegrationTests.swift @@ -38,23 +38,44 @@ class LoadShedderStoreKit1IntegrationTests: BaseStoreKitIntegrationTests { } func testCanGetOfferings() async throws { + let logger = TestLogHandler() + let receivedOfferings = try await Purchases.shared.offerings() expect(receivedOfferings.all).toNot(beEmpty()) assertSnapshot(matching: receivedOfferings.response, as: .formattedJson) + + logger.verifyMessageWasLogged( + Strings.network.request_handled_by_load_shedder(.getOfferings(appUserID: Purchases.shared.appUserID)), + level: .debug + ) } func testCanPurchasePackage() async throws { + let logger = TestLogHandler() + try await self.purchaseMonthlyOffering() + + logger.verifyMessageWasLogged( + Strings.network.request_handled_by_load_shedder(.postReceiptData), + level: .debug + ) } @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) func testProductEntitlementMapping() async throws { try AvailabilityChecks.iOS15APIAvailableOrSkipTest() + let logger = TestLogHandler() + let result = try await Purchases.shared.productEntitlementMapping() expect(result.entitlementsByProduct).to(haveCount(1)) expect(result.entitlementsByProduct["com.revenuecat.loadShedder.monthly"]) == ["premium"] + + logger.verifyMessageWasLogged( + Strings.network.request_handled_by_load_shedder(.getProductEntitlementMapping), + level: .debug + ) } } diff --git a/Tests/BackendIntegrationTests/OtherIntegrationTests.swift b/Tests/BackendIntegrationTests/OtherIntegrationTests.swift index a00d696f7a..a7ca0e7f77 100644 --- a/Tests/BackendIntegrationTests/OtherIntegrationTests.swift +++ b/Tests/BackendIntegrationTests/OtherIntegrationTests.swift @@ -32,6 +32,14 @@ class OtherIntegrationTests: BaseBackendIntegrationTests { try await Purchases.shared.healthRequest(signatureVerification: true) } + func testHandledByProductionServer() async throws { + let logger = TestLogHandler() + + try await Purchases.shared.healthRequest(signatureVerification: false) + + logger.verifyMessageWasNotLogged(Strings.network.request_handled_by_load_shedder(.health)) + } + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) func testProductEntitlementMapping() async throws { try AvailabilityChecks.iOS15APIAvailableOrSkipTest() diff --git a/Tests/UnitTests/Networking/HTTPClientTests.swift b/Tests/UnitTests/Networking/HTTPClientTests.swift index 65ff27710a..ac5ad601cf 100644 --- a/Tests/UnitTests/Networking/HTTPClientTests.swift +++ b/Tests/UnitTests/Networking/HTTPClientTests.swift @@ -1421,6 +1421,53 @@ final class HTTPClientTests: BaseHTTPClientTests { ) } + func testNormalResponsesAreNotDetectedAsLoadSheddder() throws { + let path: HTTPRequest.Path = .logIn + + stub(condition: isPath(path)) { _ in + return HTTPStubsResponse( + data: .init(), + statusCode: .success, + headers: [:] + ) + } + + let logger = TestLogHandler() + + let response: HTTPResponse.Result? = waitUntilValue { completion in + self.client.perform(.init(method: .get, path: path), completionHandler: completion) + } + expect(response).to(beSuccess()) + + logger.verifyMessageWasNotLogged(Strings.network.request_handled_by_load_shedder(path)) + } + + func testLoadShedderResponsesAreLogged() throws { + let path: HTTPRequest.Path = .logIn + + stub(condition: isPath(path)) { _ in + return HTTPStubsResponse( + data: .init(), + statusCode: .success, + headers: [ + HTTPClient.ResponseHeader.isLoadShedder.rawValue: "true" + ] + ) + } + + let logger = TestLogHandler() + + let response: HTTPResponse.Result? = waitUntilValue { completion in + self.client.perform(.init(method: .get, path: path), completionHandler: completion) + } + expect(response).to(beSuccess()) + + logger.verifyMessageWasLogged( + Strings.network.request_handled_by_load_shedder(path), + level: .debug + ) + } + } func isPath(_ path: HTTPRequest.Path) -> HTTPStubsTestBlock {