tests/tst_provider_normalizer.qml
import QtQuick
import QtTest
import "../contents/ui/ProviderNormalizer.js" as Normalizer
// The normalizer is the only place that turns untrusted `codexbar` JSON into the
// structures the popup renders, so these tests are written from the payload the
// CLI could plausibly emit while broken or hostile, not from the happy path.
TestCase {
name: "ProviderNormalizer"
function unsafeKeys() {
return ["__proto__", "prototype", "constructor"]
}
// --- bounds -------------------------------------------------------------
//
// The numbers themselves are the contract: `scripts/test_security_regressions.sh`
// asserts the call sites, and these pin the values those call sites read.
function test_publishesTheDocumentedCollectionBounds() {
compare(Normalizer.maximumProviderSnapshots, 256)
compare(Normalizer.maximumAccountIdentityLength, 256)
compare(Normalizer.maximumAccountSnapshots, 128)
compare(Normalizer.maximumCostSnapshots, 256)
compare(Normalizer.maximumExtraRateWindows, 24)
compare(Normalizer.maximumSessions, 128)
compare(Normalizer.maximumCostHistoryPoints, 365)
compare(Normalizer.maximumModelBreakdownsPerDay, 128)
compare(Normalizer.maximumCostModelRows, 6)
}
// --- object-key guards --------------------------------------------------
function test_rejectsPrototypePollutingKeys() {
var keys = unsafeKeys()
for (var i = 0; i < keys.length; i++) {
verify(Normalizer.isUnsafeObjectKey(keys[i]), keys[i] + " must be rejected")
}
verify(!Normalizer.isUnsafeObjectKey("codex"))
verify(!Normalizer.isUnsafeObjectKey("toString"))
}
function test_hasOwnKeyIgnoresInheritedPrototypeMembers() {
var item = { own: 1 }
verify(Normalizer.hasOwnKey(item, "own"))
verify(!Normalizer.hasOwnKey(item, "toString"))
verify(!Normalizer.hasOwnKey(item, "hasOwnProperty"))
// A payload that shadows hasOwnProperty must not be able to lie about
// its own keys, which is why the guard calls through Object.prototype.
verify(!Normalizer.hasOwnKey({ hasOwnProperty: function() { return true } }, "anything"))
verify(!Normalizer.hasOwnKey(null, "own"))
}
function test_copyObjectStripsPollutingKeysAndInheritedMembers() {
var payload = JSON.parse('{"provider":"codex","__proto__":{"polluted":true},"constructor":"x","prototype":"y"}')
var copy = Normalizer.copyObject(payload)
compare(copy.provider, "codex")
var keys = unsafeKeys()
for (var i = 0; i < keys.length; i++) {
verify(!Normalizer.hasOwnKey(copy, keys[i]), keys[i] + " must not survive the copy")
}
compare(({}).polluted, undefined)
}
// --- provider identity --------------------------------------------------
function test_resolvesCliAliasesToTheCanonicalProviderKey() {
compare(Normalizer.normalizedProviderID("groqcloud"), "groq")
compare(Normalizer.normalizedProviderID("alibaba-coding-plan"), "alibaba")
compare(Normalizer.normalizedProviderID(" codex "), "codex")
}
function test_rejectsProviderIDsThatAreNotUsableMapKeys() {
compare(Normalizer.normalizedProviderID(""), "")
compare(Normalizer.normalizedProviderID(" "), "")
compare(Normalizer.normalizedProviderID(null), "")
compare(Normalizer.normalizedProviderID(undefined), "")
compare(Normalizer.normalizedProviderID(42), "")
compare(Normalizer.normalizedProviderID({ provider: "codex" }), "")
compare(Normalizer.normalizedProviderID("prototype"), "")
compare(Normalizer.normalizedProviderID("__proto__"), "")
compare(Normalizer.normalizedProviderID("constructor"), "")
compare(Normalizer.normalizedProviderID("codex\nevil"), "")
}
function test_lowercasingMakesPrototypeMemberNamesSafeProviderKeys() {
// The key is lowercased before the Object.prototype screen, so "toString"
// becomes "tostring": a distinct key that cannot collide with a prototype
// member. Rejecting it instead would lose a legitimate provider id.
compare(Normalizer.normalizedProviderID("toString"), "tostring")
compare(Normalizer.normalizedProviderID("hasOwnProperty"), "hasownproperty")
verify(!Normalizer.hasOwnKey({}, "tostring"))
}
function test_rejectsOversizedProviderIDs() {
var oversized = ""
for (var i = 0; i < 200; i++) {
oversized += "a"
}
compare(Normalizer.normalizedProviderID(oversized), "")
}
// --- provider config ----------------------------------------------------
function test_keepsDisplayNamesForDisabledProvidersButOnlyEnabledIDs() {
var entries = Normalizer.normalizeProviderConfigEntries([
{ provider: "codex", displayName: "Codex", enabled: true },
{ provider: "claude", displayName: "Claude", enabled: false },
{ provider: "groqcloud", displayName: "Groq", enabled: true }
])
compare(entries.providerIDs.length, 2)
compare(entries.providerIDs[0], "codex")
compare(entries.providerIDs[1], "groq")
compare(entries.displayNames["claude"], "Claude")
compare(entries.displayNames["groq"], "Groq")
}
function test_providerConfigDistinguishesAnEmptyRosterFromAnUnsupportedEnvelope() {
var emptyEntries = Normalizer.normalizeProviderConfigEntries([])
compare(emptyEntries.providerIDs.length, 0)
compare(Object.keys(emptyEntries.displayNames).length, 0)
compare(Normalizer.normalizeProviderConfigEntries({
provider: "cli",
error: { message: "command failed" }
}), null)
compare(Normalizer.normalizeProviderConfigEntries([{
provider: "cli",
error: { message: "command failed" }
}]), null)
}
function test_providerConfigAcceptsASingleDisabledProviderRecord() {
var entries = Normalizer.normalizeProviderConfigEntries({
provider: "claude",
displayName: "Claude",
enabled: false
})
compare(entries.providerIDs.length, 0)
compare(entries.displayNames["claude"], "Claude")
}
function test_providerConfigDropsPollutingProviderIDsWithoutLosingTheOthers() {
var entries = Normalizer.normalizeProviderConfigEntries(JSON.parse(
'[{"provider":"__proto__","displayName":"Evil","enabled":true},'
+ '{"provider":"constructor","displayName":"Evil","enabled":true},'
+ '{"provider":"codex","displayName":"Codex","enabled":true}]'))
compare(entries.providerIDs.length, 1)
compare(entries.providerIDs[0], "codex")
compare(entries.displayNames["codex"], "Codex")
compare(({}).polluted, undefined)
}
function test_providerConfigDeduplicatesAliasesOfTheSameProvider() {
var entries = Normalizer.normalizeProviderConfigEntries([
{ provider: "groqcloud", enabled: true },
{ provider: "groq", enabled: true },
{ provider: "groq-api", enabled: true }
])
compare(entries.providerIDs.length, 1)
compare(entries.providerIDs[0], "groq")
}
function test_providerConfigSurvivesMalformedEntries() {
var entries = Normalizer.normalizeProviderConfigEntries([
null,
"codex",
{ enabled: true },
{ provider: "codex", displayName: { nested: "object" }, enabled: true },
{ provider: "claude", displayName: "Claude", enabled: true }
])
compare(entries.providerIDs.length, 2)
// A structured displayName is dropped, not stringified, and the other
// providers survive the malformed entry.
compare(entries.displayNames["codex"], undefined)
compare(entries.displayNames["claude"], "Claude")
}
function test_providerSnapshotsDeduplicateAliasesAndPreferHealthyData() {
var snapshots = Normalizer.dedupeProviderSnapshots([
{ provider: "groqcloud", error: "temporary failure", marker: "error" },
{ provider: "groq", error: "", marker: "healthy" },
{ provider: "claude", error: "", marker: "first" },
{ provider: "claude", error: "", marker: "later" },
{ provider: "alibaba-coding-plan", error: "", marker: "alias" }
])
compare(snapshots.length, 3)
compare(snapshots[0].marker, "healthy")
compare(snapshots[1].marker, "first")
compare(snapshots[2].provider, "alibaba")
}
// --- Codex monthly credit limit ---------------------------------------
function test_normalizesCodexMonthlyCreditLimitForThePopup() {
var limit = Normalizer.normalizeCodexCreditLimit("codex", {
title: " Monthly credit limit ",
used: 125.5,
limit: 500,
remaining: 374.5,
remainingPercent: 74.9,
resetsAt: "2026-09-30T23:59:59Z"
})
compare(limit.title, "Monthly credit limit")
compare(limit.used, 125.5)
compare(limit.limit, 500)
compare(limit.remaining, 374.5)
compare(limit.usedPercent, 25.1)
compare(limit.leftPercent, 74.9)
compare(limit.resetsAt, "2026-09-30T23:59:59Z")
}
function test_rejectsMalformedCodexMonthlyCreditLimitAmounts() {
var fields = ["used", "limit", "remaining", "remainingPercent"]
var invalidValues = [null, undefined, "", " ", true, false, [], {}, "not-a-number"]
for (var fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) {
for (var valueIndex = 0; valueIndex < invalidValues.length; valueIndex++) {
var payload = {
used: 25,
limit: 100,
remaining: 75,
remainingPercent: 75
}
payload[fields[fieldIndex]] = invalidValues[valueIndex]
compare(Normalizer.normalizeCodexCreditLimit("codex", payload), null,
fields[fieldIndex] + " must reject " + valueIndex)
}
}
compare(Normalizer.normalizeCodexCreditLimit("codex", {
used: -1, limit: 100, remaining: 101, remainingPercent: 101
}), null)
compare(Normalizer.normalizeCodexCreditLimit("codex", {
used: 0, limit: 0, remaining: 0, remainingPercent: 100
}), null)
compare(Normalizer.normalizeCodexCreditLimit("claude", {
used: 25, limit: 100, remaining: 75, remainingPercent: 75
}), null)
}
function test_codexMonthlyCreditLimitRequiresOwnAmountFields() {
function InheritedLimit() {}
InheritedLimit.prototype.used = 25
InheritedLimit.prototype.limit = 100
InheritedLimit.prototype.remaining = 75
InheritedLimit.prototype.remainingPercent = 75
compare(Normalizer.normalizeCodexCreditLimit("codex", new InheritedLimit()), null)
}
function test_clampsCodexMonthlyCreditPercentagesAndBoundsItsText() {
var oversizedTitle = ""
for (var i = 0; i < 200; i++) {
oversizedTitle += "x"
}
var over = Normalizer.normalizeCodexCreditLimit("codex", {
title: oversizedTitle,
used: 0,
limit: 100,
remaining: 100,
remainingPercent: 130,
resetsAt: { raw: "not display text" }
})
compare(over.title.length, 120)
compare(over.usedPercent, 0)
compare(over.leftPercent, 100)
compare(over.resetsAt, "")
var under = Normalizer.normalizeCodexCreditLimit("codex", {
used: 100,
limit: 100,
remaining: 0,
remainingPercent: -30
})
compare(under.usedPercent, 100)
compare(under.leftPercent, 0)
compare(Normalizer.normalizeCodexCreditLimit("codex", null), null)
compare(Normalizer.normalizeCodexCreditLimit("codex", []), null)
}
// --- rate window percentages -------------------------------------------
function test_clampsUsedPercentagesReportedOutsideTheMeterRange() {
var over = Normalizer.rateWindowMetrics({ usedPercent: 137.5 }, null, true)
compare(over.hasPercent, true)
compare(over.usedPercent, 100)
compare(over.leftPercent, 0)
var under = Normalizer.rateWindowMetrics({ usedPercent: -42 }, null, true)
compare(under.usedPercent, 0)
compare(under.leftPercent, 100)
}
function test_clampsPaceProjectionsAndBoundsTheEta() {
var metrics = Normalizer.rateWindowMetrics(
{ usedPercent: 50 },
{ expectedUsedPercent: 400, etaSeconds: 99999999999, willLastToReset: false },
true)
compare(metrics.pacePercent, 100)
compare(metrics.paceOnTop, false)
compare(metrics.paceEtaSeconds, Normalizer.maximumPaceEtaSeconds)
var negative = Normalizer.rateWindowMetrics(
{ usedPercent: 50 },
{ expectedUsedPercent: -10, etaSeconds: -60 },
true)
compare(negative.pacePercent, 0)
compare(negative.paceEtaSeconds, 0)
}
function test_keepsAnAbsentPaceDistinctFromAPaceOfZero() {
// -1 is the "no projection" sentinel. A missing pace rendered as 0 would
// draw a marker claiming the provider is comfortably ahead of schedule.
compare(Normalizer.rateWindowMetrics({ usedPercent: 10 }, null, true).pacePercent, -1)
compare(Normalizer.rateWindowMetrics({ usedPercent: 10 }, {}, true).pacePercent, -1)
compare(Normalizer.rateWindowMetrics(
{ usedPercent: 10 }, { expectedUsedPercent: null }, true).pacePercent, -1)
compare(Normalizer.rateWindowMetrics(
{ usedPercent: 10 }, { expectedUsedPercent: undefined }, true).pacePercent, -1)
compare(Normalizer.rateWindowMetrics(
{ usedPercent: 10 }, { expectedUsedPercent: "soon" }, true).pacePercent, -1)
compare(Normalizer.rateWindowMetrics(
{ usedPercent: 10 }, { expectedUsedPercent: 0 }, true).pacePercent, 0)
}
function test_rejectsEmptyBooleanAndStructuredPaceValues() {
var invalidValues = [null, undefined, "", " ", true, false, [], {}]
for (var i = 0; i < invalidValues.length; i++) {
var metrics = Normalizer.rateWindowMetrics(
{ usedPercent: 10 }, {
expectedUsedPercent: invalidValues[i],
etaSeconds: invalidValues[i]
}, true)
compare(metrics.pacePercent, -1)
compare(metrics.paceEtaSeconds, 0)
}
}
function test_strictFiniteNumberRejectsCoerciveValues() {
var invalidValues = [null, undefined, "", " ", true, false, [], {}]
for (var i = 0; i < invalidValues.length; i++) {
verify(isNaN(Normalizer.strictFiniteNumber(invalidValues[i])))
}
compare(Normalizer.strictFiniteNumber(0), 0)
compare(Normalizer.strictFiniteNumber(" 2.5 "), 2.5)
compare(Normalizer.firstStrictFiniteNumber({}, 0), 0)
compare(Normalizer.firstStrictFiniteNumber(" ", " 3.5 "), 3.5)
}
function test_reportsAnUnknownPercentageInsteadOfGuessingZero() {
var unknown = Normalizer.rateWindowMetrics({ usedPercent: 80 }, null, false)
compare(unknown.hasPercent, false)
compare(unknown.usedPercent, 0)
compare(unknown.leftPercent, 0)
compare(unknown.paceKnown, false)
var malformed = Normalizer.rateWindowMetrics({ usedPercent: "quite a lot" }, null, true)
compare(malformed.hasPercent, false)
compare(malformed.usedPercent, 0)
}
function test_marksPaceAvailabilityIndependentlyFromUsage() {
var recoveredPace = Normalizer.rateWindowMetrics(
{ usedPercent: 80 }, { willLastToReset: true }, false)
compare(recoveredPace.hasPercent, false)
compare(recoveredPace.paceKnown, true)
var activePace = Normalizer.rateWindowMetrics(
{ usedPercent: 80 }, { willLastToReset: false, etaSeconds: 60 }, false)
compare(activePace.paceKnown, true)
var incompletePace = Normalizer.rateWindowMetrics(
{ usedPercent: 80 }, { willLastToReset: false }, false)
compare(incompletePace.paceKnown, false)
var invalidFlags = [null, undefined, "true", 1, [], {}]
for (var i = 0; i < invalidFlags.length; i++) {
var invalidPace = Normalizer.rateWindowMetrics(
{ usedPercent: 80 }, {
willLastToReset: invalidFlags[i],
etaSeconds: 60
}, false)
compare(invalidPace.paceKnown, false)
}
var inheritedRecovery = Normalizer.rateWindowMetrics(
{ usedPercent: 80 }, Object.create({ willLastToReset: true }), false)
compare(inheritedRecovery.paceKnown, false)
}
function test_rejectsEmptyBooleanAndStructuredUsagePercentages() {
var invalidValues = [null, undefined, "", " ", true, false, [], {}]
for (var i = 0; i < invalidValues.length; i++) {
var metrics = Normalizer.rateWindowMetrics(
{ usedPercent: invalidValues[i] }, null, true)
compare(metrics.hasPercent, false)
compare(metrics.usedPercent, 0)
compare(metrics.leftPercent, 0)
}
}
function test_distinguishesAMissingWindowFromAnEmptyOne() {
compare(Normalizer.rateWindowMetrics(null, null, true), null)
compare(Normalizer.rateWindowMetrics(undefined, null, true), null)
compare(Normalizer.rateWindowMetrics([], null, true), null)
compare(Normalizer.rateWindowMetrics("primary", null, true), null)
verify(Normalizer.rateWindowMetrics({}, null, true) !== null)
}
// --- status -------------------------------------------------------------
function test_acceptsOnlyKnownStatusIndicators() {
compare(Normalizer.statusSeverity({ indicator: "MAJOR" }), "major")
compare(Normalizer.statusSeverity({ indicator: "maintenance" }), "maintenance")
compare(Normalizer.statusSeverity({ indicator: "none" }), "")
compare(Normalizer.statusSeverity({ indicator: "totally-made-up" }), "")
compare(Normalizer.statusSeverity({}), "")
compare(Normalizer.statusSeverity(null), "")
}
function test_statusSeverityRejectsStructuredIndicators() {
// Coercing these values runs a missing or hostile toString, which used
// to abort the whole usage refresh with a TypeError.
compare(Normalizer.statusSeverity({ indicator: { toString: null } }), "")
compare(Normalizer.statusSeverity({ indicator: ["major"] }), "")
compare(Normalizer.statusSeverity({ indicator: 42 }), "")
compare(Normalizer.statusSeverity({ indicator: null }), "")
}
function test_readsTheIncidentKeyFromEveryContractSpelling() {
compare(Normalizer.statusIncidentKey({ incidentId: "a1" }), "a1")
compare(Normalizer.statusIncidentKey({ incident_id: "a2" }), "a2")
compare(Normalizer.statusIncidentKey({ incidentID: "a3" }), "a3")
compare(Normalizer.statusIncidentKey({ id: "a4" }), "a4")
compare(Normalizer.statusIncidentKey({ incident: { id: 55 } }), "55")
compare(Normalizer.statusIncidentKey({ incident: {} }), "")
compare(Normalizer.statusIncidentKey({}), "")
compare(Normalizer.statusIncidentKey(null), "")
}
function test_statusIncidentKeyRejectsStructuredValues() {
compare(Normalizer.statusIncidentKey({ incidentId: { toString: null } }), "")
compare(Normalizer.statusIncidentKey({ incident: { id: { toString: null } } }), "")
compare(Normalizer.statusIncidentKey({ incidentId: ["a1"] }), "")
compare(Normalizer.statusIncidentKey({ incidentId: 0 }), "0")
}
function test_refusesToFollowAStatusUrlOffTheProviderHost() {
var fallback = "https://status.openai.com/"
compare(Normalizer.safeStatusUrl(fallback, "https://status.openai.com/incidents/42"),
"https://status.openai.com/incidents/42")
// Different host, lookalike host, and non-https schemes all fall back.
compare(Normalizer.safeStatusUrl(fallback, "https://evil.example/steal"), fallback)
compare(Normalizer.safeStatusUrl(fallback, "https://status.openai.com.evil.example/"), fallback)
compare(Normalizer.safeStatusUrl(fallback, "http://status.openai.com/"), fallback)
compare(Normalizer.safeStatusUrl(fallback, "javascript:alert(1)"), fallback)
compare(Normalizer.safeStatusUrl(fallback, "file:///etc/passwd"), fallback)
compare(Normalizer.safeStatusUrl(fallback, ""), fallback)
compare(Normalizer.safeStatusUrl(fallback, null), fallback)
// A structured URL must fall back instead of throwing on coercion,
// which would drop the provider's otherwise valid usage snapshot.
compare(Normalizer.safeStatusUrl(fallback, JSON.parse('{"toString":null}')), fallback)
compare(Normalizer.safeStatusUrl(fallback, ["https://status.openai.com/"]), fallback)
}
function test_offersNoStatusUrlWhenTheProviderShipsNoFallback() {
compare(Normalizer.safeStatusUrl("", "https://evil.example/"), "")
compare(Normalizer.safeStatusUrl(null, "https://evil.example/"), "")
}
// --- accounts -----------------------------------------------------------
function test_accountLabelFallsBackThroughIdentityFields() {
compare(Normalizer.accountLabel({ account: "a@example.com", organization: "Org" }), "a@example.com")
compare(Normalizer.accountLabel({ account: "", organization: "Org" }), "Org")
compare(Normalizer.accountLabel({ account: "", organization: "", loginMethod: "oauth" }), "oauth")
compare(Normalizer.accountLabel({ account: "", organization: "", loginMethod: "" }), "")
compare(Normalizer.accountLabel(null), "")
}
function test_accountLabelRejectsNonStringIdentityFields() {
compare(Normalizer.accountLabel({ account: { length: 3 }, organization: "Org" }), "Org")
compare(Normalizer.accountLabel({ account: 42, organization: 7, loginMethod: "oauth" }), "oauth")
compare(Normalizer.accountLabel({ account: ["a@example.com"] }), "")
var deduped = Normalizer.dedupeAccountOptions([{ account: { length: 3 } }, { account: "b@example.com" }])
compare(deduped.length, 1)
compare(deduped[0].account, "b@example.com")
}
function test_accountKeyValidatesWithoutRewritingTheIdentity() {
// Display text collapses "Work Team" to "Work Team"; the key must keep
// the original spacing so selection still addresses the right account.
compare(Normalizer.accountKey({ account: "Work Team" }), "Work Team")
compare(Normalizer.accountKey({ account: " padded@example.com " }), "padded@example.com")
compare(Normalizer.accountKey({ account: "", organization: "Org Name" }), "Org Name")
compare(Normalizer.accountKey({ account: 42, organization: 7, loginMethod: "oauth" }), "oauth")
compare(Normalizer.accountKey({ account: { length: 3 }, organization: "Org" }), "Org")
compare(Normalizer.accountKey({ account: ["a@example.com"] }), "")
compare(Normalizer.accountKey(null), "")
var overlong = ""
for (var i = 0; i < Normalizer.maximumAccountIdentityLength + 1; i++) {
overlong += "a"
}
compare(Normalizer.accountKey({ account: overlong }), "")
}
function test_accountKeyPrefersTheValidatedSnapshotKey() {
// A normalized snapshot's display fields are already collapsed, so the
// stored key is the only copy of the original spacing.
compare(Normalizer.accountKey({ account: "Work Team", accountKey: "Work Team" }), "Work Team")
}
function test_accountKeyRevalidatesCarriedSnapshotKeys() {
// A whitespace-only or overlong carried key must not reach dedupe,
// selection, or the --account argument as-is.
var overlong = ""
for (var i = 0; i < Normalizer.maximumAccountIdentityLength + 1; i++) {
overlong += "a"
}
compare(Normalizer.accountKey({ account: "fallback@example.com", accountKey: " " }),
"fallback@example.com")
compare(Normalizer.accountKey({ account: "fallback@example.com", accountKey: overlong }),
"fallback@example.com")
compare(Normalizer.accountKey({ accountKey: " " }), "")
compare(Normalizer.accountKey({ accountKey: overlong }), "")
// Boundary lengths still pass through untouched, internal spacing kept.
var boundary = ""
for (var j = 0; j < Normalizer.maximumAccountIdentityLength; j++) {
boundary += "b"
}
compare(Normalizer.accountKey({ account: "fallback@example.com", accountKey: boundary }), boundary)
}
function test_firstValidAccountIdentitySkipsStructuredCandidates() {
compare(Normalizer.firstValidAccountIdentity(
[{ nested: "object" }, "fallback@example.com"]), "fallback@example.com")
compare(Normalizer.firstValidAccountIdentity(
[["nested"], 42, " spaced@example.com "]), " spaced@example.com ")
compare(Normalizer.firstValidAccountIdentity(
[{ nested: "object" }, ["nested"]]), "")
compare(Normalizer.firstValidAccountIdentity([]), "")
compare(Normalizer.firstValidAccountIdentity(null), "")
compare(Normalizer.firstValidAccountIdentity("fallback@example.com"), "")
}
function test_dedupeAccountOptionsKeepsSpacingVariantsSeparate() {
var options = [
{ provider: "codex", account: "Work Team", accountKey: "Work Team" },
{ provider: "codex", account: "Work Team", accountKey: "Work Team" },
{ provider: "codex", account: "Work Team", accountKey: "Work Team" }
]
var deduped = Normalizer.dedupeAccountOptions(options)
compare(deduped.length, 2)
compare(deduped[0].accountKey, "Work Team")
compare(deduped[1].accountKey, "Work Team")
}
function test_dedupesAccountsWithoutLosingPrototypeNamedOnes() {
// "constructor" and "toString" are legitimate account labels. A raw map
// lookup would treat them as already seen and silently drop them.
var options = [
{ account: "a@example.com" },
{ account: "a@example.com" },
{ account: "constructor" },
{ account: "toString" },
{ account: "__proto__" },
{ account: "" },
{ account: "b@example.com" }
]
var deduped = Normalizer.dedupeAccountOptions(options)
compare(deduped.length, 5)
compare(deduped[0].account, "a@example.com")
compare(deduped[1].account, "constructor")
compare(deduped[2].account, "toString")
compare(deduped[3].account, "__proto__")
compare(deduped[4].account, "b@example.com")
}
function test_dedupeAccountOptionsRejectsNonArrayInputs() {
compare(Normalizer.dedupeAccountOptions(null).length, 0)
compare(Normalizer.dedupeAccountOptions(undefined).length, 0)
compare(Normalizer.dedupeAccountOptions("invalid").length, 0)
compare(Normalizer.dedupeAccountOptions({ account: "a" }).length, 0)
}
function test_dedupeAccountOptionsBoundsAtMaximumAccountSnapshots() {
var oversized = []
for (var i = 0; i < Normalizer.maximumAccountSnapshots + 20; i++) {
oversized.push({ account: "acc" + i })
}
var inspectedPastBound = false
Object.defineProperty(oversized[Normalizer.maximumAccountSnapshots], "account", {
get: function() {
inspectedPastBound = true
return "outside-bound"
}
})
var deduped = Normalizer.dedupeAccountOptions(oversized)
compare(deduped.length, Normalizer.maximumAccountSnapshots)
verify(!inspectedPastBound)
}
function test_treatsMissingTokenAccountsAsAnEmptyListNotAFailure() {
verify(Normalizer.isMissingTokenAccountsError("No token accounts configured for codex."))
verify(Normalizer.isMissingTokenAccountsError("NO TOKEN ACCOUNTS CONFIGURED"))
verify(!Normalizer.isMissingTokenAccountsError("Request failed with status 500"))
verify(!Normalizer.isMissingTokenAccountsError(""))
// A malformed error value must not abort account parsing with a TypeError.
verify(!Normalizer.isMissingTokenAccountsError(null))
verify(!Normalizer.isMissingTokenAccountsError(undefined))
verify(!Normalizer.isMissingTokenAccountsError({ message: "x" }))
}
// --- sessions -----------------------------------------------------------
function test_normalizesASessionAndPrefersTheLastActivityTimestamp() {
var session = Normalizer.normalizeSession({
provider: "codex",
projectName: "codexbar-plasma",
sessionName: "refactor",
host: "workstation",
state: "RUNNING",
source: "cli",
startedAt: "2026-08-17T09:00:00Z",
lastActivityAt: "2026-08-18T10:30:00Z"
})
compare(session.provider, "codex")
compare(session.projectName, "codexbar-plasma")
compare(session.state, "running")
compare(session.activityAt, "2026-08-18T10:30:00Z")
compare(session.activityMs, Date.parse("2026-08-18T10:30:00Z"))
}
function test_fallsBackToStartedAtWhenTheActivityStampIsUnusable() {
var session = Normalizer.normalizeSession({
provider: "codex",
projectName: "p",
lastActivityAt: "not a date",
startedAt: "2026-08-17T09:00:00Z"
})
compare(session.activityAt, "2026-08-17T09:00:00Z")
compare(session.activityMs, Date.parse("2026-08-17T09:00:00Z"))
var undated = Normalizer.normalizeSession({ provider: "codex", projectName: "p" })
compare(undated.activityAt, "")
compare(undated.activityMs, 0)
}
function test_neverRetainsLocalSessionPaths() {
var session = Normalizer.normalizeSession({
provider: "codex",
projectName: "p",
cwd: "/home/user/secret-project",
transcriptPath: "/home/user/.codexbar/transcript.jsonl",
pid: 4242
})
compare(session.cwd, undefined)
compare(session.transcriptPath, undefined)
compare(session.pid, undefined)
}
function test_dropsSessionsThatCannotBeIdentified() {
compare(Normalizer.normalizeSession(null), null)
compare(Normalizer.normalizeSession("session"), null)
compare(Normalizer.normalizeSession([]), null)
compare(Normalizer.normalizeSession({ host: "workstation" }), null)
// A session with only a host has nothing to title a row with; one with a
// numeric project still does, so it is coerced rather than dropped.
compare(Normalizer.normalizeSession({ provider: 42, projectName: 7 }).projectName, "7")
compare(Normalizer.normalizeSession({ provider: 42, projectName: 7 }).provider, "")
}
function test_sortsSessionsByRecentActivityAndTruncatesAtTheBound() {
var payload = []
for (var i = 0; i < Normalizer.maximumSessions + 30; i++) {
payload.push({
provider: "codex",
projectName: "project-" + i,
lastActivityAt: new Date(1755000000000 + i * 1000).toISOString()
})
}
var sessions = Normalizer.normalizeSessions(payload)
compare(sessions.length, Normalizer.maximumSessions)
// The bound applies before the sort, so it is the first 128 entries that
// are kept and then ordered newest first.
compare(sessions[0].projectName, "project-" + (Normalizer.maximumSessions - 1))
compare(sessions[sessions.length - 1].projectName, "project-0")
}
function test_readsBothSupportedSessionsPayloadShapes() {
var listed = Normalizer.normalizeSessions([{ provider: "codex", projectName: "p" }])
compare(listed.length, 1)
var wrapped = Normalizer.normalizeSessions({ sessions: [{ provider: "codex", projectName: "p" }] })
compare(wrapped.length, 1)
compare(Normalizer.normalizeSessions([]).length, 0)
}
function test_rejectsUnsupportedSessionsPayloadsInsteadOfEmptyingTheTab() {
// null means "unsupported shape"; an empty array would read as a valid
// snapshot and wipe the sessions the user was already looking at.
compare(Normalizer.normalizeSessions({ items: [] }), null)
compare(Normalizer.normalizeSessions({ sessions: "none" }), null)
compare(Normalizer.normalizeSessions("no sessions"), null)
compare(Normalizer.normalizeSessions(42), null)
compare(Normalizer.normalizeSessions(null), null)
}
// --- cost ---------------------------------------------------------------
function test_costProjectsKeepOnlyDisplayFieldsFromTheOfficialContract() {
var result = Normalizer.normalizeCostProjects([{
name: "Example project", path: "/private/example",
totalCost: 12.5, totalTokens: 42000,
daily: [{ path: "/private/daily" }],
sources: [{ name: "source", path: "/private/source" }],
modelBreakdowns: [{ modelName: "example" }]
}], "EUR")
compare(result.rows, [{
label: "Example project", cost: 12.5, tokens: 42000, currency: "EUR"
}])
compare(result.truncated, false)
verify(JSON.stringify(result).indexOf("/private") === -1)
}
function test_costProjectsPreserveUnknownAmountsAndExplicitZero() {
var rows = Normalizer.normalizeCostProjects([
{ name: "Tokens only", totalTokens: 12 },
{ name: "Cost only", totalCost: "2.5", totalTokens: false },
{ name: "Empty", totalCost: 0, totalTokens: 0 },
{ name: "Negative", totalCost: -1, totalTokens: -2 },
{ name: "No amounts", totalCost: null, totalTokens: true },
{ name: "Nonfinite", totalCost: Infinity, totalTokens: NaN }
], "USD").rows
compare(rows.length, 4)
compare(rows[0].cost, null)
compare(rows[0].tokens, 12)
compare(rows[1].cost, 2.5)
compare(rows[1].tokens, null)
compare(rows[2].cost, 0)
compare(rows[2].tokens, 0)
compare(rows[3].cost, 0)
compare(rows[3].tokens, 0)
}
function test_costProjectsRejectMalformedAndInheritedDisplayFields() {
var inheritedName = Object.create({ name: "Inherited" })
inheritedName.totalCost = 3
var inheritedCost = Object.create({ totalCost: 8 })
inheritedCost.name = "Inherited amount"
inheritedCost.totalTokens = 2
var result = Normalizer.normalizeCostProjects([
null, false, [], "bad", { name: {}, totalCost: 1 },
{ name: 123, totalCost: 1 }, { name: " ", totalCost: 1 },
{ path: "/private/no-name", totalCost: 1 }, inheritedName,
inheritedCost, { name: "Healthy", totalCost: 4 }
], "USD")
compare(result.rows.length, 2)
compare(result.rows[0].cost, null)
compare(result.rows[1].label, "Healthy")
var malformed = [null, undefined, {}, "projects", 12]
for (var i = 0; i < malformed.length; i++) {
compare(Normalizer.normalizeCostProjects(malformed[i], "USD"),
{ rows: [], truncated: false })
}
}
function test_costProjectNamesAreBoundedRedactedLabelsNotIdentities() {
var rows = Normalizer.normalizeCostProjects([
{ name: "same", totalCost: 1, path: "/private/one" },
{ name: "same", totalCost: 2, path: "/private/two" },
{ name: "__proto__", totalCost: 3 },
{ name: "Example\nAuthorization: Bearer synthetic-secret", totalCost: 4 },
{ name: new Array(300).join("x"), totalCost: 5 },
{ name: "<b>Example</b>", totalCost: 6 }
], "USD").rows
compare(rows.length, 6)
compare(rows[0].label, rows[1].label)
compare(rows[0].cost, 1)
compare(rows[1].cost, 2)
compare(rows[2].label, "__proto__")
verify(rows[3].label.indexOf("synthetic-secret") === -1)
verify(rows[3].label.indexOf("[redacted]") >= 0)
verify(rows[4].label.length <= 120)
compare(rows[5].label, "<b>Example</b>")
}
function test_costProjectsBoundInspectionBeforeFiltering() {
var items = new Array(129)
items[0] = { name: "First", totalCost: 1 }
items[127] = { name: "Last inspected", totalCost: 2 }
Object.defineProperty(items, "128", { get: function() {
fail("Project inspection exceeded its bound")
} })
var result = Normalizer.normalizeCostProjects(items, "USD")
compare(result.rows.length, 2)
compare(result.rows[1].label, "Last inspected")
compare(result.truncated, true)
}
function test_costEnvelopeAcceptsEmptyAndRecognizedSnapshots() {
var empty = Normalizer.normalizeCostEnvelope([])
verify(empty !== null)
compare(empty.length, 0)
var singleton = Normalizer.normalizeCostEnvelope({ provider: "groqcloud" })
verify(singleton !== null)
compare(singleton.length, 1)
compare(singleton[0].provider, "groq")
var mixed = Normalizer.normalizeCostEnvelope([
null,
{ unexpected: true },
{ provider: "codex" },
{ provider: "claude", error: { message: "cost failed" } }
])
verify(mixed !== null)
compare(mixed.length, 2)
compare(mixed[0].provider, "codex")
compare(mixed[1].provider, "claude")
verify(Normalizer.costRecordHasError(mixed[1]))
}
function test_costEnvelopeRejectsUnsupportedNonemptyPayloads() {
compare(Normalizer.normalizeCostEnvelope({ unexpected: true }), null)
compare(Normalizer.normalizeCostEnvelope([null, { unexpected: true }]), null)
compare(Normalizer.normalizeCostEnvelope({ error: { message: "cost failed" } }), null)
compare(Normalizer.normalizeCostEnvelope([
{ provider: "codex" },
{ error: { message: "cost failed" } }
]), null)
compare(Normalizer.normalizeCostEnvelope("no cost data"), null)
compare(Normalizer.normalizeCostEnvelope(42), null)
compare(Normalizer.normalizeCostEnvelope(null), null)
}
function test_costEnvelopeAppliesTheSnapshotBoundBeforeFiltering() {
var payload = []
for (var i = 0; i < 256; i++) {
payload.push({ provider: "provider-" + i })
}
payload.push({ provider: "outside-bound" })
var snapshots = Normalizer.normalizeCostEnvelope(payload)
verify(snapshots !== null)
compare(snapshots.length, 256)
compare(snapshots[255].provider, "provider-255")
}
function test_costTrustMetadataNormalizesTheOfficialContract() {
var trust = Normalizer.normalizeCostTrustMetadata({
coverage: {
priced: 4,
unpriced: 0,
unmetered: 2,
estimated: 1
},
provenance: "listPriceEstimate"
})
verify(trust !== null)
compare(trust.coverage.priced, 4)
compare(trust.coverage.unpriced, 0)
compare(trust.coverage.unmetered, 2)
compare(trust.coverage.estimated, 1)
compare(trust.sourceKind, "listPrice")
}
function test_costTrustMetadataIsQuietWhenTheCliOmitsIt() {
compare(Normalizer.normalizeCostTrustMetadata({ provider: "codex" }), null)
compare(Normalizer.normalizeCostTrustMetadata(null), null)
compare(Normalizer.normalizeCostTrustMetadata([]), null)
}
function test_costRecordErrorDetectionUsesTheOfficialEnvelope() {
verify(Normalizer.costRecordHasError({
provider: "codex",
daily: [],
error: { message: "scan failed" }
}))
verify(Normalizer.costRecordHasError({
provider: "codex",
totals: { totalCost: 0, totalTokens: 0 },
error: { message: "scan failed" }
}))
verify(!Normalizer.costRecordHasError({ provider: "codex" }))
verify(!Normalizer.costRecordHasError(null))
}
function test_partialCostMergeKeepsOnlyExplicitlyFailedProviders() {
var previous = {
codex: { marker: "old-codex" },
claude: { marker: "old-claude" },
groq: { marker: "old-groq" },
removed: { marker: "stale-provider" }
}
var fresh = {
codex: { marker: "new-codex" }
}
var merged = Normalizer.mergeCostSnapshotsAfterPartialFailure(
previous, fresh, ["claude", "groqcloud"])
compare(Object.keys(merged).sort().join(","), "claude,codex,groq")
compare(merged.codex.marker, "new-codex")
compare(merged.claude.marker, "old-claude")
compare(merged.groq.marker, "old-groq")
verify(merged.removed === undefined)
}
function test_costTrustMetadataKeepsEitherValidAxisWithoutLeakingTheOther() {
var coverageOnly = Normalizer.normalizeCostTrustMetadata({
coverage: { priced: 1, unpriced: 0, unmetered: 0, estimated: 0 },
provenance: " future-wire-value "
})
compare(coverageOnly.sourceKind, "")
compare(coverageOnly.coverage.priced, 1)
var provenanceOnly = Normalizer.normalizeCostTrustMetadata({
coverage: { priced: "1", unpriced: 0, unmetered: 0, estimated: 0 },
provenance: "vendorMetered"
})
compare(provenanceOnly.coverage, null)
compare(provenanceOnly.sourceKind, "vendor")
var officialValues = [
{ wire: "listPriceEstimate", semantic: "listPrice" },
{ wire: "vendorMetered", semantic: "vendor" },
{ wire: "mixed", semantic: "mixed" },
{ wire: "unknown", semantic: "unknown" }
]
for (var i = 0; i < officialValues.length; i++) {
compare(Normalizer.normalizeCostTrustMetadata({
provenance: officialValues[i].wire
}).sourceKind, officialValues[i].semantic)
}
}
function test_costTrustMetadataRejectsMalformedCoverageAsAWhole() {
var valid = { priced: 1, unpriced: 0, unmetered: 0, estimated: 0 }
var invalidValues = ["1", -1, 0.5, NaN, Infinity,
Normalizer.maximumCostCoverageCount + 1]
for (var i = 0; i < invalidValues.length; i++) {
var coverage = {
priced: invalidValues[i],
unpriced: valid.unpriced,
unmetered: valid.unmetered,
estimated: valid.estimated
}
compare(Normalizer.normalizeCostTrustMetadata({ coverage: coverage }), null)
}
compare(Normalizer.normalizeCostTrustMetadata({ coverage: null }), null)
compare(Normalizer.normalizeCostTrustMetadata({ coverage: [] }), null)
compare(Normalizer.normalizeCostTrustMetadata({ coverage: "priced" }), null)
compare(Normalizer.normalizeCostTrustMetadata({
coverage: { priced: 1, unpriced: 0, unmetered: 0 }
}), null)
}
function test_costTrustMetadataRequiresOwnMetadataAndCounterFields() {
function InheritedRecord() {}
InheritedRecord.prototype.coverage = {
priced: 1, unpriced: 0, unmetered: 0, estimated: 0
}
InheritedRecord.prototype.provenance = "unknown"
compare(Normalizer.normalizeCostTrustMetadata(new InheritedRecord()), null)
function InheritedCoverage() {
this.priced = 1
this.unpriced = 0
this.unmetered = 0
}
InheritedCoverage.prototype.estimated = 0
compare(Normalizer.normalizeCostTrustMetadata({
coverage: new InheritedCoverage()
}), null)
}
function test_normalizesDailyCostAndKeepsTheRequestedRange() {
var daily = []
for (var i = 0; i < 40; i++) {
daily.push({ date: "2026-07-" + i, totalCost: i, totalTokens: i * 10 })
}
var rows = Normalizer.normalizeCostDaily(daily, "USD", 7)
compare(rows.length, 7)
// The newest days are kept, restored to chronological order.
compare(rows[rows.length - 1].cost, 39)
compare(rows[0].cost, 33)
compare(rows[0].currency, "USD")
}
function test_costHistoryFillsMissingCalendarDaysInTheReportedWindow() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-27", totalCost: 6, totalTokens: 60 }
], "USD", 3, "2026-08-29T12:00:00Z")
compare(rows.length, 3)
compare(rows[0].label, "2026-08-27")
compare(rows[0].cost, 6)
compare(rows[1].label, "2026-08-28")
compare(rows[1].cost, 0)
compare(rows[1].tokens, 0)
compare(rows[2].label, "2026-08-29")
compare(rows[2].cost, 0)
}
function test_costHistoryPreservesDateOnlyUpdatedAtAcrossTimeZones() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-28", totalCost: 6, totalTokens: 60 }
], "USD", 2, "2026-08-29")
compare(rows.length, 2)
compare(rows[0].label, "2026-08-28")
compare(rows[0].cost, 6)
compare(rows[1].label, "2026-08-29")
compare(rows[1].cost, 0)
}
function test_costHistoryDoesNotReuseDatesOutsideAnEmptyWindow_data() {
return [
{ tag: "older", date: "2026-08-20" },
{ tag: "future", date: "2026-08-31" }
]
}
function test_costHistoryDoesNotReuseDatesOutsideAnEmptyWindow(data) {
var rows = Normalizer.normalizeCostDaily([
{ date: data.date, totalCost: 100, totalTokens: 1000 }
], "USD", 7, "2026-08-30")
compare(rows.length, 0)
}
function test_costHistoryNeverExceedsTheHistoryPointBound() {
var daily = []
for (var i = 0; i < Normalizer.maximumCostHistoryPoints + 50; i++) {
daily.push({ date: "d" + i, totalCost: 1 })
}
compare(Normalizer.normalizeCostDaily(daily, "USD", 100000).length,
Normalizer.maximumCostHistoryPoints)
}
function test_costHistoryStopsScanningAfterTheInspectionBound() {
// A payload padded with junk beyond the scan budget must terminate rather
// than walk the whole array looking for one more usable day.
var daily = []
for (var i = 0; i < Normalizer.maximumCostHistoryScanItems + 500; i++) {
daily.push(null)
}
daily.unshift({ date: "old", totalCost: 5 })
var rows = Normalizer.normalizeCostDaily(daily, "USD", 30)
compare(rows.length, 0)
}
function test_costHistorySkipsMalformedDaysWithoutStringifyingThem() {
var rows = Normalizer.normalizeCostDaily([
null,
"2026-08-01",
[],
{ date: "2026-08-02" },
{ date: { nested: "object" }, totalCost: 3 },
{ date: "2026-08-03", totalCost: 4 }
], "USD", 30)
compare(rows.length, 2)
// A structured date leaves the chart label empty rather than printing
// "[object Object]" on the axis; the day's numbers are still usable.
compare(rows[0].label, "")
compare(rows[0].cost, 3)
compare(rows[1].label, "2026-08-03")
compare(rows[1].cost, 4)
}
function test_costHistoryClampsNegativeAmountsAndFallsBackToTokenParts() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-01", totalCost: -12, inputTokens: 10, outputTokens: 5, cacheReadTokens: 2 }
], "USD", 30)
compare(rows.length, 1)
compare(rows[0].cost, 0)
compare(rows[0].tokens, 17)
compare(rows[0].inputTokens, 10)
}
function test_costHistoryPreservesMissingCostForTokenOnlyRows() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-01", totalTokens: 250 }
], "USD", 30)
compare(rows.length, 1)
compare(rows[0].cost, null)
compare(rows[0].tokens, 250)
}
function test_costHistoryPreservesUnknownTokensWithoutLosingMeasuredCost() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-27", totalCost: 2 },
{ date: "2026-08-28", totalCost: 3, totalTokens: 0 }
], "USD", 3, "2026-08-29")
compare(rows[0].cost, 2)
compare(rows[0].tokens, null)
compare(rows[1].tokens, 0)
compare(rows[2].tokens, 0)
compare(rows[2].models, [])
}
function test_costHistoryRejectsMalformedTokenTotalsWithoutInventingZero() {
var invalidTokens = [undefined, null, false, true, "", "unknown", {}, [], Infinity]
for (var i = 0; i < invalidTokens.length; i++) {
var rows = Normalizer.normalizeCostDaily([
{ totalCost: 2, totalTokens: invalidTokens[i], tokens: invalidTokens[i] }
], "USD", 1)
compare(rows[0].cost, 2)
compare(rows[0].tokens, null)
}
var overflow = Normalizer.normalizeCostDaily([
{ totalCost: 2, inputTokens: 1e308, outputTokens: 1e308 }
], "USD", 1)
compare(overflow[0].cost, 2)
compare(overflow[0].tokens, null)
}
function test_costHistoryPreservesZeroTokenParts_data() {
return [
{ tag: "input", field: "inputTokens", value: 0 },
{ tag: "output", field: "outputTokens", value: 0 },
{ tag: "cache-read", field: "cacheReadTokens", value: 0 },
{ tag: "cache-creation", field: "cacheCreationTokens", value: 0 },
{ tag: "legacy-cache-write", field: "cacheWriteTokens", value: 0 },
{ tag: "numeric-string", field: "cacheReadTokens", value: "0" }
]
}
function test_costHistoryPreservesZeroTokenParts(data) {
var day = { date: "2026-08-01" }
day[data.field] = data.value
var rows = Normalizer.normalizeCostDaily([day], "USD", 1, day.date)
compare(rows.length, 1)
compare(rows[0].label, day.date)
compare(rows[0].tokens, 0)
compare(rows[0].cost, null)
}
function test_zeroCacheDayDoesNotRevivePricedHistoryOutsideTheRange() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-07-31", totalCost: 9, totalTokens: 100 },
{ date: "2026-08-01", cacheReadTokens: 0 }
], "USD", 1, "2026-08-01")
compare(rows.length, 1)
compare(rows[0].label, "2026-08-01")
compare(rows[0].tokens, 0)
compare(rows[0].cost, null)
}
function test_costHistoryRejectsMissingOrInvalidCacheTokenParts_data() {
return [
{ tag: "missing", value: undefined },
{ tag: "null", value: null },
{ tag: "boolean", value: false },
{ tag: "blank", value: "" },
{ tag: "text", value: "unknown" },
{ tag: "object", value: {} },
{ tag: "not-finite", value: Infinity }
]
}
function test_costHistoryRejectsMissingOrInvalidCacheTokenParts(data) {
var rows = Normalizer.normalizeCostDaily([{
date: "2026-08-01",
cacheReadTokens: data.value,
cacheCreationTokens: data.value,
cacheWriteTokens: data.value
}], "USD", 1, "2026-08-01")
compare(rows.length, 0)
}
function test_costHistoryFillsTokenOnlyGapsWithoutInventingCost() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-01", totalTokens: 250 }
], "USD", 2, "2026-08-02")
compare(rows.length, 2)
compare(rows[0].cost, null)
compare(rows[1].cost, null)
compare(rows[1].tokens, 0)
}
function test_costHistoryIgnoresPricedRowsBeforeTheRenderedWindow() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-27", totalCost: 3, totalTokens: 30 },
{ date: "2026-08-29", totalTokens: 250 }
], "USD", 2, "2026-08-29")
compare(rows.length, 2)
compare(rows[0].label, "2026-08-28")
compare(rows[0].cost, null)
compare(rows[1].cost, null)
}
function test_costHistoryFillsGapsEvenWhenTheDayBoundIsAlreadyCollected() {
// The CLI emits its default 30-day daily array while the user renders a
// 7-day range: collecting historyDays rows must not skip the calendar
// gap fill, or the chart compresses out a missing day and keeps a day
// from before the rendered window.
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-22", totalCost: 1, totalTokens: 10 },
{ date: "2026-08-23", totalCost: 2, totalTokens: 20 },
{ date: "2026-08-24", totalCost: 3, totalTokens: 30 },
{ date: "2026-08-25", totalCost: 4, totalTokens: 40 },
{ date: "2026-08-26", totalCost: 5, totalTokens: 50 },
{ date: "2026-08-27", totalCost: 6, totalTokens: 60 },
{ date: "2026-08-29", totalCost: 8, totalTokens: 80 },
{ date: "2026-08-30", totalCost: 9, totalTokens: 90 }
], "USD", 7, "2026-08-30")
compare(rows.length, 7)
compare(rows.map(function (row) { return row.label }).join(","),
"2026-08-24,2026-08-25,2026-08-26,2026-08-27,2026-08-28,2026-08-29,2026-08-30")
compare(rows[0].cost, 3)
compare(rows[4].label, "2026-08-28")
compare(rows[4].cost, 0)
compare(rows[4].tokens, 0)
compare(rows[6].cost, 9)
}
function test_costHistoryAcceptsBothLegacyAndCurrentFieldNames() {
var current = Normalizer.normalizeCostDaily(
[{ date: "2026-08-01", totalCost: 2, totalTokens: 20, cacheCreationTokens: 3 }], "USD", 30)
var legacy = Normalizer.normalizeCostDaily(
[{ day: "2026-08-01", costUSD: 2, tokens: 20, cacheWriteTokens: 3 }], "USD", 30)
compare(current[0].cost, legacy[0].cost)
compare(current[0].tokens, legacy[0].tokens)
compare(current[0].cacheCreationTokens, legacy[0].cacheCreationTokens)
compare(current[0].label, legacy[0].label)
}
function test_costHistoryRejectsNullAndBooleanNumbersButUsesValidAliases() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-01", totalCost: null },
{ date: "2026-08-02", totalCost: false, totalTokens: true },
{ date: "2026-08-03", totalCost: null, costUSD: 3, totalTokens: false, tokens: 4 },
{ date: "2026-08-04", inputTokens: true }
], "USD", 30)
compare(rows.length, 1)
compare(rows[0].label, "2026-08-03")
compare(rows[0].cost, 3)
compare(rows[0].tokens, 4)
}
function test_costHistoryDoesNotFillAnExplicitlyMalformedDayWithZero() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-28", totalCost: 3, totalTokens: 30 },
{ date: "2026-08-29", totalCost: null }
], "USD", 2, "2026-08-29")
compare(rows.length, 1)
compare(rows[0].label, "2026-08-28")
compare(rows[0].cost, 3)
}
function test_costHistoryDoesNotFillBeyondAnIncompleteInspection() {
var daily = [{ date: "2026-08-29", totalCost: null }]
for (var i = 0; i < Normalizer.maximumCostHistoryScanItems - 1; i++) {
daily.push(null)
}
daily.push({ date: "2026-08-28", totalCost: 3, totalTokens: 30 })
var rows = Normalizer.normalizeCostDaily(daily, "USD", 2, "2026-08-29")
compare(rows.length, 1)
compare(rows[0].label, "2026-08-28")
}
function test_costHistoryInspectsMalformedDaysBeyondTheResultBound() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-29", totalCost: null },
{ date: "2026-08-27", totalCost: 2 },
{ date: "2026-08-28", totalCost: 3 }
], "USD", 2, "2026-08-29")
compare(rows.length, 2)
compare(rows[0].label, "2026-08-27")
compare(rows[1].label, "2026-08-28")
compare(rows[1].cost, 3)
}
function test_costHistoryInspectsValidDaysBeyondTheResultBound() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-29", totalCost: 9 },
{ date: "2026-08-27", totalCost: 2 },
{ date: "2026-08-28", totalCost: 3 }
], "USD", 2, "2026-08-29")
compare(rows.length, 2)
compare(rows[0].label, "2026-08-28")
compare(rows[1].label, "2026-08-29")
compare(rows[1].cost, 9)
}
function test_costHistoryIgnoresMalformedLabelsBeforeTheRetainedRows() {
var rows = Normalizer.normalizeCostDaily([
{ date: "bad-date", totalCost: 1 },
{ date: "2026-08-28", totalCost: 2 },
{ date: "2026-08-29", totalCost: 3 }
], "USD", 2, "2026-08-30")
compare(rows.length, 2)
compare(rows[0].label, "2026-08-29")
compare(rows[0].cost, 3)
compare(rows[1].label, "2026-08-30")
compare(rows[1].cost, 0)
}
function test_costHistoryIgnoresDuplicateDatesOutsideTheWindow() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-08-20", totalCost: 1 },
{ date: "2026-08-20", totalCost: 2 },
{ date: "2026-08-29", totalCost: 3 }
], "USD", 2, "2026-08-30")
compare(rows.length, 2)
compare(rows[0].label, "2026-08-29")
compare(rows[1].label, "2026-08-30")
compare(rows[1].cost, 0)
}
function test_costDailyDegradesToAnEmptyRangeForNonArrays() {
compare(Normalizer.normalizeCostDaily(null, "USD", 30).length, 0)
compare(Normalizer.normalizeCostDaily({ "0": { totalCost: 1 }, length: 1 }, "USD", 30).length, 0)
compare(Normalizer.normalizeCostDaily("2026-08-01", "USD", 30).length, 0)
}
function test_dailyCostModelsStayWithTheirOwnDayAndPreservePeriodTotals() {
var items = [
{ date: "2026-08-28", totalCost: 4, totalTokens: 40, modelBreakdowns: [
{ modelName: "shared", cost: 1, totalTokens: 10 },
{ modelName: "shared", cost: 3, totalTokens: 30 }
] },
{ date: "2026-08-29", totalCost: 5, totalTokens: 50,
modelBreakdowns: [{ modelName: "shared", cost: 5, totalTokens: 50 }] }
]
var daily = Normalizer.normalizeCostDaily(items, "EUR", 2, "2026-08-29")
compare(daily[0].models, [{ label: "shared", cost: 4, tokens: 40, currency: "EUR" }])
compare(daily[1].models, [{ label: "shared", cost: 5, tokens: 50, currency: "EUR" }])
compare(daily[0].modelsTruncated, false)
compare(daily[1].modelsTruncated, false)
compare(Normalizer.normalizeCostModels(items, "EUR", 2, "2026-08-29").rows,
[{ label: "shared", cost: 9, tokens: 90, currency: "EUR" }])
daily[0].models[0].cost = 99
compare(daily[1].models[0].cost, 5)
compare(items[0].modelBreakdowns[0].cost, 1)
}
function test_dailyCostModelsPreserveUnknownZeroAndLegacyAmounts() {
var daily = Normalizer.normalizeCostDaily([{ totalTokens: 40, modelBreakdowns: [
{ modelName: "token-only", cost: null, totalTokens: 20 },
{ modelName: "zero", cost: 0, totalTokens: 0 },
{ model: "alias", cost: null, totalCost: "3", totalTokens: false, tokens: "4" },
{ modelName: "negative", cost: -2, totalTokens: -1 }
] }], "USD", 1)
var models = daily[0].models
compare(models.slice(0, 2), [
{ label: "alias", cost: 3, tokens: 4, currency: "USD" },
{ label: "token-only", cost: null, tokens: 20, currency: "USD" }
])
var zeroModels = models.slice(2).sort(function(a, b) { return a.label.localeCompare(b.label) })
compare(zeroModels, [
{ label: "negative", cost: 0, tokens: 0, currency: "USD" },
{ label: "zero", cost: 0, tokens: 0, currency: "USD" }
])
}
function test_dailyCostModelsRejectMalformedRecordsAndInheritedFields() {
function InheritedModel() { this.cost = 1 }
InheritedModel.prototype.modelName = "inherited-name"
function InheritedAmount() { this.modelName = "inherited-cost" }
InheritedAmount.prototype.cost = 2
var arrayRecord = []
arrayRecord.modelName = "array-record"
arrayRecord.cost = 3
var daily = Normalizer.normalizeCostDaily([{ totalCost: 1, modelBreakdowns: [
null, "string-record", 3, true, arrayRecord, new InheritedModel(), new InheritedAmount(),
{ modelName: 7, cost: 2 },
{ modelName: true, cost: 2 },
{ modelName: { nested: "name" }, cost: 2 },
{ modelName: "coercive", cost: false, totalTokens: true },
{ modelName: "__proto__", cost: 2 },
{ modelName: "constructor", cost: 2 },
{ modelName: "prototype", cost: 2 },
{ modelName: "healthy", cost: 1 }
] }], "USD", 1)
compare(daily[0].models, [{ label: "healthy", cost: 1, tokens: null, currency: "USD" }])
compare(daily[0].modelsTruncated, false)
compare(({}).polluted, undefined)
}
function test_dailyCostModelsRetainOnlyBoundedRedactedDisplayFields() {
var daily = Normalizer.normalizeCostDaily([{ totalCost: 6, modelBreakdowns: [
{ modelName: "Authorization: Bearer synthetic-model-secret", cost: 3,
source: { path: "/private/source", apiKey: "private-key" },
path: "/private/model", standardCostUSD: 2, priorityCostUSD: 1 },
{ modelName: new Array(300).join("x"), cost: 2 },
{ modelName: "<b>literal model</b>", cost: 1 }
] }], "USD", 1)
var models = daily[0].models
compare(models[0].label, "Authorization: [redacted]")
compare(models[1].label.length, 120)
compare(models[2].label, "<b>literal model</b>")
for (var i = 0; i < models.length; i++) {
compare(Object.keys(models[i]).sort().join(","), "cost,currency,label,tokens")
}
var encoded = JSON.stringify(daily)
verify(encoded.indexOf("synthetic-model-secret") === -1)
verify(encoded.indexOf("/private/") === -1)
verify(encoded.indexOf("private-key") === -1)
}
function test_dailyCostModelsReportInspectionTruncationBeforeFiltering() {
var breakdowns = new Array(Normalizer.maximumModelBreakdownsPerDay + 1)
breakdowns[0] = { modelName: "first", cost: 1 }
breakdowns[Normalizer.maximumModelBreakdownsPerDay - 1] = { modelName: "last", cost: 2 }
Object.defineProperty(breakdowns, String(Normalizer.maximumModelBreakdownsPerDay), {
configurable: true,
get: function() { fail("Model inspection exceeded its bound") }
})
var daily = Normalizer.normalizeCostDaily([{ totalCost: 3, modelBreakdowns: breakdowns }], "USD", 1)
compare(daily[0].models.length, 2)
compare(daily[0].models[0].label, "last")
compare(daily[0].modelsTruncated, true)
breakdowns.length = Normalizer.maximumModelBreakdownsPerDay
daily = Normalizer.normalizeCostDaily([{ totalCost: 3, modelBreakdowns: breakdowns }], "USD", 1)
compare(daily[0].modelsTruncated, false)
}
function test_dailyCostModelDisplayCapDoesNotTruncatePeriodAggregation() {
var breakdowns = []
for (var i = 0; i < 7; i++) {
breakdowns.push({ modelName: "model-" + i, cost: 7 - i })
}
var items = [
{ date: "2026-08-28", totalCost: 28, modelBreakdowns: breakdowns },
{ date: "2026-08-29", totalCost: 100,
modelBreakdowns: [{ modelName: "model-6", cost: 100 }] }
]
var daily = Normalizer.normalizeCostDaily(items, "USD", 2, "2026-08-29")
compare(daily[0].models.length, 6)
compare(daily[0].models[5].label, "model-5")
compare(daily[0].modelsTruncated, true)
compare(daily[1].modelsTruncated, false)
var period = Normalizer.normalizeCostModels(items, "USD", 2, "2026-08-29").rows
compare(period.length, 6)
compare(period[0].label, "model-6")
compare(period[0].cost, 101)
}
function test_dailyCostModelsAggregateDuplicatesBeforeApplyingDisplayCap() {
var breakdowns = []
for (var i = 0; i < 10; i++) {
breakdowns.push({ modelName: "same", cost: 1 })
}
var daily = Normalizer.normalizeCostDaily([{ totalCost: 10, modelBreakdowns: breakdowns }], "USD", 1)
compare(daily[0].models.length, 1)
compare(daily[0].models[0].cost, 10)
compare(daily[0].modelsTruncated, false)
}
function test_dailyCostModelsPreserveAbsentTokensAndAggregateOnlyObservedCounts() {
var items = [
{ totalCost: 3, modelBreakdowns: [
{ modelName: "cost-only", cost: 2 },
{ modelName: "mixed", cost: 1 }
] },
{ totalCost: 3, modelBreakdowns: [
{ modelName: "cost-only", cost: 2, totalTokens: null },
{ modelName: "mixed", cost: 1, totalTokens: 0 }
] }
]
var daily = Normalizer.normalizeCostDaily(items, "USD", 2)
compare(daily[0].models, [
{ label: "cost-only", cost: 2, tokens: null, currency: "USD" },
{ label: "mixed", cost: 1, tokens: null, currency: "USD" }
])
compare(daily[1].models[1].tokens, 0)
compare(Normalizer.normalizeCostModels(items, "USD", 2).rows, [
{ label: "cost-only", cost: 4, tokens: null, currency: "USD" },
{ label: "mixed", cost: 2, tokens: 0, currency: "USD" }
])
items.push({ modelBreakdowns: [{ modelName: "mixed", cost: 1, totalTokens: 7 }] })
var period = Normalizer.normalizeCostModels(items, "USD", 3).rows
compare(period[1].label, "mixed")
compare(period[1].tokens, 7)
}
function test_dailyCostModelOverflowKeepsTheCompanionMetricAndStaysUnknown() {
var breakdowns = []
for (var i = 0; i < 3; i++) {
breakdowns.push({ modelName: "cost-overflow", cost: i < 2 ? 1e308 : 1, totalTokens: 2 })
breakdowns.push({ modelName: "token-overflow", cost: 2, totalTokens: i < 2 ? 1e308 : 1 })
}
var item = { totalCost: 6, totalTokens: 6, modelBreakdowns: breakdowns }
var dailyModels = Normalizer.normalizeCostDaily([item], "USD", 1)[0].models
compare(dailyModels, [
{ label: "token-overflow", cost: 6, tokens: null, currency: "USD" },
{ label: "cost-overflow", cost: null, tokens: 6, currency: "USD" }
])
compare(Normalizer.normalizeCostModels([item], "USD", 1).rows, dailyModels)
var period = Normalizer.normalizeCostModels([
{ modelBreakdowns: [{ modelName: "same", cost: 1e308, totalTokens: 1 }] },
{ modelBreakdowns: [{ modelName: "same", cost: 1e308, totalTokens: 2 }] }
], "USD", 2).rows
compare(period, [{ label: "same", cost: null, tokens: 3, currency: "USD" }])
}
function test_dailyCostModelsDoNotPopulateGapsOrReviveInvalidDays() {
var daily = Normalizer.normalizeCostDaily([
{ date: "2026-08-20", totalCost: 9, modelBreakdowns: [{ modelName: "old", cost: 9 }] },
{ date: "2026-08-28", totalCost: 2, modelBreakdowns: [{ modelName: "current", cost: 2 }] }
], "USD", 2, "2026-08-29")
compare(daily[0].models[0].label, "current")
compare(daily[1].label, "2026-08-29")
compare(daily[1].models, [])
compare(daily[1].modelsTruncated, false)
compare(Normalizer.normalizeCostDaily([
{ date: "2026-08-29", totalCost: null, modelBreakdowns: [{ modelName: "orphan", cost: 2 }] }
], "USD", 1, "2026-08-29"), [])
}
function test_dailyCostModelsIgnoreMalformedContainers() {
var containers = [undefined, null, "models", {}, { "0": { modelName: "fake", cost: 2 }, length: 1 }]
for (var i = 0; i < containers.length; i++) {
var daily = Normalizer.normalizeCostDaily([{ totalCost: 1, modelBreakdowns: containers[i] }], "USD", 1)
compare(daily[0].models, [])
compare(daily[0].modelsTruncated, false)
}
}
function test_costTotalsPreserveUnknownAndMeasuredTokenCounts_data() {
return [
{ tag: "absent", totals: { totalCost: 2 }, expected: null },
{ tag: "null", totals: { totalCost: 2, totalTokens: null }, expected: null },
{ tag: "boolean", totals: { totalCost: 2, totalTokens: false }, expected: null },
{ tag: "invalid", totals: { totalCost: 2, totalTokens: "unknown" }, expected: null },
{ tag: "zero-total", totals: { totalCost: 2, totalTokens: 0 }, expected: 0 },
{ tag: "zero-part", totals: { totalCost: 2, inputTokens: 0 }, expected: 0 },
{ tag: "negative-only-parts", totals: { totalCost: 2, inputTokens: -3 }, expected: null },
{ tag: "overflowing-parts", totals: { totalCost: 2, inputTokens: 1e308, outputTokens: 1e308 }, expected: null }
]
}
function test_costTotalsPreserveUnknownAndMeasuredTokenCounts(data) {
var totals = Normalizer.normalizeCostTotals(data.totals, undefined, undefined, "USD")
compare(totals.tokens, data.expected)
compare(totals.cost, 2)
}
function test_costDailyRejectsNegativeOnlyPartsAndKeepsMeasuredZero() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-09-01", inputTokens: -3 },
{ date: "2026-09-02", totalCost: 2, inputTokens: -3 },
{ date: "2026-09-03", inputTokens: 0 }
], "USD", 3)
compare(rows.length, 2)
compare(rows[0].tokens, null)
compare(rows[1].tokens, 0)
}
function test_costDailyRetainsUnknownOverflowAndObservedParts() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-09-01", inputTokens: 1e308, outputTokens: 1e308 }
], "USD", 1)
compare(rows.length, 1)
compare(rows[0].cost, null)
compare(rows[0].tokens, null)
compare(rows[0].inputTokens, 1e308)
}
function test_costModelsRejectNonTextNames() {
var summary = Normalizer.normalizeCostModels([{ modelBreakdowns: [
{ modelName: 123, cost: 1 },
{ model: 123, cost: 1 },
{ modelName: "123", cost: 2 }
] }], "USD", 1)
compare(summary.rows, [{ label: "123", cost: 2, tokens: null, currency: "USD" }])
}
function test_costGapFillingRequiresAnObservedMetric() {
var rows = Normalizer.normalizeCostDaily([
{ date: "2026-09-02", totalCost: 2 }
], "USD", 3, "2026-09-03")
compare(rows.length, 3)
for (var i = 0; i < rows.length; i++) {
compare(rows[i].tokens, null)
}
compare(rows[0].cost, 0)
var tokenRows = Normalizer.normalizeCostDaily([
{ date: "2026-09-02", totalTokens: 0 }
], "USD", 3, "2026-09-03")
for (var j = 0; j < tokenRows.length; j++) {
compare(tokenRows[j].tokens, 0)
compare(tokenRows[j].cost, null)
}
}
function test_periodCostModelsRetainDisplayAndInspectionTruncation() {
var breakdowns = []
for (var i = 0; i < 7; i++) {
breakdowns.push({ modelName: "model-" + i, cost: 7 - i })
}
var summary = Normalizer.normalizeCostModels([{ modelBreakdowns: breakdowns }], "USD", 30)
compare(summary.rows.length, 6)
compare(summary.truncated, true)
breakdowns.pop()
compare(Normalizer.normalizeCostModels([{ modelBreakdowns: breakdowns }], "USD", 30).truncated, false)
breakdowns.length = Normalizer.maximumModelBreakdownsPerDay + 1
compare(Normalizer.normalizeCostModels([{ modelBreakdowns: breakdowns }], "USD", 30).truncated, true)
compare(Normalizer.normalizeCostModels([], "USD", 30), { rows: [], truncated: false })
}
function test_costModelTiesHaveStableLabelOrder() {
var items = [{ modelBreakdowns: [
{ modelName: "Zulu", cost: 2, totalTokens: 20 },
{ modelName: "Alpha", cost: 2, totalTokens: 20 }
] }]
var first = Normalizer.normalizeCostModels(items, "USD", 30)
items[0].modelBreakdowns.reverse()
compare(Normalizer.normalizeCostModels(items, "USD", 30), first)
}
function test_costTotalsPreferEmittedTotalsAndFallBackToTheWindow() {
var emitted = Normalizer.normalizeCostTotals(
{ totalCost: 12.5, totalTokens: 900 }, 99, 99, "EUR")
compare(emitted.cost, 12.5)
compare(emitted.tokens, 900)
compare(emitted.currency, "EUR")
var fallback = Normalizer.normalizeCostTotals(null, 7.25, 400, "")
compare(fallback.cost, 7.25)
compare(fallback.tokens, 400)
compare(fallback.currency, "USD")
}
function test_costTotalsPreserveMissingCostForTokenOnlySnapshots() {
var totals = Normalizer.normalizeCostTotals(
{ totalTokens: 900 }, undefined, undefined, "USD")
compare(totals.cost, null)
compare(totals.tokens, 900)
}
function test_costTotalsRejectNullAndBooleanNumbersBeforeUsingFallbacks() {
var totals = Normalizer.normalizeCostTotals({
totalCost: null,
totalTokens: false,
inputTokens: true,
outputTokens: ""
}, 7.25, 400, "USD")
compare(totals.cost, 7.25)
compare(totals.tokens, 400)
compare(totals.inputTokens, 0)
compare(totals.outputTokens, 0)
}
function test_costTotalsSumTokenPartsWhenNoTotalIsEmitted() {
var totals = Normalizer.normalizeCostTotals(
{ inputTokens: 100, outputTokens: 50, cacheReadTokens: 25, cacheCreationTokens: 5 },
0, undefined, "USD")
compare(totals.tokens, 180)
}
function test_costTotalsNeverGoNegativeOrNaN() {
var totals = Normalizer.normalizeCostTotals(
{ totalCost: -5, totalTokens: "lots", inputTokens: "x" }, undefined, undefined, "USD")
compare(totals.cost, 0)
compare(totals.tokens, null)
compare(totals.inputTokens, 0)
}
function test_providerCostTotalsHideAntigravityCostButKeepOtherZeroes() {
var antigravity = Normalizer.normalizeProviderCostTotals(
"antigravity", { totalCost: 0, totalTokens: 0 }, 0, 0, "USD")
var codex = Normalizer.normalizeProviderCostTotals(
"codex", { totalCost: 0, totalTokens: 0 }, 0, 0, "USD")
compare(antigravity.cost, null)
compare(antigravity.tokens, 0)
compare(codex.cost, 0)
}
function test_providerCostAmountsPreserveUnavailableAntigravityPricing_data() {
return [
{ tag: "antigravity-idle-today", provider: "antigravity", amount: 0, expected: null },
{ tag: "antigravity-no-pricing", provider: "antigravity", amount: 1, expected: null },
{ tag: "antigravity-missing", provider: "antigravity", amount: undefined, expected: null },
{ tag: "codex-zero", provider: "codex", amount: 0, expected: 0 },
{ tag: "claude-zero", provider: "claude", amount: 0, expected: 0 },
{ tag: "codex-positive", provider: "codex", amount: 1.25, expected: 1.25 },
{ tag: "legacy-numeric-string", provider: "claude", amount: "2.5", expected: 2.5 },
{ tag: "missing", provider: "codex", amount: undefined, expected: null },
{ tag: "null", provider: "claude", amount: null, expected: null },
{ tag: "boolean", provider: "codex", amount: false, expected: null }
]
}
function test_providerCostAmountsPreserveUnavailableAntigravityPricing(data) {
compare(Normalizer.normalizeProviderCostAmount(data.provider, data.amount), data.expected)
}
function test_sumTokenPartsReportsNaNWhenNothingIsUsable() {
verify(isNaN(Normalizer.sumTokenParts(undefined, null, "x", NaN)))
compare(Normalizer.sumTokenParts(0, 0, 0, 0), 0)
verify(isNaN(Normalizer.sumTokenParts(-3, undefined, undefined, undefined)))
compare(Normalizer.sumTokenParts(1, 2, 3, 4), 10)
}
function test_costModelsAggregateAcrossDaysAndRankByCost() {
var rows = Normalizer.normalizeCostModels([
{ modelBreakdowns: [{ modelName: "gpt-5", cost: 1, totalTokens: 10 }] },
{ modelBreakdowns: [
{ modelName: "gpt-5", cost: 2, totalTokens: 20 },
{ model: "sonnet", totalCost: 5, tokens: 1 }
] }
], "USD", 30).rows
compare(rows.length, 2)
compare(rows[0].label, "sonnet")
compare(rows[0].cost, 5)
compare(rows[1].label, "gpt-5")
compare(rows[1].cost, 3)
compare(rows[1].tokens, 30)
}
function test_costModelsUseTheSameCalendarWindowAsDailyHistory_data() {
return [
{ tag: "date-only", updatedAt: "2026-08-30" },
{ tag: "timestamp", updatedAt: new Date(2026, 7, 30, 12).toISOString() }
]
}
function test_costModelsUseTheSameCalendarWindowAsDailyHistory(data) {
var items = [
{ date: "2026-08-20", totalCost: 100, totalTokens: 1000,
modelBreakdowns: [{ modelName: "old", cost: 100, totalTokens: 1000 }] },
{ date: "2026-08-24", totalCost: 2, totalTokens: 20,
modelBreakdowns: [{ modelName: "current", cost: 2, totalTokens: 20 }] },
{ date: "2026-08-29", totalCost: 1, totalTokens: 10,
modelBreakdowns: [{ modelName: "current", cost: 1, totalTokens: 10 }] },
{ date: "2026-08-31", totalCost: 200, totalTokens: 2000,
modelBreakdowns: [{ modelName: "future", cost: 200, totalTokens: 2000 }] }
]
var daily = Normalizer.normalizeCostDaily(items, "USD", 7, data.updatedAt)
var models = Normalizer.normalizeCostModels(items, "USD", 7, data.updatedAt).rows
compare(daily.length, 7)
compare(models.length, 1)
compare(models[0].label, "current")
compare(models[0].cost, 3)
compare(models[0].tokens, 30)
compare(daily.reduce(function(total, row) { return total + row.cost }, 0),
models[0].cost)
}
function test_costModelsFindInRangeDatesBeyondTheRowCount() {
var items = [
{ day: "2026-08-29", modelBreakdowns: [{ modelName: "current", cost: 2 }] },
null,
{ dayKey: "2026-08-20", modelBreakdowns: [{ modelName: "old", cost: 100 }] },
{ date: "2026-08-30", modelBreakdowns: [{ modelName: "current", cost: 3 }] }
]
var rows = Normalizer.normalizeCostModels(items, "USD", 2, "2026-08-30").rows
compare(rows.length, 1)
compare(rows[0].label, "current")
compare(rows[0].cost, 5)
}
function test_costModelsDoNotReuseDatesOutsideAnEmptyWindow() {
var rows = Normalizer.normalizeCostModels([
{ date: "2026-08-20", modelBreakdowns: [{ modelName: "old", cost: 100 }] }
], "USD", 7, "2026-08-30").rows
compare(rows.length, 0)
}
function test_costModelsKeepLegacyTailWithoutUsableDates_data() {
return [
{ tag: "missing-update", updatedAt: undefined, dated: true },
{ tag: "invalid-update", updatedAt: "not-a-date", dated: true },
{ tag: "missing-days", updatedAt: "2026-08-30", dated: false }
]
}
function test_costModelsKeepLegacyTailWithoutUsableDates(data) {
var items = []
for (var i = 0; i < 3; i++) {
items.push({
date: data.dated ? "2026-08-" + (20 + i) : "",
modelBreakdowns: [{ modelName: "model-" + i, cost: i + 1 }]
})
}
var rows = Normalizer.normalizeCostModels(items, "USD", 2, data.updatedAt).rows
compare(rows.length, 2)
compare(rows[0].label, "model-2")
compare(rows[1].label, "model-1")
}
function test_costModelsStopAtTheCalendarInspectionBound() {
var items = [{
date: "2026-08-29",
modelBreakdowns: [{ modelName: "uninspected", cost: 100 }]
}]
for (var i = 0; i < Normalizer.maximumCostHistoryScanItems - 1; i++) {
items.push(null)
}
items.push({
date: "2026-08-30",
modelBreakdowns: [{ modelName: "inspected", cost: 1 }]
})
var rows = Normalizer.normalizeCostModels(items, "USD", 2, "2026-08-30").rows
compare(rows.length, 1)
compare(rows[0].label, "inspected")
}
function test_costModelsKeepTheDayBudgetWithDuplicateDates() {
var items = []
for (var i = 0; i < 5; i++) {
items.push({
date: "2026-08-30",
modelBreakdowns: [{ modelName: "current", cost: 1 }]
})
}
var rows = Normalizer.normalizeCostModels(items, "USD", 2, "2026-08-30").rows
compare(rows.length, 1)
compare(rows[0].cost, 2)
}
function test_costModelsPreserveMissingCostForTokenOnlyBreakdowns() {
var rows = Normalizer.normalizeCostModels([{ modelBreakdowns: [{
modelName: "gemini-2.5-pro",
cost: null,
totalTokens: 198
}] }], "USD", 30).rows
compare(rows.length, 1)
compare(rows[0].cost, null)
compare(rows[0].tokens, 198)
}
function test_costModelsRejectCoerciveNumbersAndUseValidAliases() {
var rows = Normalizer.normalizeCostModels([{ modelBreakdowns: [
{ modelName: "null-cost", cost: null },
{ modelName: "boolean-tokens", cost: false, totalTokens: true },
{ modelName: "valid-aliases", cost: null, totalCost: 3,
totalTokens: false, tokens: 4 }
] }], "USD", 30).rows
compare(rows.length, 1)
compare(rows[0].label, "valid-aliases")
compare(rows[0].cost, 3)
compare(rows[0].tokens, 4)
}
function test_costModelsDropPrototypePollutingModelNames() {
var rows = Normalizer.normalizeCostModels([
{ modelBreakdowns: JSON.parse(
'[{"modelName":"__proto__","cost":9},'
+ '{"modelName":"constructor","cost":9},'
+ '{"modelName":"prototype","cost":9},'
+ '{"modelName":"gpt-5","cost":1}]') }
], "USD", 30).rows
compare(rows.length, 1)
compare(rows[0].label, "gpt-5")
compare(({}).polluted, undefined)
}
function test_costModelsKeepPrototypeMemberNamesThatAreNotDangerous() {
// "toString" is a plausible model name and must survive the own-key check
// that a bare `byName[name]` lookup would have swallowed.
var rows = Normalizer.normalizeCostModels([
{ modelBreakdowns: [{ modelName: "toString", cost: 4 }] }
], "USD", 30).rows
compare(rows.length, 1)
compare(rows[0].label, "toString")
compare(rows[0].cost, 4)
}
function test_costModelsCapBreakdownsPerDayAndRowsOverall() {
var breakdowns = []
for (var i = 0; i < Normalizer.maximumModelBreakdownsPerDay + 40; i++) {
breakdowns.push({ modelName: "model-" + i, cost: i + 1 })
}
var rows = Normalizer.normalizeCostModels([{ modelBreakdowns: breakdowns }], "USD", 30).rows
compare(rows.length, 6)
// The most expensive surviving model is the last one inside the per-day
// breakdown bound, not the most expensive one in the payload.
compare(rows[0].label, "model-" + (Normalizer.maximumModelBreakdownsPerDay - 1))
}
function test_costModelsIgnoreMalformedBreakdownContainers() {
compare(Normalizer.normalizeCostModels(null, "USD", 30).rows.length, 0)
compare(Normalizer.normalizeCostModels("daily", "USD", 30).rows.length, 0)
compare(Normalizer.normalizeCostModels([
null,
{ modelBreakdowns: "gpt-5" },
{ modelBreakdowns: { "0": { modelName: "gpt-5", cost: 1 } } },
{ modelBreakdowns: [{ modelName: "gpt-5" }] }
], "USD", 30).rows.length, 0)
}
function test_costModelsFloorFractionalDayBounds() {
// A fractional window must keep the last whole days like
// normalizeCostDaily does: without the floor the loop index stays
// fractional, every day lookup misses, and all model rows disappear.
var items = [
{ modelBreakdowns: [{ modelName: "old", cost: 1 }] },
{ modelBreakdowns: [{ modelName: "mid", cost: 2 }] },
{ modelBreakdowns: [{ modelName: "new", cost: 3 }] }
]
var rows = Normalizer.normalizeCostModels(items, "USD", 2.5).rows
compare(rows.length, 2)
compare(rows[0].label, "new")
compare(rows[1].label, "mid")
var singleDay = Normalizer.normalizeCostModels(items, "USD", 0.5).rows
compare(singleDay.length, 1)
compare(singleDay[0].label, "new")
}
// --- bounded display text ----------------------------------------------
function test_boundedDisplayTextBlanksNullishValues() {
compare(Normalizer.boundedDisplayText(null, 120), "")
compare(Normalizer.boundedDisplayText(undefined, 120), "")
compare(Normalizer.boundedDisplayText("", 120), "")
compare(Normalizer.boundedDisplayText(" ", 120), "")
}
// Structured values are dropped; scalars still coerce, because a numeric or
// boolean CLI field is a value the popup can legitimately print.
function test_boundedDisplayTextDropsStructuredPayloadsButKeepsScalars() {
compare(Normalizer.boundedDisplayText({ nested: "object" }, 120), "")
compare(Normalizer.boundedDisplayText({}, 120), "")
compare(Normalizer.boundedDisplayText([1, 2, 3], 120), "")
compare(Normalizer.boundedDisplayText([], 120), "")
compare(Normalizer.boundedDisplayText(7, 120), "7")
compare(Normalizer.boundedDisplayText(true, 120), "true")
}
function test_boundedDisplayTextRedactsCredentialsAtTheCliBoundary() {
compare(Normalizer.boundedDisplayText(
"Authorization: Bearer provider-secret", 120),
"Authorization: [redacted]")
}
function test_boundedDisplayTextKeepsFalsyScalars() {
compare(Normalizer.boundedDisplayText(0, 120), "0")
compare(Normalizer.boundedDisplayText(false, 120), "false")
}
function test_structuredSessionFieldsDegradeWithoutDroppingTheSession() {
var session = Normalizer.normalizeSession({
provider: "codex",
projectName: "codexbar-plasma",
host: { nested: "object" },
sessionName: ["a", "b"]
})
compare(session.projectName, "codexbar-plasma")
compare(session.host, "")
compare(session.sessionName, "")
}
function test_boundedDisplayTextPreservesUnicodeWithoutSplittingGraphemes() {
// Mirrors the contract exercised in tst_usage_details.qml: the CLI already
// validates its own character budget, so re-slicing UTF-16 here would
// corrupt combining marks and emoji sequences.
var text = "família 👨👩👧👦 café ünïcödé"
compare(Normalizer.boundedDisplayText(text, 120), text)
var session = Normalizer.normalizeSession({ provider: "codex", projectName: text })
compare(session.projectName, text)
}
function test_boundedDisplayTextFallsBackToASaneLimit() {
var oversized = ""
for (var i = 0; i < 900; i++) {
oversized += "a"
}
compare(Normalizer.boundedDisplayText(oversized, 0).length, 500)
compare(Normalizer.boundedDisplayText(oversized, NaN).length, 500)
compare(Normalizer.boundedDisplayText(oversized, -1).length, 500)
compare(Normalizer.boundedDisplayText(oversized, 120).length, 120)
}
// --- clamp --------------------------------------------------------------
function test_clampStaysInsideTheRequestedRange() {
compare(Normalizer.clamp(150, 0, 100), 100)
compare(Normalizer.clamp(-1, 0, 100), 0)
compare(Normalizer.clamp(42, 0, 100), 42)
}
}