use super::*;
use crate::catalog::{CatalogOffering, CatalogSource, bundled_offerings_from_models_dev};
use crate::models_dev::{ModelsDevCatalog, ModelsDevCost};
use crate::route::PricingSku;
fn priced(source: CatalogSource) -> CatalogOffering {
CatalogOffering {
provider: "deepseek".into(),
wire_model_id: "deepseek-v4-pro".into(),
canonical_model: Some("deepseek-v4-pro".into()),
endpoint_key: "chat".into(),
cost: Some(ModelsDevCost {
input: Some(0.28),
output: Some(0.42),
cache_read: Some(0.028),
cache_write: None,
}),
source,
..Default::default()
}
}
#[test]
fn maps_models_dev_cost_with_bundled_provenance_in_usd() {
let p =
OfferingPricing::from_catalog_offering(&priced(CatalogSource::Bundled)).expect("priced");
assert_eq!(p.currency, Currency::Usd);
assert_eq!(p.input_per_million, Some(0.28));
assert_eq!(p.output_per_million, Some(0.42));
assert_eq!(p.cache_read_per_million, Some(0.028));
assert_eq!(p.cache_write_per_million, None);
assert_eq!(p.provenance, PricingProvenance::ModelsDevBundled);
assert_eq!(p.effective_at, None);
assert!(p.has_any_price());
}
#[test]
fn live_source_carries_provider_live_provenance_and_effective_at() {
let src = CatalogSource::Live {
base_url_fingerprint: "fp".into(),
fetched_at: 1_700,
};
let p = OfferingPricing::from_catalog_offering(&priced(src)).expect("priced");
assert_eq!(p.provenance, PricingProvenance::ProviderLive);
assert_eq!(p.effective_at, Some(1_700));
}
#[test]
fn no_cost_or_empty_cost_object_is_unknown() {
let mut offering = priced(CatalogSource::Bundled);
offering.cost = None;
assert!(
OfferingPricing::from_catalog_offering(&offering).is_none(),
"absent cost is unknown, not free"
);
offering.cost = Some(ModelsDevCost::default());
assert!(OfferingPricing::from_catalog_offering(&offering).is_none());
}
#[test]
fn estimate_cost_sums_priced_classes() {
let p = OfferingPricing::from_catalog_offering(&priced(CatalogSource::Bundled)).unwrap();
let usage = TokenUsage {
input: 1_000_000,
output: 500_000,
cache_read: 2_000_000,
cache_write: 0,
};
let cost = p.estimate_cost(&usage).expect("priced classes estimate");
assert!((cost - 0.546).abs() < 1e-9, "got {cost}");
}
#[test]
fn estimate_cost_is_none_when_a_used_class_is_unpriced() {
let p = OfferingPricing::from_catalog_offering(&priced(CatalogSource::Bundled)).unwrap();
let usage = TokenUsage {
input: 100,
output: 0,
cache_read: 0,
cache_write: 10,
};
assert!(p.estimate_cost(&usage).is_none());
}
#[test]
fn estimate_cost_with_zero_usage_is_zero() {
let p = OfferingPricing::from_catalog_offering(&priced(CatalogSource::Bundled)).unwrap();
assert_eq!(p.estimate_cost(&TokenUsage::default()), Some(0.0));
}
#[test]
fn route_pricing_sku_is_token_when_priced_and_unknown_otherwise() {
match route_pricing_sku(&priced(CatalogSource::Bundled)) {
PricingSku::Token {
input_per_mtok,
output_per_mtok,
} => {
assert_eq!(input_per_mtok, Some(0.28));
assert_eq!(output_per_mtok, Some(0.42));
}
other => panic!("expected Token, got {other:?}"),
}
let mut unpriced = priced(CatalogSource::Bundled);
unpriced.cost = None;
assert!(matches!(
route_pricing_sku(&unpriced),
PricingSku::UnknownOrStale
));
}
#[test]
fn currency_round_trips_including_other() {
for currency in [Currency::Usd, Currency::Cny, Currency::Other("eur".into())] {
let json = serde_json::to_string(¤cy).expect("serialize");
let back: Currency = serde_json::from_str(&json).expect("deserialize");
assert_eq!(currency, back);
}
}
#[test]
fn user_override_pricing_round_trips_and_carries_no_secrets() {
let pricing = OfferingPricing {
provider: "custom".into(),
wire_model_id: "house-model".into(),
canonical_model: None,
currency: Currency::Cny,
input_per_million: Some(8.0),
output_per_million: Some(16.0),
cache_read_per_million: None,
cache_write_per_million: None,
provenance: PricingProvenance::UserOverride,
effective_at: None,
};
let json = serde_json::to_string_pretty(&pricing).expect("serialize");
let back: OfferingPricing = serde_json::from_str(&json).expect("round-trip");
assert_eq!(pricing, back);
let lower = json.to_lowercase();
for needle in [
"api_key",
"apikey",
"authorization",
"secret",
"password",
"bearer",
"access_token",
] {
assert!(!lower.contains(needle), "pricing JSON contains `{needle}`");
}
}
#[test]
fn staleness_applies_to_live_rows_only() {
let live = CatalogSource::Live {
base_url_fingerprint: "fp".into(),
fetched_at: 1_000,
};
let live_price = OfferingPricing::from_catalog_offering(&priced(live)).unwrap();
assert!(!live_price.is_stale(1_500, 3_600), "within TTL");
assert!(live_price.is_stale(5_000, 3_600), "past TTL");
let bundled = OfferingPricing::from_catalog_offering(&priced(CatalogSource::Bundled)).unwrap();
assert!(!bundled.is_stale(u64::MAX, 1));
}
#[test]
fn pricing_flows_from_the_models_dev_parser() {
let raw = r#"{
"providers": {
"zai": {
"models": {
"glm-5.2": {
"id": "glm-5.2",
"modalities": { "input": ["text"], "output": ["text"] },
"cost": { "input": 1.4, "output": 4.4, "cache_read": 0.26 }
}
}
}
}
}"#;
let catalog = ModelsDevCatalog::parse_json(raw).expect("fixture parses");
let rows = bundled_offerings_from_models_dev(&catalog);
let pricing = OfferingPricing::from_catalog_offering(&rows[0]).expect("zai glm-5.2 is priced");
assert_eq!(pricing.provider, "zai");
assert_eq!(pricing.wire_model_id, "glm-5.2");
assert_eq!(pricing.input_per_million, Some(1.4));
assert_eq!(pricing.output_per_million, Some(4.4));
assert_eq!(pricing.cache_read_per_million, Some(0.26));
assert_eq!(pricing.provenance, PricingProvenance::ModelsDevBundled);
}
#[test]
fn cache_only_offering_is_unknown_at_the_route_layer() {
let mut offering = priced(CatalogSource::Bundled);
offering.cost = Some(ModelsDevCost {
input: None,
output: None,
cache_read: Some(0.028),
cache_write: None,
});
assert!(matches!(
route_pricing_sku(&offering),
PricingSku::UnknownOrStale
));
let pricing =
OfferingPricing::from_catalog_offering(&offering).expect("cache-only row is still priced");
assert!(pricing.has_any_price());
let usage = TokenUsage {
cache_read: 1_000_000,
..Default::default()
};
assert_eq!(pricing.estimate_cost(&usage), Some(0.028));
}
#[test]
fn user_override_source_maps_through_from_catalog_offering() {
let pricing = OfferingPricing::from_catalog_offering(&priced(CatalogSource::UserOverride))
.expect("priced");
assert_eq!(pricing.provenance, PricingProvenance::UserOverride);
assert_eq!(pricing.effective_at, None);
}
#[test]
fn staleness_is_inclusive_at_the_ttl_boundary() {
let live = CatalogSource::Live {
base_url_fingerprint: "fp".into(),
fetched_at: 1_000,
};
let p = OfferingPricing::from_catalog_offering(&priced(live)).unwrap();
assert!(p.is_stale(1_100, 100));
assert!(!p.is_stale(1_099, 100));
}