"""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