feat: add portal account records
This commit is contained in:
+224
-1
@@ -1,13 +1,15 @@
|
||||
import uuid
|
||||
from decimal import Decimal
|
||||
|
||||
from allauth.account.models import EmailAddress
|
||||
from django.contrib.auth import get_user, get_user_model
|
||||
from django.core import mail
|
||||
from django.core.cache import cache
|
||||
from django.test import Client, TestCase, override_settings
|
||||
from django.utils import timezone
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.billing.models import PointsLedger
|
||||
from apps.billing.models import CallRecord, PointsLedger, RechargeOrder
|
||||
from apps.users.models import ApiKey, UserWallet
|
||||
|
||||
|
||||
@@ -33,6 +35,47 @@ class PortalAccountFlowTests(TestCase):
|
||||
)
|
||||
return user
|
||||
|
||||
def create_recharge_order(
|
||||
self,
|
||||
user,
|
||||
*,
|
||||
order_no: str,
|
||||
amount: str,
|
||||
points: int,
|
||||
status: str = RechargeOrder.Status.PAID,
|
||||
):
|
||||
return RechargeOrder.objects.create(
|
||||
user=user,
|
||||
order_no=order_no,
|
||||
amount_money=Decimal(amount),
|
||||
currency="CNY",
|
||||
pay_method=RechargeOrder.PayMethod.WEIXIN,
|
||||
exchange_rate=Decimal("10.0000"),
|
||||
points_granted=points,
|
||||
status=status,
|
||||
payment_txn_no=f"txn-{order_no}" if status == RechargeOrder.Status.PAID else "",
|
||||
paid_at=timezone.now() if status == RechargeOrder.Status.PAID else None,
|
||||
)
|
||||
|
||||
def create_call_record(
|
||||
self,
|
||||
user,
|
||||
api_key,
|
||||
*,
|
||||
alias: str,
|
||||
points_cost: int,
|
||||
status: str = CallRecord.Status.SUCCESS,
|
||||
):
|
||||
return CallRecord.objects.create(
|
||||
user=user,
|
||||
api_key=api_key,
|
||||
operation_type=CallRecord.OperationType.TITLE,
|
||||
alias=alias,
|
||||
model_used="gpt-5.5",
|
||||
points_cost=points_cost,
|
||||
status=status,
|
||||
)
|
||||
|
||||
def test_signup_creates_unverified_user_wallet_with_zero_points_and_no_ledger(self):
|
||||
suffix = uuid.uuid4().hex[:8]
|
||||
email = f"signup-{suffix}@example.com"
|
||||
@@ -211,3 +254,183 @@ class PortalAccountFlowTests(TestCase):
|
||||
self.assertEqual(delete_response.status_code, 403)
|
||||
api_key.refresh_from_db()
|
||||
self.assertEqual(api_key.status, ApiKey.Status.ACTIVE)
|
||||
|
||||
def test_dashboard_shows_account_summary_recent_records_for_current_user_only(self):
|
||||
user = self.create_verified_user()
|
||||
other_user = self.create_verified_user()
|
||||
UserWallet.objects.create(user=user, points_balance=160)
|
||||
UserWallet.objects.create(user=other_user, points_balance=999)
|
||||
api_key, _raw_key = ApiKey.create_for_user(user, name="desktop")
|
||||
other_key, _other_raw_key = ApiKey.create_for_user(other_user, name="other")
|
||||
order = self.create_recharge_order(
|
||||
user,
|
||||
order_no="R-OWN-PAID-503",
|
||||
amount="20.00",
|
||||
points=200,
|
||||
)
|
||||
self.create_recharge_order(
|
||||
other_user,
|
||||
order_no="R-OTHER-PAID-503",
|
||||
amount="99.00",
|
||||
points=990,
|
||||
)
|
||||
PointsLedger.objects.create(
|
||||
user=user,
|
||||
change_type=PointsLedger.ChangeType.RECHARGE,
|
||||
points_delta=200,
|
||||
balance_after=200,
|
||||
ref_order_id=order.id,
|
||||
)
|
||||
call = self.create_call_record(
|
||||
user,
|
||||
api_key,
|
||||
alias="title-standard",
|
||||
points_cost=50,
|
||||
status=CallRecord.Status.FAILED,
|
||||
)
|
||||
other_call = self.create_call_record(
|
||||
other_user,
|
||||
other_key,
|
||||
alias="other-alias",
|
||||
points_cost=70,
|
||||
)
|
||||
PointsLedger.objects.create(
|
||||
user=user,
|
||||
change_type=PointsLedger.ChangeType.CONSUME,
|
||||
points_delta=-50,
|
||||
balance_after=150,
|
||||
ref_call=call,
|
||||
)
|
||||
PointsLedger.objects.create(
|
||||
user=user,
|
||||
change_type=PointsLedger.ChangeType.REFUND,
|
||||
points_delta=10,
|
||||
balance_after=160,
|
||||
ref_call=call,
|
||||
)
|
||||
PointsLedger.objects.create(
|
||||
user=other_user,
|
||||
change_type=PointsLedger.ChangeType.CONSUME,
|
||||
points_delta=-70,
|
||||
balance_after=929,
|
||||
ref_call=other_call,
|
||||
)
|
||||
self.client.force_login(user)
|
||||
|
||||
response = self.client.get("/dashboard")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context["balance"].points_balance, 160)
|
||||
self.assertEqual(response.context["balance"].ledger_balance, 160)
|
||||
self.assertEqual(response.context["recharge_total_amount"], Decimal("20.00"))
|
||||
self.assertEqual(response.context["recharge_points_total"], 200)
|
||||
self.assertEqual(response.context["consumed_points_total"], 50)
|
||||
self.assertEqual(response.context["refunded_points_total"], 10)
|
||||
self.assertEqual(response.context["net_used_points"], 40)
|
||||
self.assertContains(response, "R-OWN-PAID-503")
|
||||
self.assertContains(response, "title-standard")
|
||||
self.assertNotContains(response, "R-OTHER-PAID-503")
|
||||
self.assertNotContains(response, "other-alias")
|
||||
|
||||
def test_recharge_records_require_login_and_only_show_current_user_orders(self):
|
||||
user = self.create_verified_user()
|
||||
other_user = self.create_verified_user()
|
||||
paid_order = self.create_recharge_order(
|
||||
user,
|
||||
order_no="R-OWN-RECHARGE-503",
|
||||
amount="30.00",
|
||||
points=300,
|
||||
)
|
||||
self.create_recharge_order(
|
||||
user,
|
||||
order_no="R-OWN-PENDING-503",
|
||||
amount="88.00",
|
||||
points=880,
|
||||
status=RechargeOrder.Status.PENDING,
|
||||
)
|
||||
self.create_recharge_order(
|
||||
other_user,
|
||||
order_no="R-OTHER-RECHARGE-503",
|
||||
amount="99.00",
|
||||
points=990,
|
||||
)
|
||||
PointsLedger.objects.create(
|
||||
user=user,
|
||||
change_type=PointsLedger.ChangeType.RECHARGE,
|
||||
points_delta=300,
|
||||
balance_after=300,
|
||||
ref_order_id=paid_order.id,
|
||||
)
|
||||
|
||||
anonymous_response = self.client.get("/records/recharge")
|
||||
self.assertEqual(anonymous_response.status_code, 302)
|
||||
self.assertTrue(anonymous_response["Location"].startswith("/login?next="))
|
||||
|
||||
self.client.force_login(user)
|
||||
response = self.client.get("/records/recharge")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context["recharge_total_amount"], Decimal("30.00"))
|
||||
self.assertEqual(response.context["recharge_points_total"], 300)
|
||||
self.assertEqual(response.context["paid_recharge_count"], 1)
|
||||
self.assertContains(response, "R-OWN-RECHARGE-503")
|
||||
self.assertContains(response, "R-OWN-PENDING-503")
|
||||
self.assertNotContains(response, "R-OTHER-RECHARGE-503")
|
||||
|
||||
def test_usage_records_require_login_and_only_show_current_user_usage(self):
|
||||
user = self.create_verified_user()
|
||||
other_user = self.create_verified_user()
|
||||
api_key, _raw_key = ApiKey.create_for_user(user, name="desktop")
|
||||
other_key, _other_raw_key = ApiKey.create_for_user(other_user, name="other")
|
||||
call = self.create_call_record(
|
||||
user,
|
||||
api_key,
|
||||
alias="title-standard",
|
||||
points_cost=40,
|
||||
status=CallRecord.Status.FAILED,
|
||||
)
|
||||
other_call = self.create_call_record(
|
||||
other_user,
|
||||
other_key,
|
||||
alias="other-alias",
|
||||
points_cost=90,
|
||||
)
|
||||
PointsLedger.objects.create(
|
||||
user=user,
|
||||
change_type=PointsLedger.ChangeType.CONSUME,
|
||||
points_delta=-40,
|
||||
balance_after=60,
|
||||
ref_call=call,
|
||||
)
|
||||
PointsLedger.objects.create(
|
||||
user=user,
|
||||
change_type=PointsLedger.ChangeType.REFUND,
|
||||
points_delta=15,
|
||||
balance_after=75,
|
||||
ref_call=call,
|
||||
)
|
||||
PointsLedger.objects.create(
|
||||
user=other_user,
|
||||
change_type=PointsLedger.ChangeType.CONSUME,
|
||||
points_delta=-90,
|
||||
balance_after=10,
|
||||
ref_call=other_call,
|
||||
)
|
||||
|
||||
anonymous_response = self.client.get("/records/usage")
|
||||
self.assertEqual(anonymous_response.status_code, 302)
|
||||
self.assertTrue(anonymous_response["Location"].startswith("/login?next="))
|
||||
|
||||
self.client.force_login(user)
|
||||
response = self.client.get("/records/usage")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context["consumed_points_total"], 40)
|
||||
self.assertEqual(response.context["refunded_points_total"], 15)
|
||||
self.assertEqual(response.context["net_used_points"], 25)
|
||||
self.assertContains(response, "title-standard")
|
||||
self.assertContains(response, api_key.key_prefix)
|
||||
self.assertContains(response, "-40")
|
||||
self.assertContains(response, "15")
|
||||
self.assertNotContains(response, "other-alias")
|
||||
self.assertNotContains(response, other_key.key_prefix)
|
||||
|
||||
Reference in New Issue
Block a user