use boundgraph_core::{AsOf, CostModel, Pricing, TokenUsage};
use rust_decimal::Decimal;
fn tolerance() -> Decimal {
"0.0001".parse().unwrap()
}
fn catalogue() -> Pricing {
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../pricing.json");
let json = std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("{} should be readable: {e}", path.display()));
Pricing::from_json(&json)
.unwrap_or_else(|e| panic!("{} should be a pricing catalogue: {e}", path.display()))
}
fn usd(s: &str) -> Decimal {
s.parse().unwrap()
}
fn model(id: &str) -> CostModel {
catalogue()
.get(id)
.unwrap_or_else(|| panic!("{id} should be in the catalogue"))
.clone()
}
fn assert_close(actual: Decimal, expected: Decimal, what: &str) {
let drift = (actual - expected).abs();
assert!(
drift <= tolerance(),
"{what}: priced {actual}, expected {expected} (drift {drift})"
);
}
#[test]
fn the_shipped_catalogue_parses() {
let pricing = catalogue();
assert!(!pricing.is_empty(), "an empty catalogue prices nothing");
assert!(pricing.get("claude-opus-5").is_some(), "the default model");
}
#[test]
fn every_entry_declares_a_window_and_an_output_ceiling() {
for (id, cost) in catalogue().models() {
assert!(cost.context_window > 0, "{id} declares no context window");
assert!(cost.max_output > 0, "{id} declares no output ceiling");
assert!(
cost.max_output < cost.context_window,
"{id} claims it can write more than it can read"
);
}
}
#[test]
fn a_million_prompt_tokens_costs_the_published_input_rate() {
for (id, expected) in [
("claude-opus-5", "5.00"),
("claude-sonnet-5", "3.00"),
("claude-haiku-4-5", "1.00"),
("claude-fable-5", "10.00"),
] {
let priced = model(id).price(&TokenUsage {
input: 1_000_000,
..TokenUsage::NONE
});
assert_close(priced, usd(expected), id);
}
}
#[test]
fn a_million_completion_tokens_costs_the_published_output_rate() {
for (id, expected) in [
("claude-opus-5", "25.00"),
("claude-sonnet-5", "15.00"),
("claude-haiku-4-5", "5.00"),
("claude-fable-5", "50.00"),
] {
let priced = model(id).price(&TokenUsage {
output: 1_000_000,
..TokenUsage::NONE
});
assert_close(priced, usd(expected), id);
}
}
#[test]
fn a_representative_call_reconciles_against_the_model() {
let priced = model("claude-opus-5").price(&TokenUsage {
input: 12_000,
output: 800,
..TokenUsage::NONE
});
assert_close(priced, usd("0.08"), "a 12k/800 call on opus 5");
}
#[test]
fn a_cached_call_is_priced_at_the_read_rate_not_the_input_rate() {
let cost = model("claude-opus-5");
let usage = TokenUsage {
input: 1_000,
output: 800,
cache_read: 11_000,
cache_write: 0,
};
assert_close(cost.price(&usage), usd("0.0305"), "a warm 12k/800 call");
let cold = cost.price(&TokenUsage {
input: 12_000,
output: 800,
..TokenUsage::NONE
});
assert!(
cold > cost.price(&usage) * Decimal::TWO,
"caching should be worth having"
);
}
#[test]
fn every_entry_keeps_the_documented_cache_ratios() {
let tenth = usd("0.1");
let write = usd("1.25");
let long = usd("2");
for (id, cost) in catalogue().models() {
assert_close(cost.cache_read, cost.input * tenth, &format!("{id} read"));
assert_close(cost.cache_write, cost.input * write, &format!("{id} write"));
if let Some(extended) = cost.cache_write_long {
assert_close(extended, cost.input * long, &format!("{id} long write"));
}
}
}
#[test]
fn a_read_is_cheaper_than_a_miss_and_a_write_is_dearer() {
for (id, cost) in catalogue().models() {
assert!(
cost.cache_read < cost.input,
"{id}: a read should be cheaper"
);
assert!(
cost.cache_write > cost.input,
"{id}: a write should be dearer"
);
assert!(cost.output > cost.input, "{id}: completions cost more");
}
}
#[test]
fn every_entry_is_dated() {
let catalogue = catalogue();
let ancient = AsOf::new(2020, 1, 1).unwrap();
for (id, cost) in catalogue.models() {
assert!(
!cost.is_stale(ancient),
"{id} is dated {}, which predates the catalogue's own existence",
cost.as_of
);
}
let future = AsOf::new(2099, 1, 1).unwrap();
assert_eq!(
catalogue.stale(future).len(),
catalogue.len(),
"everything is stale against a far-future horizon"
);
}
#[test]
fn the_catalogue_round_trips() {
let original = catalogue();
let rewritten = Pricing::from_json(&original.to_json().unwrap()).unwrap();
assert_eq!(original, rewritten);
}