diff --git a/Sources/PIALibrary/Account/DefaultAccountProvider.swift b/Sources/PIALibrary/Account/DefaultAccountProvider.swift index 943d3177..734e4a8e 100644 --- a/Sources/PIALibrary/Account/DefaultAccountProvider.swift +++ b/Sources/PIALibrary/Account/DefaultAccountProvider.swift @@ -440,7 +440,6 @@ open class DefaultAccountProvider: AccountProvider, ConfigurationAccess, Databas public func featureFlags(_ callback: SuccessLibraryCallback?) { featureFlagsUseCase() { result in - NSLog(">>> >>> DEfault account provider feature flags result: \(result)") switch result { case .failure(let error): callback?(error.asClientError()) @@ -451,16 +450,6 @@ open class DefaultAccountProvider: AccountProvider, ConfigurationAccess, Databas callback?(nil) } } - - -// webServices.featureFlags { (features, nil) in -// Client.configuration.featureFlags.removeAll() -// if let features = features, !features.isEmpty { -// Client.configuration.featureFlags.append(contentsOf: features) -// } -// Macros.postNotification(Notification.Name.__AppDidFetchFeatureFlags) -// callback?(nil) -// } } #if os(iOS) || os(tvOS) diff --git a/Sources/PIALibrary/WebServices/PIAWebServices.swift b/Sources/PIALibrary/WebServices/PIAWebServices.swift index fb523f29..7f859300 100644 --- a/Sources/PIALibrary/WebServices/PIAWebServices.swift +++ b/Sources/PIALibrary/WebServices/PIAWebServices.swift @@ -295,16 +295,6 @@ class PIAWebServices: WebServices, ConfigurationAccess { } } - func featureFlags(_ callback: LibraryCallback<[String]>?) { - self.accountAPI.featureFlags { (info, errors) in - if let flags = info?.flags { - callback?(flags, nil) - } else { - callback?([], ClientError.malformedResponseData) - } - } - } - #if os(iOS) || os(tvOS) func signup(with request: Signup, _ callback: ((Credentials?, Error?) -> Void)?) { var marketingJSON = "" diff --git a/Sources/PIALibrary/WebServices/WebServices.swift b/Sources/PIALibrary/WebServices/WebServices.swift index 157de395..a284aa2b 100644 --- a/Sources/PIALibrary/WebServices/WebServices.swift +++ b/Sources/PIALibrary/WebServices/WebServices.swift @@ -59,6 +59,5 @@ protocol WebServices: class { func taskForConnectivityCheck(_ callback: LibraryCallback?) func submitDebugReport(_ shouldSendPersistedData: Bool, _ protocolLogs: String, _ callback: LibraryCallback?) - - func featureFlags(_ callback: LibraryCallback<[String]>?) + } diff --git a/Tests/PIALibraryTests/Accounts/FeatureFlagsUseCaseTests.swift b/Tests/PIALibraryTests/Accounts/FeatureFlagsUseCaseTests.swift index 6585cb27..017c0a45 100644 --- a/Tests/PIALibraryTests/Accounts/FeatureFlagsUseCaseTests.swift +++ b/Tests/PIALibraryTests/Accounts/FeatureFlagsUseCaseTests.swift @@ -5,10 +5,43 @@ import XCTest class FeatureFlagsUseCaseTests: XCTestCase { class Fixture { let networkClientMock = NetworkRequestClientMock() - let refreshAutTokensCheckerMock = RefreshAuthTokensCheckerMock() + let refreshAuthTokensCheckerMock = RefreshAuthTokensCheckerMock() + + let validFeatureFlagsJsonString = "{\"flags\": [\"feature_one\", \"feature_two\"], \"other_key\":\"other_value\"}" + + var validFeatureFlagsJsonData: Data { + validFeatureFlagsJsonString.data(using: .utf8)! + } + + let invalidFeatureFlagsJsonString = "{\"invalid_flags\": [\"feature_one\", \"feature_two\"], \"other_key\":\"other_value\"}" + + var invalidFeatureFlagsJsonData: Data { + invalidFeatureFlagsJsonString.data(using: .utf8)! + } + + func stubFeatureFlagsSuccessfullResponse() { + let successResponse = NetworkRequestResponseMock(statusCode: 200, data: validFeatureFlagsJsonData) + networkClientMock.executeRequestResponse = successResponse + } + + func stubFeatureFlagsResponseWithInvalidData() { + let successResponse = NetworkRequestResponseMock(statusCode: 200, data: invalidFeatureFlagsJsonData) + networkClientMock.executeRequestResponse = successResponse + } + + func stubFeatureFlagsResponseWithError(_ error: NetworkRequestError) { + networkClientMock.executeRequestError = error + } + + func stubRefreshAuthTokensIfNeededWithError(_ error: NetworkRequestError?) { + refreshAuthTokensCheckerMock.refreshIfNeededError = error + } + + func stubFeatureFlagsResponseWithNoData() { + networkClientMock.executeRequestResponse = NetworkRequestResponseMock(statusCode: 200, data: nil) + } - // TODO: Add stub methods } var fixture: Fixture! @@ -24,19 +57,215 @@ class FeatureFlagsUseCaseTests: XCTestCase { } private func instantiateSut() { - sut = FeatureFlagsUseCase(networkClient: fixture.networkClientMock, refreshAuthTokensChecker: fixture.refreshAutTokensCheckerMock) + sut = FeatureFlagsUseCase(networkClient: fixture.networkClientMock, refreshAuthTokensChecker: fixture.refreshAuthTokensCheckerMock) } func test_get_feature_flags_successfully() { + // GIVEN that the request to get the feature flags succeeds + fixture.stubFeatureFlagsSuccessfullResponse() + + instantiateSut() + + let expectation = expectation(description: "Get feature flags request is executed") + + var capturedError: NetworkRequestError? + var capturedFlagsInfo: FeatureFlagsInformation? + + // WHEN executing the use case to get the feature flags + sut() { result in + switch result { + case .failure(let error): + capturedError = error + case .success(let featureFlagsInfo): + capturedFlagsInfo = featureFlagsInfo + } + expectation.fulfill() + } + + wait(for: [expectation], timeout: 3) + + // THEN the feature flags request is executed + XCTAssertEqual(fixture.networkClientMock.executeRequestCalledAttempt, 1) + XCTAssertEqual(fixture.networkClientMock.executeRequestWithConfiguation?.path, RequestAPI.Path.iosFeatureFlag) + + // AND the auth tokens are refreshed if needed + XCTAssertEqual(fixture.refreshAuthTokensCheckerMock.refreshIfNeededCalledAttempt, 1) + + // AND the feature flags info is returned with "feature_one" and "feature_two" enabled + XCTAssertNotNil(capturedFlagsInfo) + XCTAssertTrue(capturedFlagsInfo!.flags.count == 2) + XCTAssertTrue(capturedFlagsInfo!.flags.contains("feature_one")) + XCTAssertTrue(capturedFlagsInfo!.flags.contains("feature_two")) + + // AND no error is returned + XCTAssertNil(capturedError) } - func test_get_feature_flags_when_network_error() { + // GIVEN that the request to get the feature flags fails + fixture.stubFeatureFlagsResponseWithError(.allConnectionAttemptsFailed(statusCode: 401)) + + instantiateSut() + + let expectation = expectation(description: "Get feature flags request is executed") + + var capturedError: NetworkRequestError? + var capturedFlagsInfo: FeatureFlagsInformation? + + // WHEN executing the use case to get the feature flags + sut() { result in + switch result { + case .failure(let error): + capturedError = error + case .success(let featureFlagsInfo): + capturedFlagsInfo = featureFlagsInfo + } + expectation.fulfill() + } + + wait(for: [expectation], timeout: 3) + + // THEN the feature flags request is executed + XCTAssertEqual(fixture.networkClientMock.executeRequestCalledAttempt, 1) + XCTAssertEqual(fixture.networkClientMock.executeRequestWithConfiguation?.path, RequestAPI.Path.iosFeatureFlag) + + // AND the auth tokens are refreshed if needed + XCTAssertEqual(fixture.refreshAuthTokensCheckerMock.refreshIfNeededCalledAttempt, 1) + + // AND no feature flags are returned + XCTAssertNil(capturedFlagsInfo) + + // AND an error is returned + XCTAssertNotNil(capturedError) + + XCTAssertEqual(capturedError, .allConnectionAttemptsFailed(statusCode: 401)) + + } + + func test_get_feature_flags_when_no_data_on_the_response() { + // GIVEN that the request to get the feature flags returns no data in the response + fixture.stubFeatureFlagsResponseWithNoData() + + instantiateSut() + + let expectation = expectation(description: "Get feature flags request is executed") + + var capturedError: NetworkRequestError? + var capturedFlagsInfo: FeatureFlagsInformation? + + // WHEN executing the use case to get the feature flags + sut() { result in + switch result { + case .failure(let error): + capturedError = error + case .success(let featureFlagsInfo): + capturedFlagsInfo = featureFlagsInfo + } + expectation.fulfill() + } + + wait(for: [expectation], timeout: 3) + + // THEN the feature flags request is executed + XCTAssertEqual(fixture.networkClientMock.executeRequestCalledAttempt, 1) + XCTAssertEqual(fixture.networkClientMock.executeRequestWithConfiguation?.path, RequestAPI.Path.iosFeatureFlag) + + // AND the auth tokens are refreshed if needed + XCTAssertEqual(fixture.refreshAuthTokensCheckerMock.refreshIfNeededCalledAttempt, 1) + + // AND no feature flags are returned + XCTAssertNil(capturedFlagsInfo) + + // AND an error is returned + XCTAssertNotNil(capturedError) + + XCTAssertEqual(capturedError, .noDataContent) + + } + + func test_get_feature_flags_when_not_valid_data_on_the_response() { + // GIVEN that the request to get the feature flags returns invalid data in the response + fixture.stubFeatureFlagsResponseWithInvalidData() + + instantiateSut() + + let expectation = expectation(description: "Get feature flags request is executed") + + var capturedError: NetworkRequestError? + var capturedFlagsInfo: FeatureFlagsInformation? + + // WHEN executing the use case to get the feature flags + sut() { result in + switch result { + case .failure(let error): + capturedError = error + case .success(let featureFlagsInfo): + capturedFlagsInfo = featureFlagsInfo + } + expectation.fulfill() + } + + wait(for: [expectation], timeout: 3) + + // THEN the feature flags request is executed + XCTAssertEqual(fixture.networkClientMock.executeRequestCalledAttempt, 1) + XCTAssertEqual(fixture.networkClientMock.executeRequestWithConfiguation?.path, RequestAPI.Path.iosFeatureFlag) + + // AND the auth tokens are refreshed if needed + XCTAssertEqual(fixture.refreshAuthTokensCheckerMock.refreshIfNeededCalledAttempt, 1) + + // AND no feature flags are returned + XCTAssertNil(capturedFlagsInfo) + + // AND an error is returned + XCTAssertNotNil(capturedError) + + XCTAssertEqual(capturedError, .unableToDecodeData) } func test_get_feature_flags_when_refresh_tokens_error() { + // GIVEN that the request to refresh the auth tokens fails + fixture.stubRefreshAuthTokensIfNeededWithError(.allConnectionAttemptsFailed(statusCode: 401)) + // AND GIVEN that the request to get the feature flags succeeds + fixture.stubFeatureFlagsSuccessfullResponse() + + instantiateSut() + let expectation = expectation(description: "Get feature flags request is executed") + + var capturedError: NetworkRequestError? + var capturedFlagsInfo: FeatureFlagsInformation? + + // WHEN executing the use case to get the feature flags + sut() { result in + switch result { + case .failure(let error): + capturedError = error + case .success(let featureFlagsInfo): + capturedFlagsInfo = featureFlagsInfo + } + expectation.fulfill() + } + + wait(for: [expectation], timeout: 3) + + // THEN the feature flags request is executed + XCTAssertEqual(fixture.networkClientMock.executeRequestCalledAttempt, 1) + XCTAssertEqual(fixture.networkClientMock.executeRequestWithConfiguation?.path, RequestAPI.Path.iosFeatureFlag) + + // AND the auth tokens are refreshed if needed + XCTAssertEqual(fixture.refreshAuthTokensCheckerMock.refreshIfNeededCalledAttempt, 1) + + // AND the feature flags info is returned with "feature_one" and "feature_two" enabled + XCTAssertNotNil(capturedFlagsInfo) + XCTAssertTrue(capturedFlagsInfo!.flags.count == 2) + XCTAssertTrue(capturedFlagsInfo!.flags.contains("feature_one")) + XCTAssertTrue(capturedFlagsInfo!.flags.contains("feature_two")) + + // AND no error is returned + XCTAssertNil(capturedError) } + }