Skip to content

Commit

Permalink
KM-4537: Migrate feature flags API to native
Browse files Browse the repository at this point in the history
  • Loading branch information
kp-laura-sempere committed Jul 3, 2024
1 parent e6cf51c commit e1f47a8
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 27 deletions.
11 changes: 0 additions & 11 deletions Sources/PIALibrary/Account/DefaultAccountProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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)
Expand Down
10 changes: 0 additions & 10 deletions Sources/PIALibrary/WebServices/PIAWebServices.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ""
Expand Down
3 changes: 1 addition & 2 deletions Sources/PIALibrary/WebServices/WebServices.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,5 @@ protocol WebServices: class {
func taskForConnectivityCheck(_ callback: LibraryCallback<ConnectivityStatus>?)

func submitDebugReport(_ shouldSendPersistedData: Bool, _ protocolLogs: String, _ callback: LibraryCallback<String>?)

func featureFlags(_ callback: LibraryCallback<[String]>?)

}
237 changes: 233 additions & 4 deletions Tests/PIALibraryTests/Accounts/FeatureFlagsUseCaseTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand All @@ -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)
}

}

0 comments on commit e1f47a8

Please sign in to comment.