Files
PolyWeather/tests/test_supabase_entitlement.py
T
2026-05-30 18:34:38 +08:00

564 lines
19 KiB
Python

from datetime import datetime, timedelta, timezone
import src.auth.supabase_entitlement as entitlement_module
from src.auth.supabase_entitlement import SupabaseEntitlementService
class _Response:
def __init__(self, status_code=200, payload=None):
self.status_code = status_code
self._payload = payload
self.content = b"1"
def json(self):
return self._payload
def test_latest_active_subscription_ignores_future_start(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
now = datetime.now(timezone.utc)
current_trial = {
"id": 1,
"user_id": "user-1",
"status": "active",
"plan_code": "signup_trial_3d",
"starts_at": (now - timedelta(days=1)).isoformat(),
"expires_at": (now + timedelta(days=2)).isoformat(),
}
def _fake_get(url, headers=None, params=None, timeout=None):
assert params["select"] == "plan_code,source,starts_at,expires_at"
assert str(params["starts_at"]).startswith("lte.")
assert params["limit"] == "1"
return _Response(200, [current_trial])
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
result = service._query_latest_active_subscription("user-1")
assert result is not None
assert result["plan_code"] == "signup_trial_3d"
def test_get_identity_caches_invalid_token_result(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
calls = {"count": 0}
def _fake_get(url, headers=None, params=None, timeout=None):
calls["count"] += 1
return _Response(401, {"message": "invalid token"})
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
assert service.get_identity("bad-token") is None
assert service.get_identity("bad-token") is None
assert calls["count"] == 1
def test_get_identity_does_not_cache_transient_auth_errors(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
calls = {"count": 0}
def _fake_get(url, headers=None, params=None, timeout=None):
calls["count"] += 1
return _Response(503, {"message": "temporarily unavailable"})
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
assert service.get_identity("temporarily-bad-token") is None
assert service.get_identity("temporarily-bad-token") is None
assert calls["count"] == 2
def test_subscription_window_keeps_queued_renewal_after_current_cache(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
now = datetime.now(timezone.utc)
current = {
"id": 1,
"user_id": "user-1",
"status": "active",
"plan_code": "pro_monthly",
"starts_at": (now - timedelta(days=29)).isoformat(),
"expires_at": (now + timedelta(days=1)).isoformat(),
}
queued = {
"id": 2,
"user_id": "user-1",
"status": "active",
"plan_code": "pro_monthly",
"starts_at": (now + timedelta(days=1)).isoformat(),
"expires_at": (now + timedelta(days=31)).isoformat(),
}
calls = []
def _fake_get(url, headers=None, params=None, timeout=None):
calls.append(params)
assert params["select"] == "plan_code,source,starts_at,expires_at"
if params["limit"] == "1":
assert str(params["starts_at"]).startswith("lte.")
return _Response(200, [current])
assert params["limit"] == "100"
assert "starts_at" not in params
return _Response(200, [queued, current])
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
assert service._query_latest_active_subscription("user-1") == current
window = service.get_subscription_window("user-1", respect_requirement=False)
assert window["current"] == current
assert window["total_expires_at"] == queued["expires_at"]
assert window["queued_days"] == 30
assert window["queued_count"] == 1
assert len(calls) == 2
def test_subscription_window_query_selects_only_window_fields(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
def _fake_get(url, headers=None, params=None, timeout=None):
assert params["select"] == "plan_code,source,starts_at,expires_at"
return _Response(
200,
[
{
"plan_code": "pro_monthly",
"source": "payment_contract",
"starts_at": "2026-03-01T00:00:00+00:00",
"expires_at": "2099-04-01T00:00:00+00:00",
}
],
)
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
window = service.get_subscription_window(
"user-1",
respect_requirement=False,
bypass_cache=True,
)
assert window["current"]["plan_code"] == "pro_monthly"
def test_subscription_window_can_report_unknown_on_transient_query_failure(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
def _fake_get(url, headers=None, params=None, timeout=None):
return _Response(503, {"message": "temporarily unavailable"})
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
window = service.get_subscription_window(
"user-1",
respect_requirement=False,
bypass_cache=True,
unknown_on_error=True,
)
assert window["unknown"] is True
assert window["rows"] is None
def test_list_subscription_windows_selects_only_batch_window_fields(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
def _fake_get(url, headers=None, params=None, timeout=None):
assert params["select"] == "user_id,plan_code,source,starts_at,expires_at"
assert params["user_id"] == "in.(user-1,user-2)"
return _Response(
200,
[
{
"user_id": "user-1",
"plan_code": "pro_monthly",
"source": "payment_contract",
"starts_at": "2026-03-01T00:00:00+00:00",
"expires_at": "2099-04-01T00:00:00+00:00",
},
{
"user_id": "user-2",
"plan_code": "pro_monthly",
"source": "payment_contract",
"starts_at": "2026-03-02T00:00:00+00:00",
"expires_at": "2099-04-02T00:00:00+00:00",
},
],
)
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
windows = service.list_subscription_windows(
["user-1", "user-2"],
bypass_cache=True,
)
assert set(windows) == {"user-1", "user-2"}
def test_list_active_subscription_windows_uses_single_window_query(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
calls = []
now = datetime.now(timezone.utc)
current = {
"user_id": "user-1",
"plan_code": "pro_monthly",
"source": "payment_contract",
"starts_at": (now - timedelta(days=1)).isoformat(),
"expires_at": (now + timedelta(days=10)).isoformat(),
}
queued = {
"user_id": "user-1",
"plan_code": "pro_monthly",
"source": "payment_contract",
"starts_at": (now + timedelta(days=10)).isoformat(),
"expires_at": (now + timedelta(days=40)).isoformat(),
}
def _fake_get(url, headers=None, params=None, timeout=None):
calls.append(params)
assert params["select"] == "user_id,plan_code,source,starts_at,expires_at"
assert params["status"] == "eq.active"
assert params["order"] == "user_id.asc,expires_at.desc"
return _Response(200, [queued, current])
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
result = service.list_active_subscription_windows(limit=200)
assert result["subscriptions"] == [current]
assert result["windows"]["user-1"]["queued_count"] == 1
assert calls and len(calls) == 1
def test_latest_subscription_any_status_uses_cache(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
calls = {"count": 0}
latest = {
"id": 3,
"user_id": "user-1",
"status": "expired",
"plan_code": "pro_monthly",
"starts_at": "2026-03-01T00:00:00+00:00",
"expires_at": "2026-04-01T00:00:00+00:00",
"created_at": "2026-03-01T00:00:00+00:00",
"updated_at": "2026-04-01T00:00:00+00:00",
}
def _fake_get(url, headers=None, params=None, timeout=None):
calls["count"] += 1
assert params["user_id"] == "eq.user-1"
assert params["order"] == "created_at.desc"
assert params["select"] == "plan_code,starts_at,expires_at"
return _Response(200, [latest])
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
assert service.get_latest_subscription_any_status("user-1") == latest
assert service.get_latest_subscription_any_status("user-1") == latest
assert calls["count"] == 1
def test_get_auth_users_batches_profiles_before_admin_fallback(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
calls = []
def _fake_get(url, headers=None, params=None, timeout=None):
calls.append((url, params))
if url.endswith("/rest/v1/profiles"):
assert params["id"] == "in.(user-1,user-2)"
return _Response(
200,
[
{
"id": "user-1",
"email": "one@example.com",
"created_at": "2026-03-01T00:00:00+00:00",
},
{
"id": "user-2",
"email": "two@example.com",
"created_at": "2026-03-02T00:00:00+00:00",
},
],
)
raise AssertionError(f"unexpected admin fallback call: {url}")
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
result = service.get_auth_users(["user-1", "user-2"])
assert result == {
"user-1": {
"email": "one@example.com",
"created_at": "2026-03-01T00:00:00+00:00",
},
"user-2": {
"email": "two@example.com",
"created_at": "2026-03-02T00:00:00+00:00",
},
}
assert len(calls) == 1
def test_get_auth_users_uses_short_cache_for_profile_results(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
calls = {"count": 0}
def _fake_get(url, headers=None, params=None, timeout=None):
calls["count"] += 1
assert url.endswith("/rest/v1/profiles")
return _Response(
200,
[
{
"id": "user-1",
"email": "one@example.com",
"created_at": "2026-03-01T00:00:00+00:00",
},
],
)
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
assert service.get_auth_users(["user-1"]) == {
"user-1": {
"email": "one@example.com",
"created_at": "2026-03-01T00:00:00+00:00",
},
}
assert service.get_auth_users(["user-1"]) == {
"user-1": {
"email": "one@example.com",
"created_at": "2026-03-01T00:00:00+00:00",
},
}
assert calls["count"] == 1
def test_list_active_subscriptions_uses_cache_and_invalidation(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
calls = {"count": 0}
row = {
"id": 1,
"user_id": "user-1",
"status": "active",
"plan_code": "pro_monthly",
"starts_at": "2026-03-01T00:00:00+00:00",
"expires_at": "2099-04-01T00:00:00+00:00",
}
def _fake_get(url, headers=None, params=None, timeout=None):
calls["count"] += 1
assert params["status"] == "eq.active"
assert params["select"] == "user_id,plan_code,starts_at,expires_at"
return _Response(200, [row])
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
assert service.list_active_subscriptions(limit=200) == [row]
assert service.list_active_subscriptions(limit=200) == [row]
assert calls["count"] == 1
service.invalidate_subscription_cache("user-1")
assert service.list_active_subscriptions(limit=200) == [row]
assert calls["count"] == 2
def test_has_active_subscription_uses_lightweight_query_without_polluting_detail_cache(
monkeypatch,
):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
calls = []
def _fake_get(url, headers=None, params=None, timeout=None):
calls.append(params["select"])
if params["select"] == "expires_at":
assert str(params["starts_at"]).startswith("lte.")
assert params["limit"] == "1"
return _Response(
200,
[
{
"expires_at": "2099-04-01T00:00:00+00:00",
}
],
)
if params["select"] == "plan_code,source,starts_at,expires_at":
return _Response(
200,
[
{
"plan_code": "pro_monthly",
"source": "payment_contract",
"starts_at": "2026-03-01T00:00:00+00:00",
"expires_at": "2099-04-01T00:00:00+00:00",
}
],
)
raise AssertionError(params["select"])
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
assert service.has_active_subscription("user-1", respect_requirement=False) is True
assert service.has_active_subscription("user-1", respect_requirement=False) is True
assert service.get_latest_active_subscription(
"user-1",
respect_requirement=False,
)["plan_code"] == "pro_monthly"
assert calls == [
"expires_at",
"plan_code,source,starts_at,expires_at",
]
def test_has_active_subscription_lightweight_cache_invalidates(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
calls = {"count": 0}
def _fake_get(url, headers=None, params=None, timeout=None):
calls["count"] += 1
assert params["select"] == "expires_at"
assert str(params["starts_at"]).startswith("lte.")
assert params["limit"] == "1"
return _Response(
200,
[
{
"expires_at": "2099-04-01T00:00:00+00:00",
}
],
)
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
assert service.has_active_subscription("user-1", respect_requirement=False) is True
assert service.has_active_subscription("user-1", respect_requirement=False) is True
assert calls["count"] == 1
service.invalidate_subscription_cache("user-1")
assert service.has_active_subscription("user-1", respect_requirement=False) is True
assert calls["count"] == 2
def test_has_active_subscription_reuses_detailed_subscription_cache(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
calls = []
def _fake_get(url, headers=None, params=None, timeout=None):
calls.append(params["select"])
assert params["select"] == "plan_code,source,starts_at,expires_at"
return _Response(
200,
[
{
"plan_code": "pro_monthly",
"source": "payment_contract",
"starts_at": "2026-03-01T00:00:00+00:00",
"expires_at": "2099-04-01T00:00:00+00:00",
}
],
)
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
assert service.get_latest_active_subscription(
"user-1",
respect_requirement=False,
)["plan_code"] == "pro_monthly"
assert service.has_active_subscription("user-1", respect_requirement=False) is True
assert calls == ["plan_code,source,starts_at,expires_at"]
def test_latest_active_subscription_reuses_negative_lightweight_cache(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
service = SupabaseEntitlementService()
calls = []
def _fake_get(url, headers=None, params=None, timeout=None):
calls.append(params["select"])
assert params["select"] == "expires_at"
assert str(params["starts_at"]).startswith("lte.")
assert params["limit"] == "1"
return _Response(200, [])
monkeypatch.setattr(entitlement_module.requests, "get", _fake_get)
assert service.has_active_subscription("user-1", respect_requirement=False) is False
assert service.get_latest_active_subscription(
"user-1",
respect_requirement=False,
) is None
assert calls == ["expires_at"]