"""通用分页器单元测试.""" from __future__ import annotations import pytest from pydantic import ValidationError from packages.application.common.pagination import ( PaginatedResponse, PaginationMeta, PaginationParams, paginate, ) class TestPaginationParams: """PaginationParams 测试""" def test_default_values(self): """默认值正确""" params = PaginationParams() assert params.page == 1 assert params.page_size == 20 def test_offset_first_page(self): """第一页 offset 为 0""" params = PaginationParams(page=1, page_size=20) assert params.offset == 0 def test_offset_second_page(self): """第二页 offset 计算正确""" params = PaginationParams(page=2, page_size=20) assert params.offset == 20 def test_offset_custom_page_size(self): """自定义 page_size 的 offset""" params = PaginationParams(page=3, page_size=10) assert params.offset == 20 def test_limit_equals_page_size(self): """limit 等于 page_size""" params = PaginationParams(page_size=50) assert params.limit == 50 def test_page_must_be_at_least_1(self): """page 不能小于 1""" with pytest.raises(ValidationError): PaginationParams(page=0) def test_page_negative_raises(self): """page 不能为负数""" with pytest.raises(ValidationError): PaginationParams(page=-1) def test_page_size_must_be_at_least_1(self): """page_size 不能小于 1""" with pytest.raises(ValidationError): PaginationParams(page_size=0) def test_page_size_max_100(self): """page_size 最大 100""" with pytest.raises(ValidationError): PaginationParams(page_size=101) def test_page_size_100_is_valid(self): """page_size=100 是合法的""" params = PaginationParams(page_size=100) assert params.page_size == 100 class TestPaginationMeta: """PaginationMeta 测试""" def test_from_params_first_page(self): """第一页元数据""" params = PaginationParams(page=1, page_size=10) meta = PaginationMeta.from_params(params, total=25) assert meta.page == 1 assert meta.page_size == 10 assert meta.total == 25 assert meta.total_pages == 3 assert meta.has_next is True assert meta.has_prev is False def test_from_params_last_page(self): """最后一页元数据""" params = PaginationParams(page=3, page_size=10) meta = PaginationMeta.from_params(params, total=25) assert meta.page == 3 assert meta.total_pages == 3 assert meta.has_next is False assert meta.has_prev is True def test_from_params_middle_page(self): """中间页元数据""" params = PaginationParams(page=2, page_size=10) meta = PaginationMeta.from_params(params, total=50) assert meta.page == 2 assert meta.total_pages == 5 assert meta.has_next is True assert meta.has_prev is True def test_from_params_zero_total(self): """总数为 0 时""" params = PaginationParams(page=1, page_size=20) meta = PaginationMeta.from_params(params, total=0) assert meta.total == 0 assert meta.total_pages == 0 assert meta.has_next is False assert meta.has_prev is False def test_from_params_exact_multiple(self): """总数刚好是 page_size 的整数倍""" params = PaginationParams(page=1, page_size=10) meta = PaginationMeta.from_params(params, total=30) assert meta.total_pages == 3 def test_from_params_single_page(self): """单页即可放下所有数据""" params = PaginationParams(page=1, page_size=100) meta = PaginationMeta.from_params(params, total=50) assert meta.total_pages == 1 assert meta.has_next is False assert meta.has_prev is False class TestPaginatedResponse: """PaginatedResponse 测试""" def test_create_success(self): """创建分页响应""" params = PaginationParams(page=1, page_size=10) data = [1, 2, 3] response = PaginatedResponse.create(data, params, total=25) assert response.data == [1, 2, 3] assert response.pagination.page == 1 assert response.pagination.total == 25 assert response.pagination.total_pages == 3 def test_create_empty_data(self): """空数据分页响应""" params = PaginationParams(page=1, page_size=20) response = PaginatedResponse.create([], params, total=0) assert response.data == [] assert response.pagination.total == 0 assert response.pagination.total_pages == 0 class TestPaginateFunction: """paginate 函数测试(内存分页)""" def test_first_page(self): """第一页分页""" items = list(range(30)) params = PaginationParams(page=1, page_size=10) result = paginate(items, params) assert result.data == list(range(10)) assert result.pagination.total == 30 assert result.pagination.total_pages == 3 assert result.pagination.has_next is True assert result.pagination.has_prev is False def test_second_page(self): """第二页分页""" items = list(range(30)) params = PaginationParams(page=2, page_size=10) result = paginate(items, params) assert result.data == list(range(10, 20)) assert result.pagination.page == 2 def test_last_page(self): """最后一页分页""" items = list(range(25)) params = PaginationParams(page=3, page_size=10) result = paginate(items, params) assert result.data == list(range(20, 25)) assert len(result.data) == 5 assert result.pagination.has_next is False def test_empty_list(self): """空列表分页""" params = PaginationParams(page=1, page_size=20) result = paginate([], params) assert result.data == [] assert result.pagination.total == 0 assert result.pagination.total_pages == 0 def test_page_beyond_total(self): """页码超出总数""" items = list(range(5)) params = PaginationParams(page=10, page_size=10) result = paginate(items, params) assert result.data == [] assert result.pagination.total == 5 assert result.pagination.total_pages == 1 def test_custom_page_size(self): """自定义每页数量""" items = list(range(100)) params = PaginationParams(page=1, page_size=50) result = paginate(items, params) assert len(result.data) == 50 assert result.pagination.total_pages == 2 def test_single_item(self): """单条数据""" items = ["only_one"] params = PaginationParams(page=1, page_size=10) result = paginate(items, params) assert result.data == ["only_one"] assert result.pagination.total == 1 assert result.pagination.total_pages == 1 def test_generic_type_preserved(self): """泛型类型数据正确""" items = [{"id": 1, "name": "a"}, {"id": 2, "name": "b"}] params = PaginationParams(page=1, page_size=10) result = paginate(items, params) assert len(result.data) == 2 assert result.data[0]["id"] == 1