Back to CBAM tools

Calculator verification

Live test-suite results · last run 2 May 2026 at 19:50

Every release of the calculator runs an automated regression suite against authoritative EU sources before it ships. This page surfaces the latest results so brokers, customs officers, and counterparties can verify the methodology themselves without taking our word for it.

Numeric precision matches Implementing Reg (EU) 2025/2547 Annex II §A.2: intensity values clamped to ≤5 decimal places (§A.2.8), per-shipment emission totals preserve all significant digits (§A.2.7), monetary totals to cent precision. Means our outputs round identically to the EU's own SAT and Communication Template Excels — the CBAM Registry won't reject submissions for precision drift.

Tests run

53

Passing

53

Failing

0

Cost-calculation reference cases

Each case is a (CN code, country, tonnage) tuple with an EXPECTED net cost computed by hand from the EU formula in Reg (EU) 2025/2620§3-§4 + Article 10a(1a) of Directive 2003/87/EC. If the calculator's output drifts by more than the case's tolerance, the test fails and the build doesn't ship.

EU CBAM Self Assessment Tool cross-check

Every CN code the EU's SAT v1.0 (28 March 2025) marks as CBAM-applies is asserted to resolve as in-scope by our scope checker. The same suite verifies our sector mapping (cement / iron-steel / aluminium / fertilisers / electricity / hydrogen) matches the SAT's main-category column.

573 CN codes scoped correctly · 6 sectors mapped correctly

Source: EU CBAM Self Assessment Tool v1.0 (Excel)

Raw test suite results
  • tests/cbam-calc-coverage.test.ts

    • Per-sector calc-coverage sweep (573 CN codes) Cement: all 6 CN codes return status='ok' or 'needs_data' (no throws / no NaN)8141ms
    • Per-sector calc-coverage sweep (573 CN codes) Cement: ok-status results stay within €30–€200/t sanity band2812ms
    • Per-sector calc-coverage sweep (573 CN codes) Iron and steel: all 479 CN codes return status='ok' or 'needs_data' (no throws / no NaN)14681ms
    • Per-sector calc-coverage sweep (573 CN codes) Iron and steel: ok-status results stay within €30–€350/t sanity band3571ms
    • Per-sector calc-coverage sweep (573 CN codes) Electricity: all 1 CN codes return status='ok' or 'needs_data' (no throws / no NaN)3145ms
    • Per-sector calc-coverage sweep (573 CN codes) Electricity: ok-status results stay within €1–€100/t sanity band1830ms
    • Per-sector calc-coverage sweep (573 CN codes) Chemicals (hydrogen): all 1 CN codes return status='ok' or 'needs_data' (no throws / no NaN)1941ms
    • Per-sector calc-coverage sweep (573 CN codes) Chemicals (hydrogen): ok-status results stay within €100–€2000/t sanity band1991ms
    • Per-sector calc-coverage sweep (573 CN codes) Fertilisers: all 27 CN codes return status='ok' or 'needs_data' (no throws / no NaN)2157ms
    • Per-sector calc-coverage sweep (573 CN codes) Fertilisers: ok-status results stay within €10–€500/t sanity band1951ms
    • Per-sector calc-coverage sweep (573 CN codes) Aluminium: all 59 CN codes return status='ok' or 'needs_data' (no throws / no NaN)3255ms
    • Per-sector calc-coverage sweep (573 CN codes) Aluminium: ok-status results stay within €50–€600/t sanity band2615ms
  • tests/cbam-calc-reference.test.ts

    • CBAM cost-calculation reference cases Aluminium hollow profiles · CN 76042100 + Turkey + 100t (default data)6665ms
    • CBAM cost-calculation reference cases Hot-rolled flat steel · CN 72081000 + Turkey + 100t (default data, single EU route)3239ms
    • CBAM cost-calculation reference cases Hydrogen · CN 28041000 + Egypt + 100t (single-route sector)1921ms
    • CBAM cost-calculation reference cases Cement · Grey clinker CN 25231000 + Algeria + 100t (multi-route, route A)3310ms
    • CBAM cost-calculation reference cases Cement · White clinker CN 25231000 + Algeria + 100t (route B, higher intensity)1994ms
    • CBAM cost-calculation reference cases Aluminium · Unwrought CN 76011000 + Algeria + 100t (single route L, country-specific)4490ms
    • CBAM cost-calculation reference cases Fertilisers · Urea aqueous low-N CN 31021012 + Macao + 300t2725ms
    • CBAM cost-calculation reference cases Fertilisers · Ammonia CN 28141000 + China + 100t (high-emissions origin)1740ms
  • tests/cbam-input-validation.test.ts

    • Origin-country filter (Reg 2023/956 Annex III + EU SAT) CBAM_ORIGIN_COUNTRIES excludes all 27 EU member states7ms
    • Origin-country filter (Reg 2023/956 Annex III + EU SAT) CBAM_ORIGIN_COUNTRIES excludes EFTA + UK (Norway/Iceland/Liechtenstein/Switzerland/UK)1ms
    • Origin-country filter (Reg 2023/956 Annex III + EU SAT) CBAM_ORIGIN_COUNTRIES still contains the major CBAM origins1ms
    • Origin-country filter (Reg 2023/956 Annex III + EU SAT) CBAM_ORIGIN_COUNTRIES is strictly smaller than ORIGIN_COUNTRIES1ms
    • Inexact-CN fallback warning (Reg 2025/2547 §A.2.7) CN 31021000 (urea heading, no exact 8-digit row) emits a ⚠ note10245ms
    • Inexact-CN fallback warning (Reg 2025/2547 §A.2.7) CN 72081000 (steel, exact match via chapter heading 7208) does NOT emit fallback warning2496ms
  • tests/cbam-pricing.test.ts

    • pickPaygTier — band boundaries €0 → cbam_report_micro (zero tax (out-of-scope CN) still wants a PDF)5ms
    • pickPaygTier — band boundaries €1 → cbam_report_micro (minimal exposure)1ms
    • pickPaygTier — band boundaries €999.99 → cbam_report_micro (just under €1k boundary)0ms
    • pickPaygTier — band boundaries €1000 → cbam_report_micro (exactly €1k — tie goes to cheaper tier (buyer-friendly))0ms
    • pickPaygTier — band boundaries €1000.01 → cbam_report_standard (first cent above €1k)0ms
    • pickPaygTier — band boundaries €5000 → cbam_report_standard (mid-Standard)1ms
    • pickPaygTier — band boundaries €10000 → cbam_report_standard (exactly €10k boundary — cheaper tier wins)0ms
    • pickPaygTier — band boundaries €10000.01 → cbam_report_heavy (first cent above €10k)0ms
    • pickPaygTier — band boundaries €25000 → cbam_report_heavy (mid-Heavy)0ms
    • pickPaygTier — band boundaries €50000 → cbam_report_heavy (exactly €50k boundary)1ms
    • pickPaygTier — band boundaries €50000.01 → cbam_report_enterprise (first cent above €50k)0ms
    • pickPaygTier — band boundaries €1000000 → cbam_report_enterprise (absurd value still resolves)0ms
    • pickPaygTier — band boundaries €-10 → cbam_report_micro (negative cost (refund?) defaults to Micro)0ms
    • pickPaygTier — band boundaries €NaN → cbam_report_micro (NaN guard — never crash the buy form)0ms
    • PAYG tier metadata PAYG_TIER_IDS lists exactly 4 tiers in cheapest-first order2ms
    • PAYG tier metadata paygTierLabel returns short and long forms1ms
    • PAYG tier metadata formatUsd (kept as alias for formatEur) renders euros1ms
    • PAYG tier metadata paygTierPrice handles missing tier and missing price safely1ms
  • tests/cbam-rounding.test.ts

    • EU rounding helpers roundEmissionsTotal: preserves 4dp for per-shipment values (Reg 2025/2547 §A.2.7)5ms
    • EU rounding helpers roundAnnualReportEmissions: full integer tonnes (Reg 2025/2547 §A.2.6)1ms
    • EU rounding helpers roundIntensity: ≤5 decimal places (Reg 2025/2547 §A.2.8)1ms
    • EU rounding helpers roundEur: 2 decimal places (cent precision)1ms
    • EU rounding helpers roundPricePerTco2e: 4 decimal places (EU certificate-price format)1ms
    • EU rounding helpers symmetry: helpers are pure functions1ms
    • API boundaries clamp intensity to ≤5dp per Reg 2025/2547 §A.2.8 /api/v1/cbam/routes (lookupAvailableRoutes) — every value_for_year is ≤5dp9877ms
  • tests/cbam-sat-scope.test.ts

    • EU SAT scope cross-check (573 CN codes) every SAT-listed CN code resolves as in-scope24294ms
    • EU SAT scope cross-check (573 CN codes) scope sectors match SAT's main categories5056ms

How to read this page

  • Cost-calculation cases are derived BY HAND from the EU formulas — independent of our calculator's implementation. A regression in the calculator changes the output but NOT the expected number, so the test fails immediately.
  • Scope assertions cross-reference the EU's own published Self-Assessment Tool. If the EU adds CN codes or revises the SAT, we re-import + re-run the suite.
  • Tests run before every release. A failing test blocks the build — the calculator can't go live with known-wrong outputs.
  • Want to add a reference case? Open an issue with the input and the regulatory derivation; we'll add it to the suite.