test(11-02): add failing tests for stealth integration in ZhihuCrawler

- Test crawler accepts stealth_config parameter
- Test default stealth enabled behavior
- Test stealth can be disabled
- Test _start_browser_with_proxy calls apply_stealth_sync
- Test launch args include --disable-blink-features
- Test backward compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-18 23:52:23 +08:00
parent b9f74e5a7e
commit 56c21f3f36
@@ -0,0 +1,165 @@
"""Integration tests for ZhihuCrawler stealth functionality.
Tests that stealth fingerprint masking is properly integrated
into ZhihuCrawler's browser initialization.
"""
import pytest
from unittest.mock import MagicMock, patch, call
class TestZhihuCrawlerStealthIntegration:
"""Tests for stealth integration in ZhihuCrawler."""
def test_crawler_has_stealth_config_param(self):
"""Test that ZhihuCrawler accepts stealth_config parameter."""
from kb_workflow.crawler.zhihu_crawler import ZhihuCrawler
from kb_workflow.crawler.stealth import StealthConfig
config = StealthConfig(stealth_enabled=False)
crawler = ZhihuCrawler(stealth_config=config)
assert crawler._stealth_config == config
def test_crawler_default_stealth_enabled(self):
"""Test that stealth is enabled by default (None = use defaults)."""
from kb_workflow.crawler.zhihu_crawler import ZhihuCrawler
crawler = ZhihuCrawler()
# None means use default stealth config (enabled)
assert crawler._stealth_config is None
def test_crawler_can_disable_stealth(self):
"""Test that stealth can be explicitly disabled."""
from kb_workflow.crawler.zhihu_crawler import ZhihuCrawler
from kb_workflow.crawler.stealth import StealthConfig
disabled_config = StealthConfig(stealth_enabled=False)
crawler = ZhihuCrawler(stealth_config=disabled_config)
assert crawler._stealth_config.stealth_enabled is False
@patch('kb_workflow.crawler.zhihu_crawler.sync_playwright')
@patch('kb_workflow.crawler.zhihu_crawler.apply_stealth_sync')
def test_start_browser_applies_stealth(
self,
mock_apply_stealth,
mock_sync_playwright,
):
"""Test that _start_browser_with_proxy calls apply_stealth_sync."""
from kb_workflow.crawler.zhihu_crawler import ZhihuCrawler
# Setup mocks
mock_playwright = MagicMock()
mock_browser = MagicMock()
mock_context = MagicMock()
mock_sync_playwright.return_value.start.return_value = mock_playwright
mock_playwright.chromium.launch.return_value = mock_browser
mock_browser.new_context.return_value = mock_context
crawler = ZhihuCrawler()
crawler._start_browser_with_proxy(cookie="test_cookie")
# Verify apply_stealth_sync was called
mock_apply_stealth.assert_called_once()
call_args = mock_apply_stealth.call_args
assert call_args[0][0] == mock_context # First arg is context
@patch('kb_workflow.crawler.zhihu_crawler.sync_playwright')
@patch('kb_workflow.crawler.zhihu_crawler.apply_stealth_sync')
def test_start_browser_launch_args_include_disable_blink_features(
self,
mock_apply_stealth,
mock_sync_playwright,
):
"""Test that launch includes --disable-blink-features=AutomationControlled."""
from kb_workflow.crawler.zhihu_crawler import ZhihuCrawler
# Setup mocks
mock_playwright = MagicMock()
mock_browser = MagicMock()
mock_context = MagicMock()
mock_sync_playwright.return_value.start.return_value = mock_playwright
mock_playwright.chromium.launch.return_value = mock_browser
mock_browser.new_context.return_value = mock_context
crawler = ZhihuCrawler()
crawler._start_browser_with_proxy(cookie="test_cookie")
# Verify launch was called with correct args
launch_call = mock_playwright.chromium.launch
launch_call.assert_called_once()
call_kwargs = launch_call.call_args[1]
assert 'args' in call_kwargs
assert '--disable-blink-features=AutomationControlled' in call_kwargs['args']
@patch('kb_workflow.crawler.zhihu_crawler.sync_playwright')
@patch('kb_workflow.crawler.zhihu_crawler.apply_stealth_sync')
def test_start_browser_stealth_disabled_no_blink_features_arg(
self,
mock_apply_stealth,
mock_sync_playwright,
):
"""Test that --disable-blink-features is not added when stealth disabled."""
from kb_workflow.crawler.zhihu_crawler import ZhihuCrawler
from kb_workflow.crawler.stealth import StealthConfig
# Setup mocks
mock_playwright = MagicMock()
mock_browser = MagicMock()
mock_context = MagicMock()
mock_sync_playwright.return_value.start.return_value = mock_playwright
mock_playwright.chromium.launch.return_value = mock_browser
mock_browser.new_context.return_value = mock_context
disabled_config = StealthConfig(stealth_enabled=False)
crawler = ZhihuCrawler(stealth_config=disabled_config)
crawler._start_browser_with_proxy(cookie="test_cookie")
# Verify launch was called without args or with empty/None args
launch_call = mock_playwright.chromium.launch
call_kwargs = launch_call.call_args[1]
# When stealth disabled, args should be None or not contain the flag
if 'args' in call_kwargs and call_kwargs['args']:
assert '--disable-blink-features=AutomationControlled' not in call_kwargs['args']
# apply_stealth_sync should still be called but with disabled config
mock_apply_stealth.assert_called_once()
class TestZhihuCrawlerBackwardCompatibility:
"""Tests for backward compatibility after stealth integration."""
def test_crawler_works_without_stealth_config(self):
"""Test that ZhihuCrawler works without explicit stealth_config."""
from kb_workflow.crawler.zhihu_crawler import ZhihuCrawler
# Should not raise any errors
crawler = ZhihuCrawler(
headless=True,
timeout=30
)
assert crawler is not None
def test_crawler_accepts_all_existing_params(self):
"""Test that all existing parameters still work."""
from kb_workflow.crawler.zhihu_crawler import ZhihuCrawler
from kb_workflow.crawler.config import CrawlerConfig
config = CrawlerConfig()
crawler = ZhihuCrawler(
headless=False,
timeout=60,
config=config
)
assert crawler.headless is False
assert crawler.timeout == 60
assert crawler.config == config
# Wave 0 completion marker
WAVE_0_COMPLETE = True