diff --git a/Sources/CodexBarCore/Providers/Zai/ZaiUsageStats.swift b/Sources/CodexBarCore/Providers/Zai/ZaiUsageStats.swift index 3443271967..39380095bf 100644 --- a/Sources/CodexBarCore/Providers/Zai/ZaiUsageStats.swift +++ b/Sources/CodexBarCore/Providers/Zai/ZaiUsageStats.swift @@ -84,7 +84,9 @@ extension ZaiLimitEntry { if let computed = self.computedUsedPercent { return computed } - return self.percentage + // The raw API percentage can fall outside 0...100 (z.ai omits/misreports quota fields); + // clamp it like computedUsedPercent and every sibling provider instead of surfacing it raw. + return min(100, max(0, self.percentage)) } public var windowMinutes: Int? { diff --git a/TestsLinux/ZaiUsedPercentLinuxTests.swift b/TestsLinux/ZaiUsedPercentLinuxTests.swift new file mode 100644 index 0000000000..481a475a31 --- /dev/null +++ b/TestsLinux/ZaiUsedPercentLinuxTests.swift @@ -0,0 +1,52 @@ +#if os(Linux) +import CodexBarCore +import Foundation +import Testing + +struct ZaiUsedPercentLinuxTests { + /// usage == nil forces computedUsedPercent to return nil, exercising the raw-percentage fallback. + private func fallbackUsedPercent(_ percentage: Double) -> Double { + ZaiLimitEntry( + type: .tokensLimit, + unit: .hours, + number: 5, + usage: nil, + currentValue: nil, + remaining: nil, + percentage: percentage, + usageDetails: [], + nextResetTime: nil).usedPercent + } + + @Test + func `raw percentage fallback clamps above 100`() { + #expect(self.fallbackUsedPercent(150) == 100) + } + + @Test + func `raw percentage fallback clamps below 0`() { + #expect(self.fallbackUsedPercent(-5) == 0) + } + + @Test + func `raw percentage fallback preserves an in-range value`() { + #expect(self.fallbackUsedPercent(42) == 42) + } + + @Test + func `computed path takes precedence and ignores the raw percentage`() { + // usage(limit)=100, currentValue(used)=25 -> computed 25%, so the raw 999 must not leak. + let entry = ZaiLimitEntry( + type: .tokensLimit, + unit: .hours, + number: 5, + usage: 100, + currentValue: 25, + remaining: nil, + percentage: 999, + usageDetails: [], + nextResetTime: nil) + #expect(entry.usedPercent == 25) + } +} +#endif