wendajia/backend/tests/test_refund_payment_accounts.py

194 lines
9.2 KiB
Python

import pytest
from fastapi.testclient import TestClient
from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session
from sqlalchemy.pool import StaticPool
from app import main
from app.database import Base, get_db
from app.models import AdminUser, AlipayRefundOrder, RefundPaymentAccount, RefundRecord
ENDPOINT = "/api/admin/refund-payment-accounts"
PAYLOAD = dict(name="公司付款账户", alipay_account="payer@example.com", balance="5000.01", single_limit="500.50", is_active=True)
@pytest.fixture
def setup():
engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool)
Base.metadata.create_all(engine)
with Session(engine) as db:
admin = AdminUser(username="payer-admin", display_name="管理员", password_hash="unused", role_code="super_admin")
db.add(admin)
db.commit()
main.app.dependency_overrides[get_db] = lambda: db
main.app.dependency_overrides[main.current_user] = lambda: admin
try:
with TestClient(main.app, base_url="https://testserver") as client:
yield db, client, admin
finally:
main.app.dependency_overrides.clear()
engine.dispose()
def test_create_edit_and_persist_cents(setup):
db, client, _ = setup
response = client.post(ENDPOINT, json={**PAYLOAD, "name": " 公司付款账户 "})
assert response.status_code == 201, response.text
record = response.json()["data"]
assert record["name"] == "公司付款账户"
assert record["balance_fen"] == 500001
assert record["single_limit_fen"] == 50050
response = client.patch(f"{ENDPOINT}/{record['id']}", json={**PAYLOAD, "balance": "0.00", "is_active": False})
assert response.status_code == 200, response.text
db.expire_all()
saved = db.get(RefundPaymentAccount, record["id"])
assert saved.balance_fen == 500001
assert not saved.is_active
assert saved.updated_at is not None
assert client.get(ENDPOINT).json()["data"]["items"][0]["is_active"] is False
def test_append_and_adjust_funds_with_ledger(setup):
db, client, _ = setup
account = client.post(ENDPOINT, json=PAYLOAD).json()["data"]
response = client.post(f"{ENDPOINT}/{account['id']}/funds", json={
"adjustment_type": "追加资金", "amount": "1000.00", "note": "追加预算",
})
assert response.status_code == 200, response.text
assert response.json()["data"]["balance_fen"] == 600001
response = client.post(f"{ENDPOINT}/{account['id']}/funds", json={
"adjustment_type": "资金调整", "amount": "-10.01", "note": "修正录入",
})
assert response.status_code == 200, response.text
assert response.json()["data"]["balance_fen"] == 599000
db.expire_all()
assert db.get(RefundPaymentAccount, account["id"]).balance_fen == 599000
labels = {item["type_label"] for item in client.get(f"{ENDPOINT}/{account['id']}/ledger").json()["data"]["items"]}
assert {"初始资金", "追加资金", "资金调整"}.issubset(labels)
def test_fund_adjustment_validation_and_committed_floor(setup):
db, client, admin = setup
account = client.post(ENDPOINT, json=PAYLOAD).json()["data"]
assert client.post(f"{ENDPOINT}/{account['id']}/funds", json={
"adjustment_type": "追加资金", "amount": "-1.00", "note": "",
}).status_code == 400
assert client.post(f"{ENDPOINT}/{account['id']}/funds", json={
"adjustment_type": "资金调整", "amount": "1.00", "note": "",
}).status_code == 400
assert client.post(f"{ENDPOINT}/{account['id']}/funds", json={
"adjustment_type": "资金调整", "amount": "0.00", "note": "无效调整",
}).status_code == 422
db.add(RefundRecord(
target_kind="answer_submission", target_id=991001, owner_id=admin.id, order_no="ANS-FLOOR-1",
payee_name="测试用户", payee_account="floor@example.com", amount_fen=400000,
payer_account_id=account["id"], payer_account=account["name"], operator_name=admin.display_name,
alipay_trade_no="TRADE-FLOOR-1",
))
db.commit()
response = client.post(f"{ENDPOINT}/{account['id']}/funds", json={
"adjustment_type": "资金调整", "amount": "-1000.02", "note": "过度调减",
})
assert response.status_code == 400
assert "不能低于" in response.json()["detail"]
def test_account_usage_summary_and_ledger(setup):
db, client, admin = setup
account = client.post(ENDPOINT, json=PAYLOAD).json()["data"]
db.add(RefundRecord(
target_kind="answer_submission", target_id=900001, owner_id=admin.id,
shop_name="测试店铺", order_no="ANS-USAGE-1", payee_name="测试用户",
payee_account="user@example.com", amount_fen=12345,
payer_account_id=account["id"], payer_account=account["name"],
operator_name=admin.display_name, alipay_trade_no="TRADE-USAGE-1",
))
db.add(AlipayRefundOrder(
target_kind="answer_submission", target_id=900002, out_biz_no="RF-USAGE-PENDING",
amount_fen=5000, order_no="ANS-USAGE-2", account="user2@example.com",
payee_name="测试用户2", payer_account_id=account["id"], payer_account=account["name"],
created_by_id=admin.id, operator_name=admin.display_name, status="pending",
))
db.commit()
item = client.get(ENDPOINT).json()["data"]["items"][0]
assert item["used_fen"] == 12345
assert item["processing_fen"] == 5000
assert item["available_fen"] == 482656
ledger = client.get(f"{ENDPOINT}/{account['id']}/ledger")
assert ledger.status_code == 200
ledger_data = ledger.json()["data"]
assert ledger_data["total"] == 3
assert ledger_data["page"] == 1
assert ledger_data["page_size"] == 10
types = {row["type"] for row in ledger_data["items"]}
assert {"adjustment", "refund", "processing"}.issubset(types)
first_page = client.get(f"{ENDPOINT}/{account['id']}/ledger", params={"page": 1, "page_size": 2}).json()["data"]
second_page = client.get(f"{ENDPOINT}/{account['id']}/ledger", params={"page": 2, "page_size": 2}).json()["data"]
assert first_page["total_pages"] == 2
assert len(first_page["items"]) == 2
assert len(second_page["items"]) == 1
def test_duplicate_and_failed_edit_preserve_accounts(setup):
db, client, _ = setup
first = client.post(ENDPOINT, json=PAYLOAD).json()["data"]
second = client.post(ENDPOINT, json={**PAYLOAD, "alipay_account": "second@example.com"}).json()["data"]
assert client.post(ENDPOINT, json=PAYLOAD).status_code == 409
assert client.patch(f"{ENDPOINT}/{second['id']}", json=PAYLOAD).status_code == 409
db.expire_all()
assert len(db.scalars(select(RefundPaymentAccount)).all()) == 2
assert db.get(RefundPaymentAccount, second["id"]).alipay_account == "second@example.com"
assert db.get(RefundPaymentAccount, first["id"]).balance_fen == 500001
def test_search_pagination_and_literal_wildcard(setup):
_, client, _ = setup
for index in range(3):
assert client.post(ENDPOINT, json={**PAYLOAD, "name": f"付款{index}", "alipay_account": f"payer{index}@example.com"}).status_code == 201
data = client.get(ENDPOINT, params={"page": 2, "page_size": 2}).json()["data"]
assert data["total"] == 3
assert len(data["items"]) == 1
data = client.get(ENDPOINT, params={"q": "payer1"}).json()["data"]
assert data["total"] == 1
assert data["items"][0]["name"] == "付款1"
assert client.get(ENDPOINT, params={"q": "%"}).json()["data"]["total"] == 0
@pytest.mark.parametrize("changes", [
{"name": " "}, {"alipay_account": " "}, {"balance": "-1"},
{"balance": "1.001"}, {"single_limit": "0"}, {"single_limit": "10000000"},
{"balance": "NaN"}, {"single_limit": "Infinity"},
])
def test_reject_invalid_account(setup, changes):
_, client, _ = setup
response = client.post(ENDPOINT, json={**PAYLOAD, **changes})
assert response.status_code == 422, response.text
def test_permissions_and_missing_account(setup):
db, client, admin = setup
assert client.patch(f"{ENDPOINT}/999", json=PAYLOAD).status_code == 404
assert client.post(f"{ENDPOINT}/999/funds", json={"adjustment_type": "追加资金", "amount": "1.00", "note": ""}).status_code == 404
admin.role_code = "operator"
db.commit()
assert client.get(ENDPOINT).status_code == 403
assert client.post(ENDPOINT, json=PAYLOAD).status_code == 403
assert client.patch(f"{ENDPOINT}/999", json=PAYLOAD).status_code == 403
assert client.post(f"{ENDPOINT}/999/funds", json={"adjustment_type": "追加资金", "amount": "1.00", "note": ""}).status_code == 403
assert client.delete(f"{ENDPOINT}/999").status_code == 403
def test_delete_account_and_preserve_other_accounts(setup):
db, client, _ = setup
first = client.post(ENDPOINT, json=PAYLOAD).json()["data"]
other = client.post(ENDPOINT, json={**PAYLOAD, "alipay_account": "other@example.com"}).json()["data"]
response = client.delete(f"{ENDPOINT}/{first['id']}")
assert response.status_code == 200, response.text
db.expire_all()
assert db.get(RefundPaymentAccount, first["id"]) is None
assert db.get(RefundPaymentAccount, other["id"]) is not None
assert client.get(ENDPOINT).json()["data"]["total"] == 1
assert client.delete(f"{ENDPOINT}/{first['id']}").status_code == 404