import XCTest @testable import LCFeatureControl @testable import LCEssentials final class FeatureControlExposureTests: XCTestCase { private var api: API! override func setUp() { super.setUp() let cfg = URLSessionConfiguration.ephemeral cfg.protocolClasses = [StubURLProtocol.self] api = API(testConfiguration: cfg) } override func tearDown() { StubURLProtocol.reset() api = nil super.tearDown() } private func makeManager() -> FeatureControlManager { let config = FeatureControlConfiguration(baseURL: "https://api.example.com", environment: "production", auth: FeatureControlHeaderAuth(headers: [:])) return FeatureControlManager(configuration: config, api: api) } private func event(_ index: Int) -> FeatureControlExposureEvent { FeatureControlExposureEvent(featureKey: "fc.checkout_v2", variant: "on", subjectType: .customer, storeId: "store_\(index)") } func testFlushSendsSingleBatchUnderLimit() async { StubURLProtocol.setStub(.init(statusCode: 200, body: Data(#"{"error":false,"code":"OK"}"#.utf8))) let manager = makeManager() for i in 0..<10 { await manager.recordExposure(event(i)) } await manager.flushExposures() XCTAssertEqual(StubURLProtocol.requestCount, 1) } func testFlushSplitsIntoMultipleBatchesOverLimit() async { StubURLProtocol.setStub(.init(statusCode: 200, body: Data(#"{"error":false,"code":"OK"}"#.utf8))) let manager = makeManager() for i in 0..<130 { await manager.recordExposure(event(i)) } await manager.flushExposures() XCTAssertEqual(StubURLProtocol.requestCount, 2) } func testFailedBatchIsDroppedNotRetriedIndefinitely() async { StubURLProtocol.setStub(.init(statusCode: 500, body: Data())) let manager = makeManager() for i in 0..<10 { await manager.recordExposure(event(i)) } await manager.flushExposures() // must not throw, must not hang XCTAssertEqual(StubURLProtocol.requestCount, 1) // buffer was cleared even though the batch failed — a second flush sends nothing new StubURLProtocol.reset() StubURLProtocol.setStub(.init(statusCode: 200, body: Data(#"{"error":false,"code":"OK"}"#.utf8))) await manager.flushExposures() XCTAssertEqual(StubURLProtocol.requestCount, 0) } }