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:
@@ -6,15 +6,197 @@ Tests cover:
|
||||
- Cookie-based authentication
|
||||
- Login page detection
|
||||
- AuthError handling
|
||||
|
||||
Wave 0 Test Scaffolding (Phase 09 - Pipeline Integration):
|
||||
- Mock fixtures for ZhihuCrawler CrawlResult
|
||||
- Test skeletons for integration with new crawler
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import MagicMock, patch, PropertyMock
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
from kb_workflow.pipeline.platforms.zhihu import ZhihuAdapter
|
||||
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:
|
||||
"""Tests for ZhihuAdapter answer extraction"""
|
||||
@@ -181,3 +363,93 @@ class TestZhihuAdapterCookieHandling:
|
||||
adapter = ZhihuAdapter()
|
||||
adapter._cookie = "test" # Avoid config loading
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user