Files
ai-ticket-platform/backend/apps/accounts/tests/test_profile_api.py
2026-07-16 17:15:20 +08:00

201 lines
6.4 KiB
Python

from datetime import timedelta
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.core.management import call_command
from django.test import Client
from django.utils import timezone
from apps.audit.models import OperationAuditLog
from apps.tickets.models import Ticket, TicketEvent
def test_profile_requires_login(client, db):
assert client.patch(
"/api/accounts/auth/me",
data={"display_name": "未登录"},
content_type="application/json",
).status_code == 401
assert client.get("/api/accounts/auth/me/stats").status_code == 401
def test_user_updates_profile_and_changes_password(client, db):
user = get_user_model().objects.create_user(
username="profile-user",
password="Old-Cedar-Bridge-42",
email="old@example.com",
)
client.force_login(user)
other_session = Client()
other_session.force_login(user)
updated = client.patch(
"/api/accounts/auth/me",
data={"display_name": "个人中心用户", "email": "new@example.com"},
content_type="application/json",
)
assert updated.status_code == 200
assert updated.json()["display_name"] == "个人中心用户"
assert updated.json()["email"] == "new@example.com"
assert updated.json()["date_joined"]
wrong_password = client.post(
"/api/accounts/auth/change-password",
data={"current_password": "wrong", "new_password": "New-Harbor-Light-84"},
content_type="application/json",
)
assert wrong_password.status_code == 400
changed = client.post(
"/api/accounts/auth/change-password",
data={
"current_password": "Old-Cedar-Bridge-42",
"new_password": "New-Harbor-Light-84",
},
content_type="application/json",
)
assert changed.status_code == 200
assert client.get("/api/accounts/auth/me").status_code == 200
assert other_session.get("/api/accounts/auth/me").status_code == 401
user.refresh_from_db()
assert user.check_password("New-Harbor-Light-84")
assert OperationAuditLog.objects.filter(
user=user,
action="change_password",
request_snapshot={"fields": ["password"]},
).exists()
def test_change_password_rejects_weak_password(client, db):
user = get_user_model().objects.create_user(
username="password-user",
password="Old-Cedar-Bridge-42",
)
client.force_login(user)
response = client.post(
"/api/accounts/auth/change-password",
data={"current_password": "Old-Cedar-Bridge-42", "new_password": "12345678"},
content_type="application/json",
)
assert response.status_code == 400
user.refresh_from_db()
assert user.check_password("Old-Cedar-Bridge-42")
def test_profile_stats_use_current_user_ticket_data(client, db):
user = get_user_model().objects.create_user(
username="stats-user",
password="Stats-Harbor-Light-42",
)
other = get_user_model().objects.create_user(
username="other-user",
password="Other-Harbor-Light-42",
)
client.force_login(user)
active = Ticket.objects.create(
ticket_no="TK-PROFILE-001",
title="待处理工单",
description="待处理",
assignee=user,
created_by=user,
)
resolved = Ticket.objects.create(
ticket_no="TK-PROFILE-002",
title="已完成工单",
description="已完成",
status="resolved",
assignee=user,
created_by=other,
resolved_at=timezone.now() - timedelta(days=2),
)
Ticket.objects.create(
ticket_no="TK-PROFILE-003",
title="其他人的工单",
description="不应计入",
assignee=other,
created_by=other,
)
TicketEvent.objects.create(
ticket=active,
event_type="updated",
operator=user,
)
response = client.get("/api/accounts/auth/me/stats")
assert response.status_code == 200
payload = response.json()
assert payload["created_total"] == 1
assert payload["active_assigned"] == 1
assert payload["completed_30d"] == 1
assert payload["completion_rate"] == 50.0
assert payload["recent_activity"][0]["ticket_id"] == active.id
assert resolved.id != active.id
def test_admin_user_security_guards_and_auditing(client, db):
call_command("bootstrap_governance")
admin = get_user_model().objects.get(username="admin")
target = get_user_model().objects.create_user(
username="managed-user",
password="Managed-Harbor-42",
)
client.force_login(admin)
self_deactivate = client.patch(
f"/api/accounts/users/{admin.id}",
data={"is_active": False},
content_type="application/json",
)
assert self_deactivate.status_code == 400
self_reset = client.post(
f"/api/accounts/users/{admin.id}/reset-password",
data={"password": "Admin-New-Harbor-42"},
content_type="application/json",
)
assert self_reset.status_code == 400
updated = client.patch(
f"/api/accounts/users/{target.id}",
data={"display_name": "已编辑用户", "email": "managed@example.com"},
content_type="application/json",
)
assert updated.status_code == 200
reset = client.post(
f"/api/accounts/users/{target.id}/reset-password",
data={"password": "Reset-Harbor-Light-84"},
content_type="application/json",
)
assert reset.status_code == 200
assert OperationAuditLog.objects.filter(
action="update_user",
target_id=str(target.id),
).exists()
password_audit = OperationAuditLog.objects.get(
action="reset_password",
target_id=str(target.id),
)
assert "Reset-Harbor-Light-84" not in str(password_audit.request_snapshot)
def test_non_superuser_cannot_manage_superuser(client, db):
call_command("bootstrap_governance")
superuser = get_user_model().objects.get(username="admin")
manager = get_user_model().objects.create_user(
username="user-manager",
password="Manager-Harbor-Light-42",
)
manager.user_permissions.add(Permission.objects.get(codename="system.user.manage"))
client.force_login(manager)
response = client.patch(
f"/api/accounts/users/{superuser.id}",
data={"display_name": "不应修改"},
content_type="application/json",
)
assert response.status_code == 403