diff --git a/tests/test_platforms/test_zhihu.py b/tests/test_platforms/test_zhihu.py index 02bc4f7..4b69354 100644 --- a/tests/test_platforms/test_zhihu.py +++ b/tests/test_platforms/test_zhihu.py @@ -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="

This is the answer content from ZhihuCrawler.

", + 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="

Article Content

This is the article content.

", + 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