Files
sand-boxer/tests/test_billing_export.py
tegwick 15f031fd65 feat: cloud adapters E2B/Modal and billing export (SAND-WP-0010)
Add credentialed E2B and Modal extensions, burst routing fallback,
fin-hub meter export hook, BYOK docs, and 77 tests.
2026-06-24 12:50:19 +02:00

50 lines
1.8 KiB
Python

"""fin-hub billing export tests."""
from __future__ import annotations
from datetime import UTC, datetime
from unittest.mock import MagicMock, patch
from sandboxer.models import ActorType, Consumer, MeterRecord, SandboxState, SandboxStatus
from sandboxer.payments.billing_export import export_meter_usage
def test_export_skipped_when_url_unset() -> None:
now = datetime.now(UTC)
status = SandboxStatus(
sandbox_id="s1",
profile_id="profile.e2b-burst",
extension_id="ext.e2b",
state=SandboxState.DESTROYED,
consumer=Consumer(actor=ActorType.ADM, project="sand-boxer"),
created_at=now,
updated_at=now,
)
meter = MeterRecord(pricing_model="metered", actual_usd=0.5, duration_s=100.0)
assert export_meter_usage(status, extension_id="ext.e2b", meter=meter) is None
def test_export_posts_when_configured(monkeypatch) -> None:
monkeypatch.setenv("SANDBOXER_FIN_HUB_URL", "http://fin-hub.test")
now = datetime.now(UTC)
status = SandboxStatus(
sandbox_id="s1",
profile_id="profile.e2b-burst",
extension_id="ext.e2b",
state=SandboxState.DESTROYED,
consumer=Consumer(actor=ActorType.ADM, project="sand-boxer"),
created_at=now,
updated_at=now,
)
meter = MeterRecord(pricing_model="metered", actual_usd=0.5, duration_s=100.0)
mock_response = MagicMock()
mock_response.json.return_value = {"ok": True}
with patch("sandboxer.payments.billing_export.httpx.post", return_value=mock_response) as post:
result = export_meter_usage(status, extension_id="ext.e2b", meter=meter)
assert result == {"ok": True}
post.assert_called_once()
payload = post.call_args.kwargs["json"]
assert payload["sandbox_id"] == "s1"
assert payload["actual_usd"] == 0.5