""" HTTP 响应工具测试 """ import pytest from apiflask import APIBlueprint, Schema from apiflask.fields import Integer, String from iti.applications import create_app from iti.applications.common.utils import ( success, fail, page, pagination_builder, PaginationSchema, ) @pytest.fixture def app(): """创建测试应用""" test_bp = APIBlueprint("test_http_utils", __name__) @test_bp.get("/test/success") def test_success(): return success({"message": "ok"}, message="操作成功") @test_bp.get("/test/fail") def test_fail(): return fail("这是一个错误示例", code=400) @test_bp.get("/test/page") def test_page(): return page( [{"id": 1}, {"id": 2}, {"id": 3}], pagination_builder(None, page=1, size=10, total=30), message="分页测试成功", ) class IndexSchema(Schema): id = Integer() name = String() @test_bp.get("/") @test_bp.output(IndexSchema) def test_index(): return success({"id": 1, "name": "test"}) class TestModule: name = "test_http_utils" def register_routes(self, app): app.register_blueprint(test_bp) return create_app("test", modules=[TestModule()]) @pytest.fixture def client(app): """创建测试客户端""" return app.test_client() class TestSuccessFunction: """测试 success() 函数""" def test_success_default(self): """测试默认参数""" result = success() assert result["code"] == 200 assert result["message"] == "成功" assert result["data"] is None def test_success_with_data(self): """测试带数据""" data = {"id": 1, "name": "test"} result = success(data) assert result["code"] == 200 assert result["message"] == "成功" assert result["data"] == data def test_success_with_custom_message(self): """测试自定义消息""" result = success({"id": 1}, message="查询成功") assert result["code"] == 200 assert result["message"] == "查询成功" assert result["data"]["id"] == 1 def test_success_with_custom_code(self): """测试自定义状态码""" result = success({"id": 1}, message="创建成功", code=201) assert result["code"] == 201 assert result["message"] == "创建成功" def test_success_with_list(self): """测试列表数据""" data = [{"id": 1}, {"id": 2}] result = success(data) assert result["code"] == 200 assert isinstance(result["data"], list) assert len(result["data"]) == 2 class TestFailFunction: """测试 fail() 函数""" def test_fail_default(self): """测试默认参数""" result = fail() assert result["code"] == 500 assert result["message"] == "操作失败" assert result["data"] is None def test_fail_with_custom_message(self): """测试自定义错误消息""" result = fail("用户不存在") assert result["code"] == 500 assert result["message"] == "用户不存在" assert result["data"] is None def test_fail_with_custom_code(self): """测试自定义错误码""" result = fail("参数错误", code=400) assert result["code"] == 400 assert result["message"] == "参数错误" def test_fail_with_data(self): """测试带额外数据""" errors = {"username": ["必填项"], "email": ["格式错误"]} result = fail("验证失败", code=422, data=errors) assert result["code"] == 422 assert result["message"] == "验证失败" assert result["data"] == errors def test_fail_returns_dict(self): """测试返回字典而非元组""" result = fail("错误", code=404) # 确保返回的是字典,不是元组 assert isinstance(result, dict) assert "code" in result assert "message" in result assert "data" in result class TestPaginationBuilder: """测试 pagination_builder() 函数""" def test_manual_mode_basic(self): """测试手动模式基础功能""" result = pagination_builder(None, page=1, size=10, total=100) assert result["page"] == 1 assert result["size"] == 10 assert result["pages"] == 10 # ceil(100/10) assert result["total"] == 100 def test_manual_mode_auto_calculate_pages(self): """测试自动计算总页数""" result = pagination_builder(None, page=1, size=20, total=95) assert result["pages"] == 5 # ceil(95/20) = 5 def test_manual_mode_with_explicit_pages(self): """测试显式指定总页数""" result = pagination_builder(None, page=1, size=10, total=100, pages=8) assert result["pages"] == 8 def test_manual_mode_zero_total(self): """测试总数为 0""" result = pagination_builder(None, page=1, size=10, total=0) assert result["pages"] == 0 assert result["total"] == 0 def test_manual_mode_urls_are_none_outside_request(self): """测试在请求上下文外 URL 为 None""" result = pagination_builder(None, page=1, size=10, total=100) assert result["current"] is None assert result["next"] is None assert result["prev"] is None assert result["first"] is None assert result["last"] is None def test_auto_mode_with_mock_pagination(self): """测试自动模式(使用模拟的 Pagination 对象)""" # 创建模拟的 Pagination 对象 class MockPagination: page = 2 per_page = 10 pages = 10 total = 100 has_prev = True has_next = True prev_num = 1 next_num = 3 mock_pagination = MockPagination() result = pagination_builder(mock_pagination) assert result["page"] == 2 assert result["size"] == 10 # per_page → size assert result["pages"] == 10 assert result["total"] == 100 class TestPageFunction: """测试 page() 函数""" def test_page_with_list_and_dict(self): """测试列表 + 字典分页信息""" items = [{"id": 1}, {"id": 2}] pagination_info = pagination_builder(None, page=1, size=10, total=50) result = page(items, pagination_info) assert result["code"] == 200 assert result["message"] == "成功" assert result["data"]["items"] == items assert result["data"]["page"]["page"] == 1 assert result["data"]["page"]["total"] == 50 def test_page_with_custom_message(self): """测试自定义消息""" items = [{"id": 1}] pagination_info = pagination_builder(None, page=1, size=10, total=10) result = page(items, pagination_info, message="获取列表成功") assert result["message"] == "获取列表成功" def test_page_with_custom_code(self): """测试自定义状态码""" items = [{"id": 1}] pagination_info = pagination_builder(None, page=1, size=10, total=10) result = page(items, pagination_info, code=201) assert result["code"] == 201 def test_page_with_mock_pagination_object(self): """测试直接传入 Pagination 对象""" class MockPagination: items = [{"id": 1}, {"id": 2}, {"id": 3}] page = 1 per_page = 10 pages = 5 total = 50 has_prev = False has_next = True prev_num = None next_num = 2 mock_pagination = MockPagination() result = page(mock_pagination) assert result["code"] == 200 assert len(result["data"]["items"]) == 3 assert result["data"]["page"]["page"] == 1 assert result["data"]["page"]["size"] == 10 assert result["data"]["page"]["total"] == 50 def test_page_with_items_and_pagination_object(self): """测试传入数据列表 + Pagination 对象""" class MockPagination: items = [{"id": 1}, {"id": 2}] page = 2 per_page = 20 pages = 3 total = 60 has_prev = True has_next = True prev_num = 1 next_num = 3 mock_pagination = MockPagination() custom_items = [{"id": 10}, {"id": 20}] # 使用自定义数据(如序列化后的) result = page(custom_items, mock_pagination) assert result["data"]["items"] == custom_items # 使用传入的 items assert result["data"]["page"]["page"] == 2 assert result["data"]["page"]["size"] == 20 def test_page_empty_items(self): """测试空数据列表""" items = [] pagination_info = pagination_builder(None, page=1, size=10, total=0) result = page(items, pagination_info) assert result["data"]["items"] == [] assert result["data"]["page"]["total"] == 0 assert result["data"]["page"]["pages"] == 0 class TestSchemaDefinitions: """测试 Schema 定义""" def test_pagination_schema_exists(self): """测试 PaginationSchema 存在""" assert PaginationSchema is not None # 检查字段 schema = PaginationSchema() assert "page" in schema.fields assert "size" in schema.fields assert "pages" in schema.fields assert "total" in schema.fields class TestIntegrationWithFlaskApp: """测试与 Flask 应用集成""" def test_success_in_route(self, client): """测试 success() 在路由中使用""" response = client.get("/test/success") assert response.status_code == 200 data = response.get_json() assert data["code"] == 200 assert data["message"] == "操作成功" assert "message" in data["data"] def test_fail_in_route(self, client): """测试 fail() 在路由中使用""" response = client.get("/test/fail") # 确保 HTTP 状态码是 200(业务错误) assert response.status_code == 200 data = response.get_json() assert data["code"] == 400 assert data["message"] == "这是一个错误示例" def test_page_in_route(self, client): """测试 page() 在路由中使用""" response = client.get("/test/page") assert response.status_code == 200 data = response.get_json() assert data["code"] == 200 assert data["message"] == "分页测试成功" assert "items" in data["data"] assert "page" in data["data"] assert len(data["data"]["items"]) == 3 assert data["data"]["page"]["page"] == 1 assert data["data"]["page"]["size"] == 10 assert data["data"]["page"]["total"] == 30 def test_index_with_schema(self, client): """测试首页使用 Schema 验证""" response = client.get("/") assert response.status_code == 200 data = response.get_json() assert data["code"] == 200 assert data["message"] == "成功" assert "id" in data["data"] assert "name" in data["data"] # 验证 Schema 过滤了无关字段(如果有的话) class TestEdgeCases: """测试边界情况""" def test_pagination_builder_with_fractional_pages(self): """测试非整数页数计算""" result = pagination_builder(None, page=1, size=7, total=15) # ceil(15/7) = 3 assert result["pages"] == 3 def test_pagination_builder_exact_pages(self): """测试整数页数""" result = pagination_builder(None, page=1, size=10, total=100) assert result["pages"] == 10 def test_success_with_none_data(self): """测试 data 为 None""" result = success(None, message="无数据") assert result["data"] is None assert result["message"] == "无数据" def test_fail_with_empty_string_message(self): """测试空字符串消息""" result = fail("", code=400) assert result["message"] == "" assert result["code"] == 400 def test_page_with_large_total(self): """测试大数据量分页""" items = [{"id": i} for i in range(100)] pagination_info = pagination_builder(None, page=1, size=100, total=1000000) result = page(items, pagination_info) assert result["data"]["page"]["total"] == 1000000 assert result["data"]["page"]["pages"] == 10000