use crate::config::{ApiProvider, Config, ProviderConfig};
use crate::pricing::{CostCurrency, format_cost_amount};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BillingPresentation {
Metered,
Subscription(&'static str),
Local,
Unknown,
}
#[derive(Debug, Clone, PartialEq)]
pub enum UsageChip {
Money(String),
PricedSubtotal {
amount: String,
legacy: bool,
},
Allowance {
label: &'static str,
used_pct: Option<f32>,
},
Local,
Unknown,
Hidden,
}
impl BillingPresentation {
#[must_use]
pub const fn shows_money(self) -> bool {
matches!(self, Self::Metered)
}
#[must_use]
#[allow(dead_code)] pub const fn label(self) -> Option<&'static str> {
match self {
Self::Metered => None,
Self::Subscription(label) => Some(label),
Self::Local => Some("local"),
Self::Unknown => Some("unknown"),
}
}
}
#[cfg(test)]
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ChildBillingProvenance {
Metered,
Subscription { label: String },
Local,
Unknown,
}
#[cfg(test)]
impl From<BillingPresentation> for ChildBillingProvenance {
fn from(billing: BillingPresentation) -> Self {
match billing {
BillingPresentation::Metered => Self::Metered,
BillingPresentation::Subscription(label) => Self::Subscription {
label: label.to_string(),
},
BillingPresentation::Local => Self::Local,
BillingPresentation::Unknown => Self::Unknown,
}
}
}
#[cfg(test)]
impl ChildBillingProvenance {
#[must_use]
pub fn as_billing_presentation(&self) -> BillingPresentation {
match self {
Self::Metered => BillingPresentation::Metered,
Self::Local => BillingPresentation::Local,
Self::Unknown => BillingPresentation::Unknown,
Self::Subscription { label } => static_subscription_label(label).map_or(
BillingPresentation::Unknown,
BillingPresentation::Subscription,
),
}
}
}
#[cfg(test)]
fn static_subscription_label(label: &str) -> Option<&'static str> {
Some(match label {
"Codex OAuth quota" => "Codex OAuth quota",
"OpenCode Go quota" => "OpenCode Go quota",
"Z.ai Coding Plan quota" => "Z.ai Coding Plan quota",
"MiMo token plan" => "MiMo token plan",
"Kimi Code quota" => "Kimi Code quota",
"MiniMax Token Plan quota" => "MiniMax Token Plan quota",
"Grok OAuth quota" => "Grok OAuth quota",
"Claude OAuth quota" => "Claude OAuth quota",
"StepFun Step Plan quota" => "StepFun Step Plan quota",
_ => return None,
})
}
#[cfg(test)]
#[derive(Debug, Clone, Copy)]
pub struct DispatchedRoute<'a> {
pub provider: ApiProvider,
pub base_url: &'a str,
}
#[derive(Debug, Clone, Copy)]
pub struct DispatchedReceipt<'a> {
pub provider: ApiProvider,
pub identity: Option<&'a str>,
pub base_url: &'a str,
pub product: RouteProduct,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum RouteProduct {
#[default]
Unproven,
Subscription(&'static str),
Metered,
}
#[must_use]
pub fn for_route(config: &Config, provider: ApiProvider) -> BillingPresentation {
let base_url = config.base_url_for_route(provider);
let identity = config.provider_identity_for(provider);
classify(
provider,
Some(identity.as_str()),
&base_url,
capture_product(config, provider),
)
}
#[must_use]
pub fn capture_product(config: &Config, provider: ApiProvider) -> RouteProduct {
let provider_config = config.provider_config_for(provider);
match provider {
ApiProvider::Minimax | ApiProvider::MinimaxAnthropic => {
match minimax_credential_product(config, provider, provider_config) {
CredentialProduct::Plan => RouteProduct::Subscription("MiniMax Token Plan quota"),
CredentialProduct::PayAsYouGo => RouteProduct::Metered,
CredentialProduct::Unprovable => RouteProduct::Unproven,
}
}
ApiProvider::XiaomiMimo => {
if xiaomi_is_explicit_pay_as_you_go(provider_config) {
RouteProduct::Metered
} else {
RouteProduct::Subscription("MiMo token plan")
}
}
ApiProvider::Xai => {
if provider_config.is_some_and(uses_xai_oauth)
&& crate::xai_oauth::credentials_valid(config)
{
RouteProduct::Subscription("Grok OAuth quota")
} else {
RouteProduct::Metered
}
}
ApiProvider::Anthropic => {
if provider_config.is_some_and(uses_anthropic_oauth) {
RouteProduct::Subscription("Claude OAuth quota")
} else {
RouteProduct::Metered
}
}
ApiProvider::Custom => match provider_config {
Some(entry) if !custom_billing_unknown(entry) => RouteProduct::Metered,
_ => RouteProduct::Unproven,
},
_ => RouteProduct::Unproven,
}
}
#[must_use]
pub fn for_dispatched_receipt(receipt: DispatchedReceipt<'_>) -> BillingPresentation {
classify(
receipt.provider,
receipt.identity,
receipt.base_url,
receipt.product,
)
}
#[cfg(test)]
#[must_use]
pub fn for_dispatched_route(config: &Config, route: DispatchedRoute<'_>) -> BillingPresentation {
let identity = config.provider_identity_for(route.provider);
for_dispatched_receipt(DispatchedReceipt {
provider: route.provider,
identity: Some(identity.as_str()),
base_url: route.base_url,
product: capture_product(config, route.provider),
})
}
fn classify(
provider: ApiProvider,
identity: Option<&str>,
base_url: &str,
product: RouteProduct,
) -> BillingPresentation {
if matches!(
provider,
ApiProvider::Ollama | ApiProvider::Sglang | ApiProvider::Vllm
) {
return BillingPresentation::Local;
}
if provider == ApiProvider::OpenaiCodex {
return BillingPresentation::Subscription("Codex OAuth quota");
}
if provider == ApiProvider::OpencodeGo {
return BillingPresentation::Subscription("OpenCode Go quota");
}
match provider {
ApiProvider::Stepfun => stepfun_billing_for_endpoint(Some(base_url)),
ApiProvider::Zai if base_url.trim().is_empty() => BillingPresentation::Unknown,
ApiProvider::Zai if is_zai_coding_plan_endpoint(base_url) => {
BillingPresentation::Subscription("Z.ai Coding Plan quota")
}
ApiProvider::Zai => BillingPresentation::Metered,
ApiProvider::XiaomiMimo => product_billing(product),
ApiProvider::Moonshot if crate::config::moonshot_base_url_is_exact_kimi_code(base_url) => {
BillingPresentation::Subscription("Kimi Code quota")
}
ApiProvider::Moonshot
if crate::config::moonshot_base_url_is_exact_direct_platform(base_url) =>
{
BillingPresentation::Metered
}
ApiProvider::Moonshot => BillingPresentation::Unknown,
ApiProvider::Minimax | ApiProvider::MinimaxAnthropic
if !minimax_base_url_is_supported_direct(base_url) =>
{
BillingPresentation::Unknown
}
ApiProvider::Minimax | ApiProvider::MinimaxAnthropic => product_billing(product),
ApiProvider::Xai | ApiProvider::Anthropic => product_billing(product),
ApiProvider::Custom
if identity.is_none_or(|key| key.trim().is_empty()) || base_url.trim().is_empty() =>
{
BillingPresentation::Unknown
}
ApiProvider::Custom => product_billing(product),
_ => endpoint_shaped_payg_billing(provider, base_url),
}
}
fn endpoint_shaped_payg_billing(provider: ApiProvider, base_url: &str) -> BillingPresentation {
use crate::pricing::EndpointMetering;
let surface = crate::pricing::billing_surface_for_route(provider, Some(base_url));
match crate::pricing::endpoint_metering_for_billing_surface(surface) {
EndpointMetering::Money => BillingPresentation::Metered,
EndpointMetering::LocalNoBill => BillingPresentation::Local,
EndpointMetering::ExactSubscription => BillingPresentation::Subscription("provider plan"),
EndpointMetering::Unknown => BillingPresentation::Unknown,
}
}
#[must_use]
pub fn for_endpoint_without_config(
provider: ApiProvider,
base_url: Option<&str>,
) -> BillingPresentation {
classify(
provider,
None,
base_url.unwrap_or_default(),
RouteProduct::Unproven,
)
}
#[must_use]
pub fn billing_surface_for_dispatch(
config: Option<&Config>,
provider: ApiProvider,
base_url: Option<&str>,
) -> Option<&'static str> {
if let Some(config) = config {
match for_route(config, provider) {
BillingPresentation::Subscription(_) => {
return Some(match provider {
ApiProvider::Minimax | ApiProvider::MinimaxAnthropic => {
crate::pricing::MINIMAX_TOKEN_PLAN_BILLING_SURFACE
}
ApiProvider::OpenaiCodex
| ApiProvider::OpencodeGo
| ApiProvider::Anthropic
| ApiProvider::Xai => crate::pricing::OAUTH_SUBSCRIPTION_BILLING_SURFACE,
_ => crate::pricing::billing_surface_for_route(provider, base_url)
.unwrap_or(crate::pricing::UNCLASSIFIED_BILLING_SURFACE),
});
}
BillingPresentation::Metered
if matches!(
provider,
ApiProvider::Minimax | ApiProvider::MinimaxAnthropic
) =>
{
return Some(crate::pricing::MINIMAX_PAYG_BILLING_SURFACE);
}
BillingPresentation::Local => return Some(crate::pricing::LOCAL_BILLING_SURFACE),
BillingPresentation::Unknown | BillingPresentation::Metered => {}
}
}
crate::pricing::billing_surface_for_route(provider, base_url)
}
fn product_billing(product: RouteProduct) -> BillingPresentation {
match product {
RouteProduct::Subscription(label) => BillingPresentation::Subscription(label),
RouteProduct::Metered => BillingPresentation::Metered,
RouteProduct::Unproven => BillingPresentation::Unknown,
}
}
use crate::config::minimax_base_url_is_supported_direct;
fn stepfun_billing_for_endpoint(base_url: Option<&str>) -> BillingPresentation {
match crate::pricing::billing_surface_for_route(ApiProvider::Stepfun, base_url) {
Some(crate::pricing::STEPFUN_PAYG_BILLING_SURFACE) => BillingPresentation::Metered,
Some(crate::pricing::STEPFUN_PLAN_BILLING_SURFACE) => {
BillingPresentation::Subscription("StepFun Step Plan quota")
}
_ => BillingPresentation::Unknown,
}
}
fn is_zai_coding_plan_endpoint(base_url: &str) -> bool {
base_url
.trim()
.trim_end_matches('/')
.ends_with("/api/coding/paas/v4")
}
#[must_use]
#[cfg(test)]
pub fn for_child_route(
parent_provider: ApiProvider,
parent_billing: BillingPresentation,
child_provider: ApiProvider,
child_provenance: Option<BillingPresentation>,
) -> BillingPresentation {
if let Some(provenance) = child_provenance {
return provenance;
}
if child_provider == parent_provider {
return parent_billing;
}
match child_provider {
ApiProvider::Ollama | ApiProvider::Sglang | ApiProvider::Vllm => BillingPresentation::Local,
_ => BillingPresentation::Unknown,
}
}
#[cfg(test)]
#[must_use]
pub fn for_child_route_receipt(
parent: ChildParentRoute<'_>,
child: ChildRouteClaim<'_>,
child_provenance: Option<BillingPresentation>,
) -> BillingPresentation {
if let Some(provenance) = child_provenance {
return provenance;
}
if !child.named {
return parent.billing;
}
if child.provider == Some(parent.provider)
&& child.identity.is_some_and(|key| key == parent.identity)
{
return parent.billing;
}
match child.provider {
Some(ApiProvider::Ollama | ApiProvider::Sglang | ApiProvider::Vllm) => {
BillingPresentation::Local
}
_ => BillingPresentation::Unknown,
}
}
#[cfg(test)]
#[must_use]
pub fn child_route_metadata(
provider: ApiProvider,
identity: &str,
base_url: &str,
product: RouteProduct,
) -> serde_json::Value {
let billing = for_dispatched_receipt(DispatchedReceipt {
provider,
identity: Some(identity),
base_url,
product,
});
serde_json::json!({
"child_provider": provider.as_str(),
"child_provider_identity": identity,
"child_billing": ChildBillingProvenance::from(billing),
})
}
#[cfg(test)]
#[derive(Debug, Clone, Copy)]
pub struct ChildParentRoute<'a> {
pub provider: ApiProvider,
pub identity: &'a str,
pub billing: BillingPresentation,
}
#[cfg(test)]
#[derive(Debug, Clone, Copy, Default)]
pub struct ChildRouteClaim<'a> {
pub named: bool,
pub provider: Option<ApiProvider>,
pub identity: Option<&'a str>,
}
#[must_use]
pub fn has_priced_metered_basis(
billing: BillingPresentation,
provider: ApiProvider,
model: &str,
) -> bool {
billing.shows_money()
&& if provider == ApiProvider::Stepfun {
crate::pricing::has_pricing_for_billing_surface(
provider,
model,
Some(crate::pricing::STEPFUN_PAYG_BILLING_SURFACE),
)
} else {
crate::pricing::has_pricing_for_provider(provider, model)
}
}
#[must_use]
pub fn usage_chip(
billing: BillingPresentation,
provider: ApiProvider,
model: &str,
displayed_cost: f64,
currency: CostCurrency,
used_pct: Option<f32>,
) -> UsageChip {
match billing {
BillingPresentation::Local => UsageChip::Local,
BillingPresentation::Unknown => UsageChip::Unknown,
BillingPresentation::Subscription(label) => UsageChip::Allowance {
label,
used_pct: used_pct.filter(|pct| pct.is_finite() && *pct >= 0.0),
},
BillingPresentation::Metered => {
if !has_priced_metered_basis(billing, provider, model) {
UsageChip::Unknown
} else if displayed_cost.is_finite() && displayed_cost > 0.0 {
UsageChip::Money(format_cost_amount(displayed_cost, currency))
} else {
UsageChip::Hidden
}
}
}
}
#[must_use]
#[allow(dead_code)] pub fn format_usage_chip(chip: &UsageChip) -> Option<String> {
match chip {
UsageChip::Money(amount) => Some(amount.clone()),
UsageChip::PricedSubtotal { amount, legacy } => Some(if *legacy {
format!("saved subtotal {amount} + unknown")
} else {
format!("subtotal {amount} + unknown")
}),
UsageChip::Allowance { label, used_pct } => Some(match used_pct {
Some(pct) => format!("usage: {label} · {pct:.0}%"),
None => format!("usage: {label}"),
}),
UsageChip::Local => Some("cost: local".to_string()),
UsageChip::Unknown => Some("cost: unknown".to_string()),
UsageChip::Hidden => None,
}
}
#[must_use]
pub fn format_usage_line(chip: &UsageChip) -> String {
match chip {
UsageChip::Money(amount) => format!("cost: {amount}"),
UsageChip::PricedSubtotal { amount, legacy } => {
if *legacy {
format!("cost: saved subtotal {amount} + unknown")
} else {
format!("cost: subtotal {amount} + unknown")
}
}
UsageChip::Allowance { label, used_pct } => match used_pct {
Some(pct) => format!("usage: {label} · {pct:.0}% used"),
None => format!("usage: {label}"),
},
UsageChip::Local => "cost: local".to_string(),
UsageChip::Unknown => "cost: unknown".to_string(),
UsageChip::Hidden => "cost: —".to_string(),
}
}
fn custom_billing_unknown(config: &ProviderConfig) -> bool {
let mode = auth_mode(config);
!mode.as_deref().is_some_and(|mode| {
matches!(
mode,
"api_key"
| "api"
| "key"
| "keyring"
| "payg"
| "paygo"
| "pay_as_you_go"
| "metered"
| "standard"
)
})
}
fn normalized(value: &str) -> String {
value.trim().to_ascii_lowercase().replace(['-', ' '], "_")
}
fn auth_mode(config: &ProviderConfig) -> Option<String> {
config
.auth_mode
.as_deref()
.or(config.mode.as_deref())
.map(normalized)
}
fn uses_xai_oauth(config: &ProviderConfig) -> bool {
auth_mode(config).is_some_and(|mode| crate::xai_oauth::auth_mode_uses_xai_oauth(&mode))
}
fn uses_anthropic_oauth(config: &ProviderConfig) -> bool {
auth_mode(config).is_some_and(|mode| {
matches!(
mode.as_str(),
"oauth"
| "anthropic_oauth"
| "claude_oauth"
| "claude_cli"
| "claude_code"
| "max"
| "subscription"
)
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CredentialProduct {
Plan,
PayAsYouGo,
Unprovable,
}
fn minimax_credential_product(
config: &Config,
provider: ApiProvider,
provider_config: Option<&ProviderConfig>,
) -> CredentialProduct {
if let Some(mode) = provider_config
.and_then(|config| config.mode.as_deref())
.filter(|mode| !mode.trim().is_empty())
.map(normalized)
{
return match mode.as_str() {
"token_plan" | "tokenplan" | "plan" | "subscription" | "subscription_plan" => {
CredentialProduct::Plan
}
"pay_as_you_go" | "payg" | "paygo" | "pay_as_go" | "metered" | "standard" | "api"
| "api_key" | "default" => CredentialProduct::PayAsYouGo,
_ => CredentialProduct::Unprovable,
};
}
match visible_minimax_credential_is_plan_shaped(config, provider, provider_config) {
Some(true) => CredentialProduct::Plan,
Some(false) => CredentialProduct::PayAsYouGo,
None => CredentialProduct::Unprovable,
}
}
fn visible_minimax_credential_is_plan_shaped(
config: &Config,
provider: ApiProvider,
provider_config: Option<&ProviderConfig>,
) -> Option<bool> {
let is_plan_shaped = |key: &str| key.trim_start().starts_with("sk-cp");
if let Some(key) = provider_config
.and_then(|config| config.api_key.as_deref())
.filter(|key| {
crate::config::classify_config_api_key_value(key)
== crate::config::ConfigApiKeyValueKind::Literal
})
.map(str::trim)
{
return Some(is_plan_shaped(key));
}
if let Some(value) = provider_config
.and_then(|config| config.api_key_env.as_deref())
.map(str::trim)
.filter(|name| !name.is_empty())
.and_then(|name| std::env::var(name).ok())
.filter(|value| !value.trim().is_empty())
{
return Some(is_plan_shaped(&value));
}
if config.provider_uses_custom_endpoint(provider) {
return None;
}
std::env::var("MINIMAX_API_KEY")
.ok()
.filter(|key| !key.trim().is_empty())
.map(|key| is_plan_shaped(&key))
}
fn xiaomi_is_explicit_pay_as_you_go(config: Option<&ProviderConfig>) -> bool {
if let Some(mode) = std::env::var("XIAOMI_MIMO_MODE")
.ok()
.filter(|mode| !mode.trim().is_empty())
.map(|mode| normalized(&mode))
{
return matches!(
mode.as_str(),
"standard" | "default" | "payg" | "paygo" | "pay_as_you_go" | "pay_as_go"
);
}
if let Some(base_url) = std::env::var("XIAOMI_MIMO_BASE_URL")
.ok()
.filter(|base_url| !base_url.trim().is_empty())
{
return !base_url.to_ascii_lowercase().contains("token-plan-");
}
let token_plan_env = ["XIAOMI_MIMO_TOKEN_PLAN_API_KEY", "MIMO_TOKEN_PLAN_API_KEY"]
.iter()
.any(|name| std::env::var(name).is_ok_and(|value| !value.trim().is_empty()));
let standard_env = ["XIAOMI_MIMO_API_KEY", "XIAOMI_API_KEY", "MIMO_API_KEY"]
.iter()
.any(|name| std::env::var(name).is_ok_and(|value| !value.trim().is_empty()));
if standard_env && !token_plan_env {
return true;
}
let Some(config) = config else {
return false;
};
if let Some(mode) = config
.mode
.as_deref()
.filter(|mode| !mode.trim().is_empty())
.map(normalized)
{
return matches!(
mode.as_str(),
"pay_as_you_go" | "payg" | "paygo" | "api" | "standard" | "default"
);
}
if let Some(api_key) = config.api_key.as_deref().filter(|key| {
crate::config::classify_config_api_key_value(key)
== crate::config::ConfigApiKeyValueKind::Literal
}) {
return !api_key.trim_start().starts_with("tp-");
}
config.base_url.as_deref().is_some_and(|base_url| {
let lower = base_url.to_ascii_lowercase();
!lower.contains("token-plan-") && !lower.contains("token_plan_")
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Usage;
use crate::pricing::CostCurrency;
fn config_with(provider: ApiProvider, provider_config: ProviderConfig) -> Config {
let mut config = Config::default();
*config.provider_config_for_mut(provider) = provider_config;
config
}
fn moonshot_endpoint_env_lock() -> [crate::test_support::EnvVarGuard; 4] {
[
crate::test_support::EnvVarGuard::remove("CODEWHALE_BASE_URL"),
crate::test_support::EnvVarGuard::remove("DEEPSEEK_BASE_URL"),
crate::test_support::EnvVarGuard::remove("MOONSHOT_BASE_URL"),
crate::test_support::EnvVarGuard::remove("KIMI_BASE_URL"),
]
}
#[test]
fn imported_token_moonshot_without_table_base_url_bills_membership_quota() {
let _lock = crate::test_support::lock_test_env();
let _env = moonshot_endpoint_env_lock();
let config = config_with(
ApiProvider::Moonshot,
ProviderConfig {
auth_mode: Some("kimi_oauth".to_string()),
..ProviderConfig::default()
},
);
assert_eq!(
config.base_url_for_route(ApiProvider::Moonshot),
crate::config::DEFAULT_KIMI_CODE_BASE_URL
);
let billing = for_route(&config, ApiProvider::Moonshot);
assert_eq!(
billing,
BillingPresentation::Subscription("Kimi Code quota")
);
assert!(!billing.shows_money());
let chip = usage_chip(
billing,
ApiProvider::Moonshot,
crate::config::DEFAULT_KIMI_CODE_MODEL,
12.34,
CostCurrency::Usd,
None,
);
assert!(!matches!(chip, UsageChip::Money(_)));
assert_eq!(
format_usage_chip(&chip).as_deref(),
Some("usage: Kimi Code quota")
);
assert!(!format_usage_line(&chip).contains("OAuth"));
assert!(!format_usage_line(&chip).contains("imported token"));
assert!(!format_usage_line(&chip).contains('$'));
}
#[test]
fn turn_complete_kimi_code_receipt_accrues_no_dollars() {
let _lock = crate::test_support::lock_test_env();
let _env = moonshot_endpoint_env_lock();
let mut config = config_with(
ApiProvider::Deepseek,
ProviderConfig {
api_key: Some("sk-session-deepseek".to_string()),
..ProviderConfig::default()
},
);
config.provider = Some("deepseek".to_string());
let billing = for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Moonshot,
base_url: "https://api.kimi.com/coding/v1",
},
);
assert_eq!(
billing,
BillingPresentation::Subscription("Kimi Code quota")
);
assert!(!billing.shows_money());
let no_receipt = for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Moonshot,
base_url: "",
},
);
assert_eq!(no_receipt, BillingPresentation::Unknown);
assert!(!no_receipt.shows_money());
}
#[test]
fn moonshot_ambient_and_dispatch_billing_agree_on_the_resolved_endpoint() {
let _lock = crate::test_support::lock_test_env();
let _env = moonshot_endpoint_env_lock();
let cases = [
(
None,
Some("kimi_oauth"),
BillingPresentation::Subscription("Kimi Code quota"),
),
(None, None, BillingPresentation::Metered),
(
Some("https://api.kimi.com/coding/v1"),
None,
BillingPresentation::Subscription("Kimi Code quota"),
),
(
Some("https://api.moonshot.ai/v1"),
None,
BillingPresentation::Metered,
),
(
Some("https://proxy.example.test/v1"),
None,
BillingPresentation::Unknown,
),
];
for (base_url, auth_mode, expected) in cases {
let config = config_with(
ApiProvider::Moonshot,
ProviderConfig {
base_url: base_url.map(str::to_string),
auth_mode: auth_mode.map(str::to_string),
..ProviderConfig::default()
},
);
let resolved = config.base_url_for_route(ApiProvider::Moonshot);
let ambient = for_route(&config, ApiProvider::Moonshot);
let dispatched = for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Moonshot,
base_url: &resolved,
},
);
assert_eq!(ambient, expected, "{base_url:?}/{auth_mode:?}");
assert_eq!(
ambient, dispatched,
"{base_url:?}/{auth_mode:?} resolved to {resolved}: the pre-dispatch and \
receipt classifications must not be able to disagree"
);
}
}
#[test]
fn moonshot_custom_gateway_is_unknown_not_metered() {
let _lock = crate::test_support::lock_test_env();
let _env = moonshot_endpoint_env_lock();
for base_url in [
"https://proxy.example.test/v1",
"https://gateway.internal.test/moonshot/v1",
] {
let config = config_with(
ApiProvider::Moonshot,
ProviderConfig {
base_url: Some(base_url.to_string()),
..ProviderConfig::default()
},
);
let billing = for_route(&config, ApiProvider::Moonshot);
assert_eq!(
billing,
BillingPresentation::Unknown,
"{base_url} must not inherit a Moonshot product"
);
assert!(!billing.shows_money());
let chip = usage_chip(
billing,
ApiProvider::Moonshot,
"kimi-k2.7-code",
12.34,
CostCurrency::Usd,
None,
);
assert!(!matches!(chip, UsageChip::Money(_)));
assert!(!format_usage_line(&chip).contains('$'));
}
}
#[test]
fn moonshot_direct_platform_stays_metered_with_priced_model() {
let config = config_with(
ApiProvider::Moonshot,
ProviderConfig {
base_url: Some("https://api.moonshot.ai/v1".to_string()),
..ProviderConfig::default()
},
);
let billing = for_route(&config, ApiProvider::Moonshot);
assert_eq!(billing, BillingPresentation::Metered);
assert!(billing.shows_money());
let chip = usage_chip(
billing,
ApiProvider::Moonshot,
"kimi-k2.7-code",
0.42,
CostCurrency::Usd,
None,
);
assert!(matches!(chip, UsageChip::Money(_)));
assert!(format_usage_line(&chip).contains('$'));
}
#[test]
fn moonshot_exact_kimi_code_endpoint_is_subscription_quota() {
let config = config_with(
ApiProvider::Moonshot,
ProviderConfig {
base_url: Some("https://api.kimi.com/coding/v1".to_string()),
..ProviderConfig::default()
},
);
let billing = for_route(&config, ApiProvider::Moonshot);
assert_eq!(
billing,
BillingPresentation::Subscription("Kimi Code quota")
);
assert!(!billing.shows_money());
let chip = usage_chip(
billing,
ApiProvider::Moonshot,
"kimi-k2.7-code",
12.34,
CostCurrency::Usd,
None,
);
assert!(!matches!(chip, UsageChip::Money(_)));
assert_eq!(
chip,
UsageChip::Allowance {
label: "Kimi Code quota",
used_pct: None,
}
);
assert!(!format_usage_line(&chip).contains('$'));
}
#[test]
fn moonshot_neighboring_kimi_paths_are_unknown_not_metered() {
let _lock = crate::test_support::lock_test_env();
let _env = moonshot_endpoint_env_lock();
for base_url in [
"https://api.kimi.com/coding/v2",
"https://api.kimi.com/v1",
"https://api.kimi.com/coding/v1/preview",
] {
let config = config_with(
ApiProvider::Moonshot,
ProviderConfig {
base_url: Some(base_url.to_string()),
..ProviderConfig::default()
},
);
let billing = for_route(&config, ApiProvider::Moonshot);
assert_eq!(
billing,
BillingPresentation::Unknown,
"{base_url} must claim neither Kimi Code quota nor metered dollars"
);
assert!(!billing.shows_money());
assert_eq!(
billing,
for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Moonshot,
base_url,
},
)
);
}
}
#[test]
fn dispatched_moonshot_receipt_owns_billing_over_any_later_config_state() {
let _lock = crate::test_support::lock_test_env();
let _generic = crate::test_support::EnvVarGuard::remove("CODEWHALE_BASE_URL");
let _legacy = crate::test_support::EnvVarGuard::remove("DEEPSEEK_BASE_URL");
let _moonshot = crate::test_support::EnvVarGuard::remove("MOONSHOT_BASE_URL");
let _kimi = crate::test_support::EnvVarGuard::set(
"KIMI_BASE_URL",
"https://api.kimi.com/coding/v1",
);
let config = config_with(ApiProvider::Moonshot, ProviderConfig::default());
assert_eq!(
for_route(&config, ApiProvider::Moonshot),
BillingPresentation::Subscription("Kimi Code quota")
);
assert_eq!(
for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Moonshot,
base_url: "https://api.moonshot.ai/v1",
},
),
BillingPresentation::Metered,
"the endpoint the turn actually dispatched to owns its billing"
);
assert_eq!(
for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Moonshot,
base_url: "https://api.kimi.com/coding/v1",
},
),
BillingPresentation::Subscription("Kimi Code quota")
);
}
#[test]
fn dispatched_moonshot_endpoint_must_name_a_known_product() {
assert_eq!(
for_dispatched_route(
&Config::default(),
DispatchedRoute {
provider: ApiProvider::Moonshot,
base_url: "https://api.moonshot.ai/v1",
},
),
BillingPresentation::Metered
);
for ambiguous in [
"",
" ",
"https://api.kimi.com/v1",
"https://api.kimi.com/coding/v1/preview",
"https://gateway.internal.example/v1",
] {
let billing = for_dispatched_route(
&Config::default(),
DispatchedRoute {
provider: ApiProvider::Moonshot,
base_url: ambiguous,
},
);
assert_eq!(
billing,
BillingPresentation::Unknown,
"{ambiguous:?} names no Moonshot product"
);
assert!(!billing.shows_money());
}
}
#[test]
fn codex_oauth_never_claims_api_dollars() {
assert_eq!(
for_route(&Config::default(), ApiProvider::OpenaiCodex),
BillingPresentation::Subscription("Codex OAuth quota")
);
let chip = usage_chip(
BillingPresentation::Subscription("Codex OAuth quota"),
ApiProvider::OpenaiCodex,
"gpt-5.5",
12.34,
CostCurrency::Usd,
None,
);
assert_eq!(
format_usage_chip(&chip).as_deref(),
Some("usage: Codex OAuth quota")
);
assert!(!format_usage_line(&chip).contains('$'));
}
#[test]
fn xai_api_key_fallback_is_metered_when_external_oauth_is_unavailable() {
let _lock = crate::test_support::lock_test_env();
let temp = tempfile::tempdir().expect("xAI billing fixture");
let grok_path = temp.path().join("external-grok-auth.json");
std::fs::write(&grok_path, "must-never-be-read").expect("external trap");
let _home = crate::test_support::EnvVarGuard::set("CODEWHALE_HOME", temp.path());
let _grok = crate::test_support::EnvVarGuard::set("GROK_AUTH_PATH", &grok_path);
let config = config_with(
ApiProvider::Xai,
ProviderConfig {
auth_mode: Some("oauth".to_string()),
api_key: Some("xai-api-key".to_string()),
..ProviderConfig::default()
},
);
crate::external_credentials::reset_side_effect_trap();
assert_eq!(
for_route(&config, ApiProvider::Xai),
BillingPresentation::Metered
);
assert_eq!(
crate::external_credentials::side_effect_trap_counts(),
(0, 0)
);
assert_eq!(
std::fs::read_to_string(grok_path).expect("external trap unchanged"),
"must-never-be-read"
);
}
#[test]
fn opencode_go_quota_never_claims_token_dollars() {
let billing = for_route(&Config::default(), ApiProvider::OpencodeGo);
assert_eq!(
billing,
BillingPresentation::Subscription("OpenCode Go quota")
);
let chip = usage_chip(
billing,
ApiProvider::OpencodeGo,
"deepseek-v4-pro",
12.34,
CostCurrency::Usd,
None,
);
assert!(!format_usage_line(&chip).contains('$'));
assert_eq!(
for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::OpencodeGo,
None,
),
BillingPresentation::Unknown,
"provider identity alone must not claim OpenCode Go quota"
);
assert_eq!(
for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::OpencodeGo,
Some(BillingPresentation::Subscription("OpenCode Go quota")),
),
BillingPresentation::Subscription("OpenCode Go quota"),
"the child's own route truth is what may claim the quota"
);
}
#[test]
fn zai_coding_plan_endpoint_never_claims_api_dollars() {
let config = config_with(
ApiProvider::Zai,
ProviderConfig {
base_url: Some("https://api.z.ai/api/coding/paas/v4".to_string()),
..ProviderConfig::default()
},
);
let billing = for_route(&config, ApiProvider::Zai);
assert_eq!(
billing,
BillingPresentation::Subscription("Z.ai Coding Plan quota")
);
let chip = usage_chip(
billing,
ApiProvider::Zai,
"glm-5.2",
0.05,
CostCurrency::Usd,
None,
);
assert!(!format_usage_line(&chip).contains('$'));
}
#[test]
fn zai_default_coding_endpoint_never_claims_api_dollars() {
let _lock = crate::test_support::lock_test_env();
let _generic = crate::test_support::EnvVarGuard::remove("CODEWHALE_BASE_URL");
let _legacy = crate::test_support::EnvVarGuard::remove("DEEPSEEK_BASE_URL");
let config = config_with(ApiProvider::Zai, ProviderConfig::default());
assert_eq!(
for_route(&config, ApiProvider::Zai),
BillingPresentation::Subscription("Z.ai Coding Plan quota")
);
}
#[test]
fn stepfun_payg_shows_money_but_step_plan_stays_subscription_billed() {
let _lock = crate::test_support::lock_test_env();
let _generic = crate::test_support::EnvVarGuard::remove("CODEWHALE_BASE_URL");
let _legacy = crate::test_support::EnvVarGuard::remove("DEEPSEEK_BASE_URL");
let payg_billing = for_route(&Config::default(), ApiProvider::Stepfun);
assert_eq!(payg_billing, BillingPresentation::Metered);
let payg_chip = usage_chip(
payg_billing,
ApiProvider::Stepfun,
crate::config::DEFAULT_STEPFUN_MODEL,
0.42,
CostCurrency::Usd,
None,
);
assert_eq!(format_usage_chip(&payg_chip).as_deref(), Some("$0.42"));
let plan_config = config_with(
ApiProvider::Stepfun,
ProviderConfig {
base_url: Some("https://api.stepfun.ai/step_plan/v1".to_string()),
..ProviderConfig::default()
},
);
let plan_billing = for_route(&plan_config, ApiProvider::Stepfun);
assert_eq!(
plan_billing,
BillingPresentation::Subscription("StepFun Step Plan quota")
);
let plan_chip = usage_chip(
plan_billing,
ApiProvider::Stepfun,
crate::config::DEFAULT_STEPFUN_MODEL,
0.42,
CostCurrency::Usd,
None,
);
assert!(!format_usage_line(&plan_chip).contains('$'));
assert_eq!(
for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Stepfun,
None,
),
BillingPresentation::Unknown
);
}
#[test]
fn routed_zai_child_never_claims_api_dollars_without_full_route_config() {
let billing = for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Zai,
None,
);
assert_eq!(
billing,
BillingPresentation::Unknown,
"without the child's route truth, fail closed instead of guessing a quota"
);
assert!(!billing.shows_money());
assert_eq!(billing.label(), Some("unknown"));
}
#[test]
fn child_route_billing_fails_closed_for_every_ambiguous_provider() {
use crate::pricing::UnpricedReason;
let usage = crate::models::Usage {
input_tokens: 10_000,
output_tokens: 1_000,
..Default::default()
};
let now = chrono::Utc::now();
for provider in [
ApiProvider::Openrouter,
ApiProvider::Openai,
ApiProvider::Zai,
ApiProvider::Moonshot,
ApiProvider::Anthropic,
ApiProvider::XiaomiMimo,
ApiProvider::Xai,
ApiProvider::Minimax,
ApiProvider::MinimaxAnthropic,
ApiProvider::Stepfun,
ApiProvider::Custom,
ApiProvider::OpenaiCodex,
ApiProvider::OpencodeGo,
] {
let billing = for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
provider,
None,
);
assert_eq!(billing, BillingPresentation::Unknown, "{provider:?}");
assert!(!billing.shows_money(), "{provider:?}");
let audit = crate::pricing::audit_turn_cost_for_route(
provider,
"some-model",
None,
&usage,
now,
billing,
);
assert_eq!(
audit.unpriced_reason,
Some(UnpricedReason::UnknownBillingBasis),
"{provider:?}"
);
assert!(
audit.counts_toward_money_coverage(),
"{provider:?} must stay in the coverage denominator"
);
}
for provider in [ApiProvider::Ollama, ApiProvider::Sglang, ApiProvider::Vllm] {
let billing = for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
provider,
None,
);
assert_eq!(billing, BillingPresentation::Local, "{provider:?}");
let audit = crate::pricing::audit_turn_cost_for_route(
provider,
"some-model",
None,
&usage,
now,
billing,
);
assert_eq!(
audit.unpriced_reason,
Some(UnpricedReason::NotMoneyMetered),
"{provider:?}"
);
assert!(!audit.counts_toward_money_coverage(), "{provider:?}");
}
assert_eq!(
for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Deepseek,
None,
),
BillingPresentation::Metered
);
assert_eq!(
for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Openrouter,
Some(BillingPresentation::Metered),
),
BillingPresentation::Metered
);
assert_eq!(
for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Anthropic,
Some(BillingPresentation::Subscription("Claude OAuth quota")),
),
BillingPresentation::Subscription("Claude OAuth quota")
);
}
#[test]
fn oauth_allowance_percent_is_shown_when_provider_supplies_it() {
let chip = usage_chip(
BillingPresentation::Subscription("Grok OAuth quota"),
ApiProvider::Xai,
"grok-4",
0.0,
CostCurrency::Usd,
Some(37.0),
);
assert_eq!(
format_usage_chip(&chip).as_deref(),
Some("usage: Grok OAuth quota · 37%")
);
}
#[test]
fn api_key_metered_shows_dollars_only_with_priced_positive_spend() {
let billing = BillingPresentation::Metered;
assert!(has_priced_metered_basis(
billing,
ApiProvider::Deepseek,
"deepseek-v4-flash"
));
let spent = usage_chip(
billing,
ApiProvider::Deepseek,
"deepseek-v4-flash",
0.42,
CostCurrency::Usd,
None,
);
assert_eq!(format_usage_chip(&spent).as_deref(), Some("$0.42"));
let zero = usage_chip(
billing,
ApiProvider::Deepseek,
"deepseek-v4-flash",
0.0,
CostCurrency::Usd,
None,
);
assert_eq!(zero, UsageChip::Hidden);
assert!(format_usage_chip(&zero).is_none());
assert!(!format_usage_line(&zero).contains('$'));
}
#[test]
fn local_free_routes_never_show_dollars() {
assert_eq!(
for_route(&Config::default(), ApiProvider::Ollama),
BillingPresentation::Local
);
let chip = usage_chip(
BillingPresentation::Local,
ApiProvider::Ollama,
"llama3.2",
9.99,
CostCurrency::Usd,
None,
);
assert_eq!(format_usage_chip(&chip).as_deref(), Some("cost: local"));
assert!(!format_usage_line(&chip).contains('$'));
}
#[test]
fn ollama_cloud_is_unknown_and_counts_as_possible_spend() {
let config = Config {
provider: Some("ollama-cloud".to_string()),
providers: Some(crate::config::ProvidersConfig {
ollama_cloud: crate::config::ProviderConfig {
api_key: Some("cloud-key".to_string()),
..Default::default()
},
..Default::default()
}),
..Default::default()
};
let billing = for_route(&config, ApiProvider::OllamaCloud);
assert_eq!(billing, BillingPresentation::Unknown);
assert!(!billing.shows_money());
let audit = crate::pricing::audit_turn_cost_for_route(
ApiProvider::OllamaCloud,
crate::config::DEFAULT_OLLAMA_CLOUD_MODEL,
Some(crate::pricing::UNCLASSIFIED_BILLING_SURFACE),
&Usage {
input_tokens: 1_000,
output_tokens: 100,
..Usage::default()
},
chrono::Utc::now(),
billing,
);
assert_eq!(
audit.unpriced_reason,
Some(crate::pricing::UnpricedReason::UnknownBillingBasis)
);
assert!(audit.counts_toward_money_coverage());
}
#[test]
fn unknown_is_unknown_not_zero_dollars() {
let chip = usage_chip(
BillingPresentation::Metered,
ApiProvider::NvidiaNim,
"deepseek-ai/deepseek-v4-pro",
0.0,
CostCurrency::Usd,
None,
);
assert_eq!(chip, UsageChip::Unknown);
assert_eq!(format_usage_chip(&chip).as_deref(), Some("cost: unknown"));
assert!(!format_usage_line(&chip).contains('$'));
let unknown_billing = usage_chip(
BillingPresentation::Unknown,
ApiProvider::Custom,
"anything",
1.23,
CostCurrency::Usd,
None,
);
assert_eq!(unknown_billing, UsageChip::Unknown);
assert!(!format_usage_line(&unknown_billing).contains('$'));
}
#[test]
fn xai_oauth_and_api_key_routes_stay_distinct() {
let _lock = crate::test_support::lock_test_env();
let temp = tempfile::tempdir().expect("xAI owned credential fixture");
let owned_home = temp
.path()
.canonicalize()
.expect("canonical xAI owned credential fixture");
let _home = crate::test_support::EnvVarGuard::set("CODEWHALE_HOME", &owned_home);
let owned_path = owned_home.join("credentials/xai-auth.json");
std::fs::create_dir_all(owned_path.parent().expect("owned credential parent"))
.expect("create owned credential directory");
#[cfg(windows)]
crate::external_credentials::secure_codewhale_owned_windows_path(
owned_path.parent().expect("owned credential parent"),
true,
)
.expect("secure owned credential directory");
let scope = format!(
"{}::{}",
crate::xai_oauth::XAI_OIDC_ISSUER,
crate::xai_oauth::GROK_OIDC_CLIENT_ID
);
std::fs::write(
&owned_path,
serde_json::json!({
scope: {
"key": crate::test_support::future_test_jwt("billing"),
"auth_mode": "oidc"
}
})
.to_string(),
)
.expect("write Codewhale-owned xAI credential");
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt as _;
std::fs::set_permissions(&owned_path, std::fs::Permissions::from_mode(0o600))
.expect("secure owned credential file");
}
#[cfg(windows)]
crate::external_credentials::secure_codewhale_owned_windows_path(&owned_path, false)
.expect("secure owned credential file");
let oauth = config_with(
ApiProvider::Xai,
ProviderConfig {
auth_mode: Some("grok-oauth".to_string()),
..ProviderConfig::default()
},
);
let api = config_with(
ApiProvider::Xai,
ProviderConfig {
auth_mode: Some("api-key".to_string()),
..ProviderConfig::default()
},
);
assert!(!for_route(&oauth, ApiProvider::Xai).shows_money());
assert!(for_route(&api, ApiProvider::Xai).shows_money());
}
#[test]
fn future_claude_oauth_does_not_inherit_anthropic_api_prices() {
let oauth = config_with(
ApiProvider::Anthropic,
ProviderConfig {
auth_mode: Some("claude-code".to_string()),
..ProviderConfig::default()
},
);
assert_eq!(
for_route(&oauth, ApiProvider::Anthropic).label(),
Some("Claude OAuth quota")
);
}
#[test]
fn xiaomi_defaults_to_token_plan_but_explicit_payg_is_metered() {
let _lock = crate::test_support::lock_test_env();
let _mode = crate::test_support::EnvVarGuard::remove("XIAOMI_MIMO_MODE");
let _base = crate::test_support::EnvVarGuard::remove("XIAOMI_MIMO_BASE_URL");
let _token = crate::test_support::EnvVarGuard::remove("XIAOMI_MIMO_TOKEN_PLAN_API_KEY");
let _token_alias = crate::test_support::EnvVarGuard::remove("MIMO_TOKEN_PLAN_API_KEY");
let _standard_a = crate::test_support::EnvVarGuard::remove("XIAOMI_MIMO_API_KEY");
let _standard_b = crate::test_support::EnvVarGuard::remove("XIAOMI_API_KEY");
let _standard_c = crate::test_support::EnvVarGuard::remove("MIMO_API_KEY");
assert!(!for_route(&Config::default(), ApiProvider::XiaomiMimo).shows_money());
let payg = config_with(
ApiProvider::XiaomiMimo,
ProviderConfig {
mode: Some("pay-as-you-go".to_string()),
..ProviderConfig::default()
},
);
assert!(for_route(&payg, ApiProvider::XiaomiMimo).shows_money());
let standard_key = config_with(
ApiProvider::XiaomiMimo,
ProviderConfig {
api_key: Some("sk-standard".to_string()),
..ProviderConfig::default()
},
);
assert!(for_route(&standard_key, ApiProvider::XiaomiMimo).shows_money());
}
#[test]
fn minimax_requires_an_explicit_saved_billing_mode() {
let _lock = crate::test_support::lock_test_env();
let _env = minimax_env_guard();
for provider in [ApiProvider::Minimax, ApiProvider::MinimaxAnthropic] {
assert_eq!(
for_route(&Config::default(), provider),
BillingPresentation::Unknown
);
assert_eq!(
for_endpoint_without_config(provider, Some(provider.default_base_url())),
BillingPresentation::Unknown
);
let payg = config_with(
provider,
ProviderConfig {
mode: Some("pay-as-you-go".to_string()),
..ProviderConfig::default()
},
);
assert_eq!(for_route(&payg, provider), BillingPresentation::Metered);
assert_eq!(
billing_surface_for_dispatch(
Some(&payg),
provider,
Some(provider.default_base_url())
),
Some(crate::pricing::MINIMAX_PAYG_BILLING_SURFACE)
);
let plan = config_with(
provider,
ProviderConfig {
mode: Some("subscription-plan".to_string()),
..ProviderConfig::default()
},
);
assert_eq!(
for_route(&plan, provider),
BillingPresentation::Subscription("MiniMax Token Plan quota")
);
assert_eq!(
billing_surface_for_dispatch(
Some(&plan),
provider,
Some(provider.default_base_url())
),
Some(crate::pricing::MINIMAX_TOKEN_PLAN_BILLING_SURFACE)
);
}
}
#[test]
fn unknown_cross_provider_oauth_capable_child_never_invents_dollars() {
assert!(
!for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Xai,
None,
)
.shows_money()
);
assert!(
!for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Openrouter,
None,
)
.shows_money()
);
assert_eq!(
for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Xai,
None,
),
BillingPresentation::Unknown
);
assert!(
for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Openrouter,
Some(BillingPresentation::Metered),
)
.shows_money()
);
}
#[test]
fn standard_mimo_env_key_uses_metered_presentation() {
let _lock = crate::test_support::lock_test_env();
let _mode = crate::test_support::EnvVarGuard::remove("XIAOMI_MIMO_MODE");
let _base = crate::test_support::EnvVarGuard::remove("XIAOMI_MIMO_BASE_URL");
let _token = crate::test_support::EnvVarGuard::remove("XIAOMI_MIMO_TOKEN_PLAN_API_KEY");
let _token_alias = crate::test_support::EnvVarGuard::remove("MIMO_TOKEN_PLAN_API_KEY");
let _standard_a = crate::test_support::EnvVarGuard::remove("XIAOMI_MIMO_API_KEY");
let _standard_b = crate::test_support::EnvVarGuard::remove("XIAOMI_API_KEY");
let _standard = crate::test_support::EnvVarGuard::set("MIMO_API_KEY", "sk-metered");
assert!(for_route(&Config::default(), ApiProvider::XiaomiMimo).shows_money());
}
#[test]
fn custom_without_pay_mode_stays_unknown() {
assert_eq!(
for_route(&Config::default(), ApiProvider::Custom),
BillingPresentation::Unknown
);
let mut metered_custom = Config {
provider: Some("acme".to_string()),
..Config::default()
};
*metered_custom.provider_config_for_mut(ApiProvider::Custom) = ProviderConfig {
auth_mode: Some("api-key".to_string()),
..ProviderConfig::default()
};
assert_eq!(
for_route(&metered_custom, ApiProvider::Custom),
BillingPresentation::Metered
);
}
#[test]
fn dispatched_endpoint_shaped_routes_classify_from_the_receipt() {
let config = Config::default();
assert_eq!(
for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Stepfun,
base_url: "https://api.stepfun.ai/step_plan/v1",
},
),
BillingPresentation::Subscription("StepFun Step Plan quota")
);
assert_eq!(
for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Stepfun,
base_url: crate::config::DEFAULT_STEPFUN_BASE_URL,
},
),
BillingPresentation::Metered
);
assert_eq!(
for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Stepfun,
base_url: "https://gateway.internal.example/v1",
},
),
BillingPresentation::Unknown
);
assert_eq!(
for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Zai,
base_url: "https://api.z.ai/api/coding/paas/v4",
},
),
BillingPresentation::Subscription("Z.ai Coding Plan quota")
);
assert_eq!(
for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Zai,
base_url: "",
},
),
BillingPresentation::Unknown
);
assert_eq!(
for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Ollama,
base_url: "http://localhost:11434/v1",
},
),
BillingPresentation::Local
);
assert_eq!(
for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::OpenaiCodex,
base_url: "https://chatgpt.com/backend-api/codex",
},
),
BillingPresentation::Subscription("Codex OAuth quota")
);
}
#[test]
fn minimax_defaults_to_pay_as_you_go_metered() {
let _lock = crate::test_support::lock_test_env();
let _key = crate::test_support::EnvVarGuard::remove("MINIMAX_API_KEY");
let config = config_with(
ApiProvider::Minimax,
ProviderConfig {
base_url: Some("https://api.minimax.io/v1".to_string()),
api_key: Some("sk-test-payg-key".to_string()),
..ProviderConfig::default()
},
);
let billing = for_route(&config, ApiProvider::Minimax);
assert_eq!(billing, BillingPresentation::Metered);
assert!(billing.shows_money());
let chip = usage_chip(
billing,
ApiProvider::Minimax,
"MiniMax-M3",
0.42,
CostCurrency::Usd,
None,
);
assert!(matches!(chip, UsageChip::Money(_)));
assert!(format_usage_line(&chip).contains('$'));
}
#[test]
fn minimax_explicit_token_plan_mode_is_subscription_quota() {
let _lock = crate::test_support::lock_test_env();
let _key = crate::test_support::EnvVarGuard::remove("MINIMAX_API_KEY");
let config = config_with(
ApiProvider::Minimax,
ProviderConfig {
mode: Some("token-plan".to_string()),
api_key: Some("sk-test-payg-key".to_string()),
..ProviderConfig::default()
},
);
let billing = for_route(&config, ApiProvider::Minimax);
assert_eq!(
billing,
BillingPresentation::Subscription("MiniMax Token Plan quota")
);
assert!(!billing.shows_money());
let chip = usage_chip(
billing,
ApiProvider::Minimax,
"MiniMax-M3",
12.34,
CostCurrency::Usd,
None,
);
assert!(!matches!(chip, UsageChip::Money(_)));
assert!(!format_usage_line(&chip).contains('$'));
}
#[test]
fn minimax_sk_cp_config_key_is_subscription_quota() {
let _lock = crate::test_support::lock_test_env();
let _key = crate::test_support::EnvVarGuard::remove("MINIMAX_API_KEY");
let config = config_with(
ApiProvider::Minimax,
ProviderConfig {
api_key: Some("sk-cp-test-token-plan-key".to_string()),
..ProviderConfig::default()
},
);
let billing = for_route(&config, ApiProvider::Minimax);
assert_eq!(
billing,
BillingPresentation::Subscription("MiniMax Token Plan quota")
);
assert!(!billing.shows_money());
}
#[test]
fn minimax_anthropic_dialect_shares_the_token_plan_classification() {
let _lock = crate::test_support::lock_test_env();
let _key = crate::test_support::EnvVarGuard::remove("MINIMAX_API_KEY");
let plan = config_with(
ApiProvider::MinimaxAnthropic,
ProviderConfig {
api_key: Some("sk-cp-test-token-plan-key".to_string()),
..ProviderConfig::default()
},
);
let plan_billing = for_route(&plan, ApiProvider::MinimaxAnthropic);
assert_eq!(
plan_billing,
BillingPresentation::Subscription("MiniMax Token Plan quota")
);
assert!(!plan_billing.shows_money());
let explicit_plan = config_with(
ApiProvider::MinimaxAnthropic,
ProviderConfig {
mode: Some("token-plan".to_string()),
api_key: Some("sk-test-payg-key".to_string()),
..ProviderConfig::default()
},
);
assert_eq!(
for_route(&explicit_plan, ApiProvider::MinimaxAnthropic),
BillingPresentation::Subscription("MiniMax Token Plan quota")
);
let payg = config_with(
ApiProvider::MinimaxAnthropic,
ProviderConfig {
api_key: Some("sk-test-payg-key".to_string()),
..ProviderConfig::default()
},
);
let payg_billing = for_route(&payg, ApiProvider::MinimaxAnthropic);
assert_eq!(payg_billing, BillingPresentation::Metered);
assert!(payg_billing.shows_money());
}
#[test]
fn minimax_sk_cp_env_key_is_subscription_quota() {
let _lock = crate::test_support::lock_test_env();
let _key =
crate::test_support::EnvVarGuard::set("MINIMAX_API_KEY", "sk-cp-test-token-plan-key");
let config = config_with(ApiProvider::Minimax, ProviderConfig::default());
assert_eq!(
for_route(&config, ApiProvider::Minimax),
BillingPresentation::Subscription("MiniMax Token Plan quota")
);
}
#[test]
fn minimax_explicit_pay_as_you_go_wins_over_sk_cp_key() {
let _lock = crate::test_support::lock_test_env();
let _key = crate::test_support::EnvVarGuard::remove("MINIMAX_API_KEY");
for mode in ["pay-as-you-go", "payg", "metered"] {
let config = config_with(
ApiProvider::Minimax,
ProviderConfig {
mode: Some(mode.to_string()),
api_key: Some("sk-cp-test-token-plan-key".to_string()),
..ProviderConfig::default()
},
);
let billing = for_route(&config, ApiProvider::Minimax);
assert_eq!(
billing,
BillingPresentation::Metered,
"explicit mode {mode} must win over the sk-cp key shape"
);
assert!(billing.shows_money());
}
}
fn minimax_env_guard() -> crate::test_support::EnvVarGuard {
crate::test_support::EnvVarGuard::remove("MINIMAX_API_KEY")
}
#[test]
fn minimax_keyring_or_opaque_credential_is_unclassified_not_metered() {
let _lock = crate::test_support::lock_test_env();
let _env = minimax_env_guard();
for provider in [ApiProvider::Minimax, ApiProvider::MinimaxAnthropic] {
let opaque = config_with(provider, ProviderConfig::default());
assert_eq!(
for_route(&opaque, provider),
BillingPresentation::Unknown,
"{provider:?} must not claim pay-as-you-go it cannot prove"
);
for sentinel in [crate::config::API_KEYRING_SENTINEL, " __KEYRING__ "] {
let sentinel = config_with(
provider,
ProviderConfig {
api_key: Some(sentinel.to_string()),
..ProviderConfig::default()
},
);
assert_eq!(
for_route(&sentinel, provider),
BillingPresentation::Unknown,
"{provider:?} keyring sentinel is not a pay-as-you-go proof"
);
}
let chip = usage_chip(
for_route(&opaque, provider),
provider,
"MiniMax-M3",
12.34,
CostCurrency::Usd,
None,
);
assert_eq!(chip, UsageChip::Unknown);
assert!(!format_usage_line(&chip).contains('$'));
}
}
#[test]
fn minimax_credential_provenance_classifies_both_dialects_identically() {
let _lock = crate::test_support::lock_test_env();
let _env = minimax_env_guard();
for provider in [ApiProvider::Minimax, ApiProvider::MinimaxAnthropic] {
for (key, expected) in [
(
"sk-cp-plan-key",
BillingPresentation::Subscription("MiniMax Token Plan quota"),
),
("sk-payg-key", BillingPresentation::Metered),
] {
let config = config_with(
provider,
ProviderConfig {
api_key: Some(key.to_string()),
..ProviderConfig::default()
},
);
assert_eq!(for_route(&config, provider), expected, "{provider:?} {key}");
}
for (key, expected) in [
(
"sk-cp-plan-key",
BillingPresentation::Subscription("MiniMax Token Plan quota"),
),
("sk-payg-key", BillingPresentation::Metered),
] {
let _bound =
crate::test_support::EnvVarGuard::set("CW_TEST_MINIMAX_BOUND_KEY", key);
let config = config_with(
provider,
ProviderConfig {
api_key_env: Some("CW_TEST_MINIMAX_BOUND_KEY".to_string()),
..ProviderConfig::default()
},
);
assert_eq!(
for_route(&config, provider),
expected,
"{provider:?} api_key_env {key}"
);
}
for (key, expected) in [
(
"sk-cp-plan-key",
BillingPresentation::Subscription("MiniMax Token Plan quota"),
),
("sk-payg-key", BillingPresentation::Metered),
] {
let _ambient = crate::test_support::EnvVarGuard::set("MINIMAX_API_KEY", key);
let config = config_with(provider, ProviderConfig::default());
assert_eq!(
for_route(&config, provider),
expected,
"{provider:?} MINIMAX_API_KEY {key}"
);
}
}
}
#[test]
fn minimax_ambient_key_does_not_classify_a_custom_endpoint() {
let _lock = crate::test_support::lock_test_env();
let _env = minimax_env_guard();
let _ambient = crate::test_support::EnvVarGuard::set("MINIMAX_API_KEY", "sk-payg-key");
let config = config_with(
ApiProvider::Minimax,
ProviderConfig {
base_url: Some("https://gateway.internal.example/v1".to_string()),
..ProviderConfig::default()
},
);
assert_eq!(
for_route(&config, ApiProvider::Minimax),
BillingPresentation::Unknown
);
}
#[test]
fn minimax_unrecognized_pay_mode_is_unclassified() {
let _lock = crate::test_support::lock_test_env();
let _env = minimax_env_guard();
let config = config_with(
ApiProvider::Minimax,
ProviderConfig {
mode: Some("enterprise-committed-spend".to_string()),
api_key: Some("sk-cp-plan-key".to_string()),
..ProviderConfig::default()
},
);
assert_eq!(
for_route(&config, ApiProvider::Minimax),
BillingPresentation::Unknown
);
}
#[test]
fn dispatched_minimax_default_endpoint_does_not_invent_a_product() {
let _lock = crate::test_support::lock_test_env();
let _env = minimax_env_guard();
let config = config_with(ApiProvider::Minimax, ProviderConfig::default());
assert_eq!(
for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Minimax,
base_url: "https://api.minimax.io/v1",
},
),
BillingPresentation::Unknown
);
}
#[test]
fn same_provider_child_without_provenance_inherits_parent_billing() {
assert_eq!(
for_child_route(
ApiProvider::Moonshot,
BillingPresentation::Subscription("Kimi Code quota"),
ApiProvider::Moonshot,
None,
),
BillingPresentation::Subscription("Kimi Code quota")
);
assert_eq!(
for_child_route(
ApiProvider::Minimax,
BillingPresentation::Metered,
ApiProvider::Minimax,
None,
),
BillingPresentation::Metered
);
}
#[test]
fn cross_provider_child_without_provenance_fails_closed_unknown() {
for child in [ApiProvider::Moonshot, ApiProvider::Minimax] {
assert_eq!(
for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
child,
None,
),
BillingPresentation::Unknown,
"{child:?} identity must not guess subscription or metered billing"
);
}
for child in [ApiProvider::Ollama, ApiProvider::Sglang, ApiProvider::Vllm] {
assert_eq!(
for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
child,
None,
),
BillingPresentation::Local
);
}
}
#[test]
fn child_provenance_wins_over_parent_route_and_provider_identity() {
assert_eq!(
for_child_route(
ApiProvider::Moonshot,
BillingPresentation::Subscription("Kimi Code quota"),
ApiProvider::Moonshot,
Some(BillingPresentation::Metered),
),
BillingPresentation::Metered
);
assert_eq!(
for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Moonshot,
Some(BillingPresentation::Subscription("Kimi Code quota")),
),
BillingPresentation::Subscription("Kimi Code quota")
);
assert!(
!for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Minimax,
Some(BillingPresentation::Subscription(
"MiniMax Token Plan quota"
)),
)
.shows_money()
);
assert!(
for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Minimax,
Some(BillingPresentation::Metered),
)
.shows_money()
);
}
#[test]
fn child_billing_provenance_round_trips_through_serde() {
for billing in [
BillingPresentation::Metered,
BillingPresentation::Subscription("Kimi Code quota"),
BillingPresentation::Subscription("MiniMax Token Plan quota"),
BillingPresentation::Local,
BillingPresentation::Unknown,
] {
let provenance = ChildBillingProvenance::from(billing);
let json = serde_json::to_string(&provenance).expect("serialize provenance");
let back: ChildBillingProvenance =
serde_json::from_str(&json).expect("deserialize provenance");
assert_eq!(back.as_billing_presentation(), billing);
}
assert_eq!(
ChildBillingProvenance::Subscription {
label: "free lunch".to_string(),
}
.as_billing_presentation(),
BillingPresentation::Unknown
);
}
#[test]
fn custom_siblings_do_not_inherit_each_others_product() {
let parent = ChildParentRoute {
provider: ApiProvider::Custom,
identity: "gateway-a",
billing: BillingPresentation::Metered,
};
assert_eq!(
for_child_route_receipt(
parent,
ChildRouteClaim {
named: true,
provider: Some(ApiProvider::Custom),
identity: Some("gateway-a"),
},
None,
),
BillingPresentation::Metered
);
assert_eq!(
for_child_route_receipt(
parent,
ChildRouteClaim {
named: true,
provider: Some(ApiProvider::Custom),
identity: Some("gateway-b"),
},
None,
),
BillingPresentation::Unknown
);
}
#[test]
fn unparseable_child_provider_is_unknown_not_inherited() {
let parent = ChildParentRoute {
provider: ApiProvider::Anthropic,
identity: "anthropic",
billing: BillingPresentation::Subscription("Claude OAuth quota"),
};
assert_eq!(
for_child_route_receipt(
parent,
ChildRouteClaim {
named: true,
provider: None,
identity: Some("some-future-vendor"),
},
None,
),
BillingPresentation::Unknown
);
assert_eq!(
for_child_route_receipt(parent, ChildRouteClaim::default(), None),
BillingPresentation::Subscription("Claude OAuth quota")
);
}
#[test]
fn child_route_metadata_round_trips_through_the_consumer() {
let metadata = child_route_metadata(
ApiProvider::Ollama,
"ollama",
"http://localhost:11434/v1",
RouteProduct::Unproven,
);
assert_eq!(metadata["child_provider"], "ollama");
assert_eq!(metadata["child_provider_identity"], "ollama");
let provenance: ChildBillingProvenance =
serde_json::from_value(metadata["child_billing"].clone())
.expect("child_billing must deserialize with the consumer's type");
assert_eq!(
provenance.as_billing_presentation(),
BillingPresentation::Local
);
}
#[test]
fn dispatched_receipt_survives_the_child_provenance_boundary() {
let _lock = crate::test_support::lock_test_env();
let _kimi = crate::test_support::EnvVarGuard::set(
"KIMI_BASE_URL",
"https://api.kimi.com/coding/v1",
);
let config = config_with(ApiProvider::Moonshot, ProviderConfig::default());
let dispatched = for_dispatched_route(
&config,
DispatchedRoute {
provider: ApiProvider::Moonshot,
base_url: "https://api.kimi.com/coding/v1",
},
);
let wire = serde_json::to_string(&ChildBillingProvenance::from(dispatched))
.expect("serialize dispatch receipt");
let back: ChildBillingProvenance =
serde_json::from_str(&wire).expect("deserialize dispatch receipt");
let billing = for_child_route(
ApiProvider::Deepseek,
BillingPresentation::Metered,
ApiProvider::Moonshot,
Some(back.as_billing_presentation()),
);
assert_eq!(
billing,
BillingPresentation::Subscription("Kimi Code quota")
);
assert!(!billing.shows_money());
}
}