"""Tests for ZhihuAdapter Tests cover: - Answer content extraction - Zhuanlan (article) content extraction - Cookie-based authentication - Login page detection - AuthError handling """ import pytest from unittest.mock import MagicMock, patch, PropertyMock import asyncio from kb_pipeline.platforms.zhihu import ZhihuAdapter from kb_pipeline.errors import AuthError class TestZhihuAdapterAnswerContent: """Tests for ZhihuAdapter answer extraction""" def test_get_content_type_answer(self): """Test content type detection for answer URLs""" adapter = ZhihuAdapter() adapter._cookie = "test" # Avoid config loading assert adapter.get_content_type("https://www.zhihu.com/question/123456/answer/789012") == "answer" assert adapter.get_content_type("https://zhihu.com/question/123/answer/456") == "answer" def test_get_content_type_zhuanlan(self): """Test content type detection for zhuanlan URLs""" adapter = ZhihuAdapter() adapter._cookie = "test" # Avoid config loading assert adapter.get_content_type("https://zhuanlan.zhihu.com/p/123456") == "zhuanlan" assert adapter.get_content_type("https://zhuanlan.zhihu.com/p/abc123") == "zhuanlan" def test_get_content_type_unknown(self): """Test content type detection for unknown URLs""" adapter = ZhihuAdapter() adapter._cookie = "test" # Avoid config loading assert adapter.get_content_type("https://www.zhihu.com/question/123456") == "unknown" assert adapter.get_content_type("https://www.zhihu.com/") == "unknown" assert adapter.get_content_type("https://example.com") == "unknown" def test_get_output_filename(self): """Test output filename generation""" adapter = ZhihuAdapter() adapter._cookie = "test" # Avoid config loading assert adapter.get_output_filename("789012", "answer", "metadata.json") == "zhihu_789012_metadata.json" assert adapter.get_output_filename("123456", "zhuanlan", "content.md") == "zhihu_123456_content.md" @pytest.mark.asyncio async def test_fetch_metadata_answer(self, mock_zhihu_answer_result): """Test fetching answer metadata""" adapter = ZhihuAdapter() adapter._cookie = "test_cookie" # Set cookie directly with patch.object(adapter, '_extract_with_playwright', return_value=mock_zhihu_answer_result): metadata = await adapter.fetch_metadata("789012", "answer") assert metadata["title"] == "Test Question Title" assert metadata["author"] == "Test Author" assert metadata["type"] == "answer" @pytest.mark.asyncio async def test_fetch_content_answer(self, mock_zhihu_answer_result): """Test fetching answer content as Markdown""" adapter = ZhihuAdapter() adapter._cookie = "test_cookie" # Set cookie directly with patch.object(adapter, '_extract_with_playwright', return_value=mock_zhihu_answer_result): content = await adapter.fetch_content("789012", "answer") assert "---" in content # YAML frontmatter assert "title:" in content assert "Test Question Title" in content assert "This is the answer content" in content @pytest.mark.asyncio async def test_fetch_metadata_no_cookie_raises_auth_error(self): """Test that missing cookie raises AuthError""" adapter = ZhihuAdapter() adapter._cookie = "" # Empty cookie with pytest.raises(AuthError) as exc_info: await adapter.fetch_metadata("789012", "answer") assert exc_info.value.auth_type == "zhihu_cookie" class TestZhihuAdapterZhuanlanContent: """Tests for ZhihuAdapter zhuanlan (article) extraction""" @pytest.mark.asyncio async def test_fetch_metadata_zhuanlan(self, mock_zhihu_zhuanlan_result): """Test fetching zhuanlan metadata""" adapter = ZhihuAdapter() adapter._cookie = "test_cookie" # Set cookie directly with patch.object(adapter, '_extract_with_playwright', return_value=mock_zhihu_zhuanlan_result): metadata = await adapter.fetch_metadata("123456", "zhuanlan") assert metadata["title"] == "Test Article Title" assert metadata["author"] == "Test Author" assert metadata["type"] == "zhuanlan" @pytest.mark.asyncio async def test_fetch_content_zhuanlan(self, mock_zhihu_zhuanlan_result): """Test fetching zhuanlan content as Markdown""" adapter = ZhihuAdapter() adapter._cookie = "test_cookie" # Set cookie directly with patch.object(adapter, '_extract_with_playwright', return_value=mock_zhihu_zhuanlan_result): content = await adapter.fetch_content("123456", "zhuanlan") assert "---" in content # YAML frontmatter assert "title:" in content assert "Test Article Title" in content assert "This is the article content" in content @pytest.mark.asyncio async def test_detect_login_page_raises_auth_error(self): """Test that login page detection raises AuthError""" adapter = ZhihuAdapter() adapter._cookie = "expired_cookie" # Set cookie directly # Simulate login page response (empty content or login title) login_page_result = { "type": "answer", "title": "登录 - 知乎", "content": "", "content_markdown": "", } with patch.object(adapter, '_extract_with_playwright', return_value=login_page_result): with pytest.raises(AuthError) as exc_info: await adapter.fetch_metadata("789012", "answer") assert exc_info.value.auth_type == "zhihu_cookie" assert "过期" in str(exc_info.value.cause) or "expired" in str(exc_info.value.cause).lower() @pytest.mark.asyncio async def test_detect_short_content_raises_auth_error(self): """Test that very short content (< 100 chars) raises AuthError""" adapter = ZhihuAdapter() adapter._cookie = "test_cookie" # Simulate response with very short content short_content_result = { "type": "answer", "title": "Some Title", "content": "x", # Very short content "content_markdown": "", } with patch.object(adapter, '_extract_with_playwright', return_value=short_content_result): with pytest.raises(AuthError) as exc_info: await adapter.fetch_metadata("789012", "answer") assert exc_info.value.auth_type == "zhihu_cookie" class TestZhihuAdapterCookieHandling: """Tests for cookie handling""" def test_cookie_from_settings(self): """Test cookie is loaded from settings""" with patch('kb_pipeline.platforms.zhihu.get_settings') as mock_settings: mock_secret = MagicMock() mock_secret.get_secret_value.return_value = "test_cookie_value" mock_settings.return_value.zhihu_cookie = mock_secret adapter = ZhihuAdapter() assert adapter.cookie == "test_cookie_value" def test_platform_constant(self): """Test PLATFORM constant""" adapter = ZhihuAdapter() adapter._cookie = "test" # Avoid config loading assert adapter.PLATFORM == "zhihu"