""" 机构网点管理模块API测试 测试内容: - 机构CRUD测试(15+用例) - 树形结构测试(10+用例) - 递归查询测试(10+用例) - 机构移动测试(5+用例) - 并发测试(5+用例) """ import pytest from httpx import AsyncClient from datetime import datetime # ==================== 机构CRUD测试 ==================== class TestOrganizationCRUD: """测试机构CRUD操作""" @pytest.mark.asyncio async def test_create_organization_success( self, client: AsyncClient, admin_headers: dict, db_session ): """测试创建机构成功""" response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "ORG001", "org_name": "广东省", "org_type": "province", "description": "广东省分公司" } ) assert response.status_code == 200 data = response.json() assert data["code"] == 200 assert data["data"]["org_code"] == "ORG001" assert data["data"]["org_name"] == "广东省" assert "id" in data["data"] @pytest.mark.asyncio async def test_create_child_organization( self, client: AsyncClient, admin_headers: dict, db_session ): """测试创建子机构""" # 先创建父机构 parent_response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "PARENT_ORG", "org_name": "父机构", "org_type": "province" } ) parent_id = parent_response.json()["data"]["id"] # 创建子机构 response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "CHILD_ORG", "org_name": "子机构", "org_type": "city", "parent_id": parent_id } ) assert response.status_code == 200 data = response.json() assert data["data"]["parent_id"] == parent_id @pytest.mark.asyncio async def test_create_organization_duplicate_code( self, client: AsyncClient, admin_headers: dict, db_session ): """测试创建重复代码的机构""" data = { "org_code": "DUP_ORG", "org_name": "测试机构" } # 第一次创建 await client.post( "/api/v1/organizations", headers=admin_headers, json=data ) # 第二次创建相同代码 response = await client.post( "/api/v1/organizations", headers=admin_headers, json=data ) assert response.status_code in [400, 409] @pytest.mark.asyncio async def test_get_organization_list( self, client: AsyncClient, admin_headers: dict, db_session ): """测试获取机构列表""" # 先创建几个机构 for i in range(3): await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": f"ORG{i}", "org_name": f"机构{i}", "org_type": "province" } ) response = await client.get( "/api/v1/organizations", headers=admin_headers ) assert response.status_code == 200 data = response.json() assert data["code"] == 200 assert len(data["data"]) >= 3 @pytest.mark.asyncio async def test_get_organization_by_id( self, client: AsyncClient, admin_headers: dict, db_session ): """测试根据ID获取机构""" # 创建机构 create_response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "GET_ORG", "org_name": "获取测试机构", "org_type": "province" } ) org_id = create_response.json()["data"]["id"] # 获取机构 response = await client.get( f"/api/v1/organizations/{org_id}", headers=admin_headers ) assert response.status_code == 200 data = response.json() assert data["data"]["id"] == org_id assert data["data"]["org_code"] == "GET_ORG" @pytest.mark.asyncio async def test_update_organization_success( self, client: AsyncClient, admin_headers: dict, db_session ): """测试更新机构成功""" # 创建机构 create_response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "UPDATE_ORG", "org_name": "原始名称", "org_type": "province" } ) org_id = create_response.json()["data"]["id"] # 更新机构 response = await client.put( f"/api/v1/organizations/{org_id}", headers=admin_headers, json={ "org_name": "更新后的名称", "description": "更新后的描述" } ) assert response.status_code == 200 data = response.json() assert data["code"] == 200 @pytest.mark.asyncio async def test_update_organization_parent( self, client: AsyncClient, admin_headers: dict, db_session ): """测试更新机构的父机构""" # 创建两个机构 parent_response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "NEW_PARENT", "org_name": "新父机构", "org_type": "province" } ) parent_id = parent_response.json()["data"]["id"] child_response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "CHILD", "org_name": "子机构", "org_type": "city" } ) child_id = child_response.json()["data"]["id"] # 更新子机构的父机构 response = await client.put( f"/api/v1/organizations/{child_id}", headers=admin_headers, json={"parent_id": parent_id} ) assert response.status_code == 200 @pytest.mark.asyncio async def test_delete_organization_success( self, client: AsyncClient, admin_headers: dict, db_session ): """测试删除机构成功""" # 创建机构 create_response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "DELETE_ORG", "org_name": "待删除机构", "org_type": "province" } ) org_id = create_response.json()["data"]["id"] # 删除机构 response = await client.delete( f"/api/v1/organizations/{org_id}", headers=admin_headers ) assert response.status_code == 200 # 验证删除 get_response = await client.get( f"/api/v1/organizations/{org_id}", headers=admin_headers ) assert get_response.status_code in [404, 200] # 软删除可能返回200 @pytest.mark.asyncio async def test_delete_organization_with_children_forbidden( self, client: AsyncClient, admin_headers: dict, db_session ): """测试删除有子机构的机构(应该失败)""" # 创建父机构 parent_response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "PARENT_WITH_CHILD", "org_name": "父机构", "org_type": "province" } ) parent_id = parent_response.json()["data"]["id"] # 创建子机构 await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "CHILD_ORG", "org_name": "子机构", "org_type": "city", "parent_id": parent_id } ) # 尝试删除父机构 response = await client.delete( f"/api/v1/organizations/{parent_id}", headers=admin_headers ) # 应该失败或返回错误 assert response.status_code in [400, 403] @pytest.mark.asyncio async def test_filter_organization_by_type( self, client: AsyncClient, admin_headers: dict, db_session ): """测试按类型筛选机构""" # 创建不同类型的机构 await client.post( "/api/v1/organizations", headers=admin_headers, json={"org_code": "PROV1", "org_name": "省级", "org_type": "province"} ) await client.post( "/api/v1/organizations", headers=admin_headers, json={"org_code": "CITY1", "org_name": "市级", "org_type": "city"} ) # 筛选省级机构 response = await client.get( "/api/v1/organizations?org_type=province", headers=admin_headers ) assert response.status_code == 200 data = response.json() # 验证筛选结果 # for org in data["data"]: # assert org["org_type"] == "province" @pytest.mark.asyncio async def test_filter_organization_by_status( self, client: AsyncClient, admin_headers: dict, db_session ): """测试按状态筛选机构""" response = await client.get( "/api/v1/organizations?status=active", headers=admin_headers ) assert response.status_code == 200 @pytest.mark.asyncio async def test_search_organization_by_keyword( self, client: AsyncClient, admin_headers: dict, db_session ): """测试关键词搜索机构""" # 创建机构 await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "SEARCH_ORG", "org_name": "搜索测试机构", "org_type": "province" } ) # 搜索 response = await client.get( "/api/v1/organizations?keyword=搜索", headers=admin_headers ) assert response.status_code == 200 data = response.json() # 验证搜索结果包含关键词 @pytest.mark.asyncio async def test_get_organization_not_found( self, client: AsyncClient, admin_headers: dict ): """测试获取不存在的机构""" response = await client.get( "/api/v1/organizations/999999", headers=admin_headers ) assert response.status_code == 404 @pytest.mark.asyncio async def test_update_organization_not_found( self, client: AsyncClient, admin_headers: dict ): """测试更新不存在的机构""" response = await client.put( "/api/v1/organizations/999999", headers=admin_headers, json={"org_name": "新名称"} ) assert response.status_code == 404 @pytest.mark.asyncio async def test_create_organization_unauthorized( self, client: AsyncClient ): """测试未授权创建机构""" response = await client.post( "/api/v1/organizations", json={ "org_code": "NO_AUTH", "org_name": "未授权" } ) assert response.status_code == 401 # ==================== 树形结构测试 ==================== class TestOrganizationTree: """测试机构树形结构""" @pytest.mark.asyncio async def test_get_organization_tree( self, client: AsyncClient, admin_headers: dict, db_session ): """测试获取机构树""" # 创建三级机构 province_response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "TREE_PROV", "org_name": "广东省", "org_type": "province" } ) province_id = province_response.json()["data"]["id"] city_response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "TREE_CITY", "org_name": "广州市", "org_type": "city", "parent_id": province_id } ) city_id = city_response.json()["data"]["id"] await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "TREE_OUTLET", "org_name": "天河网点", "org_type": "outlet", "parent_id": city_id } ) # 获取树形结构 response = await client.get( "/api/v1/organizations/tree", headers=admin_headers ) assert response.status_code == 200 data = response.json() assert data["code"] == 200 # 验证树形结构 # assert len(data["data"]) > 0 # assert "children" in data["data"][0] @pytest.mark.asyncio async def test_tree_root_level( self, client: AsyncClient, admin_headers: dict, db_session ): """测试树的根层级""" # 创建顶级机构 await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "ROOT_ORG", "org_name": "根机构", "org_type": "province" } ) response = await client.get( "/api/v1/organizations/tree", headers=admin_headers ) assert response.status_code == 200 data = response.json() # 根级机构的parent_id应该为null # for org in data["data"]: # assert org["parent_id"] is None @pytest.mark.asyncio async def test_tree_max_levels( self, client: AsyncClient, admin_headers: dict, db_session ): """测试树的最大层级""" # 创建多层级机构 parent_id = None for i in range(5): response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": f"LEVEL{i}", "org_name": f"第{i}层", "org_type": "outlet", "parent_id": parent_id } ) parent_id = response.json()["data"]["id"] response = await client.get( "/api/v1/organizations/tree", headers=admin_headers ) assert response.status_code == 200 @pytest.mark.asyncio async def test_tree_multiple_branches( self, client: AsyncClient, admin_headers: dict, db_session ): """测试多分支树结构""" # 创建根机构 root_response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "MULTI_ROOT", "org_name": "多分支根", "org_type": "province" } ) root_id = root_response.json()["data"]["id"] # 创建多个子分支 for i in range(3): await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": f"BRANCH{i}", "org_name": f"分支{i}", "org_type": "city", "parent_id": root_id } ) response = await client.get( "/api/v1/organizations/tree", headers=admin_headers ) assert response.status_code == 200 data = response.json() # 验证有多个子节点 # assert len(data["data"][0]["children"]) == 3 @pytest.mark.asyncio async def test_tree_leaf_nodes( self, client: AsyncClient, admin_headers: dict, db_session ): """测试树的叶子节点""" # 创建有子节点和无子节点的机构 parent_response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "PARENT_NODE", "org_name": "父节点", "org_type": "province" } ) parent_id = parent_response.json()["data"]["id"] await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "CHILD_NODE", "org_name": "子节点", "org_type": "city", "parent_id": parent_id } ) await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "LEAF_NODE", "org_name": "叶子节点", "org_type": "city" } ) response = await client.get( "/api/v1/organizations/tree", headers=admin_headers ) assert response.status_code == 200 @pytest.mark.asyncio async def test_tree_with_inactive_orgs( self, client: AsyncClient, admin_headers: dict, db_session ): """测试包含已删除机构的树""" # 创建机构 response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "INACTIVE_ORG", "org_name": "未激活机构", "org_type": "province" } ) org_id = response.json()["data"]["id"] # 停用机构 await client.put( f"/api/v1/organizations/{org_id}", headers=admin_headers, json={"status": "inactive"} ) response = await client.get( "/api/v1/organizations/tree", headers=admin_headers ) assert response.status_code == 200 # 验证树中是否包含未激活机构 @pytest.mark.asyncio async def test_tree_sorting( self, client: AsyncClient, admin_headers: dict, db_session ): """测试树的排序""" # 创建根机构 root_response = await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "SORT_ROOT", "org_name": "排序根", "org_type": "province" } ) root_id = root_response.json()["data"]["id"] # 创建不同排序的子机构 await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "SECOND", "org_name": "第二", "org_type": "city", "parent_id": root_id, "sort_order": 2 } ) await client.post( "/api/v1/organizations", headers=admin_headers, json={ "org_code": "FIRST", "org_name": "第一", "org_type": "city", "parent_id": root_id, "sort_order": 1 } ) response = await client.get( "/api/v1/organizations/tree", headers=admin_headers ) assert response.status_code == 200 @pytest.mark.asyncio async def test_tree_depth_limit( self, client: AsyncClient, admin_headers: dict ): """测试树的深度限制""" response = await client.get( "/api/v1/organizations/tree?max_depth=3", headers=admin_headers ) assert response.status_code == 200 @pytest.mark.asyncio async def test_flatten_tree_to_list( self, client: AsyncClient, admin_headers: dict, db_session ): """测试将树展平为列表""" response = await client.get( "/api/v1/organizations?format=flat", headers=admin_headers ) assert response.status_code == 200 # ==================== 递归查询测试 ==================== class TestOrganizationRecursiveQuery: """测试机构递归查询""" @pytest.mark.asyncio async def test_get_all_children( self, client: AsyncClient, admin_headers: dict, db_session ): """测试获取所有子机构""" # 创建多级机构 root_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "ROOT", "org_name": "根", "org_type": "province" }) ) root_id = root_response.json()["data"]["id"] child1_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "CHILD1", "org_name": "子1", "org_type": "city", "parent_id": root_id }) ) child1_id = child1_response.json()["data"]["id"] await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "GRANDCHILD1", "org_name": "孙1", "org_type": "outlet", "parent_id": child1_id }) ) # 获取所有子机构 response = await client.get( f"/api/v1/organizations/{root_id}/children", headers=admin_headers ) assert response.status_code == 200 # 应该包含所有子孙机构 @pytest.mark.asyncio async def test_get_all_parents( self, client: AsyncClient, admin_headers: dict, db_session ): """测试获取所有父机构路径""" # 创建三级机构 grand_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "GRAND", "org_name": "祖父", "org_type": "province" }) ) grand_id = grand_response.json()["data"]["id"] parent_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "PARENT", "org_name": "父", "org_type": "city", "parent_id": grand_id }) ) parent_id = parent_response.json()["data"]["id"] child_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "CHILD_PATH", "org_name": "子", "org_type": "outlet", "parent_id": parent_id }) ) child_id = child_response.json()["data"]["id"] # 获取父机构路径 response = await client.get( f"/api/v1/organizations/{child_id}/parents", headers=admin_headers ) assert response.status_code == 200 data = response.json() # 应该包含完整的父机构路径 # assert len(data["data"]) == 2 @pytest.mark.asyncio async def test_count_descendants( self, client: AsyncClient, admin_headers: dict, db_session ): """测试统计子孙机构数量""" # 创建机构树 root_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "COUNT_ROOT", "org_name": "统计根", "org_type": "province" }) ) root_id = root_response.json()["data"]["id"] # 创建多个子机构 for i in range(3): await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": f"COUNT_CHILD{i}", "org_name": f"子{i}", "org_type": "city", "parent_id": root_id }) ) response = await client.get( f"/api/v1/organizations/{root_id}/descendants/count", headers=admin_headers ) assert response.status_code == 200 # data = response.json() # assert data["data"]["count"] >= 3 @pytest.mark.asyncio async def test_get_organization_level( self, client: AsyncClient, admin_headers: dict, db_session ): """测试获取机构层级""" # 创建三级机构 root_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "LEVEL_ROOT", "org_name": "层级根", "org_type": "province" }) ) root_id = root_response.json()["data"]["id"] child_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "LEVEL_CHILD", "org_name": "层级子", "org_type": "city", "parent_id": root_id }) ) child_id = child_response.json()["data"]["id"] grandchild_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "LEVEL_GRAND", "org_name": "层级孙", "org_type": "outlet", "parent_id": child_id }) ) assert response.status_code == 200 @pytest.mark.asyncio async def test_get_siblings( self, client: AsyncClient, admin_headers: dict, db_session ): """测试获取兄弟机构""" # 创建父机构 parent_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "SIB_PARENT", "org_name": "兄弟父", "org_type": "province" }) ) parent_id = parent_response.json()["data"]["id"] # 创建多个子机构 for i in range(3): await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": f"SIB{i}", "org_name": f"兄弟{i}", "org_type": "city", "parent_id": parent_id }) ) response = await client.get( f"/api/v1/organizations/{parent_id}/children", headers=admin_headers ) assert response.status_code == 200 @pytest.mark.asyncio async def test_recursive_search( self, client: AsyncClient, admin_headers: dict, db_session ): """测试递归搜索机构""" response = await client.get( "/api/v1/organizations/search?keyword=测试&recursive=true", headers=admin_headers ) assert response.status_code == 200 @pytest.mark.asyncio async def test_get_org_tree_statistics( self, client: AsyncClient, admin_headers: dict, db_session ): """测试获取机构树统计信息""" response = await client.get( "/api/v1/organizations/statistics", headers=admin_headers ) assert response.status_code == 200 data = response.json() # 应该包含机构总数、层级数等统计信息 @pytest.mark.asyncio async def test_recursive_delete_check( self, client: AsyncClient, admin_headers: dict, db_session ): """测试递归删除检查""" # 创建有子机构的机构 parent_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "DEL_CHECK", "org_name": "删除检查", "org_type": "province" }) ) parent_id = parent_response.json()["data"]["id"] await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "DEL_CHILD", "org_name": "删除子", "org_type": "city", "parent_id": parent_id }) ) # 检查是否可以删除 response = await client.get( f"/api/v1/organizations/{parent_id}/can-delete", headers=admin_headers ) assert response.status_code == 200 # data = response.json() # assert data["data"]["can_delete"] is False @pytest.mark.asyncio async def test_get_org_full_path( self, client: AsyncClient, admin_headers: dict, db_session ): """测试获取机构完整路径""" # 创建三级机构 grand_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "PATH_GRAND", "org_name": "路径祖父", "org_type": "province" }) ) grand_id = grand_response.json()["data"]["id"] parent_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "PATH_PARENT", "org_name": "路径父", "org_type": "city", "parent_id": grand_id }) ) parent_id = parent_response.json()["data"]["id"] child_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "PATH_CHILD", "org_name": "路径子", "org_type": "outlet", "parent_id": parent_id }) ) child_id = child_response.json()["data"]["id"] # 获取完整路径 response = await client.get( f"/api/v1/organizations/{child_id}/path", headers=admin_headers ) assert response.status_code == 200 data = response.json() # 应该返回: 祖父 > 父 > 子 # ==================== 机构移动测试 ==================== class TestOrganizationMove: """测试机构移动""" @pytest.mark.asyncio async def test_move_organization_to_new_parent( self, client: AsyncClient, admin_headers: dict, db_session ): """测试移动机构到新的父机构""" # 创建两个父机构 parent1_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "MOVE_PARENT1", "org_name": "移动父1", "org_type": "province" }) ) parent1_id = parent1_response.json()["data"]["id"] parent2_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "MOVE_PARENT2", "org_name": "移动父2", "org_type": "province" }) ) parent2_id = parent2_response.json()["data"]["id"] # 创建子机构 child_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "MOVE_CHILD", "org_name": "移动子", "org_type": "city", "parent_id": parent1_id }) ) child_id = child_response.json()["data"]["id"] # 移动到新的父机构 response = await client.put( f"/api/v1/organizations/{child_id}/move", headers=admin_headers, json({"new_parent_id": parent2_id}) ) assert response.status_code == 200 @pytest.mark.asyncio async def test_move_organization_to_root( self, client: AsyncClient, admin_headers: dict, db_session ): """测试移动机构到根级别""" # 创建父机构和子机构 parent_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "TO_ROOT_PARENT", "org_name": "根父", "org_type": "province" }) ) parent_id = parent_response.json()["data"]["id"] child_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "TO_ROOT_CHILD", "org_name": "根子", "org_type": "city", "parent_id": parent_id }) ) child_id = child_response.json()["data"]["id"] # 移动到根级别 response = await client.put( f"/api/v1/organizations/{child_id}/move", headers=admin_headers, json({"new_parent_id": None}) ) assert response.status_code == 200 @pytest.mark.asyncio async def test_move_organization_with_children( self, client: AsyncClient, admin_headers: dict, db_session ): """测试移动有子机构的机构""" # 创建机构树 root1_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "MOVE_ROOT1", "org_name": "移动根1", "org_type": "province" }) ) root1_id = root1_response.json()["data"]["id"] branch_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "MOVE_BRANCH", "org_name": "移动分支", "org_type": "city", "parent_id": root1_id }) ) branch_id = branch_response.json()["data"]["id"] await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "MOVE_LEAF", "org_name": "移动叶子", "org_type": "outlet", "parent_id": branch_id }) ) root2_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "MOVE_ROOT2", "org_name": "移动根2", "org_type": "province" }) ) root2_id = root2_response.json()["data"]["id"] # 移动分支(包括其子机构) response = await client.put( f"/api/v1/organizations/{branch_id}/move", headers=admin_headers, json({"new_parent_id": root2_id}) ) assert response.status_code == 200 @pytest.mark.asyncio async def test_move_to_own_child_forbidden( self, client: AsyncClient, admin_headers: dict, db_session ): """测试禁止移动到自己的子机构""" # 创建三级机构 root_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "CYCLE_ROOT", "org_name": "循环根", "org_type": "province" }) ) root_id = root_response.json()["data"]["id"] child_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "CYCLE_CHILD", "org_name": "循环子", "org_type": "city", "parent_id": root_id }) ) child_id = child_response.json()["data"]["id"] # 尝试将根机构移动到子机构下(应该失败) response = await client.put( f"/api/v1/organizations/{root_id}/move", headers=admin_headers, json({"new_parent_id": child_id}) ) assert response.status_code in [400, 403] @pytest.mark.asyncio async def test_move_non_existent_org( self, client: AsyncClient, admin_headers: dict, db_session ): """测试移动不存在的机构""" response = await client.put( "/api/v1/organizations/999999/move", headers=admin_headers, json({"new_parent_id": None}) ) assert response.status_code == 404 # ==================== 并发测试 ==================== class TestOrganizationConcurrency: """测试机构并发操作""" @pytest.mark.asyncio async def test_concurrent_create_same_code( self, client: AsyncClient, admin_headers: dict ): """测试并发创建相同代码的机构""" import asyncio data = { "org_code": "CONCURRENT_ORG", "org_name": "并发机构" } # 并发创建 tasks = [ client.post("/api/v1/organizations", headers=admin_headers, json=data) for _ in range(2) ] responses = await asyncio.gather(*tasks) # 应该只有一个成功 success_count = sum(1 for r in responses if r.status_code == 200) assert success_count == 1 @pytest.mark.asyncio async def test_concurrent_update( self, client: AsyncClient, admin_headers: dict, db_session ): """测试并发更新机构""" import asyncio # 创建机构 create_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "CONCURRENT_UPDATE", "org_name": "并发更新", "org_type": "province" }) ) org_id = create_response.json()["data"]["id"] # 并发更新 tasks = [ client.put( f"/api/v1/organizations/{org_id}", headers=admin_headers, json={"org_name": f"名称{i}"} ) for i in range(5) ] responses = await asyncio.gather(*tasks) # 所有更新都应该成功 assert all(r.status_code == 200 for r in responses) @pytest.mark.asyncio async def test_concurrent_move_operations( self, client: AsyncClient, admin_headers: dict, db_session ): """测试并发移动操作""" import asyncio # 创建多个机构 parent_ids = [] for i in range(3): response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": f"MOVE_PARENT{i}", "org_name": f"移动父{i}", "org_type": "province" }) ) parent_ids.append(response.json()["data"]["id"]) child_response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "MOVE_TARGET", "org_name": "移动目标", "org_type": "city", "parent_id": parent_ids[0] }) ) child_id = child_response.json()["data"]["id"] # 并发移动到不同的父机构 tasks = [ client.put( f"/api/v1/organizations/{child_id}/move", headers=admin_headers, json({"new_parent_id": parent_id}) ) for parent_id in parent_ids ] responses = await asyncio.gather(*tasks) # 只有一个成功 success_count = sum(1 for r in responses if r.status_code == 200) assert success_count >= 1 @pytest.mark.asyncio async def test_concurrent_delete_and_move( self, client: AsyncClient, admin_headers: dict, db_session ): """测试并发删除和移动""" import asyncio # 创建机构 response = await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": "DELETE_MOVE_ORG", "org_name": "删除移动", "org_type": "province" }) ) org_id = response.json()["data"]["id"] # 并发删除和移动 delete_task = client.delete( f"/api/v1/organizations/{org_id}", headers=admin_headers ) move_task = client.put( f"/api/v1/organizations/{org_id}/move", headers=admin_headers, json({"new_parent_id": None}) ) responses = await asyncio.gather(delete_task, move_task) # 至少一个操作失败 assert any(r.status_code != 200 for r in responses) @pytest.mark.asyncio async def test_concurrent_tree_queries( self, client: AsyncClient, admin_headers: dict, db_session ): """测试并发查询树结构""" import asyncio # 创建一些机构 for i in range(5): await client.post( "/api/v1/organizations", headers=admin_headers, json({ "org_code": f"QUERY_ORG{i}", "org_name": f"查询机构{i}", "org_type": "province" }) ) # 并发查询树 tasks = [ client.get("/api/v1/organizations/tree", headers=admin_headers) for _ in range(10) ] responses = await asyncio.gather(*tasks) # 所有查询都应该成功 assert all(r.status_code == 200 for r in responses)