from rest_framework.test import APIClient from django.test import TestCase from apps.accounts.models import AdminAuditLog, Invitation, Team, TeamMember, User class AdminInvitationApiTests(TestCase): """Phase 2:平台超管邀请码发放管理(发开团队码 / 列跨团队码 / 撤销)+ 权限 + 审计。""" def setUp(self): self.admin = User.objects.create_user(username="padmin", password="x", is_platform_admin=True) self.normal = User.objects.create_user(username="normal", password="x") self.team = Team.objects.create(name="Normal Team", owner=self.normal) TeamMember.objects.create(team=self.team, user=self.normal, role=TeamMember.Role.OWNER) self.admin_client = APIClient() self.admin_client.force_authenticate(self.admin) self.normal_client = APIClient() self.normal_client.force_authenticate(self.normal) def test_list_requires_platform_admin(self): self.assertEqual(self.normal_client.get("/api/admin/invitations/").status_code, 403) self.assertEqual(self.admin_client.get("/api/admin/invitations/").status_code, 200) def test_create_create_team_code_and_audit(self): r = self.admin_client.post("/api/admin/invitations/", {"email": "x@y.z"}, format="json") self.assertEqual(r.status_code, 201) self.assertEqual(r.data["kind"], Invitation.Kind.CREATE_TEAM) self.assertTrue(r.data["code"].startswith("TEAM-")) self.assertIsNone(r.data["team"]) self.assertEqual(r.data["register_url"], f"/register?invite={r.data['code']}") self.assertTrue(AdminAuditLog.objects.filter(action="invite.issue_create_team").exists()) def test_create_requires_platform_admin(self): self.assertEqual(self.normal_client.post("/api/admin/invitations/", {}, format="json").status_code, 403) def test_list_is_cross_team(self): self.admin_client.post("/api/admin/invitations/", {}, format="json") Invitation.objects.create(kind=Invitation.Kind.JOIN_TEAM, team=self.team, role=TeamMember.Role.MEMBER) r = self.admin_client.get("/api/admin/invitations/") self.assertEqual(r.status_code, 200) self.assertGreaterEqual(r.data["count"], 2) kinds = {i["kind"] for i in r.data["results"]} self.assertIn(Invitation.Kind.CREATE_TEAM, kinds) self.assertIn(Invitation.Kind.JOIN_TEAM, kinds) def test_filter_by_kind(self): self.admin_client.post("/api/admin/invitations/", {}, format="json") Invitation.objects.create(kind=Invitation.Kind.JOIN_TEAM, team=self.team, role=TeamMember.Role.MEMBER) r = self.admin_client.get("/api/admin/invitations/?kind=create_team") self.assertTrue(all(i["kind"] == Invitation.Kind.CREATE_TEAM for i in r.data["results"])) def test_revoke_and_audit(self): created = self.admin_client.post("/api/admin/invitations/", {}, format="json") inv_id = created.data["id"] r = self.admin_client.post(f"/api/admin/invitations/{inv_id}/revoke/") self.assertEqual(r.status_code, 200) self.assertEqual(r.data["status"], Invitation.Status.REVOKED) self.assertTrue(AdminAuditLog.objects.filter(action="invite.revoke").exists()) def test_revoke_requires_platform_admin(self): created = self.admin_client.post("/api/admin/invitations/", {}, format="json") inv_id = created.data["id"] self.assertEqual(self.normal_client.post(f"/api/admin/invitations/{inv_id}/revoke/").status_code, 403) def test_issued_create_team_code_registers_new_team(self): created = self.admin_client.post("/api/admin/invitations/", {}, format="json") code = created.data["code"] reg = APIClient().post( "/api/auth/register/", {"username": "new-via-admin-code", "password": "strong-pass-1", "team_name": "Via Admin", "invite_code": code}, format="json", ) self.assertEqual(reg.status_code, 201) self.assertTrue(Team.objects.filter(name="Via Admin").exists()) # 码标 used self.assertEqual(Invitation.objects.get(code=code).status, Invitation.Status.USED) def test_revoked_code_cannot_register(self): created = self.admin_client.post("/api/admin/invitations/", {}, format="json") code = created.data["code"] self.admin_client.post(f"/api/admin/invitations/{created.data['id']}/revoke/") reg = APIClient().post( "/api/auth/register/", {"username": "blocked-user", "password": "strong-pass-1", "team_name": "Blocked", "invite_code": code}, format="json", ) self.assertEqual(reg.status_code, 400)