Skip to content

Commit

Permalink
fix: could not parser error when the body is a text or empty (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
duyhungtnn authored Nov 16, 2023
1 parent cb061da commit 4a5dc0d
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 9 deletions.
4 changes: 4 additions & 0 deletions Bucketeer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
942AD4EF2A7EAE1200348B3E /* EvaluationStorageImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 942AD4EE2A7EAE1200348B3E /* EvaluationStorageImpl.swift */; };
942AD4F12A7EC06D00348B3E /* EvaluationDaoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 942AD4F02A7EC06D00348B3E /* EvaluationDaoTests.swift */; };
9433F2EE2A79745800436F91 /* EvaluationUserDefaultDaoTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9433F2ED2A79745800436F91 /* EvaluationUserDefaultDaoTest.swift */; };
944AF78E2AEF4D1400FEADF3 /* E2EMetricsEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 944AF78D2AEF4D1400FEADF3 /* E2EMetricsEventTests.swift */; };
945B9B642A7B9A7900F14934 /* E2EEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 945B9B632A7B9A7900F14934 /* E2EEventTests.swift */; };
945B9B662A7B9A8800F14934 /* E2EBKTClientForceUpdateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 945B9B652A7B9A8800F14934 /* E2EBKTClientForceUpdateTests.swift */; };
946540372A821A11009BF89F /* EvaluationMemCacheDao.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946540362A821A11009BF89F /* EvaluationMemCacheDao.swift */; };
Expand Down Expand Up @@ -333,6 +334,7 @@
942AD4EE2A7EAE1200348B3E /* EvaluationStorageImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EvaluationStorageImpl.swift; sourceTree = "<group>"; };
942AD4F02A7EC06D00348B3E /* EvaluationDaoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EvaluationDaoTests.swift; sourceTree = "<group>"; };
9433F2ED2A79745800436F91 /* EvaluationUserDefaultDaoTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EvaluationUserDefaultDaoTest.swift; sourceTree = "<group>"; };
944AF78D2AEF4D1400FEADF3 /* E2EMetricsEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = E2EMetricsEventTests.swift; sourceTree = "<group>"; };
945B9B632A7B9A7900F14934 /* E2EEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = E2EEventTests.swift; sourceTree = "<group>"; };
945B9B652A7B9A8800F14934 /* E2EBKTClientForceUpdateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = E2EBKTClientForceUpdateTests.swift; sourceTree = "<group>"; };
946540362A821A11009BF89F /* EvaluationMemCacheDao.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EvaluationMemCacheDao.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -515,6 +517,7 @@
children = (
00664C0528F9340700AB4995 /* E2EEvaluationTests.swift */,
945B9B632A7B9A7900F14934 /* E2EEventTests.swift */,
944AF78D2AEF4D1400FEADF3 /* E2EMetricsEventTests.swift */,
945B9B652A7B9A8800F14934 /* E2EBKTClientForceUpdateTests.swift */,
00664C0828FAE79100AB4995 /* E2ETestHelpers.swift */,
);
Expand Down Expand Up @@ -1168,6 +1171,7 @@
00664C0928FAE79100AB4995 /* E2ETestHelpers.swift in Sources */,
942AD4F12A7EC06D00348B3E /* EvaluationDaoTests.swift in Sources */,
9340CA0928D9D1DD00E690CC /* EvaluationStorageTests.swift in Sources */,
944AF78E2AEF4D1400FEADF3 /* E2EMetricsEventTests.swift in Sources */,
934889C728EB0ADB007BA05C /* EvaluationForegroundTaskTests.swift in Sources */,
006E85C328DED24500B5D90D /* EvaluationInteractorTests.swift in Sources */,
935D9AB028F5B0DB007775F5 /* EvaluationTests.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion Bucketeer/Sources/Internal/Remote/ApiClientImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ final class ApiClientImpl: ApiClient {
}
do {
guard 200..<300 ~= urlResponse.statusCode else {
let response: ErrorResponse? = try JSONDecoder().decode(ErrorResponse.self, from: data)
let response: ErrorResponse? = try? JSONDecoder().decode(ErrorResponse.self, from: data)
let error = ResponseError.unacceptableCode(code: urlResponse.statusCode, response: response)
return .failure(error)
}
Expand Down
2 changes: 1 addition & 1 deletion Bucketeer/Sources/Public/BKTClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class BKTClient {
}
}

private func execute(_ handler: @escaping () throws -> Void) {
func execute(_ handler: @escaping () throws -> Void) {
dispatchQueue.async {
do {
try handler()
Expand Down
5 changes: 2 additions & 3 deletions BucketeerTests/ApiClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -831,12 +831,11 @@ class ApiClientTests: XCTestCase {
XCTFail()
case .failure(let error):
guard
let error = error as? DecodingError,
case .keyNotFound(let codingKey, _) = error else {
let error = error as? ResponseError,
case .unacceptableCode(let code, _) = error, code == 400 else {
XCTFail()
return
}
XCTAssertEqual(codingKey.stringValue, "error")
}
expectation.fulfill()
}
Expand Down
92 changes: 89 additions & 3 deletions BucketeerTests/E2E/E2EEventTests.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation
import XCTest
import Bucketeer
@testable import Bucketeer

@available(iOS 13, *)
final class E2EEventTests: XCTestCase {
Expand All @@ -9,7 +9,6 @@ final class E2EEventTests: XCTestCase {

override func setUp() async throws {
try await super.setUp()

UserDefaults.standard.removeObject(forKey: "bucketeer_user_evaluations_id")

let config = try BKTConfig.e2e()
Expand All @@ -20,7 +19,6 @@ final class E2EEventTests: XCTestCase {
)
}

@MainActor
override func tearDown() async throws {
try await super.tearDown()

Expand All @@ -43,4 +41,92 @@ final class E2EEventTests: XCTestCase {
XCTFail(error.localizedDescription)
}
}

// refs: https://github.com/bucketeer-io/javascript-client-sdk/blob/main/e2e/events.spec.ts#L112
func testEvaluationEvents() async throws {
do {
let client = try BKTClient.shared
XCTAssertEqual(client.stringVariation(featureId: FEATURE_ID_STRING, defaultValue: ""), "value-1")
XCTAssertEqual(client.intVariation(featureId: FEATURE_ID_INT, defaultValue: 0), 10)
XCTAssertEqual(client.doubleVariation(featureId: FEATURE_ID_DOUBLE, defaultValue: 0.0), 2.1)
XCTAssertEqual(client.boolVariation(featureId: FEATURE_ID_BOOLEAN, defaultValue: false), true)
XCTAssertEqual(client.jsonVariation(featureId: FEATURE_ID_JSON, defaultValue: [:]), ["key":"value-1"])

guard let component = client.component as? ComponentImpl else {
XCTFail("could not access client.component")
return
}

// getVariationValue() is logging events using another dispatch queue, we need to wait a few secs
try await Task.sleep(nanoseconds: 10_000_000)
let events = try component.dataModule.eventDao.getEvents()
// It includes the Latency and ResponseSize metrics
XCTAssertEqual(events.count, 7)
XCTAssertTrue(events.contains { event in
if case .evaluation = event.type,
case .evaluation(let data) = event.event,
case .`default` = data.reason.type {
return true
}
return false
})

try await client.flush()

XCTAssertEqual(try component.dataModule.eventDao.getEvents().count, 0)
} catch {
XCTFail(error.localizedDescription)
}
}

func testDefaultEvaluationEvents() async throws {
do {
let client = try BKTClient.shared
guard let component = client.component as? ComponentImpl else {
XCTFail("could not access client.component")
return
}
let userId = client.component.userHolder.userId
try await withCheckedThrowingContinuation({ continuation in
client.execute {
do {
try component.dataModule.evaluationStorage.deleteAllAndInsert(userId: userId, evaluations: [], evaluatedAt: "0")
continuation.resume(returning: ())
} catch {
continuation.resume(throwing: error)
}
}
})
XCTAssertEqual(client.stringVariation(featureId: FEATURE_ID_STRING, defaultValue: "value-default"), "value-default")
XCTAssertEqual(client.intVariation(featureId: FEATURE_ID_INT, defaultValue: 100), 100)
XCTAssertEqual(client.doubleVariation(featureId: FEATURE_ID_DOUBLE, defaultValue: 3.0), 3.0)
XCTAssertEqual(client.boolVariation(featureId: FEATURE_ID_BOOLEAN, defaultValue: false), false)
XCTAssertEqual(client.jsonVariation(featureId: FEATURE_ID_JSON, defaultValue: ["key":"value-default"]), ["key":"value-default"])

guard let component = client.component as? ComponentImpl else {
XCTFail("could not access client.component")
return
}

// getVariationValue() is logging events using another dispatch queue, we need to wait a few secs
try await Task.sleep(nanoseconds: 10_000_000)
let events = try component.dataModule.eventDao.getEvents()
// It includes the Latency and ResponseSize metrics
XCTAssertEqual(events.count, 7)
XCTAssertTrue(events.contains { event in
if case .evaluation = event.type,
case .evaluation(let data) = event.event,
case .client = data.reason.type {
return true
}
return false
})

try await client.flush()

XCTAssertEqual(try component.dataModule.eventDao.getEvents().count, 0)
} catch {
XCTFail(error.localizedDescription)
}
}
}
134 changes: 134 additions & 0 deletions BucketeerTests/E2E/E2EMetricsEventTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import Foundation
import XCTest
@testable import Bucketeer

@available(iOS 13, *)
final class E2EMetricsEventTests: XCTestCase {

private var config: BKTConfig!

override func tearDown() async throws {
try await super.tearDown()
try BKTClient.destroy()
UserDefaults.standard.removeObject(forKey: "bucketeer_user_evaluations_id")
try? FileManager.default.removeItem(at: .database)
}

// Metrics Event Tests
// refs: https://github.com/bucketeer-io/javascript-client-sdk/blob/main/e2e/events.spec.ts#L112

// Using a random string in the api key setting should throw Forbidden
func testUsingRamdomStringInTheAPIKeyShouldThrowForbiden() async throws {
let apiKey = "some-random-string"
let apiEndpoint = ProcessInfo.processInfo.environment["E2E_API_ENDPOINT"]!

let builder = BKTConfig.Builder()
.with(apiKey: apiKey)
.with(apiEndpoint: apiEndpoint)
.with(featureTag: FEATURE_TAG)
.with(appVersion: "1.2.3")
.with(logger: E2ELogger())

let config = try builder.build()
let user = try BKTUser.Builder().with(id: USER_ID).build()
do {
try await BKTClient.initialize(
config: config,
user: user
)
XCTFail("Using a random string in the api key setting should throw Forbidden")
} catch {
guard case BKTError.forbidden(_) = error else {
XCTFail("Using a random string in the api key setting should throw Forbidden")
return
}
}

let client = try BKTClient.shared
guard let component = client.component as? ComponentImpl else {
XCTFail("could not access client.component")
return
}
let events : [Event] = try component.dataModule.eventDao.getEvents()
// It includes the Latency and ResponseSize metrics
XCTAssertEqual(events.count, 1)
XCTAssertTrue(events.contains { event in
if case .metrics = event.type,
case .metrics(let data) = event.event,
case .forbiddenError = data.type,
case .forbiddenError(let errorData) = data.event,
case .getEvaluations = errorData.apiId {
return true
}
return false
})

do {
try await client.flush()
XCTFail("Using a random string in the api key setting should throw Forbidden")
} catch {
guard case BKTError.forbidden(_) = error else {
XCTFail("Using a random string in the api key setting should throw Forbidden")
return
}
}

let events2 : [Event] = try component.dataModule.eventDao.getEvents()
// It includes the Latency and ResponseSize metrics
XCTAssertEqual(events2.count, 2)
}

// Using a random string in the featureTag setting should not affect api request
func testARandomStringInTheFeatureTagShouldNotAffectAPIRequest() async throws {
let featureTag = "some-random-string"
let config = try BKTConfig.e2e(featureTag: featureTag)
let user = try BKTUser.Builder().with(id: USER_ID).build()
do {
try await BKTClient.initialize(
config: config,
user: user
)
} catch {
XCTFail("Using a random string in the featureTag setting should not affect api request")
}
}

func testTimeout() async throws {
let config = try BKTConfig.e2e()
let user = try BKTUser.Builder().with(id: USER_ID).build()
do {
try await BKTClient.initialize(
config: config,
user: user,
timeoutMillis: 10
)
XCTFail("Should throw timeout error")
} catch {
guard case BKTError.timeout( _, _, let timeoutMillis ) = error, timeoutMillis == 10 else {
XCTFail("Should throw timeout error")
return
}
}
let client = try BKTClient.shared
guard let component = client.component as? ComponentImpl else {
XCTFail("could not access client.component")
return
}
let events : [Event] = try component.dataModule.eventDao.getEvents()
XCTAssertEqual(events.count, 1)
XCTAssertTrue(events.contains { event in
if case .metrics = event.type,
case .metrics(let data) = event.event,
case .timeoutError = data.type,
case .timeoutError(let errorData) = data.event,
case .getEvaluations = errorData.apiId {
return true
}
return false
})

try await client.flush()

XCTAssertEqual(try component.dataModule.eventDao.getEvents().count, 0)
}
}
2 changes: 1 addition & 1 deletion BucketeerTests/E2E/E2ETestHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ extension BKTClient {
return try await withCheckedThrowingContinuation { continuation in
DispatchQueue.main.async {
do {
try self.initialize(config: config, user: user) { error in
try self.initialize(config: config, user: user, timeoutMillis: timeoutMillis) { error in
if let error = error {
continuation.resume(throwing: error)
} else {
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ TEST_WITHOUT_BUILDING=$(XCODEBUILD) $(OPTIONS) $(DESTINATION) \
-skip-testing:BucketeerTests/E2EBKTClientForceUpdateTests \
-skip-testing:BucketeerTests/E2EEvaluationTests \
-skip-testing:BucketeerTests/E2EEventTests \
-skip-testing:BucketeerTests/E2EMetricsEventTests \
test-without-building
E2E_WITHOUT_BUILDING=$(XCODEBUILD) $(OPTIONS) $(DESTINATION) \
-configuration $(CONFIGURATION) \
-only-testing:BucketeerTests/E2EBKTClientForceUpdateTests \
-only-testing:BucketeerTests/E2EEvaluationTests \
-only-testing:BucketeerTests/E2EEventTests \
-only-testing:BucketeerTests/E2EMetricsEventTests \
test-without-building E2E_API_ENDPOINT=$(E2E_API_ENDPOINT) E2E_API_KEY=$(E2E_API_KEY)
ALL_TEST_WITHOUT_BUILDING=$(XCODEBUILD) $(OPTIONS) $(DESTINATION) \
-configuration $(CONFIGURATION) \
Expand Down

0 comments on commit 4a5dc0d

Please sign in to comment.