This repository has been archived on 2026-08-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
kb-skills/tests/test_crawler/test_stealth.py
T
starmen1265 e152b8ea05 feat(11-01): create stealth fingerprint masking module
- Add StealthConfig Pydantic model with all playwright-stealth options
- Add apply_stealth_sync/apply_stealth_async functions for browser context
- Add get_default_stealth_config convenience function
- Add 9 unit tests covering config defaults, stealth application, and disabled mode
- Update conftest.py with stealth_config and mock_browser_context fixtures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 23:47:50 +08:00

138 lines
5.0 KiB
Python

"""Tests for stealth fingerprint masking module."""
import pytest
from unittest.mock import MagicMock, patch, call
class TestStealthConfig:
"""Tests for StealthConfig class."""
def test_stealth_config_defaults(self):
"""Test that default StealthConfig has all options enabled."""
from kb_workflow.crawler.stealth import StealthConfig
config = StealthConfig()
assert config.stealth_enabled is True
# STEALTH-01: navigator.webdriver hiding
assert config.navigator_webdriver is True
# STEALTH-04: User-Agent modification
assert config.navigator_user_agent is True
# STEALTH-03: Chrome properties
assert config.chrome_app is True
assert config.chrome_csi is True
assert config.chrome_load_times is True
# Other important options
assert config.navigator_plugins is True
assert config.webgl_vendor is True
def test_stealth_config_disabled(self):
"""Test that stealth can be disabled."""
from kb_workflow.crawler.stealth import StealthConfig
config = StealthConfig(stealth_enabled=False)
assert config.stealth_enabled is False
def test_create_stealth_returns_stealth_instance(self):
"""Test that create_stealth() returns configured Stealth."""
from kb_workflow.crawler.stealth import StealthConfig
from playwright_stealth import Stealth
config = StealthConfig()
stealth = config.create_stealth()
assert isinstance(stealth, Stealth)
def test_stealth_options_mapping(self):
"""Test that StealthConfig options map correctly to Stealth."""
from kb_workflow.crawler.stealth import StealthConfig
config = StealthConfig(
navigator_webdriver=True,
navigator_user_agent=False, # Disable one option
chrome_app=True,
)
stealth = config.create_stealth()
# Verify the Stealth object was created with correct options
# The Stealth class stores these as attributes
assert stealth.navigator_webdriver is True
assert stealth.navigator_user_agent is False
class TestApplyStealth:
"""Tests for apply_stealth functions."""
def test_apply_stealth_sync_calls_library(self, mock_browser_context):
"""Test that apply_stealth_sync calls playwright-stealth correctly."""
from kb_workflow.crawler.stealth import apply_stealth_sync, StealthConfig
config = StealthConfig()
with patch('kb_workflow.crawler.stealth.stealth_config.Stealth') as mock_stealth_class:
mock_stealth_instance = MagicMock()
mock_stealth_class.return_value = mock_stealth_instance
apply_stealth_sync(mock_browser_context, config)
# Verify Stealth was created with config options
mock_stealth_class.assert_called_once()
# Verify apply_stealth_sync was called on the Stealth instance
mock_stealth_instance.apply_stealth_sync.assert_called_once_with(
mock_browser_context
)
def test_apply_stealth_sync_disabled_skips_stealth(self, mock_browser_context):
"""Test that stealth is skipped when disabled."""
from kb_workflow.crawler.stealth import apply_stealth_sync, StealthConfig
config = StealthConfig(stealth_enabled=False)
with patch('kb_workflow.crawler.stealth.stealth_config.Stealth') as mock_stealth_class:
apply_stealth_sync(mock_browser_context, config)
# Stealth should not be created when disabled
mock_stealth_class.assert_not_called()
def test_apply_stealth_sync_default_config(self, mock_browser_context):
"""Test that apply_stealth_sync works with default config."""
from kb_workflow.crawler.stealth import apply_stealth_sync
with patch('kb_workflow.crawler.stealth.stealth_config.Stealth') as mock_stealth_class:
mock_stealth_instance = MagicMock()
mock_stealth_class.return_value = mock_stealth_instance
# Should work without explicit config
apply_stealth_sync(mock_browser_context)
mock_stealth_class.assert_called_once()
class TestGetDefaultStealthConfig:
"""Tests for get_default_stealth_config function."""
def test_get_default_stealth_config(self):
"""Test that get_default_stealth_config returns StealthConfig."""
from kb_workflow.crawler.stealth import get_default_stealth_config
config = get_default_stealth_config()
assert config.stealth_enabled is True
assert isinstance(config, type(config)) # Is a StealthConfig
def test_get_default_stealth_config_is_fresh_instance(self):
"""Test that each call returns a new instance."""
from kb_workflow.crawler.stealth import get_default_stealth_config
config1 = get_default_stealth_config()
config2 = get_default_stealth_config()
# Should be equal but not the same object
assert config1 == config2
assert config1 is not config2
# Wave 0 completion marker
WAVE_0_COMPLETE = True