"""Tests for Audit Rule seed endpoint and value field (Phase 4.2+).""" import pytest from tests.conftest import auth_header pytestmark = pytest.mark.skip(reason="Skip: awaiting Go backend alignment") class TestSeedAuditRules: @pytest.mark.asyncio async def test_seed_mysql_rules(self, client, admin_token): """MySQL 种子规则批量创建""" resp = await client.post( "/api/v1/audit-rules/seed/mysql", headers=auth_header(admin_token), ) assert resp.status_code == 200 data = resp.json() assert len(data) == 15 # MySQL 有 15 条预设规则 names = {r["name"] for r in data} assert "DDLMultiToCommit" in names assert "DDLMaxKeyParts" in names assert "DMLMaxInsertRows" in names assert "DMLWhere" in names # 验证 value 字段 key_parts = next(r for r in data if r["name"] == "DDLMaxKeyParts") assert key_parts["value"] == "8" max_key = next(r for r in data if r["name"] == "DDLMaxKey") assert max_key["value"] == "12" insert_rows = next(r for r in data if r["name"] == "DMLMaxInsertRows") assert insert_rows["value"] == "500" table_name = next(r for r in data if r["name"] == "MaxTableNameLen") assert table_name["value"] == "45" @pytest.mark.asyncio async def test_seed_postgres_rules(self, client, admin_token): """PostgreSQL 种子规则批量创建""" resp = await client.post( "/api/v1/audit-rules/seed/postgres", headers=auth_header(admin_token), ) assert resp.status_code == 200 data = resp.json() assert len(data) == 11 names = {r["name"] for r in data} assert "PGDDLMultiToCommit" in names assert "PGDDLMaxKeyParts" in names assert "PGMaxTableNameLen" in names # 验证 value 字段 key_parts = next(r for r in data if r["name"] == "PGDDLMaxKeyParts") assert key_parts["value"] == "8" table_len = next(r for r in data if r["name"] == "PGMaxTableNameLen") assert table_len["value"] == "63" insert_rows = next(r for r in data if r["name"] == "PGDMLMaxInsertRows") assert insert_rows["value"] == "500" @pytest.mark.asyncio async def test_seed_redis_rules(self, client, admin_token): """Redis 种子规则批量创建""" resp = await client.post( "/api/v1/audit-rules/seed/redis", headers=auth_header(admin_token), ) assert resp.status_code == 200 data = resp.json() assert len(data) == 4 names = {r["name"] for r in data} assert "RedisAllowFlushDB" in names assert "RedisMaxKeyLen" in names assert "RedisRequireTTL" in names assert "RedisDefaultTTL" in names # 验证 value 字段 key_len = next(r for r in data if r["name"] == "RedisMaxKeyLen") assert key_len["value"] == "256" ttl = next(r for r in data if r["name"] == "RedisDefaultTTL") assert ttl["value"] == "3600" @pytest.mark.asyncio async def test_seed_unsupported_db_type(self, client, admin_token): """不支持的数据库类型应返回 400""" resp = await client.post( "/api/v1/audit-rules/seed/oracle", headers=auth_header(admin_token), ) assert resp.status_code == 400 assert "不支持的数据库类型" in resp.json()["detail"] @pytest.mark.asyncio async def test_seed_idempotent(self, client, admin_token): """重复调用 seed 应幂等,不创建重复规则""" resp1 = await client.post( "/api/v1/audit-rules/seed/mysql", headers=auth_header(admin_token), ) assert resp1.status_code == 200 count1 = len(resp1.json()) resp2 = await client.post( "/api/v1/audit-rules/seed/mysql", headers=auth_header(admin_token), ) assert resp2.status_code == 200 count2 = len(resp2.json()) assert count2 == 0 # 第二次调用不再创建 @pytest.mark.asyncio async def test_seed_forbidden_for_dba(self, client, dba_token): """DBA 无权调用 seed 接口""" resp = await client.post( "/api/v1/audit-rules/seed/mysql", headers=auth_header(dba_token), ) assert resp.status_code == 403 @pytest.mark.asyncio async def test_seed_forbidden_for_developer(self, client, developer_token): """Developer 无权调用 seed 接口""" resp = await client.post( "/api/v1/audit-rules/seed/mysql", headers=auth_header(developer_token), ) assert resp.status_code == 403 class TestValueFieldAndDDLType: @pytest.mark.asyncio async def test_create_rule_with_value(self, client, admin_token): """创建带 value 字段的规则""" resp = await client.post( "/api/v1/audit-rules", json={ "name": "test_with_value", "description": "测试value字段", "rule_type": "DDL", "severity": "warning", "db_type": "mysql", "value": "100", "message": "测试规则带value", }, headers=auth_header(admin_token), ) assert resp.status_code == 201 data = resp.json() assert data["value"] == "100" assert data["rule_type"] == "DDL" @pytest.mark.asyncio async def test_create_rule_with_dml_type(self, client, admin_token): """创建 DML 类型规则""" resp = await client.post( "/api/v1/audit-rules", json={ "name": "test_dml_rule", "rule_type": "DML", "severity": "error", "db_type": "mysql", "message": "DML测试规则", }, headers=auth_header(admin_token), ) assert resp.status_code == 201 assert resp.json()["rule_type"] == "DML" @pytest.mark.asyncio async def test_create_rule_null_value(self, client, admin_token): """value 可以为 null""" resp = await client.post( "/api/v1/audit-rules", json={ "name": "test_null_value", "rule_type": "DDL", "severity": "info", "message": "null value test", }, headers=auth_header(admin_token), ) assert resp.status_code == 201 assert resp.json()["value"] is None @pytest.mark.asyncio async def test_update_rule_value(self, client, admin_token): """更新 value 字段""" create = await client.post( "/api/v1/audit-rules", json={ "name": "update_value_test", "rule_type": "DDL", "severity": "warning", "value": "50", "message": "test", }, headers=auth_header(admin_token), ) rule_id = create.json()["id"] resp = await client.put( f"/api/v1/audit-rules/{rule_id}", json={"value": "200"}, headers=auth_header(admin_token), ) assert resp.status_code == 200 assert resp.json()["value"] == "200" @pytest.mark.asyncio async def test_list_rules_with_ddl_filter(self, client, admin_token): """按 DDL 类型筛选规则""" # 先 seed MySQL await client.post( "/api/v1/audit-rules/seed/mysql", headers=auth_header(admin_token), ) resp = await client.get( "/api/v1/audit-rules?rule_type=DDL", headers=auth_header(admin_token), ) assert resp.status_code == 200 data = resp.json() assert len(data) > 0 assert all(r["rule_type"] == "DDL" for r in data) @pytest.mark.asyncio async def test_response_includes_value_field(self, client, admin_token): """响应中包含 value 字段""" create = await client.post( "/api/v1/audit-rules", json={ "name": "response_value_test", "rule_type": "DDL", "severity": "warning", "value": "42", "message": "test", }, headers=auth_header(admin_token), ) rule_id = create.json()["id"] resp = await client.get( f"/api/v1/audit-rules/{rule_id}", headers=auth_header(admin_token), ) assert resp.status_code == 200 assert "value" in resp.json() assert resp.json()["value"] == "42"