Reduce Supabase disk IO

This commit is contained in:
2569718930@qq.com
2026-05-29 17:22:33 +08:00
parent 9e3a4e2f45
commit 2269aaefd7
63 changed files with 4637 additions and 410 deletions
+40
View File
@@ -381,6 +381,46 @@ def test_join_request_approves_trial_user_with_queued_paid_subscription(monkeypa
assert bot.approved_join_requests == [{"chat_id": -100123, "user_id": 12345}]
def test_join_request_uses_subscription_window_without_latest_fallback(monkeypatch):
monkeypatch.setenv("POLYWEATHER_TELEGRAM_GROUP_ID", "-100123")
bot = DummyBot()
db = SimpleNamespace(list_supabase_user_ids_for_telegram=lambda telegram_id: ["user-1"])
io_layer = SimpleNamespace(
build_welcome_text=lambda: "WELCOME",
build_points_rank_text=lambda _user: "TOP",
db=db,
)
def _fail_latest(*_args, **_kwargs):
raise AssertionError("subscription window rows should avoid latest subscription fallback")
entitlement = SimpleNamespace(
get_subscription_window=lambda user_id, respect_requirement=False: {
"rows": [
{"plan_code": "signup_trial_3d", "source": "signup_trial"},
]
},
get_latest_active_subscription=_fail_latest,
)
handler = BasicCommandHandler(
bot=bot,
io_layer=io_layer,
runtime_status_provider=lambda: RuntimeStatus(
started_at="2026-03-12 00:00:00 UTC",
loops=[],
command_access_mode="group_member",
protected_commands=["/city", "/deb"],
required_group_chat_id="-100123",
),
entitlement_service=entitlement,
)
result = handler.handle_chat_join_request(_join_request())
assert result == "pending:no_active_subscription"
assert bot.approved_join_requests == []
def test_join_request_can_decline_ineligible_user_when_configured(monkeypatch):
monkeypatch.setenv("POLYWEATHER_TELEGRAM_GROUP_ID", "-100123")
monkeypatch.setenv("POLYWEATHER_TELEGRAM_JOIN_INELIGIBLE_ACTION", "decline")
+21 -11
View File
@@ -186,14 +186,15 @@ def test_confirm_direct_transfer_uses_intent_chain_rpc(monkeypatch, tmp_path):
)
confirmed_intent = PaymentIntentRecord(**{**intent.__dict__, "status": "confirmed"})
intents = [intent, confirmed_intent]
get_intent_calls = []
requested_chains = []
tx_rows = []
monkeypatch.setattr(
service,
"get_intent",
lambda user_id, intent_id: intents.pop(0) if intents else confirmed_intent,
)
def fake_get_intent(user_id, intent_id):
get_intent_calls.append((user_id, intent_id))
return intents.pop(0) if intents else confirmed_intent
monkeypatch.setattr(service, "get_intent", fake_get_intent)
class _Eth:
chain_id = 1
@@ -260,7 +261,9 @@ def test_confirm_direct_transfer_uses_intent_chain_rpc(monkeypatch, tmp_path):
if method == "GET" and table == "payment_transactions":
return []
if method == "PATCH" and table == "payment_intents":
return [{"id": intent.intent_id, "status": "confirmed"}]
assert kwargs["prefer"] == "return=representation"
assert kwargs["params"]["select"] == "id"
return [{"id": intent.intent_id}]
if method == "POST" and table == "payment_transactions":
tx_rows.append(kwargs["payload"])
return [kwargs["payload"]]
@@ -273,6 +276,7 @@ def test_confirm_direct_transfer_uses_intent_chain_rpc(monkeypatch, tmp_path):
assert 1 in requested_chains
assert tx_rows[0]["chain_id"] == 1
assert result["payment"]["chain_id"] == 1
assert get_intent_calls == [("user-1", intent.intent_id)]
def test_direct_intent_does_not_require_bound_wallet(monkeypatch, tmp_path):
@@ -344,7 +348,7 @@ def test_direct_submit_tx_does_not_require_from_address(monkeypatch, tmp_path):
monkeypatch.setattr(service, "_rest", fake_rest)
monkeypatch.setattr(
service,
"validate_intent_tx",
"_validate_loaded_intent_tx",
lambda *args, **kwargs: {"valid": True, "checks": {"tx_mined": True}},
)
@@ -384,7 +388,7 @@ def test_submit_rejects_direct_tx_until_validation_passes(monkeypatch, tmp_path)
)
monkeypatch.setattr(
service,
"validate_intent_tx",
"_validate_loaded_intent_tx",
lambda *args, **kwargs: {
"valid": False,
"reason": "tx_not_mined",
@@ -437,7 +441,7 @@ def test_submit_rejects_mined_direct_tx_when_receiver_mismatches(monkeypatch, tm
)
monkeypatch.setattr(
service,
"validate_intent_tx",
"_validate_loaded_intent_tx",
lambda *args, **kwargs: {
"valid": False,
"reason": "receiver_mismatch",
@@ -543,7 +547,7 @@ def test_confirm_direct_transfer_uses_erc20_transfer_without_wallet_binding(monk
if method == "PATCH" and table == "payment_intents":
return [{"id": intent.intent_id, "status": "confirmed"}]
if method == "POST" and table == "payment_transactions":
return [kwargs["payload"]]
return []
raise AssertionError((method, table, kwargs))
monkeypatch.setattr(service, "_rest", fake_rest)
@@ -552,8 +556,14 @@ def test_confirm_direct_transfer_uses_erc20_transfer_without_wallet_binding(monk
result = service.confirm_intent_tx("user-1", intent.intent_id, tx_hash)
assert result["subscription"]["status"] == "active"
assert result["transaction"]["tx_hash"] == tx_hash
assert result["tx"]["event"]["amount_units"] == 5000000
assert any(call[1] == "payment_transactions" for call in rest_calls)
transaction_write = next(
call
for call in rest_calls
if call[0] == "POST" and call[1] == "payment_transactions"
)
assert transaction_write[2]["prefer"] == "resolution=merge-duplicates,return=minimal"
def test_submit_rejects_tx_hash_used_by_another_intent(monkeypatch, tmp_path):
_setup_env(monkeypatch, tmp_path)
+738 -4
View File
@@ -21,6 +21,517 @@ def _payment_env(monkeypatch, tmp_path):
monkeypatch.delenv("POLYWEATHER_PAYMENT_DIRECT_RECEIVER_ADDRESS", raising=False)
def test_wallet_challenge_insert_uses_minimal_return(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
return []
monkeypatch.setattr(service, "_rest", _fake_rest)
result = service.create_wallet_challenge(
"user-1",
"0x1111111111111111111111111111111111111111",
)
assert result["address"] == "0x1111111111111111111111111111111111111111"
assert calls[0]["table"] == "wallet_link_challenges"
assert calls[0]["prefer"] == "return=minimal"
def test_entitlement_event_insert_uses_minimal_return(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
if method == "GET" and table == "subscriptions":
return []
if method == "POST" and table == "subscriptions":
return [kwargs.get("payload") or {}]
return []
monkeypatch.setattr(service, "_rest", _fake_rest)
service._grant_subscription(
user_id="user-1",
plan_code="pro_monthly",
duration_days=30,
tx_hash="0x" + "1" * 64,
payload={"kind": "test"},
)
entitlement_event = next(call for call in calls if call["table"] == "entitlement_events")
assert entitlement_event["prefer"] == "return=minimal"
def test_subscription_insert_uses_minimal_return_and_local_payload(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
if method == "GET" and table == "subscriptions":
return []
if method == "POST" and table == "subscriptions":
return []
if method == "POST" and table == "entitlement_events":
return []
raise AssertionError((method, table, kwargs))
monkeypatch.setattr(service, "_rest", _fake_rest)
result = service._grant_subscription(
user_id="user-1",
plan_code="pro_monthly",
duration_days=30,
tx_hash="0x" + "1" * 64,
payload={"kind": "test"},
)
subscription_write = next(
call for call in calls if call["method"] == "POST" and call["table"] == "subscriptions"
)
assert subscription_write["prefer"] == "return=minimal"
assert result == subscription_write["payload"]
assert result["user_id"] == "user-1"
assert result["plan_code"] == "pro_monthly"
assert result["status"] == "active"
def test_wallet_binding_writes_use_minimal_return(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
address = "0x1111111111111111111111111111111111111111"
calls = []
monkeypatch.setattr(
"src.payments.contract_checkout.Account.recover_message",
lambda *args, **kwargs: address,
)
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
if method == "GET" and table == "wallet_link_challenges":
return [
{
"id": "challenge-1",
"user_id": "user-1",
"address": address,
"nonce": "nonce-1",
"message": "message",
"expires_at": "2099-01-01T00:00:00+00:00",
"consumed_at": None,
}
]
if method == "GET" and table == "user_wallets":
return []
return []
monkeypatch.setattr(service, "_rest", _fake_rest)
result = service.verify_wallet_binding("user-1", address, "nonce-1", "0xsig")
assert result.address == address
writes = [call for call in calls if call["method"] in {"POST", "PATCH"}]
assert writes[0]["table"] == "user_wallets"
assert writes[0]["prefer"] == "resolution=merge-duplicates,return=minimal"
assert writes[1]["table"] == "wallet_link_challenges"
assert writes[1]["prefer"] == "return=minimal"
def test_require_user_wallet_selects_only_status(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
return [{"status": "active"}]
monkeypatch.setattr(service, "_rest", _fake_rest)
result = service._require_user_wallet(
"user-1",
"0x1111111111111111111111111111111111111111",
)
assert result == {"status": "active"}
assert calls[0]["params"]["select"] == "status"
def test_list_wallets_omits_status_from_active_wallet_query(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
return [
{
"chain_id": 137,
"address": "0x1111111111111111111111111111111111111111",
"is_primary": True,
"verified_at": "2099-01-01T00:00:00+00:00",
}
]
monkeypatch.setattr(service, "_rest", _fake_rest)
wallets = service.list_wallets("user-1")
assert calls[0]["params"]["select"] == "chain_id,address,is_primary,verified_at"
assert wallets[0].status == "active"
def test_wallet_binding_existing_lookup_selects_only_owner_status(
monkeypatch,
tmp_path,
):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
address = "0x1111111111111111111111111111111111111111"
calls = []
monkeypatch.setattr(
"src.payments.contract_checkout.Account.recover_message",
lambda *args, **kwargs: address,
)
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
if method == "GET" and table == "wallet_link_challenges":
return [
{
"id": "challenge-1",
"user_id": "user-1",
"address": address,
"nonce": "nonce-1",
"message": "message",
"expires_at": "2099-01-01T00:00:00+00:00",
"consumed_at": None,
}
]
if method == "GET" and table == "user_wallets":
return []
return []
monkeypatch.setattr(service, "_rest", _fake_rest)
service.verify_wallet_binding("user-1", address, "nonce-1", "0xsig")
existing_lookup = [
call
for call in calls
if call["method"] == "GET"
and call["table"] == "user_wallets"
and "address" in call["params"]
][0]
assert existing_lookup["params"]["select"] == "user_id,status"
challenge_lookup = [
call
for call in calls
if call["method"] == "GET"
and call["table"] == "wallet_link_challenges"
][0]
assert challenge_lookup["params"]["select"] == "id,message,expires_at"
assert "order" not in challenge_lookup["params"]
def test_wallet_unbind_writes_use_minimal_return(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
monkeypatch.setattr(service, "_require_user_wallet", lambda *args, **kwargs: {})
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
if method == "GET" and table == "user_wallets" and kwargs["params"].get("is_primary") == "eq.true":
return []
if method == "GET" and table == "user_wallets":
return [{"id": "wallet-2", "address": "0x2222222222222222222222222222222222222222"}]
return []
monkeypatch.setattr(service, "_rest", _fake_rest)
result = service.unbind_wallet("user-1", "0x1111111111111111111111111111111111111111")
assert result["new_primary"] == "0x2222222222222222222222222222222222222222"
writes = [call for call in calls if call["method"] == "PATCH"]
assert [call["prefer"] for call in writes] == ["return=minimal", "return=minimal"]
def test_submit_intent_status_patch_uses_minimal_return(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
monkeypatch.setattr(
service,
"get_intent",
lambda user_id, intent_id: service._serialize_intent(
{
"id": intent_id,
"plan_code": "pro_monthly",
"plan_id": 101,
"chain_id": 137,
"token_address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"receiver_address": "0xeD2f13Aa5fF033c58FB436E178451Cd07f693f32",
"amount_units": "5000000",
"payment_mode": "direct",
"allowed_wallet": None,
"order_id_hex": "0x" + "1" * 64,
"status": "created",
"expires_at": "2099-01-01T00:00:00+00:00",
"tx_hash": None,
"metadata": {},
}
),
)
monkeypatch.setattr(
service,
"_validate_loaded_intent_tx",
lambda *args, **kwargs: {"valid": True, "checks": {"tx_mined": True}},
)
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
if method == "GET" and table == "payment_transactions":
return []
if method == "POST" and table == "payment_transactions":
return []
return []
monkeypatch.setattr(service, "_rest", _fake_rest)
result = service.submit_intent_tx("user-1", "intent-1", "0x" + "2" * 64, "")
assert result["status"] == "submitted"
intent_patch = next(call for call in calls if call["method"] == "PATCH")
assert intent_patch["table"] == "payment_intents"
assert intent_patch["prefer"] == "return=minimal"
transaction_write = next(
call
for call in calls
if call["method"] == "POST" and call["table"] == "payment_transactions"
)
assert transaction_write["prefer"] == "resolution=merge-duplicates,return=minimal"
assert result["transaction"]["tx_hash"] == "0x" + "2" * 64
def test_submit_intent_tx_reuses_loaded_intent_for_validation(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
intent = service._serialize_intent(
{
"id": "intent-1",
"plan_code": "pro_monthly",
"plan_id": 101,
"chain_id": 137,
"token_address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"receiver_address": "0xeD2f13Aa5fF033c58FB436E178451Cd07f693f32",
"amount_units": "5000000",
"payment_mode": "direct",
"allowed_wallet": None,
"order_id_hex": "0x" + "1" * 64,
"status": "created",
"expires_at": "2099-01-01T00:00:00+00:00",
"tx_hash": None,
"metadata": {},
}
)
calls = {"get_intent": 0}
rest_calls = []
class _FakeEth:
def get_transaction_receipt(self, tx_hash):
return {"status": 1, "to": intent.receiver_address, "blockNumber": 123}
class _FakeWeb3:
eth = _FakeEth()
def _fake_get_intent(user_id, intent_id):
calls["get_intent"] += 1
return intent
def _fake_rest(method, table, **kwargs):
rest_calls.append({"method": method, "table": table, **kwargs})
if method == "GET" and table == "payment_transactions":
return []
if method == "POST" and table == "payment_transactions":
return [kwargs["payload"]]
return []
monkeypatch.setattr(service, "get_intent", _fake_get_intent)
monkeypatch.setattr(service, "_get_web3", lambda *args, **kwargs: _FakeWeb3())
monkeypatch.setattr(
service,
"_extract_direct_transfer_event",
lambda receipt, loaded_intent: {
"from": "0x2222222222222222222222222222222222222222",
"to": loaded_intent.receiver_address,
"amount_units": int(loaded_intent.amount_units),
},
)
monkeypatch.setattr(service, "_rest", _fake_rest)
result = service.submit_intent_tx("user-1", "intent-1", "0x" + "2" * 64, "")
assert result["status"] == "submitted"
assert calls["get_intent"] == 1
assert [call["table"] for call in rest_calls].count("payment_transactions") == 2
def test_failed_intent_writes_use_minimal_return(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
intent = PaymentIntentRecord(
intent_id="intent-1",
order_id_hex="0x" + "1" * 64,
plan_code="pro_monthly",
plan_id=101,
chain_id=137,
amount_units=5_000_000,
amount_usdc="5",
token_address="0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
token_decimals=6,
token_symbol="USDC",
receiver_address="0xed2f13aa5ff033c58fb436e178451cd07f693f32",
status="submitted",
payment_mode="direct",
allowed_wallet=None,
expires_at="2099-01-01T00:00:00+00:00",
tx_hash=None,
metadata={},
)
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
return []
monkeypatch.setattr(service, "_rest", _fake_rest)
service._mark_intent_failed(
user_id="user-1",
intent=intent,
tx_hash="0x" + "3" * 64,
reason="test_failure",
detail="test",
)
assert calls[0]["table"] == "payment_intents"
assert calls[0]["prefer"] == "return=minimal"
assert calls[1]["table"] == "payment_transactions"
assert calls[1]["prefer"] == "resolution=merge-duplicates,return=minimal"
def test_duplicate_transaction_write_uses_minimal_return(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
intent = PaymentIntentRecord(
intent_id="intent-1",
order_id_hex="0x" + "1" * 64,
plan_code="pro_monthly",
plan_id=101,
chain_id=137,
amount_units=5_000_000,
amount_usdc="5",
token_address="0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
token_decimals=6,
token_symbol="USDC",
receiver_address="0xed2f13aa5ff033c58fb436e178451cd07f693f32",
status="confirmed",
payment_mode="direct",
allowed_wallet=None,
expires_at="2099-01-01T00:00:00+00:00",
tx_hash="0x" + "2" * 64,
metadata={},
)
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
return []
monkeypatch.setattr(service, "_rest", _fake_rest)
result = service._record_duplicate_transaction(
intent=intent,
tx_hash="0x" + "3" * 64,
from_address="0x2222222222222222222222222222222222222222",
status="refund_required",
)
assert result == {}
assert calls[0]["table"] == "payment_transactions"
assert calls[0]["prefer"] == "resolution=merge-duplicates,return=minimal"
def test_payment_record_upsert_uses_minimal_return(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
return []
monkeypatch.setattr(service, "_rest", _fake_rest)
result = service._insert_payment_record(
user_id="user-1",
tx_hash="0x" + "4" * 64,
amount_units=5_000_000,
token_address="0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
chain_id=137,
payload={"kind": "test"},
)
assert calls[0]["table"] == "payments"
assert calls[0]["prefer"] == "resolution=merge-duplicates,return=minimal"
assert result["tx_hash"] == "0x" + "4" * 64
assert result["status"] == "confirmed"
assert result["amount"] == "5"
def test_create_intent_insert_uses_minimal_return_and_local_payload(
monkeypatch, tmp_path
):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
if method == "POST" and table == "payment_intents":
return []
raise AssertionError((method, table, kwargs))
monkeypatch.setattr(service, "_rest", _fake_rest)
result = service.create_intent(
"00000000-0000-0000-0000-000000000001",
"pro_monthly",
payment_mode="direct",
)
intent_write = calls[0]
payload = intent_write["payload"]
assert intent_write["prefer"] == "return=minimal"
assert payload["id"] == result["intent"]["intent_id"]
assert payload["order_id_hex"] == result["intent"]["order_id_hex"]
assert result["intent"]["status"] == "created"
assert result["direct_payment"]["amount_units"] == str(payload["amount_units"])
def test_payment_runtime_state_and_audit_event_roundtrip(tmp_path):
db_path = tmp_path / "payments.db"
db = DBManager(str(db_path))
@@ -246,6 +757,121 @@ def test_reconcile_latest_intent_confirms_submitted_first(monkeypatch, tmp_path)
assert result["action"] == "confirmed_submitted_intent"
def test_reconcile_latest_intent_reuses_confirmed_row_after_repair(monkeypatch, tmp_path):
monkeypatch.setenv("POLYWEATHER_PAYMENT_ENABLED", "true")
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
monkeypatch.setenv("POLYWEATHER_PAYMENT_RPC_URL", "https://rpc-1.example")
monkeypatch.setenv(
"POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON",
'[{"code":"usdc_e","address":"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174","decimals":6,"receiver_contract":"0xeD2f13Aa5fF033c58FB436E178451Cd07f693f32","is_default":true}]',
)
monkeypatch.setenv("POLYWEATHER_DB_PATH", str(tmp_path / "payments.db"))
service = PaymentContractCheckoutService()
monkeypatch.setattr(
service,
"_rest",
lambda method, table, **kwargs: [
{
"id": "intent-1",
"plan_code": "pro_monthly",
"plan_id": 101,
"chain_id": 137,
"token_address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"receiver_address": "0xeD2f13Aa5fF033c58FB436E178451Cd07f693f32",
"amount_units": "5000000",
"payment_mode": "strict",
"allowed_wallet": "0x1111111111111111111111111111111111111111",
"order_id_hex": "0x" + "1" * 64,
"status": "confirmed",
"expires_at": "2099-01-01T00:00:00+00:00",
"tx_hash": "0x" + "2" * 64,
"metadata": {},
}
],
)
monkeypatch.setattr(
service,
"get_intent",
lambda user_id, intent_id: (_ for _ in ()).throw(
AssertionError("confirmed repair should reuse loaded row")
),
)
monkeypatch.setattr(
service,
"_ensure_confirm_side_effects",
lambda user_id, local_intent, tx_hash: {
"payment": {"tx_hash": tx_hash},
"subscription": {"plan_code": local_intent.plan_code},
},
)
result = service.reconcile_latest_intent("user-1")
assert result["ok"] is True
assert result["action"] == "reconciled_confirmed_intent"
assert result["intent"]["intent_id"] == "intent-1"
def test_reconcile_latest_intent_without_candidates_does_not_clear_subscription_cache(
monkeypatch,
tmp_path,
):
monkeypatch.setenv("POLYWEATHER_PAYMENT_ENABLED", "true")
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
monkeypatch.setenv("POLYWEATHER_PAYMENT_RPC_URL", "https://rpc-1.example")
monkeypatch.setenv(
"POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON",
'[{"code":"usdc_e","address":"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174","decimals":6,"receiver_contract":"0xeD2f13Aa5fF033c58FB436E178451Cd07f693f32","is_default":true}]',
)
monkeypatch.setenv("POLYWEATHER_DB_PATH", str(tmp_path / "payments.db"))
service = PaymentContractCheckoutService()
monkeypatch.setattr(
service,
"_rest",
lambda method, table, **kwargs: [
{
"id": "intent-created",
"plan_code": "pro_monthly",
"plan_id": 101,
"chain_id": 137,
"token_address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"receiver_address": "0xeD2f13Aa5fF033c58FB436E178451Cd07f693f32",
"amount_units": "5000000",
"payment_mode": "strict",
"allowed_wallet": "0x1111111111111111111111111111111111111111",
"order_id_hex": "0x" + "1" * 64,
"status": "created",
"expires_at": "2099-01-01T00:00:00+00:00",
"tx_hash": None,
"metadata": {},
}
],
)
invalidations = []
monkeypatch.setattr(
"src.payments.contract_checkout.SUPABASE_ENTITLEMENT.invalidate_subscription_cache",
lambda user_id: invalidations.append(user_id),
)
monkeypatch.setattr(
"src.payments.contract_checkout.SUPABASE_ENTITLEMENT.get_latest_active_subscription",
lambda user_id, respect_requirement=False: {
"plan_code": "pro_monthly",
"expires_at": "2099-01-01T00:00:00+00:00",
},
)
result = service.reconcile_latest_intent("user-1")
assert result["ok"] is True
assert result["action"] == "checked_without_repair"
assert invalidations == []
def test_confirm_intent_tx_repairs_side_effect_failure(monkeypatch, tmp_path):
monkeypatch.setenv("POLYWEATHER_PAYMENT_ENABLED", "true")
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
@@ -386,6 +1012,117 @@ def test_reconcile_recent_intents_dedupes_users(monkeypatch, tmp_path):
assert seen == ["user-1", "user-2"]
def test_reconcile_recent_intents_selects_only_user_ids(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
return [{"user_id": "user-1"}, {"user_id": "user-1"}, {"user_id": "user-2"}]
seen = []
monkeypatch.setattr(service, "_rest", _fake_rest)
monkeypatch.setattr(
service,
"reconcile_latest_intent",
lambda user_id: seen.append(user_id) or {"ok": True, "subscription": {"user_id": user_id}},
)
result = service.reconcile_recent_intents(limit=10)
assert calls[0]["params"]["select"] == "user_id"
assert seen == ["user-1", "user-2"]
assert result["processed_users"] == 2
def test_pending_confirm_intents_selects_only_confirm_loop_fields(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
return [
{
"id": "intent-1",
"user_id": "user-1",
"chain_id": 137,
"tx_hash": "0x" + "2" * 64,
}
]
monkeypatch.setattr(service, "_rest", _fake_rest)
result = service.list_pending_confirm_intents(limit=20)
assert result == [
{
"intent_id": "intent-1",
"user_id": "user-1",
"chain_id": 137,
"tx_hash": "0x" + "2" * 64,
}
]
assert calls[0]["params"]["select"] == "id,user_id,tx_hash,chain_id"
def test_open_intents_by_order_id_selects_only_event_loop_fields(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
return [
{
"id": "intent-1",
"user_id": "user-1",
"status": "submitted",
"tx_hash": "0x" + "2" * 64,
"plan_id": 101,
"token_address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"amount_units": "5000000",
}
]
monkeypatch.setattr(service, "_rest", _fake_rest)
result = service.list_open_intents_by_order_id("0x" + "1" * 64)
assert result == [
{
"intent_id": "intent-1",
"user_id": "user-1",
"status": "submitted",
"tx_hash": "0x" + "2" * 64,
"plan_id": 101,
"token_address": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
"amount_units": 5000000,
}
]
assert (
calls[0]["params"]["select"]
== "id,user_id,status,tx_hash,plan_id,token_address,amount_units"
)
def test_tx_hash_unused_check_selects_only_intent_id(monkeypatch, tmp_path):
_payment_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
calls = []
def _fake_rest(method, table, **kwargs):
calls.append({"method": method, "table": table, **kwargs})
return [{"intent_id": "intent-1"}]
monkeypatch.setattr(service, "_rest", _fake_rest)
service._ensure_tx_hash_unused("0x" + "1" * 64, "intent-1")
assert calls[0]["params"]["select"] == "intent_id"
def test_grant_subscription_starts_after_trial_when_only_trial_is_active(monkeypatch, tmp_path):
monkeypatch.setenv("POLYWEATHER_PAYMENT_ENABLED", "true")
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
@@ -403,12 +1140,9 @@ def test_grant_subscription_starts_after_trial_when_only_trial_is_active(monkeyp
def _fake_rest(method, table, **kwargs):
if method == "GET" and table == "subscriptions":
assert kwargs["params"]["select"] == "starts_at,expires_at"
return [
{
"id": 1,
"status": "active",
"plan_code": "signup_trial_3d",
"source": "signup_trial",
"starts_at": (datetime.now(timezone.utc) - timedelta(days=1)).isoformat(),
"expires_at": trial_end.isoformat(),
}
+119
View File
@@ -0,0 +1,119 @@
import importlib.util
from pathlib import Path
def _load_script():
root = Path(__file__).resolve().parents[1]
path = root / "scripts" / "reconcile_payment_tx.py"
spec = importlib.util.spec_from_file_location("reconcile_payment_tx", path)
module = importlib.util.module_from_spec(spec)
assert spec and spec.loader
spec.loader.exec_module(module)
return module
def test_find_intent_by_tx_queries_tx_hash_directly(monkeypatch):
module = _load_script()
tx_hash = "0x" + "a" * 64
calls = []
class FakeCheckout:
def _rest(self, method, table, *, params, allowed_status):
calls.append(
{
"method": method,
"table": table,
"params": params,
"allowed_status": allowed_status,
}
)
return [{"id": "intent-1", "user_id": "user-1", "tx_hash": tx_hash}]
monkeypatch.setattr(module, "PAYMENT_CHECKOUT", FakeCheckout())
result = module._find_intent_by_tx("user-1", tx_hash.upper())
assert result["id"] == "intent-1"
assert calls == [
{
"method": "GET",
"table": "payment_intents",
"params": {
"select": "id,user_id",
"tx_hash": f"eq.{tx_hash}",
"limit": "1",
},
"allowed_status": [200],
}
]
def test_find_intent_by_tx_rejects_other_user(monkeypatch):
module = _load_script()
class FakeCheckout:
def _rest(self, method, table, *, params, allowed_status):
return [{"id": "intent-other", "user_id": "user-2"}]
monkeypatch.setattr(module, "PAYMENT_CHECKOUT", FakeCheckout())
assert module._find_intent_by_tx("user-1", "0x" + "a" * 64) is None
def test_find_intent_by_order_id_uses_unique_order_lookup(monkeypatch):
module = _load_script()
calls = []
class FakeCheckout:
def _rest(self, method, table, *, params, allowed_status):
calls.append(
{
"method": method,
"table": table,
"params": params,
"allowed_status": allowed_status,
}
)
return [
{
"id": "intent-1",
"user_id": "user-1",
"order_id_hex": "0x" + "b" * 64,
}
]
monkeypatch.setattr(module, "PAYMENT_CHECKOUT", FakeCheckout())
result = module._find_intent_by_order_id("user-1", "0X" + "B" * 64)
assert result["id"] == "intent-1"
assert calls == [
{
"method": "GET",
"table": "payment_intents",
"params": {
"select": "id,user_id",
"order_id_hex": "eq.0x" + "b" * 64,
"limit": "1",
},
"allowed_status": [200],
}
]
def test_find_intent_by_order_id_rejects_other_user(monkeypatch):
module = _load_script()
class FakeCheckout:
def _rest(self, method, table, *, params, allowed_status):
return [
{
"id": "intent-other",
"user_id": "user-2",
"order_id_hex": "0x" + "b" * 64,
}
]
monkeypatch.setattr(module, "PAYMENT_CHECKOUT", FakeCheckout())
assert module._find_intent_by_order_id("user-1", "0x" + "b" * 64) is None
+104
View File
@@ -0,0 +1,104 @@
import importlib.util
from pathlib import Path
def _load_script(name: str):
root = Path(__file__).resolve().parents[1]
path = root / "scripts" / f"{name}.py"
spec = importlib.util.spec_from_file_location(name, path)
module = importlib.util.module_from_spec(spec)
assert spec and spec.loader
spec.loader.exec_module(module)
return module
grant_script = _load_script("grant_subscription_by_email")
reconcile_script = _load_script("reconcile_subscription_by_email")
class _Checkout:
supabase_url = "https://example.supabase.co"
supabase_service_role_key = "service-role"
def __init__(self):
self.rest_calls = []
self.admin_calls = []
def _rest(self, method, table, params=None, **kwargs):
self.rest_calls.append((method, table, params))
assert table == "profiles"
return [{"id": "user-1", "email": "user@example.com"}]
def _auth_admin_request(self, *args, **kwargs):
self.admin_calls.append((args, kwargs))
raise AssertionError("profile-backed lookup should avoid Auth Admin")
def test_grant_script_email_lookup_prefers_profiles(monkeypatch):
checkout = _Checkout()
monkeypatch.setattr("src.payments.contract_checkout.PAYMENT_CHECKOUT", checkout)
assert grant_script._lookup_user_id_by_email("user@example.com") == "user-1"
assert checkout.rest_calls == [
(
"GET",
"profiles",
{
"select": "id",
"email": "eq.user@example.com",
"limit": "1",
},
)
]
assert checkout.admin_calls == []
def test_reconcile_script_email_lookup_prefers_profiles(monkeypatch):
checkout = _Checkout()
monkeypatch.setattr("src.payments.contract_checkout.PAYMENT_CHECKOUT", checkout)
assert reconcile_script._lookup_user_id_by_email("user@example.com") == "user-1"
assert checkout.rest_calls == [
(
"GET",
"profiles",
{
"select": "id",
"email": "eq.user@example.com",
"limit": "1",
},
)
]
assert checkout.admin_calls == []
def test_grant_script_entitlement_event_uses_minimal_return():
root = Path(__file__).resolve().parents[1]
source = (root / "scripts" / "grant_subscription_by_email.py").read_text(
encoding="utf-8"
)
entitlement_event_pos = source.index('"entitlement_events"')
minimal_pos = source.index('prefer="return=minimal"', entitlement_event_pos)
assert minimal_pos > entitlement_event_pos
def test_grant_script_subscription_writes_use_minimal_return():
root = Path(__file__).resolve().parents[1]
source = (root / "scripts" / "grant_subscription_by_email.py").read_text(
encoding="utf-8"
)
subscription_write_count = source.count('prefer="return=minimal"')
assert subscription_write_count >= 3
assert 'prefer="return=representation"' not in source
def test_grant_script_subscription_lookup_selects_only_grant_fields():
root = Path(__file__).resolve().parents[1]
source = (root / "scripts" / "grant_subscription_by_email.py").read_text(
encoding="utf-8"
)
assert '"select": "id,plan_code,source,starts_at,expires_at"' in source
assert '"select": "id,expires_at,status,plan_code,starts_at,source,created_at"' not in source
+460 -10
View File
@@ -30,17 +30,11 @@ def test_latest_active_subscription_ignores_future_start(monkeypatch):
"starts_at": (now - timedelta(days=1)).isoformat(),
"expires_at": (now + timedelta(days=2)).isoformat(),
}
future_paid = {
"id": 2,
"user_id": "user-1",
"status": "active",
"plan_code": "pro_monthly",
"starts_at": (now + timedelta(days=2)).isoformat(),
"expires_at": (now + timedelta(days=32)).isoformat(),
}
def _fake_get(url, headers=None, params=None, timeout=None):
return _Response(200, [future_paid, current_trial])
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)
@@ -50,6 +44,44 @@ def test_latest_active_subscription_ignores_future_start(monkeypatch):
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")
@@ -75,7 +107,16 @@ def test_subscription_window_keeps_queued_renewal_after_current_cache(monkeypatc
"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)
@@ -88,3 +129,412 @@ def test_subscription_window_keeps_queued_renewal_after_current_cache(monkeypatc
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_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"]
+93
View File
@@ -0,0 +1,93 @@
from types import SimpleNamespace
import src.database.db_manager as db_manager_module
from src.database.db_manager import DBManager
def _bound_db(tmp_path, monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
db = DBManager(str(tmp_path / "points-sync.db"))
db.upsert_user(1001, "eraer")
with db._get_connection() as conn: # noqa: SLF001
conn.execute(
"""
UPDATE users
SET supabase_user_id = ?, supabase_email = ?
WHERE telegram_id = ?
""",
("supabase-user-1", "eraer@example.com", 1001),
)
conn.execute(
"""
INSERT INTO supabase_bindings (supabase_user_id, telegram_id, supabase_email)
VALUES (?, ?, ?)
""",
("supabase-user-1", 1001, "eraer@example.com"),
)
conn.commit()
return db
def test_message_points_sync_to_supabase_metadata_is_throttled(tmp_path, monkeypatch):
db = _bound_db(tmp_path, monkeypatch)
calls = []
now = {"value": 1000.0}
monkeypatch.setattr(
db_manager_module,
"time",
SimpleNamespace(monotonic=lambda: now["value"]),
raising=False,
)
monkeypatch.setattr(
db_manager_module.requests,
"patch",
lambda *args, **kwargs: calls.append((args, kwargs))
or SimpleNamespace(status_code=204, text="", content=b""),
)
first = db.add_message_activity(
1001,
"第一条有效发言",
cooldown_sec=0,
daily_cap=1000,
)
now["value"] += 10.0
second = db.add_message_activity(
1001,
"第二条有效发言",
cooldown_sec=0,
daily_cap=1000,
)
assert first["awarded"] is True
assert second["awarded"] is True
assert len(calls) == 1
def test_manual_point_grant_forces_supabase_metadata_sync(tmp_path, monkeypatch):
db = _bound_db(tmp_path, monkeypatch)
calls = []
now = {"value": 1000.0}
monkeypatch.setattr(
db_manager_module,
"time",
SimpleNamespace(monotonic=lambda: now["value"]),
raising=False,
)
monkeypatch.setattr(
db_manager_module.requests,
"patch",
lambda *args, **kwargs: calls.append((args, kwargs))
or SimpleNamespace(status_code=204, text="", content=b""),
)
db.add_message_activity(1001, "第一条有效发言", cooldown_sec=0, daily_cap=1000)
now["value"] += 10.0
result = db.grant_points_by_supabase_email("eraer@example.com", 300)
assert result["ok"] is True
assert len(calls) == 2
assert calls[-1][1]["json"]["user_metadata"]["points"] == result["points_after"]
+83
View File
@@ -0,0 +1,83 @@
from types import SimpleNamespace
import src.database.db_manager as db_manager_module
from src.database.db_manager import DBManager
def _bound_db(tmp_path, monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
monkeypatch.setenv("POLYWEATHER_SUPABASE_PROFILE_SYNC_MIN_INTERVAL_SEC", "3600")
DBManager._profile_sync_cache.clear() # noqa: SLF001
db = DBManager(str(tmp_path / "profile-sync.db"))
db.upsert_user(1001, "eraer")
with db._get_connection() as conn: # noqa: SLF001
conn.execute(
"""
UPDATE users
SET supabase_user_id = ?, supabase_email = ?
WHERE telegram_id = ?
""",
("supabase-user-1", "eraer@example.com", 1001),
)
conn.execute(
"""
INSERT INTO supabase_bindings (supabase_user_id, telegram_id, supabase_email)
VALUES (?, ?, ?)
""",
("supabase-user-1", 1001, "eraer@example.com"),
)
conn.commit()
return db
def test_repeated_user_upsert_coalesces_supabase_profile_sync(tmp_path, monkeypatch):
db = _bound_db(tmp_path, monkeypatch)
calls = []
now = {"value": 1000.0}
monkeypatch.setattr(
db_manager_module,
"time",
SimpleNamespace(monotonic=lambda: now["value"]),
raising=False,
)
monkeypatch.setattr(
db_manager_module.requests,
"patch",
lambda *args, **kwargs: calls.append((args, kwargs))
or SimpleNamespace(status_code=204, text="", content=b""),
)
db.upsert_user(1001, "eraer")
now["value"] += 10.0
db.upsert_user(1001, "eraer")
assert len(calls) == 1
assert calls[0][1]["headers"]["Prefer"] == "return=minimal"
def test_changed_username_bypasses_supabase_profile_sync_coalescing(tmp_path, monkeypatch):
db = _bound_db(tmp_path, monkeypatch)
calls = []
now = {"value": 1000.0}
monkeypatch.setattr(
db_manager_module,
"time",
SimpleNamespace(monotonic=lambda: now["value"]),
raising=False,
)
monkeypatch.setattr(
db_manager_module.requests,
"patch",
lambda *args, **kwargs: calls.append((args, kwargs))
or SimpleNamespace(status_code=204, text="", content=b""),
)
db.upsert_user(1001, "eraer")
now["value"] += 10.0
db.upsert_user(1001, "new-name")
assert len(calls) == 2
assert calls[-1][1]["json"]["telegram_username"] == "new-name"
+137
View File
@@ -0,0 +1,137 @@
from pathlib import Path
def _schema_sql() -> str:
root = Path(__file__).resolve().parents[1]
return (root / "scripts" / "supabase" / "schema.sql").read_text(encoding="utf-8").lower()
def test_supabase_schema_has_io_friendly_indexes_for_hot_ops_queries():
schema = _schema_sql()
assert (
"create index if not exists idx_profiles_email\n"
" on public.profiles(email)\n"
" include (id)"
) in schema
assert (
"create index if not exists idx_profiles_id_lookup\n"
" on public.profiles(id)\n"
" include (email, created_at)"
) in schema
assert "idx_subscriptions_status_expiry" in schema
assert "on public.subscriptions(expires_at asc)" in schema
assert "idx_subscriptions_user_status_expiry" in schema
assert "on public.subscriptions(user_id, expires_at desc)" in schema
assert "include (id, starts_at, plan_code, source)" in schema
assert schema.count("where status = 'active'") >= 2
assert "idx_subscriptions_user_created" in schema
assert "on public.subscriptions(user_id, created_at desc)" in schema
assert "include (id, status, plan_code, source, starts_at, expires_at, updated_at)" in schema
assert "idx_payment_intents_status_updated" in schema
assert "on public.payment_intents(status, updated_at desc)" in schema
assert "include (user_id)" in schema
assert "where status in ('submitted', 'confirmed')" in schema
assert "idx_payment_intents_user_status_updated" in schema
assert "on public.payment_intents(user_id, status, updated_at desc)" in schema
assert "idx_payment_intents_user_status\n" not in schema
assert "idx_payment_intents_submitted_tx_updated" in schema
assert "include (id, user_id, tx_hash, chain_id)" in schema
assert "where status = 'submitted' and tx_hash is not null" in schema
assert "idx_payment_intents_user_created" in schema
assert "on public.payment_intents(user_id, created_at desc)" in schema
assert "idx_payment_intents_tx_hash" in schema
assert "on public.payment_intents(tx_hash)" in schema
assert "include (id, user_id)" in schema
assert "where tx_hash is not null" in schema
assert "idx_payments_created_at" in schema
assert "on public.payments(created_at desc)" in schema
assert "include (id, user_id, amount, currency, chain, tx_hash, status)" in schema
assert "idx_user_wallets_user_chain" in schema
assert "on public.user_wallets(user_id, chain_id, is_primary desc, verified_at desc)" in schema
assert "include (id, address)" in schema
assert (
"create index if not exists idx_user_wallets_chain_address_owner\n"
" on public.user_wallets(chain_id, address)\n"
" include (user_id, status)"
) in schema
assert schema.count("where status = 'active'") >= 3
assert "idx_wallet_link_challenges_lookup" not in schema
assert (
"create index if not exists idx_payment_transactions_tx_hash_intent\n"
" on public.payment_transactions(tx_hash)\n"
" include (intent_id)"
) in schema
def test_supabase_io_budget_scripts_are_production_runnable():
root = Path(__file__).resolve().parents[1]
indexes = (root / "scripts" / "supabase" / "io_budget_indexes.sql").read_text(encoding="utf-8").lower()
diagnostics = (root / "scripts" / "supabase" / "disk_io_diagnostics.sql").read_text(encoding="utf-8").lower()
assert "drop index if exists public.idx_profiles_email" in indexes
assert (
"create index if not exists idx_profiles_email\n"
" on public.profiles(email)\n"
" include (id)"
) in indexes
assert "drop index if exists public.idx_profiles_id_lookup" in indexes
assert (
"create index if not exists idx_profiles_id_lookup\n"
" on public.profiles(id)\n"
" include (email, created_at)"
) in indexes
assert "idx_subscriptions_user_created" in indexes
assert "drop index if exists public.idx_subscriptions_user_status_expiry" in indexes
assert "drop index if exists public.idx_subscriptions_status_expiry" in indexes
assert "on public.subscriptions(user_id, expires_at desc)" in indexes
assert "on public.subscriptions(expires_at asc)" in indexes
assert "include (id, starts_at, plan_code, source)" in indexes
assert "include (user_id, starts_at, plan_code)" in indexes
assert "include (id, status, plan_code, source, starts_at, expires_at, updated_at)" in indexes
assert "drop index if exists public.idx_payment_intents_user_status" in indexes
assert "drop index if exists public.idx_payment_intents_status_updated" in indexes
assert "include (user_id)" in indexes
assert "where status in ('submitted', 'confirmed')" in indexes
assert "drop index if exists public.idx_payment_intents_submitted_tx_updated" in indexes
assert "include (id, user_id, tx_hash, chain_id)" in indexes
assert "drop index if exists public.idx_payment_intents_tx_hash" in indexes
assert "include (id, user_id)" in indexes
assert "where tx_hash is not null" in indexes
assert "drop index if exists public.idx_payments_created_at" in indexes
assert "include (id, user_id, amount, currency, chain, tx_hash, status)" in indexes
assert "drop index if exists public.idx_user_wallets_user_chain" in indexes
assert "on public.user_wallets(user_id, chain_id, is_primary desc, verified_at desc)" in indexes
assert "include (id, address)" in indexes
assert "drop index if exists public.idx_user_wallets_chain_address_owner" in indexes
assert (
"create index if not exists idx_user_wallets_chain_address_owner\n"
" on public.user_wallets(chain_id, address)\n"
" include (user_id, status)"
) in indexes
assert "drop index if exists public.idx_wallet_link_challenges_lookup" in indexes
assert "create index if not exists idx_wallet_link_challenges_lookup" not in indexes
assert "drop index if exists public.idx_payment_transactions_tx_hash_intent" in indexes
assert (
"create index if not exists idx_payment_transactions_tx_hash_intent\n"
" on public.payment_transactions(tx_hash)\n"
" include (intent_id)"
) in indexes
assert "analyze public.payment_intents" in indexes
assert "pg_stat_user_tables" in diagnostics
assert "pg_stat_statements" in diagnostics
assert "shared_blks_read" in diagnostics
assert "pg_stat_user_indexes" in diagnostics
assert "pg_statio_user_indexes" in diagnostics
assert "indexrelname" in diagnostics
assert "idx_blks_read" in diagnostics
assert "idx_scan = 0" in diagnostics
def test_supabase_setup_doc_includes_io_budget_runbook():
root = Path(__file__).resolve().parents[1]
doc = (root / "docs" / "SUPABASE_SETUP_ZH.md").read_text(encoding="utf-8")
assert "scripts/supabase/io_budget_indexes.sql" in doc
assert "scripts/supabase/disk_io_diagnostics.sql" in doc
assert "Disk IO" in doc
+940 -8
View File
@@ -1,10 +1,14 @@
from datetime import datetime
from fastapi.testclient import TestClient
from starlette.requests import Request
import web.core as web_core
import web.services.auth_api as auth_api
from web.app import app
import web.routes as routes
import web.services.ops_api as ops_api
import web.scan_terminal_cache as scan_terminal_cache
import web.scan_terminal_service as scan_terminal_service
from web.scan_terminal_cache import scan_terminal_cache_key
@@ -94,6 +98,138 @@ def test_payment_runtime_endpoint_returns_shape():
assert 'recent_audit_events' in payload
def test_payment_config_does_not_require_entitlement(monkeypatch):
monkeypatch.setattr(
routes,
"_assert_entitlement",
lambda request: (_ for _ in ()).throw(
AssertionError("public payment config should not validate Supabase auth"),
),
)
monkeypatch.setattr(
routes.PAYMENT_CHECKOUT,
"get_config_payload",
lambda: {"enabled": True, "plans": []},
)
response = client.get("/api/payments/config")
assert response.status_code == 200
assert response.json()["enabled"] is True
def test_payment_wallets_requires_identity_without_subscription_gate(monkeypatch):
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "enabled", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "require_subscription", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "supabase_url", "https://example.supabase.co")
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "anon_key", "anon-key")
monkeypatch.setattr(web_core, "_SUPABASE_AUTH_REQUIRED", True)
class _Identity:
user_id = "user-1"
email = "user@example.com"
points = 0
created_at = "2026-05-01T00:00:00+00:00"
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "get_identity", lambda token: _Identity())
monkeypatch.setattr(
web_core.SUPABASE_ENTITLEMENT,
"has_active_subscription",
lambda user_id: (_ for _ in ()).throw(
AssertionError("payment identity endpoints should not query subscription gate"),
),
)
monkeypatch.setattr(routes.PAYMENT_CHECKOUT, "list_wallets", lambda user_id: [])
monkeypatch.setattr(routes.PAYMENT_CHECKOUT, "chain_id", 137)
response = client.get(
"/api/payments/wallets",
headers={"Authorization": "Bearer access-token"},
)
assert response.status_code == 200
assert response.json() == {"wallets": [], "chain_id": 137}
def test_telegram_identity_endpoints_skip_subscription_gate(monkeypatch):
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "enabled", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "require_subscription", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "supabase_url", "https://example.supabase.co")
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "anon_key", "anon-key")
monkeypatch.setattr(web_core, "_SUPABASE_AUTH_REQUIRED", True)
class _Identity:
user_id = "user-1"
email = "user@example.com"
points = 0
created_at = "2026-05-01T00:00:00+00:00"
class _FakePricing:
configured = True
@staticmethod
def verify_login_payload(payload):
return {"telegram_id": int(payload["id"]), "username": "tester"}
@staticmethod
def get_member_status(telegram_id):
return "member"
@staticmethod
def resolve_price_for_telegram_id(telegram_id):
return {"telegram_id": telegram_id, "pricing_source": "telegram_group_member"}
class _FakeDB:
@staticmethod
def upsert_user(telegram_id, username):
return None
@staticmethod
def bind_supabase_identity(*, telegram_id, supabase_user_id, supabase_email):
return {"ok": True}
@staticmethod
def consume_bind_token(token):
return 12345
@staticmethod
def create_web_bind_token(*, supabase_user_id, supabase_email, ttl_minutes):
return "bind-token"
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "get_identity", lambda token: _Identity())
monkeypatch.setattr(
web_core.SUPABASE_ENTITLEMENT,
"has_active_subscription",
lambda user_id: (_ for _ in ()).throw(
AssertionError("telegram identity endpoints should not query subscription gate"),
),
)
monkeypatch.setattr(auth_api, "TelegramGroupPricing", lambda: _FakePricing())
monkeypatch.setattr(auth_api, "DBManager", lambda: _FakeDB())
auth_headers = {"Authorization": "Bearer access-token"}
login_response = client.post(
"/api/auth/telegram/login",
headers=auth_headers,
json={"id": 12345, "username": "tester", "auth_date": 1770000000, "hash": "x" * 64},
)
assert login_response.status_code == 200
token_response = client.post(
"/api/auth/telegram/bind-by-token",
headers=auth_headers,
json={"token": "token-12345"},
)
assert token_response.status_code == 200
link_response = client.post(
"/api/auth/telegram/bot-bind-link",
headers=auth_headers,
)
assert link_response.status_code == 200
def test_auth_me_does_not_reconcile_on_status_probe(monkeypatch):
monkeypatch.setattr(routes, "_assert_entitlement", lambda request: None)
@@ -106,21 +242,21 @@ def test_auth_me_does_not_reconcile_on_status_probe(monkeypatch):
monkeypatch.setattr(routes, "_resolve_weekly_profile", lambda request: {"weekly_points": 0, "weekly_rank": None})
monkeypatch.setattr(routes.SUPABASE_ENTITLEMENT, "enabled", True)
calls = {"count": 0}
reconcile_calls = {"count": 0}
def _latest_subscription(user_id, respect_requirement=False):
calls["count"] += 1
def _subscription_window(user_id, respect_requirement=False):
return {
"plan_code": "pro_monthly",
"starts_at": "2026-03-22T00:00:00+00:00",
"expires_at": "2026-04-21T00:00:00+00:00",
"current": {
"plan_code": "pro_monthly",
"starts_at": "2026-03-22T00:00:00+00:00",
"expires_at": "2026-04-21T00:00:00+00:00",
},
}
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"get_latest_active_subscription",
_latest_subscription,
"get_subscription_window",
_subscription_window,
)
monkeypatch.setattr(routes.PAYMENT_CHECKOUT, "enabled", True)
@@ -143,6 +279,265 @@ def test_auth_me_does_not_reconcile_on_status_probe(monkeypatch):
assert reconcile_calls["count"] == 0
def test_auth_me_reuses_identity_bound_by_entitlement(monkeypatch):
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "enabled", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "require_subscription", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "supabase_url", "https://example.supabase.co")
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "anon_key", "anon-key")
monkeypatch.setattr(web_core, "_SUPABASE_AUTH_REQUIRED", True)
monkeypatch.setattr(routes, "_resolve_weekly_profile", lambda request: {"weekly_points": 0, "weekly_rank": None})
class _Identity:
user_id = "user-1"
email = "user@example.com"
points = 7
created_at = "2026-05-01T00:00:00+00:00"
calls = {"identity": 0}
def _get_identity(token):
calls["identity"] += 1
assert token == "access-token"
return _Identity()
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "get_identity", _get_identity)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "has_active_subscription", lambda user_id: True)
monkeypatch.setattr(
web_core.SUPABASE_ENTITLEMENT,
"get_subscription_window",
lambda user_id, respect_requirement=False: {
"current": {
"plan_code": "pro_monthly",
"starts_at": "2026-03-22T00:00:00+00:00",
"expires_at": "2026-04-21T00:00:00+00:00",
},
},
)
response = client.get(
"/api/auth/me",
headers={"Authorization": "Bearer access-token"},
)
assert response.status_code == 200
payload = response.json()
assert payload["authenticated"] is True
assert payload["points"] == 7
assert calls["identity"] == 1
def test_auth_me_uses_subscription_window_as_required_subscription_gate(monkeypatch):
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "enabled", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "require_subscription", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "supabase_url", "https://example.supabase.co")
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "anon_key", "anon-key")
monkeypatch.setattr(web_core, "_SUPABASE_AUTH_REQUIRED", True)
monkeypatch.setattr(routes, "_resolve_weekly_profile", lambda request: {"weekly_points": 0, "weekly_rank": None})
class _Identity:
user_id = "user-1"
email = "user@example.com"
points = 7
created_at = "2026-05-01T00:00:00+00:00"
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "get_identity", lambda token: _Identity())
monkeypatch.setattr(
web_core.SUPABASE_ENTITLEMENT,
"has_active_subscription",
lambda user_id: (_ for _ in ()).throw(
AssertionError("auth/me should not run a separate lightweight subscription gate"),
),
)
monkeypatch.setattr(
web_core.SUPABASE_ENTITLEMENT,
"get_latest_active_subscription",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("auth/me should derive current subscription from the window query"),
),
)
monkeypatch.setattr(
web_core.SUPABASE_ENTITLEMENT,
"get_subscription_window",
lambda user_id, respect_requirement=False: {
"current": {
"plan_code": "pro_monthly",
"starts_at": "2026-03-22T00:00:00+00:00",
"expires_at": "2026-04-21T00:00:00+00:00",
},
"total_expires_at": "2026-05-21T00:00:00+00:00",
"queued_days": 30,
"queued_count": 1,
},
)
response = client.get(
"/api/auth/me",
headers={"Authorization": "Bearer access-token"},
)
assert response.status_code == 200
payload = response.json()
assert payload["subscription_active"] is True
assert payload["subscription_plan_code"] == "pro_monthly"
assert payload["subscription_queued_days"] == 30
def test_auth_me_uses_window_rows_for_non_required_latest_known_subscription(monkeypatch):
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "enabled", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "require_subscription", False)
monkeypatch.setattr(web_core, "_SUPABASE_AUTH_REQUIRED", False)
monkeypatch.setattr(routes, "_resolve_weekly_profile", lambda request: {"weekly_points": 0, "weekly_rank": None})
monkeypatch.setattr(routes, "_resolve_auth_points", lambda request: 0)
def _bind_identity(request):
request.state.auth_user_id = "user-1"
request.state.auth_email = "user@example.com"
monkeypatch.setattr(routes, "_assert_entitlement", lambda request: None)
monkeypatch.setattr(routes, "_bind_optional_supabase_identity", _bind_identity)
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"get_subscription_window",
lambda user_id, respect_requirement=False: {
"current": None,
"rows": [
{
"plan_code": "pro_monthly",
"starts_at": "2026-06-01T00:00:00+00:00",
"expires_at": "2026-07-01T00:00:00+00:00",
}
],
"total_expires_at": "2026-07-01T00:00:00+00:00",
"queued_days": 0,
"queued_count": 0,
},
)
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"get_latest_active_subscription",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("auth/me should reuse window rows before latest active fallback"),
),
)
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"get_latest_subscription_any_status",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("auth/me should reuse window rows before historical fallback"),
),
)
response = client.get("/api/auth/me")
assert response.status_code == 200
payload = response.json()
assert payload["subscription_active"] is False
assert payload["subscription_plan_code"] == "pro_monthly"
assert payload["subscription_expires_at"] == "2026-07-01T00:00:00+00:00"
def test_auth_me_skips_latest_active_after_empty_non_required_window(monkeypatch):
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "enabled", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "require_subscription", False)
monkeypatch.setattr(web_core, "_SUPABASE_AUTH_REQUIRED", False)
monkeypatch.setattr(routes, "_resolve_weekly_profile", lambda request: {"weekly_points": 0, "weekly_rank": None})
monkeypatch.setattr(routes, "_resolve_auth_points", lambda request: 0)
def _bind_identity(request):
request.state.auth_user_id = "user-1"
request.state.auth_email = "user@example.com"
latest_any_calls = {"count": 0}
def _latest_any_status(user_id):
latest_any_calls["count"] += 1
return {
"plan_code": "expired_pro",
"starts_at": "2026-03-01T00:00:00+00:00",
"expires_at": "2026-04-01T00:00:00+00:00",
}
monkeypatch.setattr(routes, "_assert_entitlement", lambda request: None)
monkeypatch.setattr(routes, "_bind_optional_supabase_identity", _bind_identity)
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"get_subscription_window",
lambda user_id, respect_requirement=False: {},
)
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"get_latest_active_subscription",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("empty subscription window should skip latest active fallback"),
),
)
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"get_latest_subscription_any_status",
_latest_any_status,
)
response = client.get("/api/auth/me")
assert response.status_code == 200
payload = response.json()
assert payload["subscription_active"] is False
assert payload["subscription_plan_code"] == "expired_pro"
assert latest_any_calls["count"] == 1
def test_auth_me_preserves_required_subscription_403_from_window(monkeypatch):
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "enabled", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "require_subscription", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "supabase_url", "https://example.supabase.co")
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "anon_key", "anon-key")
monkeypatch.setattr(web_core, "_SUPABASE_AUTH_REQUIRED", True)
class _Identity:
user_id = "user-1"
email = "user@example.com"
points = 0
created_at = "2026-05-01T00:00:00+00:00"
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "get_identity", lambda token: _Identity())
monkeypatch.setattr(
web_core.SUPABASE_ENTITLEMENT,
"has_active_subscription",
lambda user_id: (_ for _ in ()).throw(
AssertionError("auth/me should not run a separate lightweight subscription gate"),
),
)
monkeypatch.setattr(
web_core.SUPABASE_ENTITLEMENT,
"get_subscription_window",
lambda user_id, respect_requirement=False: {},
)
latest_any_calls = {"count": 0}
def _latest_any_status(user_id):
latest_any_calls["count"] += 1
return {
"plan_code": "expired_pro",
"starts_at": "2026-03-01T00:00:00+00:00",
"expires_at": "2026-04-01T00:00:00+00:00",
}
monkeypatch.setattr(
web_core.SUPABASE_ENTITLEMENT,
"get_latest_subscription_any_status",
_latest_any_status,
)
response = client.get(
"/api/auth/me",
headers={"Authorization": "Bearer access-token"},
)
assert response.status_code == 403
assert response.json()["detail"] == "Subscription required"
assert latest_any_calls["count"] == 0
def test_backend_entitlement_token_binds_forwarded_supabase_identity(monkeypatch):
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "enabled", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "supabase_url", "https://example.supabase.co")
@@ -167,6 +562,45 @@ def test_backend_entitlement_token_binds_forwarded_supabase_identity(monkeypatch
assert request.state.auth_email == "user@example.com"
def test_backend_entitlement_token_without_forwarded_identity_validates_bearer(monkeypatch):
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "enabled", True)
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "supabase_url", "https://example.supabase.co")
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "anon_key", "anon-key")
monkeypatch.setattr(web_core, "_SUPABASE_AUTH_REQUIRED", False)
monkeypatch.setattr(web_core, "_ENTITLEMENT_TOKEN", "backend-token")
class _Identity:
user_id = "user-1"
email = "user@example.com"
points = 7
created_at = "2026-05-01T00:00:00+00:00"
calls = {"count": 0}
def _get_identity(token):
calls["count"] += 1
assert token == "access-token"
return _Identity()
monkeypatch.setattr(web_core.SUPABASE_ENTITLEMENT, "get_identity", _get_identity)
request = Request(
{
"type": "http",
"headers": [
(b"x-polyweather-entitlement", b"backend-token"),
(b"authorization", b"Bearer access-token"),
],
}
)
web_core._assert_entitlement(request)
assert calls["count"] == 1
assert request.state.auth_user_id == "user-1"
assert request.state.auth_email == "user@example.com"
def test_ops_memberships_prefers_supabase_auth_email(monkeypatch):
monkeypatch.setattr(routes, "_assert_entitlement", lambda request: None)
monkeypatch.setattr(routes, "_require_ops_admin", lambda request: None)
@@ -216,6 +650,504 @@ def test_ops_memberships_prefers_supabase_auth_email(monkeypatch):
assert payload["memberships"][0]["email"] == "fresh@example.com"
def test_ops_memberships_uses_batched_subscription_windows(monkeypatch):
monkeypatch.setattr(routes, "_assert_entitlement", lambda request: None)
monkeypatch.setattr(routes, "_require_ops_admin", lambda request: None)
monkeypatch.setattr(routes.PAYMENT_CHECKOUT, "enabled", False)
class _FakeDB:
@staticmethod
def get_users_by_supabase_user_ids(user_ids):
return {
"user-1": {"supabase_email": "one@example.com"},
"user-2": {"supabase_email": "two@example.com"},
}
import src.database.db_manager as db_module
monkeypatch.setattr(db_module, "DBManager", lambda: _FakeDB())
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"list_active_subscriptions",
lambda limit=200: [
{
"user_id": "user-1",
"plan_code": "pro_monthly",
"starts_at": "2026-03-01T00:00:00+00:00",
"expires_at": "2026-04-01T00:00:00+00:00",
},
{
"user_id": "user-2",
"plan_code": "pro_monthly",
"starts_at": "2026-03-02T00:00:00+00:00",
"expires_at": "2026-04-02T00:00:00+00:00",
},
],
)
monkeypatch.setattr(routes.SUPABASE_ENTITLEMENT, "get_auth_users", lambda user_ids: {})
def _fail_per_user_window(*args, **kwargs):
raise AssertionError("per-user subscription window query should not run")
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"get_subscription_window",
_fail_per_user_window,
)
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"list_subscription_windows",
lambda user_ids, bypass_cache=True: {
"user-1": {"total_expires_at": "2026-04-15T00:00:00+00:00", "queued_days": 14, "queued_count": 1},
"user-2": {"total_expires_at": "2026-04-02T00:00:00+00:00", "queued_days": 0, "queued_count": 0},
},
raising=False,
)
response = client.get("/api/ops/memberships")
assert response.status_code == 200
payload = response.json()
rows = {row["user_id"]: row for row in payload["memberships"]}
assert rows["user-1"]["queued_days"] == 14
assert rows["user-2"]["queued_days"] == 0
def test_ops_memberships_prefers_single_active_subscription_window_query(monkeypatch):
monkeypatch.setattr(routes, "_assert_entitlement", lambda request: None)
monkeypatch.setattr(routes, "_require_ops_admin", lambda request: None)
monkeypatch.setattr(routes.PAYMENT_CHECKOUT, "enabled", False)
class _FakeDB:
@staticmethod
def get_users_by_supabase_user_ids(user_ids):
return {"user-1": {"supabase_email": "one@example.com"}}
import src.database.db_manager as db_module
monkeypatch.setattr(db_module, "DBManager", lambda: _FakeDB())
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"list_active_subscriptions",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("memberships should not run a separate active subscription query"),
),
)
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"list_subscription_windows",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("memberships should not run a second window query"),
),
)
monkeypatch.setattr(routes.SUPABASE_ENTITLEMENT, "get_auth_users", lambda user_ids: {})
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"list_active_subscription_windows",
lambda limit=200: {
"subscriptions": [
{
"user_id": "user-1",
"plan_code": "pro_monthly",
"source": "payment_contract",
"starts_at": "2026-03-01T00:00:00+00:00",
"expires_at": "2026-04-01T00:00:00+00:00",
}
],
"windows": {
"user-1": {
"total_expires_at": "2026-05-01T00:00:00+00:00",
"queued_days": 30,
"queued_count": 1,
}
},
},
raising=False,
)
response = client.get("/api/ops/memberships")
assert response.status_code == 200
row = response.json()["memberships"][0]
assert row["queued_days"] == 30
assert row["expires_at"] == "2026-05-01T00:00:00+00:00"
def test_ops_memberships_growth_reuses_active_subscription_window_query(monkeypatch):
monkeypatch.setattr(routes, "_assert_entitlement", lambda request: None)
monkeypatch.setattr(routes, "_require_ops_admin", lambda request: None)
starts_at = datetime.utcnow().replace(microsecond=0).isoformat()
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"list_active_subscriptions",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("growth should not run a separate active subscription query"),
),
)
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"list_active_subscription_windows",
lambda limit=5000: {
"subscriptions": [
{
"user_id": "user-1",
"plan_code": "pro_monthly",
"source": "payment_contract",
"starts_at": starts_at,
"expires_at": "2099-01-01T00:00:00+00:00",
}
],
"windows": {},
},
raising=False,
)
response = client.get("/api/ops/memberships/growth?days=7")
assert response.status_code == 200
assert any(day["paid"] == 1 for day in response.json()["daily"])
def test_ops_telegram_audit_reuses_active_subscription_window_query(monkeypatch):
monkeypatch.setenv("TELEGRAM_BOT_TOKEN", "bot-token")
monkeypatch.setenv("TELEGRAM_CHAT_IDS", "chat-1")
monkeypatch.delenv("TELEGRAM_CHAT_ID", raising=False)
monkeypatch.delenv("POLYWEATHER_TELEGRAM_GROUP_ID", raising=False)
monkeypatch.delenv("POLYWEATHER_TELEGRAM_TOPICS_GROUP_ID", raising=False)
monkeypatch.setattr(ops_api, "_require_ops", lambda request: None)
class _FakeRows(list):
def fetchall(self):
return self
class _FakeConnection:
row_factory = None
def __enter__(self):
return self
def __exit__(self, *args):
return False
def execute(self, sql):
if "FROM users" in sql:
return _FakeRows([{"telegram_id": 1, "username": "tester"}])
if "FROM supabase_bindings" in sql:
return _FakeRows(
[
{
"telegram_id": 1,
"supabase_user_id": "user-1",
"supabase_email": "one@example.com",
}
]
)
raise AssertionError(sql)
class _FakeDB:
@staticmethod
def _get_connection():
return _FakeConnection()
class _Response:
status_code = 200
@staticmethod
def json():
return {"ok": True, "result": {"status": "member"}}
import requests as requests_module
import src.database.db_manager as db_module
monkeypatch.setattr(db_module, "DBManager", lambda: _FakeDB())
monkeypatch.setattr(requests_module, "get", lambda *args, **kwargs: _Response())
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"list_active_subscriptions",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("telegram audit should not run a separate active subscription query"),
),
)
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"list_active_subscription_windows",
lambda limit=5000: {
"subscriptions": [
{
"user_id": "user-1",
"plan_code": "pro_monthly",
"source": "payment_contract",
"starts_at": "2026-03-01T00:00:00+00:00",
"expires_at": "2099-01-01T00:00:00+00:00",
}
],
"windows": {},
},
raising=False,
)
result = ops_api.get_ops_telegram_audit(object())
assert result["valid_count"] == 1
assert result["anomaly_count"] == 0
def test_ops_memberships_overview_combines_memberships_and_growth_in_one_subscription_query(monkeypatch):
monkeypatch.setattr(routes, "_assert_entitlement", lambda request: None)
monkeypatch.setattr(routes, "_require_ops_admin", lambda request: None)
monkeypatch.setattr(routes.PAYMENT_CHECKOUT, "enabled", False)
starts_at = datetime.utcnow().replace(microsecond=0).isoformat()
calls = {"active_windows": 0}
class _FakeDB:
@staticmethod
def get_users_by_supabase_user_ids(user_ids):
return {"user-1": {"supabase_email": "one@example.com"}}
import src.database.db_manager as db_module
monkeypatch.setattr(db_module, "DBManager", lambda: _FakeDB())
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"list_active_subscriptions",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("overview should not run a separate active subscription query"),
),
)
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"list_subscription_windows",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("overview should not run a second window query"),
),
)
monkeypatch.setattr(routes.SUPABASE_ENTITLEMENT, "get_auth_users", lambda user_ids: {})
def _active_windows(limit=5000):
calls["active_windows"] += 1
assert limit == 5000
return {
"subscriptions": [
{
"user_id": "user-1",
"plan_code": "pro_monthly",
"source": "payment_contract",
"starts_at": starts_at,
"expires_at": "2099-01-01T00:00:00+00:00",
},
{
"user_id": "user-2",
"plan_code": "signup_trial_3d",
"source": "signup_trial",
"starts_at": starts_at,
"expires_at": "2099-01-02T00:00:00+00:00",
},
],
"windows": {
"user-1": {
"total_expires_at": "2099-01-01T00:00:00+00:00",
"queued_days": 0,
"queued_count": 0,
},
"user-2": {
"total_expires_at": "2099-01-02T00:00:00+00:00",
"queued_days": 0,
"queued_count": 0,
},
},
}
monkeypatch.setattr(
routes.SUPABASE_ENTITLEMENT,
"list_active_subscription_windows",
_active_windows,
raising=False,
)
response = client.get("/api/ops/memberships/overview?limit=1&days=7")
assert response.status_code == 200
payload = response.json()
assert len(payload["memberships"]) == 1
assert payload["memberships"][0]["user_id"] == "user-1"
assert any(day["paid"] == 1 and day["trial"] == 1 for day in payload["daily"])
assert calls["active_windows"] == 1
def test_ops_memberships_does_not_reconcile_payments_by_default(monkeypatch):
monkeypatch.delenv("POLYWEATHER_OPS_MEMBERSHIPS_RECONCILE_ENABLED", raising=False)
monkeypatch.setattr(routes, "_assert_entitlement", lambda request: None)
monkeypatch.setattr(routes, "_require_ops_admin", lambda request: None)
monkeypatch.setattr(routes.PAYMENT_CHECKOUT, "enabled", True)
calls = {"count": 0}
def _count_reconcile(*args, **kwargs):
calls["count"] += 1
return {"ok": True}
monkeypatch.setattr(routes.PAYMENT_CHECKOUT, "reconcile_recent_intents", _count_reconcile)
class _FakeDB:
@staticmethod
def get_users_by_supabase_user_ids(user_ids):
return {}
import src.database.db_manager as db_module
monkeypatch.setattr(db_module, "DBManager", lambda: _FakeDB())
monkeypatch.setattr(routes.SUPABASE_ENTITLEMENT, "list_active_subscriptions", lambda limit=200: [])
monkeypatch.setattr(routes.SUPABASE_ENTITLEMENT, "get_auth_users", lambda user_ids: {})
monkeypatch.setattr(routes.SUPABASE_ENTITLEMENT, "list_subscription_windows", lambda user_ids, bypass_cache=True: {})
response = client.get("/api/ops/memberships")
assert response.status_code == 200
assert response.json()["memberships"] == []
assert calls["count"] == 0
def test_ops_email_lookup_prefers_profiles_over_auth_admin(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
calls = []
class _Response:
ok = True
status_code = 200
content = b"1"
text = ""
def __init__(self, payload):
self._payload = payload
def json(self):
return self._payload
def _fake_get(url, headers=None, params=None, timeout=None):
calls.append((url, params))
if url.endswith("/rest/v1/profiles"):
assert headers is not None
assert headers.get("Prefer") != "return=representation"
assert params["select"] == "id"
assert params["email"] == "eq.user@example.com"
return _Response([{"id": "user-1"}])
raise AssertionError(f"unexpected auth admin lookup: {url}")
monkeypatch.setattr(ops_api._requests, "get", _fake_get)
assert (
ops_api._lookup_supabase_user_id_by_email(
"https://example.supabase.co",
"service-role",
"user@example.com",
)
== "user-1"
)
assert len(calls) == 1
def test_ops_subscription_grant_invalidates_subscription_cache(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
monkeypatch.setattr(ops_api, "_require_ops", lambda request: {"email": "admin@example.com"})
class _Response:
ok = True
status_code = 200
content = b"1"
text = ""
def __init__(self, payload):
self._payload = payload
def json(self):
return self._payload
def _fake_get(url, headers=None, params=None, timeout=None):
assert url.endswith("/rest/v1/profiles")
assert params["select"] == "id"
return _Response([{"id": "user-1"}])
def _fake_post(url, headers=None, json=None, timeout=None):
assert url.endswith("/rest/v1/subscriptions")
assert headers["Prefer"] == "return=minimal"
return _Response([{"id": 1, "user_id": "user-1"}])
invalidated = []
monkeypatch.setattr(ops_api._requests, "get", _fake_get)
monkeypatch.setattr(ops_api._requests, "post", _fake_post)
monkeypatch.setattr(
ops_api.legacy_routes.SUPABASE_ENTITLEMENT,
"invalidate_subscription_cache",
lambda user_id: invalidated.append(user_id),
)
result = ops_api.grant_ops_subscription(object(), "user@example.com")
assert result["ok"] is True
assert invalidated == ["user-1"]
def test_ops_subscription_extend_uses_minimal_return_and_invalidates_cache(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
monkeypatch.setattr(ops_api, "_require_ops", lambda request: {"email": "admin@example.com"})
class _Response:
ok = True
status_code = 200
content = b"1"
text = ""
def __init__(self, payload):
self._payload = payload
def json(self):
return self._payload
def _fake_get(url, headers=None, params=None, timeout=None):
if url.endswith("/rest/v1/profiles"):
assert params["select"] == "id"
return _Response([{"id": "user-1"}])
if url.endswith("/rest/v1/subscriptions"):
assert params["select"] == "id,expires_at"
return _Response(
[
{
"id": 7,
"expires_at": "2026-04-01T00:00:00+00:00",
}
]
)
raise AssertionError(url)
def _fake_patch(url, headers=None, json=None, timeout=None):
assert url.endswith("/rest/v1/subscriptions?id=eq.7")
assert headers["Prefer"] == "return=minimal"
assert "expires_at" in json
return _Response([])
invalidated = []
monkeypatch.setattr(ops_api._requests, "get", _fake_get)
monkeypatch.setattr(ops_api._requests, "patch", _fake_patch)
monkeypatch.setattr(
ops_api.legacy_routes.SUPABASE_ENTITLEMENT,
"invalidate_subscription_cache",
lambda user_id: invalidated.append(user_id),
)
result = ops_api.extend_ops_subscription(object(), "user@example.com", additional_days=7)
assert result["ok"] is True
assert result["new_expires_at"].startswith("2026-04-08")
assert invalidated == ["user-1"]
def test_ops_truth_history_returns_filtered_rows(monkeypatch):
monkeypatch.setattr(routes, "_assert_entitlement", lambda request: None)
monkeypatch.setattr(routes, "_require_ops_admin", lambda request: None)
+42
View File
@@ -0,0 +1,42 @@
from datetime import datetime
import src.bot.weekly_reward_loop as weekly_reward_loop
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_weekly_reward_bonus_subscription_insert_uses_minimal_return(monkeypatch):
calls = []
def _fake_get(url, headers=None, params=None, timeout=None):
calls.append(("GET", url, headers, params))
return _Response(200, [])
def _fake_post(url, headers=None, json=None, timeout=None):
calls.append(("POST", url, headers, json))
assert headers["Prefer"] == "return=minimal"
return _Response(201, [])
monkeypatch.setattr(weekly_reward_loop.requests, "get", _fake_get)
monkeypatch.setattr(weekly_reward_loop.requests, "post", _fake_post)
ok, reason, expires_at = weekly_reward_loop._grant_bonus_subscription_days(
supabase_url="https://example.supabase.co",
service_role_key="service-role",
user_id="user-1",
days=7,
timeout_sec=5,
)
assert ok is True
assert reason == ""
assert datetime.fromisoformat(str(expires_at)).tzinfo is not None
assert [call[0] for call in calls] == ["GET", "POST"]