Skip to content

Commit 08e18fe

Browse files
authored
Merge pull request #105 from talltechy/copilot/improve-test-coverage
Improve test coverage from 29.7% to 51.3% - Add comprehensive tests for core modules
2 parents 56247cc + 2b9700a commit 08e18fe

19 files changed

Lines changed: 1431 additions & 0 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,3 +752,4 @@ insightvm-python.code-workspace
752752
# Explicitly include tests directory (must be at end to override patterns above)
753753
!tests/
754754
!tests/**
755+
coverage.json
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
Binary file not shown.
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
29.4 KB
Binary file not shown.
41.3 KB
Binary file not shown.

tests/test_config.py

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
"""
2+
Tests for Rapid7 InsightVM config module.
3+
4+
Tests the config.py module for persistent configuration management.
5+
"""
6+
7+
import json
8+
import pytest
9+
from pathlib import Path
10+
from unittest.mock import Mock, patch, mock_open
11+
import tempfile
12+
import shutil
13+
14+
from rapid7.config import Config, get_config
15+
16+
17+
class TestConfig:
18+
"""Test Config class functionality."""
19+
20+
@pytest.fixture
21+
def temp_config_dir(self):
22+
"""Create a temporary config directory for testing."""
23+
temp_dir = tempfile.mkdtemp()
24+
yield temp_dir
25+
shutil.rmtree(temp_dir, ignore_errors=True)
26+
27+
def test_init_creates_directories(self, temp_config_dir):
28+
"""Test that Config creates necessary directories."""
29+
config = Config(config_dir=temp_config_dir)
30+
31+
assert config.config_dir.exists()
32+
assert config.state_dir.exists()
33+
assert config.config_dir == Path(temp_config_dir)
34+
35+
def test_init_with_default_directory(self):
36+
"""Test that Config uses default directory when not specified."""
37+
with patch('pathlib.Path.mkdir'):
38+
with patch('pathlib.Path.exists', return_value=False):
39+
config = Config()
40+
41+
expected_dir = Path.home() / '.insightvm'
42+
assert config.config_dir == expected_dir
43+
44+
def test_default_config_structure(self, temp_config_dir):
45+
"""Test that default configuration has expected structure."""
46+
config = Config(config_dir=temp_config_dir)
47+
48+
# Check top-level keys
49+
assert 'version' in config.data
50+
assert 'preferences' in config.data
51+
assert 'tools' in config.data
52+
53+
# Check preferences
54+
prefs = config.data['preferences']
55+
assert 'confirm_destructive_operations' in prefs
56+
assert 'colored_output' in prefs
57+
assert 'show_progress_bars' in prefs
58+
assert 'verbose' in prefs
59+
60+
# Check tools
61+
tools = config.data['tools']
62+
assert 'sonar_queries' in tools
63+
assert 'insight_agent' in tools
64+
assert 'scan_assistant' in tools
65+
66+
def test_save_config(self, temp_config_dir):
67+
"""Test saving configuration to file."""
68+
config = Config(config_dir=temp_config_dir)
69+
config.data['preferences']['verbose'] = True
70+
71+
config.save()
72+
73+
# Verify file was created
74+
assert config.config_file.exists()
75+
76+
# Verify content
77+
with open(config.config_file, 'r') as f:
78+
saved_data = json.load(f)
79+
80+
assert saved_data['preferences']['verbose'] is True
81+
82+
def test_load_existing_config(self, temp_config_dir):
83+
"""Test loading existing configuration from file."""
84+
# Create a config file
85+
config_file = Path(temp_config_dir) / 'config.json'
86+
config_data = {
87+
'version': '2.0.0',
88+
'preferences': {'verbose': True},
89+
'tools': {}
90+
}
91+
92+
Path(temp_config_dir).mkdir(parents=True, exist_ok=True)
93+
with open(config_file, 'w') as f:
94+
json.dump(config_data, f)
95+
96+
# Load config
97+
config = Config(config_dir=temp_config_dir)
98+
99+
assert config.data['preferences']['verbose'] is True
100+
101+
def test_get_preference(self, temp_config_dir):
102+
"""Test getting a preference value."""
103+
config = Config(config_dir=temp_config_dir)
104+
105+
value = config.get_preference('colored_output')
106+
assert value is True
107+
108+
# Test with default value
109+
value = config.get_preference('nonexistent', default=False)
110+
assert value is False
111+
112+
def test_set_preference(self, temp_config_dir):
113+
"""Test setting a preference value."""
114+
config = Config(config_dir=temp_config_dir)
115+
116+
config.set_preference('verbose', True)
117+
assert config.data['preferences']['verbose'] is True
118+
119+
def test_get_tool_config(self, temp_config_dir):
120+
"""Test getting tool configuration."""
121+
config = Config(config_dir=temp_config_dir)
122+
123+
sonar_config = config.get_tool_config('sonar_queries')
124+
assert sonar_config is not None
125+
assert 'default_days' in sonar_config
126+
assert sonar_config['default_days'] == 30
127+
128+
def test_set_tool_config(self, temp_config_dir):
129+
"""Test setting tool configuration."""
130+
config = Config(config_dir=temp_config_dir)
131+
132+
new_config = {'custom_setting': 'value'}
133+
config.set_tool_config('custom_tool', new_config)
134+
135+
assert 'custom_tool' in config.data['tools']
136+
assert config.data['tools']['custom_tool']['custom_setting'] == 'value'
137+
138+
def test_save_state(self, temp_config_dir):
139+
"""Test saving tool state."""
140+
config = Config(config_dir=temp_config_dir)
141+
142+
state_data = {'progress': 50, 'last_item': 'item_123'}
143+
config.save_state('test_tool', state_data)
144+
145+
state_file = config.state_dir / 'test_tool_state.json'
146+
assert state_file.exists()
147+
148+
with open(state_file, 'r') as f:
149+
saved_state = json.load(f)
150+
151+
assert saved_state['progress'] == 50
152+
assert saved_state['last_item'] == 'item_123'
153+
154+
def test_load_state(self, temp_config_dir):
155+
"""Test loading tool state."""
156+
config = Config(config_dir=temp_config_dir)
157+
158+
# Save state first
159+
state_data = {'progress': 75}
160+
config.save_state('test_tool', state_data)
161+
162+
# Load state
163+
loaded_state = config.load_state('test_tool')
164+
165+
assert loaded_state is not None
166+
assert loaded_state['progress'] == 75
167+
168+
def test_load_state_nonexistent(self, temp_config_dir):
169+
"""Test loading state that doesn't exist."""
170+
config = Config(config_dir=temp_config_dir)
171+
172+
state = config.load_state('nonexistent_tool')
173+
assert state is None
174+
175+
def test_clear_state(self, temp_config_dir):
176+
"""Test clearing tool state."""
177+
config = Config(config_dir=temp_config_dir)
178+
179+
# Save state
180+
config.save_state('test_tool', {'data': 'test'})
181+
state_file = config.state_dir / 'test_tool_state.json'
182+
assert state_file.exists()
183+
184+
# Clear state
185+
config.clear_state('test_tool')
186+
assert not state_file.exists()
187+
188+
def test_load_config_with_json_error(self, temp_config_dir):
189+
"""Test handling of corrupted JSON config file."""
190+
config_file = Path(temp_config_dir) / 'config.json'
191+
Path(temp_config_dir).mkdir(parents=True, exist_ok=True)
192+
193+
# Write invalid JSON
194+
with open(config_file, 'w') as f:
195+
f.write('{ invalid json }')
196+
197+
# Should fall back to default config
198+
config = Config(config_dir=temp_config_dir)
199+
assert 'version' in config.data
200+
assert config.data['version'] == '2.0.0'
201+
202+
203+
class TestGetConfig:
204+
"""Test get_config helper function."""
205+
206+
def test_get_config_singleton(self):
207+
"""Test that get_config returns a singleton instance."""
208+
config1 = get_config()
209+
config2 = get_config()
210+
211+
# Should return the same instance
212+
assert config1 is config2
213+
214+
def test_get_config_returns_config_instance(self):
215+
"""Test that get_config returns a Config instance."""
216+
config = get_config()
217+
218+
assert isinstance(config, Config)
219+
assert hasattr(config, 'data')
220+
assert hasattr(config, 'save')
221+
assert hasattr(config, 'get_preference')
0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)