test(09-00): add Wave 0 test scaffolding for Pipeline integration

- Add mock fixtures for ZhihuCrawler CrawlResult models:
  - mock_crawl_answer_result: simulates answer crawl with comments
  - mock_crawl_article_result: simulates zhuanlan article crawl
  - mock_comments_list: test data for comment formatting
  - mock_zhihu_login_result: simulates QR code login success
  - mock_crawl_failed_result: for error handling tests

- Add test skeletons (marked skip) for Wave 1 implementation:
  - test_fetch_metadata_answer
  - test_fetch_metadata_article
  - test_fetch_content_with_comments
  - test_cookie_expired_triggers_login
  - test_idempotent_execution
  - test_comment_formatting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 22:59:07 +08:00
parent bee696b96d
commit 95218c18cd
+272
View File
@@ -6,15 +6,197 @@ Tests cover:
- Cookie-based authentication - Cookie-based authentication
- Login page detection - Login page detection
- AuthError handling - AuthError handling
Wave 0 Test Scaffolding (Phase 09 - Pipeline Integration):
- Mock fixtures for ZhihuCrawler CrawlResult
- Test skeletons for integration with new crawler
""" """
import pytest import pytest
from unittest.mock import MagicMock, patch, PropertyMock from unittest.mock import MagicMock, patch, PropertyMock
import asyncio import asyncio
from pathlib import Path
from kb_workflow.pipeline.platforms.zhihu import ZhihuAdapter from kb_workflow.pipeline.platforms.zhihu import ZhihuAdapter
from kb_workflow.pipeline.errors import AuthError from kb_workflow.pipeline.errors import AuthError
# =============================================================================
# Wave 0 Mock Fixtures for ZhihuCrawler Integration (Phase 09)
# =============================================================================
@pytest.fixture
def mock_crawl_answer_result():
"""Mock CrawlResult from ZhihuCrawler.crawl_answer().
Simulates successful answer crawl with metadata and content.
Used for testing ZhihuAdapter integration with new crawler.
"""
from kb_workflow.crawler.models import (
CrawlResult, AnswerData, AuthorInfo, ContentMetadata, CommentData, CommentAuthor
)
return CrawlResult(
success=True,
content_type="answer",
data=AnswerData(
id=789012,
question_id=123456,
question_title="Test Question Title for Pipeline Integration",
content="<p>This is the <strong>answer</strong> content from ZhihuCrawler.</p>",
excerpt="This is the answer content from ZhihuCrawler.",
metadata=ContentMetadata(
author=AuthorInfo(
name="Test Author",
url_token="test-author-123",
headline="Test Author Headline"
),
voteup_count=1234,
comment_count=56,
created_time=1705312800, # 2024-01-15 10:00:00 UTC
updated_time=1705399200 # 2024-01-16 10:00:00 UTC
)
),
comments=[
CommentData(
id=111,
content="Great answer! Very helpful.",
author=CommentAuthor(name="Commenter1", url_token="commenter-1"),
created_time=1705316400,
like_count=42,
child_count=3,
is_author=False
),
CommentData(
id=222,
content="Thanks for sharing!",
author=CommentAuthor(name="Commenter2", url_token="commenter-2"),
created_time=1705320000,
like_count=15,
child_count=0,
is_author=False
)
],
error=None
)
@pytest.fixture
def mock_crawl_article_result():
"""Mock CrawlResult from ZhihuCrawler.crawl_article().
Simulates successful zhuanlan article crawl.
Used for testing ZhihuAdapter integration with new crawler.
"""
from kb_workflow.crawler.models import (
CrawlResult, ArticleData, AuthorInfo, ContentMetadata, CommentData, CommentAuthor
)
return CrawlResult(
success=True,
content_type="article",
data=ArticleData(
id=123456,
title="Test Article Title for Pipeline Integration",
content="<h1>Article Content</h1><p>This is the <em>article</em> content.</p>",
excerpt="Article Content This is the article content.",
metadata=ContentMetadata(
author=AuthorInfo(
name="Test Article Author",
url_token="test-article-author",
headline="Writer and Developer"
),
voteup_count=5678,
comment_count=89,
created_time=1705226400, # 2024-01-14 10:00:00 UTC
updated_time=None
)
),
comments=[
CommentData(
id=333,
content="Excellent article!",
author=CommentAuthor(name="Reader1", url_token="reader-1"),
created_time=1705230000,
like_count=28,
child_count=1,
is_author=False
)
],
error=None
)
@pytest.fixture
def mock_comments_list():
"""Mock list of CommentData for testing comment formatting.
Provides a realistic set of comments with various attributes.
"""
from kb_workflow.crawler.models import CommentData, CommentAuthor
return [
CommentData(
id=1001,
content="This is the first comment with some content.",
author=CommentAuthor(name="User1", url_token="user-1"),
created_time=1705316400,
like_count=100,
child_count=5,
is_author=False
),
CommentData(
id=1002,
content="This is the second comment, from the author.",
author=CommentAuthor(name="AuthorName", url_token="author-token"),
created_time=1705320000,
like_count=50,
child_count=2,
is_author=True
),
CommentData(
id=1003,
content="Third comment here.",
author=CommentAuthor(name="User3", url_token="user-3"),
created_time=1705323600,
like_count=25,
child_count=0,
is_author=False
)
]
@pytest.fixture
def mock_zhihu_login_result():
"""Mock ZhihuLoginResult from ZhihuLogin.login_by_qrcode().
Simulates successful QR code login result.
Used for testing cookie refresh flow.
"""
from kb_workflow.auth.zhihu_login import ZhihuLoginResult
from pathlib import Path
return ZhihuLoginResult(
success=True,
cookie_str="z_c0=test_cookie_value; _xsrf=test_xsrf; d_c0=test_d_c0",
qrcode_path=Path("/tmp/test_qrcode.png"),
error_message=None
)
@pytest.fixture
def mock_crawl_failed_result():
"""Mock failed CrawlResult for error handling tests."""
from kb_workflow.crawler.models import CrawlResult
return CrawlResult(
success=False,
content_type="answer",
data=None,
comments=None,
error="Cookie expired or invalid"
)
class TestZhihuAdapterAnswerContent: class TestZhihuAdapterAnswerContent:
"""Tests for ZhihuAdapter answer extraction""" """Tests for ZhihuAdapter answer extraction"""
@@ -181,3 +363,93 @@ class TestZhihuAdapterCookieHandling:
adapter = ZhihuAdapter() adapter = ZhihuAdapter()
adapter._cookie = "test" # Avoid config loading adapter._cookie = "test" # Avoid config loading
assert adapter.PLATFORM == "zhihu" assert adapter.PLATFORM == "zhihu"
# =============================================================================
# Wave 0 Test Skeletons for Pipeline Integration (Phase 09)
# These tests are marked skip until ZhihuAdapter is updated to use ZhihuCrawler
# =============================================================================
@pytest.mark.skip(reason="Wave 0 skeleton - awaiting ZhihuCrawler integration")
class TestZhihuAdapterCrawlerIntegration:
"""Test skeletons for ZhihuAdapter integration with ZhihuCrawler.
These tests will verify the adapter correctly uses the new ZhihuCrawler
instead of ZhihuPlaywrightExtractor.
"""
@pytest.mark.asyncio
async def test_fetch_metadata_answer(self, mock_crawl_answer_result):
"""Test fetching answer metadata via ZhihuCrawler.
Verifies:
- ZhihuCrawler.crawl_answer() is called with correct ID
- Metadata is correctly extracted from CrawlResult
- Output format matches expected adapter response
"""
pass
@pytest.mark.asyncio
async def test_fetch_metadata_article(self, mock_crawl_article_result):
"""Test fetching zhuanlan article metadata via ZhihuCrawler.
Verifies:
- ZhihuCrawler.crawl_article() is called with correct ID
- Metadata is correctly extracted from CrawlResult
- Output format matches expected adapter response
"""
pass
@pytest.mark.asyncio
async def test_fetch_content_with_comments(
self, mock_crawl_answer_result, mock_comments_list
):
"""Test fetching content with comments appended.
Verifies:
- Content is fetched with include_comments=True
- Comments are appended to content in correct Markdown format
- Comment format: numbered list with author mention
"""
pass
@pytest.mark.asyncio
async def test_cookie_expired_triggers_login(
self, mock_crawl_failed_result, mock_zhihu_login_result
):
"""Test that expired cookie triggers QR code login flow.
Verifies:
- CrawlResult with success=False triggers login
- ZhihuLogin.login_by_qrcode() is called
- New cookie is used to retry the fetch
- User sees QR code prompt
"""
pass
@pytest.mark.asyncio
async def test_idempotent_execution(self, mock_crawl_answer_result):
"""Test that repeated calls with same content_id are idempotent.
Verifies:
- Multiple calls with same ID produce same result
- No duplicate fetches are made
- State is correctly managed
"""
pass
def test_comment_formatting(self, mock_comments_list):
"""Test comment formatting to Markdown.
Verifies:
- Comments are formatted as numbered list
- Author name is bold/mentioned
- Like count is included
- Format matches expected style:
1. **@AuthorName** (100 likes): Comment content...
"""
pass
# WAVE_0_COMPLETE: Test scaffolding ready for implementation