56c21f3f36
- 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>
166 lines
6.2 KiB
Python
166 lines
6.2 KiB
Python
"""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
|