Mock-based tests (no live services) covering:
- ollama_llm_model resolution under the OLLAMA_MODEL env collision
- per-leg retrieval failure -> source_status/degraded signaling
- Phase 0/Phase 4 LLM timeout fallbacks
- /stats wiki page count using the users/{user} path prefix
- Wiki.js list_all_pages limit-growth pagination and
list_pages limit-after-filter behavior
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
104 lines
3.5 KiB
Python
104 lines
3.5 KiB
Python
"""
|
|
Unit tests for WikiJSClient page listing and pagination (offline).
|
|
|
|
Wiki.js 2.x `pages.list` supports only a `limit` argument (no offset), so
|
|
exhaustive listing works by growing the limit until the API returns fewer
|
|
pages than requested. These tests mock the GraphQL layer.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from src.clients.wikijs_client import WikiJSClient
|
|
|
|
|
|
def make_pages(count_other: int, count_user: int):
|
|
"""Build a fake TITLE-ordered page listing: 'other/' pages sort before 'users/jp/' pages."""
|
|
pages = [
|
|
{"id": i, "path": f"other/p{i:03d}", "title": f"A{i:03d}", "tags": []}
|
|
for i in range(count_other)
|
|
]
|
|
pages += [
|
|
{"id": count_other + i, "path": f"users/jp/p{i:03d}", "title": f"Z{i:03d}", "tags": ["projects"]}
|
|
for i in range(count_user)
|
|
]
|
|
return pages
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
"""WikiJSClient with a mocked GraphQL layer serving 138 pages (100 other + 38 user)."""
|
|
client = WikiJSClient("http://wiki.test", "")
|
|
client._all_pages = make_pages(100, 38)
|
|
client._requested_limits = []
|
|
|
|
async def fake_execute(query, variables=None):
|
|
limit = variables["limit"]
|
|
client._requested_limits.append(limit)
|
|
return {"pages": {"list": client._all_pages[:limit]}}
|
|
|
|
client._execute_query = fake_execute
|
|
return client
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestListAllPagesPagination:
|
|
"""list_all_pages must exhaust the listing via a limit-growth loop."""
|
|
|
|
async def test_grows_limit_until_exhausted(self, client):
|
|
pages = await client.list_all_pages(batch_size=50)
|
|
|
|
# 50 -> full batch, 100 -> full batch, 200 -> 138 < 200 done
|
|
assert client._requested_limits == [50, 100, 200]
|
|
assert len(pages) == 138
|
|
|
|
async def test_single_call_when_first_batch_not_full(self, client):
|
|
pages = await client.list_all_pages(batch_size=500)
|
|
|
|
assert client._requested_limits == [500]
|
|
assert len(pages) == 138
|
|
|
|
async def test_path_prefix_filter_after_exhaustion(self, client):
|
|
"""All 38 user pages are returned even though they sort last (beyond batch_size)."""
|
|
pages = await client.list_all_pages(path_prefix="users/jp", batch_size=50)
|
|
|
|
assert len(pages) == 38
|
|
assert all(p["path"].startswith("users/jp") for p in pages)
|
|
|
|
async def test_empty_wiki(self, client):
|
|
client._all_pages = []
|
|
|
|
pages = await client.list_all_pages(batch_size=100)
|
|
|
|
assert pages == []
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestListPagesLimitAfterFilter:
|
|
"""list_pages must apply `limit` AFTER client-side filters, not before."""
|
|
|
|
async def test_prefix_filter_with_small_limit(self, client):
|
|
"""Old defect: API limit=5 returned 5 'other/' pages, filter dropped all -> 0 results."""
|
|
pages = await client.list_pages(path_prefix="users/jp", limit=5)
|
|
|
|
assert len(pages) == 5
|
|
assert all(p["path"].startswith("users/jp") for p in pages)
|
|
|
|
async def test_tag_filter_with_small_limit(self, client):
|
|
pages = await client.list_pages(tags=["projects"], limit=10)
|
|
|
|
assert len(pages) == 10
|
|
assert all("projects" in p["tags"] for p in pages)
|
|
|
|
async def test_unfiltered_passes_limit_to_api(self, client):
|
|
pages = await client.list_pages(limit=7)
|
|
|
|
assert client._requested_limits == [7]
|
|
assert len(pages) == 7
|
|
|
|
async def test_tags_normalized_to_list(self, client):
|
|
client._all_pages = [{"id": 1, "path": "home", "title": "Home", "tags": None}]
|
|
|
|
pages = await client.list_pages(limit=10)
|
|
|
|
assert pages[0]["tags"] == []
|