import Foundation import Testing @testable import CodexBarCore struct AlibabaCodingPlanSettingsReaderTests { @Test func `api token reads from environment`() { let token = AlibabaCodingPlanSettingsReader.apiToken(environment: ["ALIBABA_CODING_PLAN_API_KEY": "abc123"]) #expect(token == "abc123") } @Test func `api token reads qwen alias from environment`() { let token = AlibabaCodingPlanSettingsReader.apiToken(environment: ["ALIBABA_QWEN_API_KEY": "qwen123"]) #expect(token == "qwen123") } @Test func `api token reads dashscope alias from environment`() { let token = AlibabaCodingPlanSettingsReader.apiToken(environment: ["DASHSCOPE_API_KEY": "dashscope123"]) #expect(token == "dashscope123") } @Test func `api token prefers coding plan key over aliases`() { let token = AlibabaCodingPlanSettingsReader.apiToken(environment: [ "ALIBABA_CODING_PLAN_API_KEY": "coding-plan", "ALIBABA_QWEN_API_KEY": "qwen", "DASHSCOPE_API_KEY": "dashscope", ]) #expect(token == "coding-plan") } @Test func `api token strips quotes`() { let token = AlibabaCodingPlanSettingsReader .apiToken(environment: ["ALIBABA_CODING_PLAN_API_KEY": "\"token-xyz\""]) #expect(token == "token-xyz") } @Test func `quota URL infers scheme`() { let url = AlibabaCodingPlanSettingsReader .quotaURL(environment: [AlibabaCodingPlanSettingsReader .quotaURLKey: "modelstudio.console.alibabacloud.com/data/api.json"]) #expect(url?.absoluteString == "https://modelstudio.console.alibabacloud.com/data/api.json") } @Test func `endpoint overrides allow custom https hosts by default`() { let env = [ AlibabaCodingPlanSettingsReader.hostKey: "https://attacker.example", AlibabaCodingPlanSettingsReader.quotaURLKey: "https://attacker.example/data/api.json", ] #expect(AlibabaCodingPlanSettingsReader.hostOverride(environment: env) == "attacker.example") #expect(AlibabaCodingPlanSettingsReader.quotaURL(environment: env)?.host == "attacker.example") #expect(AlibabaCodingPlanSettingsReader.rejectedEndpointOverrideKey(environment: env) == nil) } @Test func `host endpoint overrides preserve explicit port`() { let env = [AlibabaCodingPlanSettingsReader.hostKey: "proxy.example.test:8443"] #expect(AlibabaCodingPlanSettingsReader.hostOverride(environment: env) == "proxy.example.test:8443") #expect( AlibabaCodingPlanUsageFetcher.resolveQuotaURL(region: .international, environment: env).absoluteString == "https://proxy.example.test:8443/data/api.json?action=zeldaEasy.broadscope-bailian.codingPlan.queryCodingPlanInstanceInfoV2&product=broadscope-bailian&api=queryCodingPlanInstanceInfoV2¤tRegionId=ap-southeast-1") #expect( AlibabaCodingPlanUsageFetcher.resolveConsoleDashboardURL(region: .international, environment: env) .absoluteString .hasPrefix("https://proxy.example.test:8443/") == true) } @Test func `endpoint overrides reject encoded host delimiters before suffix matching`() { let encodedSlash = "https://attacker.example%2f.modelstudio.console.alibabacloud.com" let doubleEncodedSlash = "https://attacker.example%252f.modelstudio.console.alibabacloud.com" let env = [ AlibabaCodingPlanSettingsReader.hostKey: encodedSlash, AlibabaCodingPlanSettingsReader.quotaURLKey: "\(encodedSlash)/data/api.json", ] #expect(AlibabaCodingPlanSettingsReader.hostOverride(environment: env) == nil) #expect(AlibabaCodingPlanSettingsReader.quotaURL(environment: env) == nil) #expect(AlibabaCodingPlanSettingsReader.hostOverride(environment: [ AlibabaCodingPlanSettingsReader.hostKey: doubleEncodedSlash, ]) == nil) } @Test func `endpoint overrides reject whitespace and control characters in hosts`() { for host in ["https://bad host", "https://bad%20host", "https://bad%09host"] { #expect(AlibabaCodingPlanSettingsReader.hostOverride(environment: [ AlibabaCodingPlanSettingsReader.hostKey: host, ]) == nil) #expect(AlibabaCodingPlanSettingsReader.quotaURL(environment: [ AlibabaCodingPlanSettingsReader.quotaURLKey: "\(host)/data/api.json", ]) == nil) } } @Test func `endpoint overrides require https and no userinfo`() { #expect(AlibabaCodingPlanSettingsReader.hostOverride(environment: [ AlibabaCodingPlanSettingsReader.hostKey: "http://modelstudio.console.alibabacloud.com", ]) == nil) #expect(AlibabaCodingPlanSettingsReader.quotaURL(environment: [ AlibabaCodingPlanSettingsReader.quotaURLKey: "https://user:pass@modelstudio.console.alibabacloud.com/data/api.json", ]) == nil) } @Test func `strict provider endpoint mode rejects custom hosts`() { let env = [ AlibabaCodingPlanSettingsReader.requireProviderEndpointOverridesKey: "true", AlibabaCodingPlanSettingsReader.hostKey: "proxy.example.test", AlibabaCodingPlanSettingsReader.quotaURLKey: "https://proxy.example.test/data/api.json", ] #expect(AlibabaCodingPlanSettingsReader.hostOverride(environment: env) == nil) #expect(AlibabaCodingPlanSettingsReader.quotaURL(environment: env) == nil) #expect(AlibabaCodingPlanSettingsReader .rejectedEndpointOverrideKey(environment: env) == AlibabaCodingPlanSettingsReader.hostKey) } @Test func `strict provider endpoint mode rejects customer controlled Alibaba Cloud hosts`() { let env = [ AlibabaCodingPlanSettingsReader.requireProviderEndpointOverridesKey: "true", AlibabaCodingPlanSettingsReader.hostKey: "tenant.cn-beijing.fc.aliyuncs.com", ] #expect(AlibabaCodingPlanSettingsReader.hostOverride(environment: env) == nil) #expect(AlibabaCodingPlanSettingsReader .rejectedEndpointOverrideKey(environment: env) == AlibabaCodingPlanSettingsReader.hostKey) } @Test func `strict provider endpoint mode accepts known Coding Plan hosts`() { let env = [ AlibabaCodingPlanSettingsReader.requireProviderEndpointOverridesKey: "true", AlibabaCodingPlanSettingsReader.hostKey: "bailian-beijing-cs.aliyuncs.com", ] #expect( AlibabaCodingPlanSettingsReader.hostOverride(environment: env) == "bailian-beijing-cs.aliyuncs.com") #expect(AlibabaCodingPlanSettingsReader.rejectedEndpointOverrideKey(environment: env) == nil) } @Test func `custom https compatibility mode still rejects http and userinfo`() { #expect(AlibabaCodingPlanSettingsReader.hostOverride(environment: [ AlibabaCodingPlanSettingsReader.hostKey: "http://proxy.example.test", ]) == nil) #expect(AlibabaCodingPlanSettingsReader.rejectedEndpointOverrideKey(environment: [ AlibabaCodingPlanSettingsReader.quotaURLKey: "https://user:pass@proxy.example.test/data/api.json", ]) == AlibabaCodingPlanSettingsReader.quotaURLKey) } @Test func `missing cookie error includes access hint when present`() { let error = AlibabaCodingPlanSettingsError .missingCookie(details: "Safari cookie file exists but is not readable.") #expect(error.errorDescription?.contains("Safari cookie file exists but is not readable.") == true) } } struct AlibabaCodingPlanUsageSnapshotTests { @Test func `maps usage snapshot windows`() { let now = Date(timeIntervalSince1970: 1_700_000_000) let reset5h = Date(timeIntervalSince1970: 1_700_000_300) let resetWeek = Date(timeIntervalSince1970: 1_700_010_000) let resetMonth = Date(timeIntervalSince1970: 1_700_100_000) let snapshot = AlibabaCodingPlanUsageSnapshot( planName: "Pro", fiveHourUsedQuota: 20, fiveHourTotalQuota: 100, fiveHourNextRefreshTime: reset5h, weeklyUsedQuota: 120, weeklyTotalQuota: 400, weeklyNextRefreshTime: resetWeek, monthlyUsedQuota: 500, monthlyTotalQuota: 2000, monthlyNextRefreshTime: resetMonth, updatedAt: now) let usage = snapshot.toUsageSnapshot() #expect(usage.primary?.usedPercent == 20) #expect(usage.primary?.windowMinutes == 300) #expect(usage.secondary?.usedPercent == 30) #expect(usage.secondary?.windowMinutes == 10080) #expect(usage.tertiary?.usedPercent == 25) #expect(usage.tertiary?.windowMinutes == 43200) #expect(usage.loginMethod(for: .alibaba) == "Pro") } @Test func `shifts primary reset forward when backend reset is not future`() { let now = Date(timeIntervalSince1970: 1_700_000_000) let stalePrimaryReset = Date(timeIntervalSince1970: 1_699_999_900) let snapshot = AlibabaCodingPlanUsageSnapshot( planName: "Lite", fiveHourUsedQuota: 70, fiveHourTotalQuota: 1200, fiveHourNextRefreshTime: stalePrimaryReset, weeklyUsedQuota: 80, weeklyTotalQuota: 9000, weeklyNextRefreshTime: Date(timeIntervalSince1970: 1_700_010_000), monthlyUsedQuota: 80, monthlyTotalQuota: 18000, monthlyNextRefreshTime: Date(timeIntervalSince1970: 1_700_100_000), updatedAt: now) let usage = snapshot.toUsageSnapshot() #expect(usage.primary?.resetsAt == stalePrimaryReset.addingTimeInterval(TimeInterval(5 * 60 * 60))) } } struct AlibabaCodingPlanUsageParsingTests { @Test func `parses quota payload`() throws { let now = Date(timeIntervalSince1970: 1_700_000_000) let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Alibaba Coding Plan Pro" } ], "codingPlanQuotaInfo": { "per5HourUsedQuota": 52, "per5HourTotalQuota": 1000, "per5HourQuotaNextRefreshTime": 1700000300000, "perWeekUsedQuota": 800, "perWeekTotalQuota": 5000, "perWeekQuotaNextRefreshTime": 1700100000000, "perBillMonthUsedQuota": 1200, "perBillMonthTotalQuota": 20000, "perBillMonthQuotaNextRefreshTime": 1701000000000 } }, "status_code": 0 } """ let snapshot = try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(json.utf8), now: now) #expect(snapshot.planName == "Alibaba Coding Plan Pro") #expect(snapshot.fiveHourUsedQuota == 52) #expect(snapshot.fiveHourTotalQuota == 1000) #expect(snapshot.weeklyTotalQuota == 5000) #expect(snapshot.monthlyTotalQuota == 20000) #expect(snapshot.fiveHourNextRefreshTime == Date(timeIntervalSince1970: 1_700_000_300)) } @Test func `multi instance quota payload uses selected active instance plan name`() throws { let now = Date(timeIntervalSince1970: 1_700_000_000) let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Expired Starter", "status": "EXPIRED", "endTime": "2025-04-01 17:00", "codingPlanQuotaInfo": { "per5HourUsedQuota": 7, "per5HourTotalQuota": 100, "per5HourQuotaNextRefreshTime": 1700000100000 } }, { "planName": "Active Pro", "status": "VALID", "codingPlanQuotaInfo": { "per5HourUsedQuota": 52, "per5HourTotalQuota": 1000, "per5HourQuotaNextRefreshTime": 1700000300000 } } ] }, "status_code": 0 } """ let snapshot = try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(json.utf8), now: now) #expect(snapshot.planName == "Active Pro") #expect(snapshot.fiveHourUsedQuota == 52) #expect(snapshot.fiveHourTotalQuota == 1000) #expect(snapshot.fiveHourNextRefreshTime == Date(timeIntervalSince1970: 1_700_000_300)) } @Test func `missing quota data without positive active signal fails`() { let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Alibaba Coding Plan Pro" } ] }, "status_code": 0 } """ #expect(throws: AlibabaCodingPlanUsageError.self) { try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) } } @Test func `plan usage without positive active proof fails`() { let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Alibaba Coding Plan Pro", "planUsage": "18%" } ] }, "status_code": 0 } """ #expect(throws: AlibabaCodingPlanUsageError.self) { try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) } } @Test func `parses wrapped JSON string payload`() throws { let now = Date(timeIntervalSince1970: 1_700_000_000) let inner = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Coding Plan Lite", "status": "VALID", "codingPlanQuotaInfo": { "per5HourUsedQuota": 0, "per5HourTotalQuota": 1000, "per5HourQuotaNextRefreshTime": 1700000300000 } } ] }, "statusCode": 200 } """ .replacingOccurrences(of: "\n", with: "") .replacingOccurrences(of: " ", with: "") .replacingOccurrences(of: "\"", with: "\\\"") let wrapped = """ { "successResponse": { "body": "\(inner)" } } """ let snapshot = try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(wrapped.utf8), now: now) #expect(snapshot.planName == "Coding Plan Lite") #expect(snapshot.fiveHourTotalQuota == 1000) #expect(snapshot.fiveHourUsedQuota == 0) } @Test func `plan usage fallback stays visible but non quantitative`() throws { let now = Date(timeIntervalSince1970: 1_700_000_000) let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Coding Plan Lite", "status": "VALID", "planUsage": "0%", "endTime": "2026-04-01 17:00" } ] }, "status_code": 0 } """ let snapshot = try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(json.utf8), now: now) #expect(snapshot.planName == "Coding Plan Lite") #expect(snapshot.fiveHourUsedQuota == nil) #expect(snapshot.fiveHourTotalQuota == nil) #expect(snapshot.fiveHourNextRefreshTime == nil) let usage = snapshot.toUsageSnapshot() #expect(usage.primary == nil) #expect(usage.loginMethod(for: .alibaba) == "Coding Plan Lite") } @Test func `falls back to active plan when quota and usage missing`() throws { let now = Date(timeIntervalSince1970: 1_700_000_000) let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Coding Plan Lite", "status": "VALID", "endTime": "2026-04-01 17:00" } ] }, "status_code": 0 } """ let snapshot = try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(json.utf8), now: now) #expect(snapshot.planName == "Coding Plan Lite") #expect(snapshot.fiveHourUsedQuota == nil) #expect(snapshot.fiveHourTotalQuota == nil) let usage = snapshot.toUsageSnapshot() #expect(usage.primary == nil) #expect(usage.loginMethod(for: .alibaba) == "Coding Plan Lite") } @Test func `future end time counts as positive active signal`() throws { let now = Date(timeIntervalSince1970: 1_700_000_000) let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Coding Plan Lite", "endTime": "2030-04-01 17:00" } ] }, "status_code": 0 } """ let snapshot = try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(json.utf8), now: now) #expect(snapshot.planName == "Coding Plan Lite") #expect(snapshot.fiveHourUsedQuota == nil) #expect(snapshot.weeklyTotalQuota == nil) let usage = snapshot.toUsageSnapshot() #expect(usage.primary == nil) #expect(usage.loginMethod(for: .alibaba) == "Coding Plan Lite") } @Test func `multi instance fallback uses selected active instance plan name`() throws { let now = Date(timeIntervalSince1970: 1_700_000_000) let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Expired Starter", "status": "EXPIRED", "endTime": "2025-04-01 17:00" }, { "planName": "Active Pro", "status": "VALID", "planUsage": "42%" } ] }, "status_code": 0 } """ let snapshot = try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(json.utf8), now: now) #expect(snapshot.planName == "Active Pro") #expect(snapshot.fiveHourUsedQuota == nil) #expect(snapshot.fiveHourTotalQuota == nil) let usage = snapshot.toUsageSnapshot() #expect(usage.primary == nil) #expect(usage.loginMethod(for: .alibaba) == "Active Pro") } @Test func `active instance without quota does not borrow quota from another instance`() throws { let now = Date(timeIntervalSince1970: 1_700_000_000) let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Expired Starter", "status": "EXPIRED", "endTime": "2025-04-01 17:00", "codingPlanQuotaInfo": { "per5HourUsedQuota": 7, "per5HourTotalQuota": 100, "per5HourQuotaNextRefreshTime": 1700000100000 } }, { "planName": "Active Pro", "status": "VALID" } ] }, "status_code": 0 } """ let snapshot = try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(json.utf8), now: now) #expect(snapshot.planName == "Active Pro") #expect(snapshot.fiveHourUsedQuota == nil) #expect(snapshot.fiveHourTotalQuota == nil) #expect(snapshot.fiveHourNextRefreshTime == nil) } @Test func `payload level active proof does not label first instance when no instance is active`() { let now = Date(timeIntervalSince1970: 1_700_000_000) let json = """ { "data": { "status": "VALID", "codingPlanInstanceInfos": [ { "planName": "Expired Starter", "status": "EXPIRED", "endTime": "2025-04-01 17:00" }, { "planName": "No Proof Pro" } ] }, "status_code": 0 } """ #expect(throws: AlibabaCodingPlanUsageError.self) { try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(json.utf8), now: now) } } @Test func `does not fallback for inactive plan without quota`() { let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Coding Plan Lite", "status": "EXPIRED" } ] }, "status_code": 0 } """ #expect(throws: AlibabaCodingPlanUsageError.self) { try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) } } @Test func `console need login payload maps to login required`() { let json = """ { "code": "ConsoleNeedLogin", "message": "You need to log in.", "requestId": "abc", "successResponse": false } """ #expect(throws: AlibabaCodingPlanUsageError.loginRequired) { try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) } } @Test func `console need login payload maps to unavailable API key mode`() { let json = """ { "code": "ConsoleNeedLogin", "message": "You need to log in.", "requestId": "abc", "successResponse": false } """ #expect(throws: AlibabaCodingPlanUsageError.apiKeyUnavailableInRegion) { try AlibabaCodingPlanUsageFetcher.parseUsageSnapshot( from: Data(json.utf8), authMode: .apiKey) } } } @Suite(.serialized) struct AlibabaCodingPlanFallbackTests { private struct StubClaudeFetcher: ClaudeUsageFetching { func loadLatestUsage(model _: String) async throws -> ClaudeUsageSnapshot { throw ClaudeUsageError.parseFailed("stub") } func debugRawProbe(model _: String) async -> String { "stub" } func detectVersion() -> String? { nil } } private func makeContext( sourceMode: ProviderSourceMode, settings: ProviderSettingsSnapshot? = nil, env: [String: String] = [:]) -> ProviderFetchContext { let browserDetection = BrowserDetection(cacheTTL: 0) return ProviderFetchContext( runtime: .cli, sourceMode: sourceMode, includeCredits: false, webTimeout: 1, webDebugDumpHTML: false, verbose: false, env: env, settings: settings, fetcher: UsageFetcher(environment: env), claudeFetcher: StubClaudeFetcher(), browserDetection: browserDetection) } @Test func `falls back on TLS failure in auto mode`() { let strategy = AlibabaCodingPlanWebFetchStrategy() let context = self.makeContext(sourceMode: .auto) #expect(strategy.shouldFallback(on: URLError(.secureConnectionFailed), context: context)) } @Test func `does not fallback on TLS failure when source forced to web`() { let strategy = AlibabaCodingPlanWebFetchStrategy() let context = self.makeContext(sourceMode: .web) #expect(strategy.shouldFallback(on: URLError(.secureConnectionFailed), context: context) == false) } @Test func `auto mode does not borrow manual cookie authority when browser import fails`() { let strategy = AlibabaCodingPlanWebFetchStrategy() let settings = ProviderSettingsSnapshot.make( alibaba: ProviderSettingsSnapshot.AlibabaCodingPlanProviderSettings( cookieSource: .auto, manualCookieHeader: "session=manual-cookie", apiRegion: .international)) let context = self.makeContext(sourceMode: .auto, settings: settings) CookieHeaderCache.clear(provider: .alibaba) AlibabaCodingPlanCookieImporter.importSessionOverrideForTesting = { _, _ in throw AlibabaCodingPlanSettingsError.missingCookie() } defer { AlibabaCodingPlanCookieImporter.importSessionOverrideForTesting = nil } do { _ = try AlibabaCodingPlanWebFetchStrategy.resolveCookieHeader(context: context, allowCached: false) Issue.record("Expected auto mode to fail instead of borrowing the manual cookie header") } catch let error as AlibabaCodingPlanSettingsError { guard case .missingCookie = error else { Issue.record("Expected missingCookie, got \(error)") return } #expect(strategy.shouldFallback(on: error, context: context)) } catch { Issue.record("Expected AlibabaCodingPlanSettingsError, got \(error)") } } @Test func `auto mode skips web when no alibaba session is available`() async { let strategy = AlibabaCodingPlanWebFetchStrategy() let settings = ProviderSettingsSnapshot.make( alibaba: ProviderSettingsSnapshot.AlibabaCodingPlanProviderSettings( cookieSource: .auto, manualCookieHeader: nil, apiRegion: .international)) let context = self.makeContext( sourceMode: .auto, settings: settings, env: [AlibabaCodingPlanSettingsReader.apiTokenKey: "token-abc"]) CookieHeaderCache.clear(provider: .alibaba) AlibabaCodingPlanCookieImporter.importSessionOverrideForTesting = { _, _ in throw AlibabaCodingPlanSettingsError.missingCookie() } defer { AlibabaCodingPlanCookieImporter.importSessionOverrideForTesting = nil } #expect(await strategy.isAvailable(context) == false) } } struct AlibabaCodingPlanRegionTests { @Test func `defaults to international endpoint`() { let url = AlibabaCodingPlanUsageFetcher.resolveQuotaURL(region: .international, environment: [:]) #expect(url.host == "modelstudio.console.alibabacloud.com") #expect(url.path == "/data/api.json") } @Test func `uses china mainland host`() { let url = AlibabaCodingPlanUsageFetcher.resolveQuotaURL(region: .chinaMainland, environment: [:]) #expect(url.host == "bailian.console.aliyun.com") } @Test func `host override wins for quota URL`() { let env = [AlibabaCodingPlanSettingsReader.hostKey: "custom.aliyun.com"] let url = AlibabaCodingPlanUsageFetcher.resolveQuotaURL(region: .international, environment: env) #expect(url.host == "custom.aliyun.com") #expect(url.path == "/data/api.json") } @Test func `host override uses selected region for quota URL`() { let env = [AlibabaCodingPlanSettingsReader.hostKey: "custom.aliyun.com"] let url = AlibabaCodingPlanUsageFetcher.resolveQuotaURL(region: .chinaMainland, environment: env) #expect(url.host == "custom.aliyun.com") let components = URLComponents(url: url, resolvingAgainstBaseURL: false) let currentRegion = components?.queryItems?.first(where: { $0.name == "currentRegionId" })?.value #expect(currentRegion == AlibabaCodingPlanAPIRegion.chinaMainland.currentRegionID) } @Test func `bare host override builds console dashboard URL`() { let env = [AlibabaCodingPlanSettingsReader.hostKey: "custom.aliyun.com"] let url = AlibabaCodingPlanUsageFetcher.resolveConsoleDashboardURL(region: .international, environment: env) #expect(url.scheme == "https") #expect(url.host == "custom.aliyun.com") #expect(url.path == AlibabaCodingPlanAPIRegion.international.dashboardURL.path) let components = URLComponents(url: url, resolvingAgainstBaseURL: false) let tab = components?.queryItems?.first(where: { $0.name == "tab" })?.value #expect(tab == "coding-plan") } @Test func `quota url override beats host`() { let env = [ AlibabaCodingPlanSettingsReader.quotaURLKey: "https://modelstudio.console.alibabacloud.com/custom/quota", ] let url = AlibabaCodingPlanUsageFetcher.resolveQuotaURL(region: .international, environment: env) #expect(url.absoluteString == "https://modelstudio.console.alibabacloud.com/custom/quota") } @Test func `custom quota url override is preserved by default`() { let env = [AlibabaCodingPlanSettingsReader.quotaURLKey: "https://attacker.example/custom/quota"] let url = AlibabaCodingPlanUsageFetcher.resolveQuotaURL(region: .international, environment: env) #expect(url.host == "attacker.example") } @Test func `strict provider endpoint mode falls back to provider endpoint`() { let env = [ AlibabaCodingPlanSettingsReader.requireProviderEndpointOverridesKey: "true", AlibabaCodingPlanSettingsReader.quotaURLKey: "https://attacker.example/custom/quota", ] let url = AlibabaCodingPlanUsageFetcher.resolveQuotaURL(region: .international, environment: env) #expect(url.host == AlibabaCodingPlanAPIRegion.international.quotaURL.host) } @Test func `explicit endpoint override rejects invalid api scheme before network`() async { await #expect(throws: ProviderEndpointOverrideError.alibabaCodingPlan( AlibabaCodingPlanSettingsReader.quotaURLKey)) { _ = try await AlibabaCodingPlanUsageFetcher.fetchUsage( apiKey: "cpk-test", environment: [AlibabaCodingPlanSettingsReader .quotaURLKey: "http://modelstudio.console.alibabacloud.com/custom/quota"]) } } @Test func `explicit endpoint override rejects invalid cookie scheme before network`() async { await #expect(throws: ProviderEndpointOverrideError.alibabaCodingPlan( AlibabaCodingPlanSettingsReader.quotaURLKey)) { _ = try await AlibabaCodingPlanUsageFetcher.fetchUsage( cookieHeader: "login_aliyunid_ticket=ticket; login_aliyunid_pk=user", environment: [AlibabaCodingPlanSettingsReader .quotaURLKey: "http://modelstudio.console.alibabacloud.com/custom/quota"]) } } } @Suite(.serialized) struct AlibabaCodingPlanUsageFetcherRequestTests { @Test func `api401 maps to invalid credentials`() async throws { let transport = ProviderHTTPTransportHandler { request in guard let url = request.url else { throw URLError(.badURL) } let (response, data) = Self.makeResponse( url: url, body: #"{"message":"unauthorized"}"#, statusCode: 401) return (data, response) } await #expect(throws: AlibabaCodingPlanUsageError.invalidCredentials) { _ = try await AlibabaCodingPlanUsageFetcher.fetchUsage( apiKey: "cpk-test", region: .chinaMainland, environment: [ AlibabaCodingPlanSettingsReader.quotaURLKey: "https://bailian.console.aliyun.com/data/api.json", ], transport: transport) } } @Test func `cookie SEC token fallback survives user info request failure`() async throws { let registered = URLProtocol.registerClass(AlibabaConsoleSECTokenStubURLProtocol.self) defer { if registered { URLProtocol.unregisterClass(AlibabaConsoleSECTokenStubURLProtocol.self) } AlibabaConsoleSECTokenStubURLProtocol.handler = nil } AlibabaConsoleSECTokenStubURLProtocol.handler = { request in guard let url = request.url else { throw URLError(.badURL) } if url.host == "modelstudio.console.alibabacloud.com", request.httpMethod == "GET" { return Self.makeResponse(url: url, body: "", statusCode: 200) } if url.host == "modelstudio.console.alibabacloud.com", url.path == "/tool/user/info.json" { throw URLError(.timedOut) } if url.host == "bailian-singapore-cs.alibabacloud.com", request.httpMethod == "POST" { let body = Self.requestBodyString(from: request) #expect(body.contains("sec_token=cookie-sec-token")) let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Alibaba Coding Plan Pro", "status": "VALID" } ], "codingPlanQuotaInfo": { "per5HourUsedQuota": 52, "per5HourTotalQuota": 1000, "per5HourQuotaNextRefreshTime": 1700000300000 } }, "status_code": 0 } """ return Self.makeResponse(url: url, body: json, statusCode: 200) } throw URLError(.unsupportedURL) } let snapshot = try await AlibabaCodingPlanUsageFetcher.fetchUsage( cookieHeader: "sec_token=cookie-sec-token; login_aliyunid_ticket=ticket; login_aliyunid_pk=user", region: .international, environment: [:], now: Date(timeIntervalSince1970: 1_700_000_000)) #expect(snapshot.planName == "Alibaba Coding Plan Pro") #expect(snapshot.fiveHourUsedQuota == 52) #expect(snapshot.fiveHourTotalQuota == 1000) } @Test func `host override applies to user info SEC token fallback`() async throws { let registered = URLProtocol.registerClass(AlibabaConsoleSECTokenStubURLProtocol.self) defer { if registered { URLProtocol.unregisterClass(AlibabaConsoleSECTokenStubURLProtocol.self) } AlibabaConsoleSECTokenStubURLProtocol.handler = nil } AlibabaConsoleSECTokenStubURLProtocol.handler = { request in guard let url = request.url else { throw URLError(.badURL) } #expect(url.host == "modelstudio.console.alibabacloud.com") if request.httpMethod == "GET", url.path == AlibabaCodingPlanAPIRegion.international.dashboardURL.path { return Self.makeResponse(url: url, body: "", statusCode: 200) } if request.httpMethod == "GET", url.path == "/tool/user/info.json" { return Self.makeResponse( url: url, body: #"{"data":{"secToken":"override-sec-token"}}"#, statusCode: 200) } if request.httpMethod == "POST", url.path == "/data/api.json" { let body = Self.requestBodyString(from: request) #expect(body.contains("sec_token=override-sec-token")) let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Alibaba Coding Plan Pro", "status": "VALID" } ], "codingPlanQuotaInfo": { "per5HourUsedQuota": 21, "per5HourTotalQuota": 1000, "per5HourQuotaNextRefreshTime": 1700000300000 } }, "status_code": 0 } """ return Self.makeResponse(url: url, body: json, statusCode: 200) } throw URLError(.unsupportedURL) } let snapshot = try await AlibabaCodingPlanUsageFetcher.fetchUsage( cookieHeader: "sec_token=cookie-sec-token; login_aliyunid_ticket=ticket; login_aliyunid_pk=user", region: .international, environment: [AlibabaCodingPlanSettingsReader.hostKey: "https://modelstudio.console.alibabacloud.com"], now: Date(timeIntervalSince1970: 1_700_000_000)) #expect(snapshot.planName == "Alibaba Coding Plan Pro") #expect(snapshot.fiveHourUsedQuota == 21) #expect(snapshot.fiveHourTotalQuota == 1000) } @Test func `console request body uses region specific metadata`() async throws { let registered = URLProtocol.registerClass(AlibabaConsoleSECTokenStubURLProtocol.self) defer { if registered { URLProtocol.unregisterClass(AlibabaConsoleSECTokenStubURLProtocol.self) } AlibabaConsoleSECTokenStubURLProtocol.handler = nil } AlibabaConsoleSECTokenStubURLProtocol.handler = { request in guard let url = request.url else { throw URLError(.badURL) } if request.httpMethod == "GET", url.path == AlibabaCodingPlanAPIRegion.chinaMainland.dashboardURL.path { return Self.makeResponse(url: url, body: "", statusCode: 200) } if request.httpMethod == "GET", url.path == "/tool/user/info.json" { return Self.makeResponse(url: url, body: #"{"data":{"secToken":"cn-sec-token"}}"#, statusCode: 200) } if request.httpMethod == "POST", url.path == "/data/api.json" { let body = Self.requestBodyString(from: request) let params = try #require(Self.requestParamsDictionary(from: body)) let data = try #require(params["Data"] as? [String: Any]) let cornerstone = try #require(data["cornerstoneParam"] as? [String: Any]) #expect(cornerstone["domain"] as? String == AlibabaCodingPlanAPIRegion.chinaMainland.consoleDomain) #expect(cornerstone["consoleSite"] as? String == AlibabaCodingPlanAPIRegion.chinaMainland.consoleSite) #expect( cornerstone["feURL"] as? String == AlibabaCodingPlanAPIRegion.chinaMainland.dashboardURL.absoluteString) let json = """ { "data": { "codingPlanInstanceInfos": [ { "planName": "Alibaba Coding Plan Pro", "status": "VALID" } ], "codingPlanQuotaInfo": { "per5HourUsedQuota": 21, "per5HourTotalQuota": 1000, "per5HourQuotaNextRefreshTime": 1700000300000 } }, "status_code": 0 } """ return Self.makeResponse(url: url, body: json, statusCode: 200) } throw URLError(.unsupportedURL) } let snapshot = try await AlibabaCodingPlanUsageFetcher.fetchUsage( cookieHeader: "sec_token=cookie-sec-token; login_aliyunid_ticket=ticket; login_aliyunid_pk=user", region: .chinaMainland, environment: [:], now: Date(timeIntervalSince1970: 1_700_000_000)) #expect(snapshot.planName == "Alibaba Coding Plan Pro") #expect(snapshot.fiveHourUsedQuota == 21) #expect(snapshot.fiveHourTotalQuota == 1000) } private static func makeResponse(url: URL, body: String, statusCode: Int) -> (HTTPURLResponse, Data) { let response = HTTPURLResponse( url: url, statusCode: statusCode, httpVersion: "HTTP/1.1", headerFields: ["Content-Type": "application/json"])! return (response, Data(body.utf8)) } private static func requestBodyString(from request: URLRequest) -> String { if let data = request.httpBody { return String(data: data, encoding: .utf8) ?? "" } guard let stream = request.httpBodyStream else { return "" } stream.open() defer { stream.close() } var data = Data() let bufferSize = 4096 let buffer = UnsafeMutablePointer.allocate(capacity: bufferSize) defer { buffer.deallocate() } while stream.hasBytesAvailable { let count = stream.read(buffer, maxLength: bufferSize) if count <= 0 { break } data.append(buffer, count: count) } return String(data: data, encoding: .utf8) ?? "" } private static func requestParamsDictionary(from body: String) -> [String: Any]? { guard let components = URLComponents(string: "https://example.invalid/?\(body)"), let params = components.queryItems?.first(where: { $0.name == "params" })?.value, let data = params.data(using: .utf8) else { return nil } let object = try? JSONSerialization.jsonObject(with: data, options: []) return object as? [String: Any] } } final class AlibabaUsageFetcherStubURLProtocol: URLProtocol { nonisolated(unsafe) static var handler: ((URLRequest) throws -> (HTTPURLResponse, Data))? override static func canInit(with request: URLRequest) -> Bool { request.url?.host == "bailian.console.aliyun.com" } override static func canonicalRequest(for request: URLRequest) -> URLRequest { request } override func startLoading() { guard let handler = Self.handler else { self.client?.urlProtocol(self, didFailWithError: URLError(.badServerResponse)) return } do { let (response, data) = try handler(self.request) self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) self.client?.urlProtocol(self, didLoad: data) self.client?.urlProtocolDidFinishLoading(self) } catch { self.client?.urlProtocol(self, didFailWithError: error) } } override func stopLoading() {} } final class AlibabaConsoleSECTokenStubURLProtocol: URLProtocol { nonisolated(unsafe) static var handler: ((URLRequest) throws -> (HTTPURLResponse, Data))? override static func canInit(with request: URLRequest) -> Bool { guard let host = request.url?.host else { return false } return [ "modelstudio.console.alibabacloud.com", "bailian-singapore-cs.alibabacloud.com", "bailian.console.aliyun.com", "bailian-cs.console.aliyun.com", "bailian-beijing-cs.aliyuncs.com", ].contains(host) } override static func canonicalRequest(for request: URLRequest) -> URLRequest { request } override func startLoading() { guard let handler = Self.handler else { self.client?.urlProtocol(self, didFailWithError: URLError(.badServerResponse)) return } do { let (response, data) = try handler(self.request) self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) self.client?.urlProtocol(self, didLoad: data) self.client?.urlProtocolDidFinishLoading(self) } catch { self.client?.urlProtocol(self, didFailWithError: error) } } override func stopLoading() {} }