"""Tests for ZhihuLogin QR code login functionality Tests the core login flow: 1. Access login page 2. Capture QR code screenshot 3. Poll for login cookie (z_c0) 4. Extract cookie string on success 5. Handle timeout """ import pytest from unittest.mock import MagicMock, patch, AsyncMock from pathlib import Path class TestZhihuLogin: """Test suite for ZhihuLogin class.""" def test_login_page_access(self, mock_browser): """Test that login page is accessed with correct URL.""" from kb_workflow.auth import ZhihuLogin login = ZhihuLogin(intermediate_path="/tmp/test") login.browser = mock_browser # Simulate calling goto login.browser.goto("https://www.zhihu.com/signin") # Verify goto was called with correct URL mock_browser.goto.assert_called_once_with("https://www.zhihu.com/signin") def test_qrcode_screenshot(self, mock_browser, mock_canvas, intermediate_path): """Test QR code screenshot is captured and saved correctly.""" from kb_workflow.auth import ZhihuLogin login = ZhihuLogin(intermediate_path=intermediate_path) login.browser = mock_browser # Mock the canvas screenshot mock_browser.query_selector.return_value = mock_canvas mock_canvas.screenshot.return_value = b"fake_png_bytes" # Call _save_qrcode_image screenshot_bytes = b"fake_png_bytes" qrcode_path = login._save_qrcode_image(screenshot_bytes) # Verify file was created assert qrcode_path.exists() assert qrcode_path.suffix == ".png" assert "zhihu_qrcode" in qrcode_path.name def test_cookie_detection_with_z_c0(self, mock_browser, sample_cookies): """Test that login is detected when z_c0 cookie exists.""" from kb_workflow.auth import ZhihuLogin login = ZhihuLogin(intermediate_path="/tmp/test") login.browser = mock_browser # Mock cookies to include z_c0 mock_browser.context.cookies.return_value = sample_cookies # Check login cookie is_logged_in = login.check_login_cookie() assert is_logged_in is True def test_cookie_detection_without_z_c0(self, mock_browser, sample_cookies_no_login): """Test that login is NOT detected when z_c0 cookie is missing.""" from kb_workflow.auth import ZhihuLogin login = ZhihuLogin(intermediate_path="/tmp/test") login.browser = mock_browser # Mock cookies without z_c0 mock_browser.context.cookies.return_value = sample_cookies_no_login # Check login cookie is_logged_in = login.check_login_cookie() assert is_logged_in is False def test_cookie_extraction(self, mock_browser, sample_cookies): """Test cookie string extraction after successful login.""" from kb_workflow.auth import ZhihuLogin login = ZhihuLogin(intermediate_path="/tmp/test") login.browser = mock_browser # Mock cookies mock_browser.context.cookies.return_value = sample_cookies # Get cookie string cookie_str = login.get_cookies_str() # Verify format: "name1=value1; name2=value2" assert "z_c0=test_z_c0_value" in cookie_str assert "_xsrf=test_xsrf_value" in cookie_str assert ";" in cookie_str def test_login_timeout(self, mock_browser, sample_cookies_no_login): """Test that TimeoutError is raised after 60 seconds of polling.""" from kb_workflow.auth import ZhihuLogin from tenacity import RetryError login = ZhihuLogin(intermediate_path="/tmp/test", timeout=2) # Short timeout for test login.browser = mock_browser # Mock cookies to always return no login mock_browser.context.cookies.return_value = sample_cookies_no_login # Should raise RetryError (wrapped by tenacity) or TimeoutError with pytest.raises((TimeoutError, RetryError)): login._wait_for_login() class TestZhihuLoginResult: """Test suite for ZhihuLoginResult model.""" def test_result_model_success(self): """Test ZhihuLoginResult model with successful login.""" from kb_workflow.auth import ZhihuLoginResult result = ZhihuLoginResult( success=True, cookie_str="z_c0=abc123; _xsrf=xyz789", qrcode_path=Path("/tmp/qrcode.png"), error_message=None ) assert result.success is True assert "z_c0" in result.cookie_str assert result.error_message is None def test_result_model_failure(self): """Test ZhihuLoginResult model with failed login.""" from kb_workflow.auth import ZhihuLoginResult result = ZhihuLoginResult( success=False, cookie_str="", qrcode_path=Path("/tmp/qrcode.png"), error_message="Login timeout after 60 seconds" ) assert result.success is False assert result.cookie_str == "" assert "timeout" in result.error_message.lower()