from httpx import AsyncClient from tests.conftest import auth_header async def _create_ticket(client: AsyncClient, token: str, instance_id: int): return await client.post("/api/v1/tickets", headers=auth_header(token), json={ "title": "测试工单标题", "db_type": "mysql", "instance_id": instance_id, "target_database": "test_db", "sql_content": "SELECT * FROM users LIMIT 10;", "rollback_sql": "", "reason": "测试原因12345", "priority": "medium", }) class TestListTickets: async def test_developer_sees_own_tickets( self, client: AsyncClient, developer_user, developer_token, test_instance ): await _create_ticket(client, developer_token, test_instance.id) resp = await client.get("/api/v1/tickets", headers=auth_header(developer_token)) assert resp.status_code == 200 data = resp.json() for item in data["items"]: assert item["applicant_id"] == developer_user.id async def test_admin_sees_all_tickets( self, client: AsyncClient, admin_token, developer_user, developer_token, test_instance, admin_user, ): await _create_ticket(client, developer_token, test_instance.id) resp = await client.get("/api/v1/tickets", headers=auth_header(admin_token)) assert resp.status_code == 200 data = resp.json() assert data["total"] >= 1 class TestCreateTicket: async def test_create_ticket_success( self, client: AsyncClient, developer_token, test_instance ): resp = await _create_ticket(client, developer_token, test_instance.id) assert resp.status_code == 201 data = resp.json() assert data["status"] == "pending" assert data["title"] == "测试工单标题" async def test_create_ticket_instance_not_found(self, client: AsyncClient, developer_token): resp = await _create_ticket(client, developer_token, instance_id=9999) assert resp.status_code == 400 class TestGetTicket: async def test_get_ticket_detail( self, client: AsyncClient, developer_token, test_instance ): create_resp = await _create_ticket(client, developer_token, test_instance.id) ticket_id = create_resp.json()["id"] resp = await client.get(f"/api/v1/tickets/{ticket_id}", headers=auth_header(developer_token)) assert resp.status_code == 200 assert resp.json()["id"] == ticket_id async def test_developer_cannot_see_others_ticket( self, client: AsyncClient, admin_token, admin_user, developer_token, developer_user, test_instance, ): create_resp = await _create_ticket(client, developer_token, test_instance.id) ticket_id = create_resp.json()["id"] # admin creates a separate developer to test access resp = await client.get(f"/api/v1/tickets/{ticket_id}", headers=auth_header(admin_token)) assert resp.status_code == 200 # admin can see all class TestReviewTicket: async def test_approve_ticket( self, client: AsyncClient, developer_token, dba_token, dba_user, test_instance, ): create_resp = await _create_ticket(client, developer_token, test_instance.id) ticket_id = create_resp.json()["id"] resp = await client.post( f"/api/v1/tickets/{ticket_id}/review", headers=auth_header(dba_token), json={"action": "approve", "comment": "通过"}, ) assert resp.status_code == 200 assert resp.json()["status"] == "approved" async def test_reject_ticket( self, client: AsyncClient, developer_token, dba_token, test_instance, ): create_resp = await _create_ticket(client, developer_token, test_instance.id) ticket_id = create_resp.json()["id"] resp = await client.post( f"/api/v1/tickets/{ticket_id}/review", headers=auth_header(dba_token), json={"action": "reject", "comment": "拒绝"}, ) assert resp.status_code == 200 assert resp.json()["status"] == "rejected" async def test_non_dba_cannot_review( self, client: AsyncClient, developer_token, test_instance, ): create_resp = await _create_ticket(client, developer_token, test_instance.id) ticket_id = create_resp.json()["id"] resp = await client.post( f"/api/v1/tickets/{ticket_id}/review", headers=auth_header(developer_token), json={"action": "approve", "comment": "非法审批"}, ) assert resp.status_code == 403 async def test_review_already_executed_ticket( self, client: AsyncClient, developer_token, dba_token, test_instance, ): create_resp = await _create_ticket(client, developer_token, test_instance.id) ticket_id = create_resp.json()["id"] # Approve first await client.post( f"/api/v1/tickets/{ticket_id}/review", headers=auth_header(dba_token), json={"action": "approve", "comment": "通过"}, ) # The status is "approved" which is not in ("pending", "reviewing") so re-review should fail resp = await client.post( f"/api/v1/tickets/{ticket_id}/review", headers=auth_header(dba_token), json={"action": "approve", "comment": "再次审批"}, ) assert resp.status_code == 400 class TestExecuteTicket: async def test_execute_unapproved_ticket( self, client: AsyncClient, developer_token, dba_token, test_instance, ): create_resp = await _create_ticket(client, developer_token, test_instance.id) ticket_id = create_resp.json()["id"] resp = await client.post( f"/api/v1/tickets/{ticket_id}/execute", headers=auth_header(dba_token), ) assert resp.status_code == 400 class TestTicketStatistics: async def test_ticket_statistics( self, client: AsyncClient, developer_token, test_instance, ): await _create_ticket(client, developer_token, test_instance.id) resp = await client.get("/api/v1/tickets/statistics", headers=auth_header(developer_token)) assert resp.status_code == 200 data = resp.json() assert "total" in data assert "pending" in data class TestPagination: async def test_pagination( self, client: AsyncClient, developer_token, test_instance, ): for i in range(3): await _create_ticket(client, developer_token, test_instance.id) resp = await client.get( "/api/v1/tickets?page=1&page_size=2", headers=auth_header(developer_token), ) assert resp.status_code == 200 data = resp.json() assert data["page"] == 1 assert data["page_size"] == 2 assert len(data["items"]) <= 2