import Foundation import Testing @testable import CodexBarCore struct ZaiSettingsReaderTests { @Test func `api token reads from environment`() { let token = ZaiSettingsReader.apiToken(environment: ["Z_AI_API_KEY": "abc123"]) #expect(token == "abc123") } @Test func `api token strips quotes`() { let token = ZaiSettingsReader.apiToken(environment: ["Z_AI_API_KEY": "\"token-xyz\""]) #expect(token == "token-xyz") } @Test func `api host reads from environment`() { let host = ZaiSettingsReader.apiHost(environment: [ZaiSettingsReader.apiHostKey: " open.bigmodel.cn "]) #expect(host == "open.bigmodel.cn") } @Test func `quota URL infers scheme`() { let url = ZaiSettingsReader .quotaURL(environment: [ZaiSettingsReader.quotaURLKey: "open.bigmodel.cn/api/coding"]) #expect(url?.absoluteString == "https://open.bigmodel.cn/api/coding") } @Test func `endpoint override validation accepts HTTPS and bare hosts`() throws { try ZaiSettingsReader.validateEndpointOverrides(environment: [ ZaiSettingsReader.quotaURLKey: "https://open.bigmodel.cn/api/coding", ]) try ZaiSettingsReader.validateEndpointOverrides(environment: [ ZaiSettingsReader.apiHostKey: "open.bigmodel.cn", ]) } @Test func `endpoint override validation rejects insecure URLs`() { #expect(throws: ZaiSettingsError.invalidEndpointOverride(ZaiSettingsReader.quotaURLKey)) { try ZaiSettingsReader.validateEndpointOverrides(environment: [ ZaiSettingsReader.quotaURLKey: "http://attacker.test/quota", ]) } #expect(throws: ZaiSettingsError.invalidEndpointOverride(ZaiSettingsReader.apiHostKey)) { try ZaiSettingsReader.validateEndpointOverrides(environment: [ ZaiSettingsReader.apiHostKey: "http://attacker.test", ]) } } } struct ZaiUsageSnapshotTests { @Test func `maps usage snapshot windows`() { let reset = Date(timeIntervalSince1970: 123) let tokenLimit = ZaiLimitEntry( type: .tokensLimit, unit: .hours, number: 5, usage: 100, currentValue: 20, remaining: 80, percentage: 25, usageDetails: [], nextResetTime: reset) let timeLimit = ZaiLimitEntry( type: .timeLimit, unit: .days, number: 30, usage: 200, currentValue: 40, remaining: 160, percentage: 50, usageDetails: [], nextResetTime: nil) let snapshot = ZaiUsageSnapshot( tokenLimit: tokenLimit, timeLimit: timeLimit, planName: nil, updatedAt: reset) let usage = snapshot.toUsageSnapshot() #expect(usage.primary?.usedPercent == 20) #expect(usage.primary?.windowMinutes == 300) #expect(usage.primary?.resetsAt == reset) #expect(usage.primary?.resetDescription == "5 hours window") #expect(usage.secondary?.usedPercent == 20) #expect(usage.secondary?.resetDescription == "30 days window") #expect(usage.tertiary == nil) #expect(usage.zaiUsage?.tokenLimit?.usage == 100) #expect(usage.zaiUsage?.sessionTokenLimit == nil) } @Test func `maps usage snapshot windows with missing fields`() { let reset = Date(timeIntervalSince1970: 123) let tokenLimit = ZaiLimitEntry( type: .tokensLimit, unit: .hours, number: 5, usage: nil, currentValue: nil, remaining: nil, percentage: 25, usageDetails: [], nextResetTime: reset) let snapshot = ZaiUsageSnapshot( tokenLimit: tokenLimit, timeLimit: nil, planName: nil, updatedAt: reset) let usage = snapshot.toUsageSnapshot() #expect(usage.primary?.usedPercent == 25) #expect(usage.primary?.windowMinutes == 300) #expect(usage.primary?.resetsAt == reset) #expect(usage.primary?.resetDescription == "5 hours window") #expect(usage.zaiUsage?.tokenLimit?.usage == nil) } @Test func `maps usage snapshot windows with missing remaining uses current value`() { let reset = Date(timeIntervalSince1970: 123) let tokenLimit = ZaiLimitEntry( type: .tokensLimit, unit: .hours, number: 5, usage: 100, currentValue: 20, remaining: nil, percentage: 25, usageDetails: [], nextResetTime: reset) let snapshot = ZaiUsageSnapshot( tokenLimit: tokenLimit, timeLimit: nil, planName: nil, updatedAt: reset) let usage = snapshot.toUsageSnapshot() #expect(usage.primary?.usedPercent == 20) } @Test func `maps usage snapshot windows with missing current value uses remaining`() { let reset = Date(timeIntervalSince1970: 123) let tokenLimit = ZaiLimitEntry( type: .tokensLimit, unit: .hours, number: 5, usage: 100, currentValue: nil, remaining: 80, percentage: 25, usageDetails: [], nextResetTime: reset) let snapshot = ZaiUsageSnapshot( tokenLimit: tokenLimit, timeLimit: nil, planName: nil, updatedAt: reset) let usage = snapshot.toUsageSnapshot() #expect(usage.primary?.usedPercent == 20) } @Test func `maps usage snapshot windows with missing remaining and current value falls back to percentage`() { let reset = Date(timeIntervalSince1970: 123) let tokenLimit = ZaiLimitEntry( type: .tokensLimit, unit: .hours, number: 5, usage: 100, currentValue: nil, remaining: nil, percentage: 25, usageDetails: [], nextResetTime: reset) let snapshot = ZaiUsageSnapshot( tokenLimit: tokenLimit, timeLimit: nil, planName: nil, updatedAt: reset) let usage = snapshot.toUsageSnapshot() #expect(usage.primary?.usedPercent == 25) } } struct ZaiUsageParsingTests { @Test func `empty body returns parse failed`() { #expect { _ = try ZaiUsageFetcher.parseUsageSnapshot(from: Data()) } throws: { error in guard case let ZaiUsageError.parseFailed(message) = error else { return false } return message == "Empty response body" } } @Test func `parses usage response`() throws { let json = """ { "code": 200, "msg": "Operation successful", "data": { "limits": [ { "type": "TIME_LIMIT", "unit": 5, "number": 1, "usage": 100, "currentValue": 102, "remaining": 0, "percentage": 100, "usageDetails": [ { "modelCode": "search-prime", "usage": 95 } ] }, { "type": "TOKENS_LIMIT", "unit": 3, "number": 5, "usage": 40000000, "currentValue": 13628365, "remaining": 26371635, "percentage": 34, "nextResetTime": 1768507567547 } ], "planName": "Pro" }, "success": true } """ let snapshot = try ZaiUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) #expect(snapshot.planName == "Pro") #expect(snapshot.tokenLimit?.usage == 40_000_000) #expect(snapshot.timeLimit?.usageDetails.first?.modelCode == "search-prime") #expect(snapshot.tokenLimit?.percentage == 34.0) let usage = snapshot.toUsageSnapshot() #expect(usage.secondary?.windowMinutes == nil) #expect(usage.secondary?.resetDescription == "Monthly") } @Test func `zai mcp time limit displays monthly instead of one minute window`() throws { let json = """ { "code": 200, "msg": "Operation successful", "data": { "limits": [ { "type": "TIME_LIMIT", "unit": 5, "number": 1, "usage": 100, "currentValue": 50, "remaining": 50, "percentage": 50, "usageDetails": [] }, { "type": "TOKENS_LIMIT", "unit": 3, "number": 5, "percentage": 34, "nextResetTime": 1768507567547 } ] }, "success": true } """ let snapshot = try ZaiUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) let usage = snapshot.toUsageSnapshot() #expect(snapshot.timeLimit?.windowDescription == "1 minute") #expect(usage.secondary?.windowMinutes == nil) #expect(usage.secondary?.resetDescription == "Monthly") } @Test func `missing data returns api error`() { let json = """ { "code": 1001, "msg": "Authorization Token Missing", "success": false } """ #expect { _ = try ZaiUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) } throws: { error in guard case let ZaiUsageError.apiError(message) = error else { return false } return message == "Authorization Token Missing" } } @Test func `failed response without message reports the API code`() { let json = """ { "code": 1001, "success": false } """ #expect { _ = try ZaiUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) } throws: { error in guard case let ZaiUsageError.apiError(message) = error else { return false } return message == "Z.ai quota API returned code 1001" } } @Test func `success without data returns parse failed`() { let json = """ { "code": 200, "msg": "Operation successful", "success": true } """ #expect { _ = try ZaiUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) } throws: { error in guard case let ZaiUsageError.parseFailed(message) = error else { return false } return message == "Missing data" } } @Test func `success without limits parses empty usage`() throws { let json = """ { "code": 200, "msg": "Operation successful", "data": { "planName": "Pro" }, "success": true } """ let snapshot = try ZaiUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) #expect(snapshot.planName == "Pro") #expect(snapshot.tokenLimit == nil) #expect(snapshot.timeLimit == nil) } @Test func `parses new schema with missing token limit fields`() throws { let json = """ { "code": 200, "msg": "Operation successful", "data": { "limits": [ { "type": "TIME_LIMIT", "unit": 5, "number": 1, "usage": 100, "currentValue": 0, "remaining": 100, "percentage": 0, "usageDetails": [ { "modelCode": "search-prime", "usage": 0 }, { "modelCode": "web-reader", "usage": 1 }, { "modelCode": "zread", "usage": 0 } ] }, { "type": "TOKENS_LIMIT", "unit": 3, "number": 5, "percentage": 1, "nextResetTime": 1770724088678 } ] }, "success": true } """ let snapshot = try ZaiUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) #expect(snapshot.tokenLimit?.percentage == 1.0) #expect(snapshot.tokenLimit?.usage == nil) #expect(snapshot.tokenLimit?.currentValue == nil) #expect(snapshot.tokenLimit?.remaining == nil) #expect(snapshot.tokenLimit?.usedPercent == 1.0) #expect(snapshot.tokenLimit?.windowMinutes == 300) #expect(snapshot.timeLimit?.usage == 100) } @Test func `parses BigModel CN quota response without message`() throws { let json = """ { "code": 200, "data": { "limits": [ { "type": "TIME_LIMIT", "unit": 5, "number": 1, "usage": 1000, "currentValue": 147, "remaining": 853, "percentage": 14, "nextResetTime": 1784706344993, "usageDetails": [ { "modelCode": "search-prime", "usage": 84 }, { "modelCode": "web-reader", "usage": 41 }, { "modelCode": "zread", "usage": 8 } ] }, { "type": "TOKENS_LIMIT", "unit": 3, "number": 5, "percentage": 8, "nextResetTime": 1783049703178 }, { "type": "TOKENS_LIMIT", "unit": 6, "number": 1, "percentage": 7, "nextResetTime": 1783496744998 } ], "level": "pro" }, "success": true } """ let snapshot = try ZaiUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) let usage = snapshot.toUsageSnapshot() #expect(usage.primary?.usedPercent == 7) #expect(usage.secondary?.usedPercent == 14.7) #expect(usage.tertiary?.usedPercent == 8) } } struct ZaiBigModelTeamScopeTests { @Test func `team scope appends type 2 and sends BigModel project headers`() async throws { let transport = ProviderHTTPTransportStub { request in let json = """ { "code": 200, "msg": "操作成功", "data": { "level": "pro", "limits": [ { "type": "TIME_LIMIT", "unit": 5, "number": 1, "usage": 1000, "currentValue": 224, "remaining": 776, "percentage": 22, "nextResetTime": 1777575229998, "usageDetails": [] }, { "type": "TOKENS_LIMIT", "unit": 3, "number": 5, "percentage": 25, "nextResetTime": 1775020168897 }, { "type": "TOKENS_LIMIT", "unit": 6, "number": 1, "percentage": 9, "nextResetTime": 1775588029998 } ] }, "success": true } """ return ( Data(json.utf8), HTTPURLResponse( url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!) } let snapshot = try await ZaiUsageFetcher.fetchUsage( apiKey: "zai-test-token", region: .bigmodelCN, usageScope: .team, teamContext: ZaiBigModelTeamContext( organizationID: "org-test", projectID: "proj-test"), environment: [:], transport: transport) let requests = await transport.requests() let request = try #require(requests.first) #expect(request.url?.absoluteString == "https://open.bigmodel.cn/api/monitor/usage/quota/limit?type=2") #expect(request.value(forHTTPHeaderField: "Authorization") == "Bearer zai-test-token") #expect(request.value(forHTTPHeaderField: "Bigmodel-Organization") == "org-test") #expect(request.value(forHTTPHeaderField: "Bigmodel-Project") == "proj-test") #expect(snapshot.tokenLimit?.unit == .weeks) #expect(snapshot.sessionTokenLimit?.unit == .hours) #expect(snapshot.timeLimit?.usage == 1000) } @Test func `personal scope keeps existing quota URL and omits team headers`() async throws { let transport = ProviderHTTPTransportStub { request in let json = """ { "code": 200, "msg": "Operation successful", "data": { "limits": [ { "type": "TOKENS_LIMIT", "unit": 3, "number": 5, "percentage": 34, "nextResetTime": 1768507567547 } ] }, "success": true } """ return ( Data(json.utf8), HTTPURLResponse( url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!) } _ = try await ZaiUsageFetcher.fetchUsage( apiKey: "zai-test-token", region: .bigmodelCN, usageScope: .personal, teamContext: ZaiBigModelTeamContext( organizationID: "org-test", projectID: "proj-test"), environment: [:], transport: transport) let requests = await transport.requests() let request = try #require(requests.first) #expect(request.url?.absoluteString == "https://open.bigmodel.cn/api/monitor/usage/quota/limit") #expect(request.value(forHTTPHeaderField: "Bigmodel-Organization") == nil) #expect(request.value(forHTTPHeaderField: "Bigmodel-Project") == nil) } @Test func `team scope requires complete BigModel context`() async { let transport = ProviderHTTPTransportStub { request in ( Data(), HTTPURLResponse( url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!) } await self.expectMissingTeamContext { _ = try await ZaiUsageFetcher.fetchUsage( apiKey: "zai-test-token", region: .bigmodelCN, usageScope: .team, teamContext: nil, environment: [:], transport: transport) } let requests = await transport.requests() #expect(requests.isEmpty) await self.expectMissingTeamContext { _ = try await ZaiUsageFetcher.fetchUsage( apiKey: "zai-test-token", region: .bigmodelCN, usageScope: .team, teamContext: nil, environment: [ZaiSettingsReader.bigModelOrganizationKey: "org-only"], transport: transport) } await self.expectMissingTeamContext { _ = try await ZaiUsageFetcher.fetchUsage( apiKey: "zai-test-token", region: .bigmodelCN, usageScope: .team, teamContext: nil, environment: [ZaiSettingsReader.bigModelProjectKey: "proj-only"], transport: transport) } } @Test func `team model usage appends type 3 and sends BigModel project headers`() async throws { let transport = ProviderHTTPTransportStub { request in let json = """ { "code": 200, "msg": "success", "success": true, "data": { "x_time": ["2026-06-21 08:00"], "modelDataList": [ { "modelName": "glm-4.6", "tokensUsage": [100] } ] } } """ return ( Data(json.utf8), HTTPURLResponse( url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!) } let usage = try await ZaiUsageFetcher.fetchModelUsage( apiKey: "zai-test-token", region: .bigmodelCN, usageScope: .team, teamContext: ZaiBigModelTeamContext( organizationID: "org-test", projectID: "proj-test"), environment: [:], transport: transport) let requests = await transport.requests() let request = try #require(requests.first) let requestURL = try #require(request.url) let components = try #require(URLComponents(url: requestURL, resolvingAgainstBaseURL: false)) #expect(components.path == "/api/monitor/usage/model-usage") #expect(components.queryItems?.first { $0.name == "type" }?.value == "3") #expect(request.value(forHTTPHeaderField: "Authorization") == "Bearer zai-test-token") #expect(request.value(forHTTPHeaderField: "Bigmodel-Organization") == "org-test") #expect(request.value(forHTTPHeaderField: "Bigmodel-Project") == "proj-test") #expect(usage.modelNames == ["glm-4.6"]) } @Test func `team quota rejects insecure override before sending credentials`() async throws { let transport = ProviderHTTPTransportStub { request in Issue.record("Unexpected z.ai team quota request to \(request.url?.absoluteString ?? "")") throw URLError(.badURL) } await #expect(throws: ZaiSettingsError.invalidEndpointOverride(ZaiSettingsReader.quotaURLKey)) { try await ZaiUsageFetcher.fetchUsage( apiKey: "zai-test-token", region: .bigmodelCN, usageScope: .team, teamContext: ZaiBigModelTeamContext( organizationID: "org-test", projectID: "proj-test"), environment: [ZaiSettingsReader.quotaURLKey: "http://attacker.test/quota"], transport: transport) } let requests = await transport.requests() #expect(requests.isEmpty) } @Test func `team model usage rejects insecure API host before sending credentials`() async throws { let transport = ProviderHTTPTransportStub { request in Issue.record("Unexpected z.ai team model usage request to \(request.url?.absoluteString ?? "")") throw URLError(.badURL) } await #expect(throws: ZaiSettingsError.invalidEndpointOverride(ZaiSettingsReader.apiHostKey)) { try await ZaiUsageFetcher.fetchModelUsage( apiKey: "zai-test-token", region: .bigmodelCN, usageScope: .team, teamContext: ZaiBigModelTeamContext( organizationID: "org-test", projectID: "proj-test"), environment: [ZaiSettingsReader.apiHostKey: "http://attacker.test"], transport: transport) } let requests = await transport.requests() #expect(requests.isEmpty) } private func expectMissingTeamContext(_ operation: () async throws -> Void) async { do { try await operation() Issue.record("Expected z.ai missing team context error.") } catch ZaiUsageError.missingTeamContext { // Expected. } catch { Issue.record("Expected z.ai missing team context error, got \(error).") } } @Test func `team context can be resolved from environment`() { let env = [ ZaiSettingsReader.bigModelOrganizationKey: " org-env ", ZaiSettingsReader.bigModelProjectKey: " proj-env ", ] #expect(ZaiBigModelTeamContext(environment: env)?.organizationID == "org-env") #expect(ZaiBigModelTeamContext(environment: env)?.projectID == "proj-env") } } struct ZaiHourlyUsageTests { @Test func `model usage parser decodes hourly model payload`() throws { let json = """ { "code": 200, "msg": "success", "success": true, "data": { "x_time": ["2026-05-14 08:00", "2026-05-14 09:00"], "modelDataList": [ { "modelName": "glm-4.6", "tokensUsage": [100, null] }, { "modelName": "glm-4.5", "tokensUsage": [50, 25] } ] } } """ let usage = try ZaiUsageFetcher.parseModelUsage(from: Data(json.utf8)) #expect(usage.xTime == ["2026-05-14 08:00", "2026-05-14 09:00"]) #expect(usage.modelNames == ["glm-4.6", "glm-4.5"]) #expect(usage.modelDataList[0].tokensUsage == [100, nil]) #expect(usage.modelDataList[1].tokensUsage == [50, 25]) } @Test func `today hourly bars filter earlier days and skip empty hours`() { let reference = Self.localDate(year: 2026, month: 5, day: 14, hour: 12) let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: reference) ?? reference let modelData = ZaiModelUsageData( xTime: [ Self.hourString(yesterday), "2026-05-14 08:00", "2026-05-14 09:00", ], modelDataList: [ ZaiModelDataItem(modelName: "glm-4.6", tokensUsage: [999, 100, 0]), ZaiModelDataItem(modelName: "glm-4.5", tokensUsage: [0, 50, nil]), ]) let bars = ZaiHourlyBars.from(modelData: modelData, range: .today(referenceDate: reference), now: reference) #expect(bars.map(\.label) == ["08"]) #expect(bars.first?.totalTokens == 150) #expect(bars.first?.segments.count == 2) } @Test func `last 24 hour bars filter data outside trailing window`() { let reference = Self.localDate(year: 2026, month: 5, day: 14, hour: 12) let old = Calendar.current.date(byAdding: .hour, value: -25, to: reference) ?? reference let inWindow = Calendar.current.date(byAdding: .hour, value: -23, to: reference) ?? reference let modelData = ZaiModelUsageData( xTime: [ Self.hourString(old), Self.hourString(inWindow), Self.hourString(reference), ], modelDataList: [ ZaiModelDataItem(modelName: "glm-4.6", tokensUsage: [10, 20, 30]), ]) let bars = ZaiHourlyBars.from(modelData: modelData, range: .last24h, now: reference) #expect(bars.map(\.label) == [Self.hourLabel(inWindow), Self.hourLabel(reference)]) #expect(bars.map(\.totalTokens) == [20, 30]) } private static func localDate(year: Int, month: Int, day: Int, hour: Int) -> Date { Calendar.current.date(from: DateComponents(year: year, month: month, day: day, hour: hour)) ?? Date() } private static func hourString(_ date: Date) -> String { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm" formatter.locale = Locale(identifier: "en_US_POSIX") return formatter.string(from: date) } private static func hourLabel(_ date: Date) -> String { let formatter = DateFormatter() formatter.dateFormat = "HH" formatter.locale = Locale(identifier: "en_US_POSIX") return formatter.string(from: date) } } struct ZaiThreeLimitTests { @Test func `parses three limit entries into session weekly and mcp slots`() throws { let json = """ { "code": 200, "msg": "操作成功", "data": { "limits": [ { "type": "TOKENS_LIMIT", "unit": 3, "number": 5, "percentage": 25, "nextResetTime": 1775020168897 }, { "type": "TOKENS_LIMIT", "unit": 6, "number": 1, "percentage": 9, "nextResetTime": 1775588029998 }, { "type": "TIME_LIMIT", "unit": 5, "number": 1, "usage": 1000, "currentValue": 224, "remaining": 776, "percentage": 22, "nextResetTime": 1777575229998, "usageDetails": [ { "modelCode": "search-prime", "usage": 210 }, { "modelCode": "web-reader", "usage": 14 } ] } ], "level": "pro" }, "success": true } """ let snapshot = try ZaiUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) // Weekly token limit (unit:6=weeks, longer window) → tokenLimit (primary) #expect(snapshot.tokenLimit?.unit == .weeks) #expect(snapshot.tokenLimit?.number == 1) #expect(snapshot.tokenLimit?.percentage == 9.0) #expect(snapshot.tokenLimit?.windowMinutes == 10080) // 5-hour token limit (unit:3=hours, number:5 → 300 min) → sessionTokenLimit (tertiary) #expect(snapshot.sessionTokenLimit?.unit == .hours) #expect(snapshot.sessionTokenLimit?.number == 5) #expect(snapshot.sessionTokenLimit?.percentage == 25.0) #expect(snapshot.sessionTokenLimit?.windowMinutes == 300) // MCP time limit → timeLimit (secondary) #expect(snapshot.timeLimit?.usage == 1000) #expect(snapshot.timeLimit?.usageDetails.first?.modelCode == "search-prime") // UsageSnapshot slot mapping let usage = snapshot.toUsageSnapshot() #expect(usage.primary?.usedPercent == 9.0) #expect(usage.primary?.windowMinutes == 10080) #expect(usage.secondary != nil) // MCP #expect(usage.tertiary?.usedPercent == 25.0) #expect(usage.tertiary?.windowMinutes == 300) } @Test func `unit 6 maps to weeks with correct window minutes`() { let entry = ZaiLimitEntry( type: .tokensLimit, unit: .weeks, number: 1, usage: nil, currentValue: nil, remaining: nil, percentage: 9, usageDetails: [], nextResetTime: nil) #expect(entry.windowMinutes == 10080) #expect(entry.windowDescription == "1 week") #expect(entry.windowLabel == "1 week window") } @Test func `two limit entries remain backward compatible`() throws { let json = """ { "code": 200, "msg": "Operation successful", "data": { "limits": [ { "type": "TIME_LIMIT", "unit": 5, "number": 1, "usage": 100, "currentValue": 50, "remaining": 50, "percentage": 50, "usageDetails": [] }, { "type": "TOKENS_LIMIT", "unit": 3, "number": 5, "percentage": 34, "nextResetTime": 1768507567547 } ] }, "success": true } """ let snapshot = try ZaiUsageFetcher.parseUsageSnapshot(from: Data(json.utf8)) #expect(snapshot.tokenLimit != nil) #expect(snapshot.sessionTokenLimit == nil) #expect(snapshot.timeLimit != nil) let usage = snapshot.toUsageSnapshot() #expect(usage.primary != nil) #expect(usage.secondary != nil) #expect(usage.tertiary == nil) } } struct ZaiAPIRegionTests { @Test func `dashboard URLs follow selected region`() { #expect( ZaiAPIRegion.global.dashboardURL.absoluteString == "https://z.ai/manage-apikey/coding-plan/personal/my-plan") #expect( ZaiAPIRegion.bigmodelCN.dashboardURL.absoluteString == "https://bigmodel.cn/coding-plan/personal/usage") #expect( ZaiProviderDescriptor.descriptor.metadata.dashboardURL == ZaiAPIRegion.global.dashboardURL.absoluteString) } @Test func `defaults to global endpoint`() { let url = ZaiUsageFetcher.resolveQuotaURL(region: .global, environment: [:]) #expect(url.absoluteString == "https://api.z.ai/api/monitor/usage/quota/limit") } @Test func `uses big model region when selected`() { let url = ZaiUsageFetcher.resolveQuotaURL(region: .bigmodelCN, environment: [:]) #expect(url.absoluteString == "https://open.bigmodel.cn/api/monitor/usage/quota/limit") } @Test func `quota url environment override wins`() { let env = [ZaiSettingsReader.quotaURLKey: "https://open.bigmodel.cn/api/coding/paas/v4"] let url = ZaiUsageFetcher.resolveQuotaURL(region: .global, environment: env) #expect(url.absoluteString == "https://open.bigmodel.cn/api/coding/paas/v4") } @Test func `api host environment appends quota path`() { let env = [ZaiSettingsReader.apiHostKey: "open.bigmodel.cn"] let url = ZaiUsageFetcher.resolveQuotaURL(region: .global, environment: env) #expect(url.absoluteString == "https://open.bigmodel.cn/api/monitor/usage/quota/limit") } @Test func `dashboard follows known endpoint overrides`() { let china = ZaiUsageFetcher.resolveDashboardURL( region: .global, environment: [ZaiSettingsReader.apiHostKey: "open.bigmodel.cn"]) #expect(china == ZaiAPIRegion.bigmodelCN.dashboardURL) let global = ZaiUsageFetcher.resolveDashboardURL( region: .bigmodelCN, environment: [ZaiSettingsReader.apiHostKey: "api.z.ai"]) #expect(global == ZaiAPIRegion.global.dashboardURL) } @Test func `dashboard keeps selected region for custom endpoint override`() { let dashboard = ZaiUsageFetcher.resolveDashboardURL( region: .bigmodelCN, environment: [ZaiSettingsReader.apiHostKey: "zai.internal.example"]) #expect(dashboard == ZaiAPIRegion.bigmodelCN.dashboardURL) } }