PyGithub MCP Server
by AstroMined
- tests
- unit
- converters
- common
"""Unit tests for pagination utilities.
This module contains tests for the pagination utility functions without using mocks,
following the approach outlined in ADR-002: Real API Testing.
"""
import pytest
from dataclasses import dataclass
from typing import List, Optional, Any, Union
from pygithub_mcp_server.converters.common.pagination import get_paginated_slice, get_paginated_items
@dataclass
class MockPaginatedItem:
"""Simple dataclass for items in a paginated list."""
id: int
name: str
class PaginatedListFixture:
"""A dataclass-based PaginatedList substitute for testing.
This adheres to the ADR-002 guidance on using dataclasses instead of mocks.
"""
def __init__(self, items: Optional[List[Any]] = None, total_count: Optional[int] = None):
"""Initialize the test paginated list.
Args:
items: Optional list of items
total_count: Optional total count (defaults to len(items))
"""
self.items = items or []
self.totalCount = total_count if total_count is not None else len(self.items)
def __getitem__(self, index: Union[int, slice]) -> Union[Any, List[Any]]:
"""Implement getitem to simulate PaginatedList behavior.
Args:
index: Integer index or slice
Returns:
Item or list of items
Raises:
IndexError: If index is out of range
"""
if isinstance(index, slice):
start = index.start or 0
stop = index.stop or len(self.items)
# Simulate IndexError for invalid ranges
if start >= len(self.items):
raise IndexError("list index out of range")
return self.items[start:stop]
else:
if index >= len(self.items):
raise IndexError("list index out of range")
return self.items[index]
def get_page(self, page_index: int) -> List[Any]:
"""Simulate PyGithub's get_page method.
Args:
page_index: 0-based page index
Returns:
List of items for the requested page
Raises:
IndexError: If page index is out of range
"""
# PyGithub uses 0-based indexing for get_page
start = page_index * 30 # Default per_page is 30
if start >= len(self.items):
raise IndexError("Page index out of range")
return self.items[start:start+30]
class TestPaginationUtilities:
"""Tests for the pagination utility functions."""
def test_get_paginated_slice_normal_case(self):
"""Test get_paginated_slice with a normal case."""
# Create test items
items = [MockPaginatedItem(id=i, name=f"Item {i}") for i in range(5)]
paginated_list = PaginatedListFixture(items)
# Call the function
result = get_paginated_slice(paginated_list, 0, 3)
# Verify the result
assert len(result) == 3
assert result[0].id == 0
assert result[1].id == 1
assert result[2].id == 2
def test_get_paginated_slice_empty_list(self):
"""Test get_paginated_slice with an empty list."""
# Create an empty paginated list
paginated_list = PaginatedListFixture([], total_count=0)
# Call the function
result = get_paginated_slice(paginated_list, 0, 3)
# Verify the result is an empty list
assert result == []
def test_get_paginated_slice_index_error(self):
"""Test get_paginated_slice with an index error."""
# Create a paginated list where accessing invalid indices raises IndexError
items = [MockPaginatedItem(id=i, name=f"Item {i}") for i in range(3)]
paginated_list = PaginatedListFixture(items)
# Call the function with start index > list length
result = get_paginated_slice(paginated_list, 5, 8)
# Verify the result is an empty list
assert result == []
def test_get_paginated_items_with_page_and_per_page(self):
"""Test get_paginated_items with page and per_page."""
# Create test items
items = [MockPaginatedItem(id=i, name=f"Item {i}") for i in range(10)]
paginated_list = PaginatedListFixture(items)
# Call the function with page=2, per_page=3
# This should get items 3,4,5 (0-indexed, page 2 starts at index 3)
result = get_paginated_items(paginated_list, page=2, per_page=3)
# Verify the result
assert len(result) == 3
assert result[0].id == 3
assert result[1].id == 4
assert result[2].id == 5
def test_get_paginated_items_with_page_only(self):
"""Test get_paginated_items with page parameter only."""
# Create test items - enough for multiple pages
items = [MockPaginatedItem(id=i, name=f"Item {i}") for i in range(40)]
paginated_list = PaginatedListFixture(items)
# Call the function with page=2 only (should use default per_page=30)
result = get_paginated_items(paginated_list, page=2)
# Verify the result (page 2 should start at index 30)
assert len(result) == 10 # Only 10 items left after page 1
assert result[0].id == 30
def test_get_paginated_items_with_per_page_only(self):
"""Test get_paginated_items with per_page parameter only."""
# Create test items
items = [MockPaginatedItem(id=i, name=f"Item {i}") for i in range(10)]
paginated_list = PaginatedListFixture(items)
# Call the function with per_page=5 only
result = get_paginated_items(paginated_list, per_page=5)
# Verify the result (should get first 5 items)
assert len(result) == 5
assert result[0].id == 0
assert result[4].id == 4
def test_get_paginated_items_no_parameters(self):
"""Test get_paginated_items with no pagination parameters."""
# Create test items
items = [MockPaginatedItem(id=i, name=f"Item {i}") for i in range(10)]
paginated_list = PaginatedListFixture(items)
# Call the function with no parameters
result = get_paginated_items(paginated_list)
# Verify the result (should get all items)
assert len(result) == 10
assert result[0].id == 0
assert result[9].id == 9
def test_get_paginated_items_empty_list(self):
"""Test get_paginated_items with an empty list."""
# Create an empty paginated list
paginated_list = PaginatedListFixture([], total_count=0)
# Call the function
result = get_paginated_items(paginated_list)
# Verify the result is an empty list
assert result == []
def test_get_paginated_items_out_of_range_page(self):
"""Test get_paginated_items with a page that's out of range."""
# Create test items
items = [MockPaginatedItem(id=i, name=f"Item {i}") for i in range(10)]
paginated_list = PaginatedListFixture(items)
# Call the function with a page that's out of range
result = get_paginated_items(paginated_list, page=100)
# Verify the result is an empty list
assert result == []