hatch-xclam 0.7.1.dev3__py3-none-any.whl → 0.8.0.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. hatch/__init__.py +1 -1
  2. hatch/cli/__init__.py +71 -0
  3. hatch/cli/__main__.py +1035 -0
  4. hatch/cli/cli_env.py +865 -0
  5. hatch/cli/cli_mcp.py +1965 -0
  6. hatch/cli/cli_package.py +566 -0
  7. hatch/cli/cli_system.py +136 -0
  8. hatch/cli/cli_utils.py +1289 -0
  9. hatch/cli_hatch.py +160 -2838
  10. hatch/mcp_host_config/__init__.py +10 -10
  11. hatch/mcp_host_config/adapters/__init__.py +34 -0
  12. hatch/mcp_host_config/adapters/base.py +170 -0
  13. hatch/mcp_host_config/adapters/claude.py +105 -0
  14. hatch/mcp_host_config/adapters/codex.py +104 -0
  15. hatch/mcp_host_config/adapters/cursor.py +83 -0
  16. hatch/mcp_host_config/adapters/gemini.py +75 -0
  17. hatch/mcp_host_config/adapters/kiro.py +78 -0
  18. hatch/mcp_host_config/adapters/lmstudio.py +79 -0
  19. hatch/mcp_host_config/adapters/registry.py +149 -0
  20. hatch/mcp_host_config/adapters/vscode.py +83 -0
  21. hatch/mcp_host_config/backup.py +5 -3
  22. hatch/mcp_host_config/fields.py +126 -0
  23. hatch/mcp_host_config/models.py +161 -456
  24. hatch/mcp_host_config/reporting.py +57 -16
  25. hatch/mcp_host_config/strategies.py +155 -87
  26. hatch/template_generator.py +1 -1
  27. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/METADATA +3 -2
  28. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/RECORD +52 -43
  29. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/WHEEL +1 -1
  30. hatch_xclam-0.8.0.dev1.dist-info/entry_points.txt +2 -0
  31. tests/cli_test_utils.py +280 -0
  32. tests/integration/cli/__init__.py +14 -0
  33. tests/integration/cli/test_cli_reporter_integration.py +2439 -0
  34. tests/integration/mcp/__init__.py +0 -0
  35. tests/integration/mcp/test_adapter_serialization.py +173 -0
  36. tests/regression/cli/__init__.py +16 -0
  37. tests/regression/cli/test_color_logic.py +268 -0
  38. tests/regression/cli/test_consequence_type.py +298 -0
  39. tests/regression/cli/test_error_formatting.py +328 -0
  40. tests/regression/cli/test_result_reporter.py +586 -0
  41. tests/regression/cli/test_table_formatter.py +211 -0
  42. tests/regression/mcp/__init__.py +0 -0
  43. tests/regression/mcp/test_field_filtering.py +162 -0
  44. tests/test_cli_version.py +7 -5
  45. tests/test_data/fixtures/cli_reporter_fixtures.py +184 -0
  46. tests/unit/__init__.py +0 -0
  47. tests/unit/mcp/__init__.py +0 -0
  48. tests/unit/mcp/test_adapter_protocol.py +138 -0
  49. tests/unit/mcp/test_adapter_registry.py +158 -0
  50. tests/unit/mcp/test_config_model.py +146 -0
  51. hatch_xclam-0.7.1.dev3.dist-info/entry_points.txt +0 -2
  52. tests/integration/test_mcp_kiro_integration.py +0 -153
  53. tests/regression/test_mcp_codex_backup_integration.py +0 -162
  54. tests/regression/test_mcp_codex_host_strategy.py +0 -163
  55. tests/regression/test_mcp_codex_model_validation.py +0 -117
  56. tests/regression/test_mcp_kiro_backup_integration.py +0 -241
  57. tests/regression/test_mcp_kiro_cli_integration.py +0 -141
  58. tests/regression/test_mcp_kiro_decorator_registration.py +0 -71
  59. tests/regression/test_mcp_kiro_host_strategy.py +0 -214
  60. tests/regression/test_mcp_kiro_model_validation.py +0 -116
  61. tests/regression/test_mcp_kiro_omni_conversion.py +0 -104
  62. tests/test_mcp_atomic_operations.py +0 -276
  63. tests/test_mcp_backup_integration.py +0 -308
  64. tests/test_mcp_cli_all_host_specific_args.py +0 -496
  65. tests/test_mcp_cli_backup_management.py +0 -295
  66. tests/test_mcp_cli_direct_management.py +0 -456
  67. tests/test_mcp_cli_discovery_listing.py +0 -582
  68. tests/test_mcp_cli_host_config_integration.py +0 -823
  69. tests/test_mcp_cli_package_management.py +0 -360
  70. tests/test_mcp_cli_partial_updates.py +0 -859
  71. tests/test_mcp_environment_integration.py +0 -520
  72. tests/test_mcp_host_config_backup.py +0 -257
  73. tests/test_mcp_host_configuration_manager.py +0 -331
  74. tests/test_mcp_host_registry_decorator.py +0 -348
  75. tests/test_mcp_pydantic_architecture_v4.py +0 -603
  76. tests/test_mcp_server_config_models.py +0 -242
  77. tests/test_mcp_server_config_type_field.py +0 -221
  78. tests/test_mcp_sync_functionality.py +0 -316
  79. tests/test_mcp_user_feedback_reporting.py +0 -359
  80. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/licenses/LICENSE +0 -0
  81. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/top_level.txt +0 -0
@@ -1,276 +0,0 @@
1
- """Tests for MCP atomic file operations.
2
-
3
- This module contains tests for atomic file operations and backup-aware
4
- operations with host-agnostic design.
5
- """
6
-
7
- import unittest
8
- import tempfile
9
- import shutil
10
- import json
11
- from pathlib import Path
12
- from unittest.mock import patch, mock_open
13
-
14
- from wobble.decorators import regression_test
15
- from test_data_utils import MCPBackupTestDataLoader
16
-
17
- from hatch.mcp_host_config.backup import (
18
- AtomicFileOperations,
19
- MCPHostConfigBackupManager,
20
- BackupAwareOperation,
21
- BackupError
22
- )
23
-
24
-
25
- class TestAtomicFileOperations(unittest.TestCase):
26
- """Test atomic file operations with host-agnostic design."""
27
-
28
- def setUp(self):
29
- """Set up test environment."""
30
- self.temp_dir = Path(tempfile.mkdtemp(prefix="test_atomic_"))
31
- self.test_file = self.temp_dir / "test_config.json"
32
- self.backup_manager = MCPHostConfigBackupManager(backup_root=self.temp_dir / "backups")
33
- self.atomic_ops = AtomicFileOperations()
34
- self.test_data = MCPBackupTestDataLoader()
35
-
36
- def tearDown(self):
37
- """Clean up test environment."""
38
- shutil.rmtree(self.temp_dir, ignore_errors=True)
39
-
40
- @regression_test
41
- def test_atomic_write_success_host_agnostic(self):
42
- """Test successful atomic write with any JSON configuration format."""
43
- test_data = self.test_data.load_host_agnostic_config("complex_server")
44
-
45
- result = self.atomic_ops.atomic_write_with_backup(
46
- self.test_file, test_data, self.backup_manager, "claude-desktop"
47
- )
48
-
49
- self.assertTrue(result)
50
- self.assertTrue(self.test_file.exists())
51
-
52
- # Verify content (host-agnostic)
53
- with open(self.test_file) as f:
54
- written_data = json.load(f)
55
- self.assertEqual(written_data, test_data)
56
-
57
- @regression_test
58
- def test_atomic_write_with_existing_file(self):
59
- """Test atomic write with existing file creates backup."""
60
- # Create initial file
61
- initial_data = self.test_data.load_host_agnostic_config("simple_server")
62
- with open(self.test_file, 'w') as f:
63
- json.dump(initial_data, f)
64
-
65
- # Update with atomic write
66
- new_data = self.test_data.load_host_agnostic_config("complex_server")
67
- result = self.atomic_ops.atomic_write_with_backup(
68
- self.test_file, new_data, self.backup_manager, "vscode"
69
- )
70
-
71
- self.assertTrue(result)
72
-
73
- # Verify backup was created
74
- backups = self.backup_manager.list_backups("vscode")
75
- self.assertEqual(len(backups), 1)
76
-
77
- # Verify backup contains original data
78
- with open(backups[0].file_path) as f:
79
- backup_data = json.load(f)
80
- self.assertEqual(backup_data, initial_data)
81
-
82
- # Verify file contains new data
83
- with open(self.test_file) as f:
84
- current_data = json.load(f)
85
- self.assertEqual(current_data, new_data)
86
-
87
- @regression_test
88
- def test_atomic_write_skip_backup(self):
89
- """Test atomic write with backup skipped."""
90
- # Create initial file
91
- initial_data = self.test_data.load_host_agnostic_config("simple_server")
92
- with open(self.test_file, 'w') as f:
93
- json.dump(initial_data, f)
94
-
95
- # Update with atomic write, skipping backup
96
- new_data = self.test_data.load_host_agnostic_config("complex_server")
97
- result = self.atomic_ops.atomic_write_with_backup(
98
- self.test_file, new_data, self.backup_manager, "cursor", skip_backup=True
99
- )
100
-
101
- self.assertTrue(result)
102
-
103
- # Verify no backup was created
104
- backups = self.backup_manager.list_backups("cursor")
105
- self.assertEqual(len(backups), 0)
106
-
107
- # Verify file contains new data
108
- with open(self.test_file) as f:
109
- current_data = json.load(f)
110
- self.assertEqual(current_data, new_data)
111
-
112
- @regression_test
113
- def test_atomic_write_failure_rollback(self):
114
- """Test atomic write failure triggers rollback."""
115
- # Create initial file
116
- initial_data = self.test_data.load_host_agnostic_config("simple_server")
117
- with open(self.test_file, 'w') as f:
118
- json.dump(initial_data, f)
119
-
120
- # Mock file write failure after backup creation
121
- with patch('builtins.open', side_effect=[
122
- # First call succeeds (backup creation)
123
- open(self.test_file, 'r'),
124
- # Second call fails (atomic write)
125
- PermissionError("Access denied")
126
- ]):
127
- with self.assertRaises(BackupError):
128
- self.atomic_ops.atomic_write_with_backup(
129
- self.test_file, {"new": "data"}, self.backup_manager, "lmstudio"
130
- )
131
-
132
- # Verify original file is unchanged
133
- with open(self.test_file) as f:
134
- current_data = json.load(f)
135
- self.assertEqual(current_data, initial_data)
136
-
137
- @regression_test
138
- def test_atomic_copy_success(self):
139
- """Test successful atomic copy operation."""
140
- source_file = self.temp_dir / "source.json"
141
- target_file = self.temp_dir / "target.json"
142
-
143
- test_data = self.test_data.load_host_agnostic_config("simple_server")
144
- with open(source_file, 'w') as f:
145
- json.dump(test_data, f)
146
-
147
- result = self.atomic_ops.atomic_copy(source_file, target_file)
148
-
149
- self.assertTrue(result)
150
- self.assertTrue(target_file.exists())
151
-
152
- # Verify content integrity
153
- with open(target_file) as f:
154
- copied_data = json.load(f)
155
- self.assertEqual(copied_data, test_data)
156
-
157
- @regression_test
158
- def test_atomic_copy_failure_cleanup(self):
159
- """Test atomic copy failure cleans up temporary files."""
160
- source_file = self.temp_dir / "source.json"
161
- target_file = self.temp_dir / "target.json"
162
-
163
- test_data = self.test_data.load_host_agnostic_config("simple_server")
164
- with open(source_file, 'w') as f:
165
- json.dump(test_data, f)
166
-
167
- # Mock copy failure
168
- with patch('shutil.copy2', side_effect=PermissionError("Access denied")):
169
- result = self.atomic_ops.atomic_copy(source_file, target_file)
170
-
171
- self.assertFalse(result)
172
- self.assertFalse(target_file.exists())
173
-
174
- # Verify no temporary files left behind
175
- temp_files = list(self.temp_dir.glob("*.tmp"))
176
- self.assertEqual(len(temp_files), 0)
177
-
178
-
179
- class TestBackupAwareOperation(unittest.TestCase):
180
- """Test backup-aware operation API."""
181
-
182
- def setUp(self):
183
- """Set up test environment."""
184
- self.temp_dir = Path(tempfile.mkdtemp(prefix="test_backup_aware_"))
185
- self.test_file = self.temp_dir / "test_config.json"
186
- self.backup_manager = MCPHostConfigBackupManager(backup_root=self.temp_dir / "backups")
187
- self.test_data = MCPBackupTestDataLoader()
188
-
189
- def tearDown(self):
190
- """Clean up test environment."""
191
- shutil.rmtree(self.temp_dir, ignore_errors=True)
192
-
193
- @regression_test
194
- def test_prepare_backup_success(self):
195
- """Test explicit backup preparation."""
196
- # Create initial configuration
197
- initial_data = self.test_data.load_host_agnostic_config("simple_server")
198
- with open(self.test_file, 'w') as f:
199
- json.dump(initial_data, f)
200
-
201
- # Test backup-aware operation
202
- operation = BackupAwareOperation(self.backup_manager)
203
-
204
- # Test explicit backup preparation
205
- backup_result = operation.prepare_backup(self.test_file, "gemini", no_backup=False)
206
- self.assertIsNotNone(backup_result)
207
- self.assertTrue(backup_result.success)
208
-
209
- # Verify backup was created
210
- backups = self.backup_manager.list_backups("gemini")
211
- self.assertEqual(len(backups), 1)
212
-
213
- @regression_test
214
- def test_prepare_backup_no_backup_mode(self):
215
- """Test no-backup mode."""
216
- # Create initial configuration
217
- initial_data = self.test_data.load_host_agnostic_config("simple_server")
218
- with open(self.test_file, 'w') as f:
219
- json.dump(initial_data, f)
220
-
221
- operation = BackupAwareOperation(self.backup_manager)
222
-
223
- # Test no-backup mode
224
- no_backup_result = operation.prepare_backup(self.test_file, "claude-code", no_backup=True)
225
- self.assertIsNone(no_backup_result)
226
-
227
- # Verify no backup was created
228
- backups = self.backup_manager.list_backups("claude-code")
229
- self.assertEqual(len(backups), 0)
230
-
231
- @regression_test
232
- def test_prepare_backup_failure_raises_exception(self):
233
- """Test backup preparation failure raises BackupError."""
234
- # Test with nonexistent file
235
- nonexistent_file = self.temp_dir / "nonexistent.json"
236
-
237
- operation = BackupAwareOperation(self.backup_manager)
238
-
239
- with self.assertRaises(BackupError):
240
- operation.prepare_backup(nonexistent_file, "vscode", no_backup=False)
241
-
242
- @regression_test
243
- def test_rollback_on_failure_success(self):
244
- """Test successful rollback functionality."""
245
- # Create initial configuration
246
- initial_data = self.test_data.load_host_agnostic_config("simple_server")
247
- with open(self.test_file, 'w') as f:
248
- json.dump(initial_data, f)
249
-
250
- operation = BackupAwareOperation(self.backup_manager)
251
-
252
- # Create backup
253
- backup_result = operation.prepare_backup(self.test_file, "cursor", no_backup=False)
254
- self.assertTrue(backup_result.success)
255
-
256
- # Modify file (simulate failed operation)
257
- modified_data = self.test_data.load_host_agnostic_config("complex_server")
258
- with open(self.test_file, 'w') as f:
259
- json.dump(modified_data, f)
260
-
261
- # Test rollback functionality
262
- rollback_success = operation.rollback_on_failure(backup_result, self.test_file, "cursor")
263
- self.assertTrue(rollback_success)
264
-
265
- @regression_test
266
- def test_rollback_on_failure_no_backup(self):
267
- """Test rollback with no backup result."""
268
- operation = BackupAwareOperation(self.backup_manager)
269
-
270
- # Test rollback with None backup result
271
- rollback_success = operation.rollback_on_failure(None, self.test_file, "lmstudio")
272
- self.assertFalse(rollback_success)
273
-
274
-
275
- if __name__ == '__main__':
276
- unittest.main()
@@ -1,308 +0,0 @@
1
- """Tests for MCP backup system integration.
2
-
3
- This module contains integration tests for the backup system with existing
4
- Hatch infrastructure and end-to-end workflows.
5
- """
6
-
7
- import unittest
8
- import tempfile
9
- import shutil
10
- import json
11
- import time
12
- from pathlib import Path
13
- from unittest.mock import Mock, patch
14
-
15
- from wobble.decorators import integration_test, slow_test, regression_test
16
- from test_data_utils import MCPBackupTestDataLoader
17
-
18
- from hatch.mcp_host_config.backup import (
19
- MCPHostConfigBackupManager,
20
- BackupAwareOperation,
21
- BackupInfo,
22
- BackupResult
23
- )
24
-
25
-
26
- class TestMCPBackupIntegration(unittest.TestCase):
27
- """Test backup system integration with existing Hatch infrastructure."""
28
-
29
- def setUp(self):
30
- """Set up integration test environment."""
31
- self.temp_dir = Path(tempfile.mkdtemp(prefix="test_integration_"))
32
- self.backup_manager = MCPHostConfigBackupManager(backup_root=self.temp_dir / "backups")
33
- self.test_data = MCPBackupTestDataLoader()
34
-
35
- # Create test configuration files
36
- self.config_dir = self.temp_dir / "configs"
37
- self.config_dir.mkdir(parents=True)
38
-
39
- self.test_configs = {}
40
- for hostname in ['claude-desktop', 'claude-code', 'vscode', 'cursor']:
41
- config_data = self.test_data.load_host_agnostic_config("simple_server")
42
- config_file = self.config_dir / f"{hostname}_config.json"
43
- with open(config_file, 'w') as f:
44
- json.dump(config_data, f, indent=2)
45
- self.test_configs[hostname] = config_file
46
-
47
- def tearDown(self):
48
- """Clean up integration test environment."""
49
- shutil.rmtree(self.temp_dir, ignore_errors=True)
50
-
51
- @integration_test(scope="component")
52
- def test_complete_backup_restore_cycle(self):
53
- """Test complete backup creation and restoration cycle."""
54
- hostname = 'claude-desktop'
55
- config_file = self.test_configs[hostname]
56
-
57
- # Create backup
58
- backup_result = self.backup_manager.create_backup(config_file, hostname)
59
- self.assertTrue(backup_result.success)
60
-
61
- # Modify original file
62
- modified_data = self.test_data.load_host_agnostic_config("complex_server")
63
- with open(config_file, 'w') as f:
64
- json.dump(modified_data, f)
65
-
66
- # Verify file was modified
67
- with open(config_file) as f:
68
- current_data = json.load(f)
69
- self.assertEqual(current_data, modified_data)
70
-
71
- # Restore from backup (placeholder - actual restore would need host config paths)
72
- restore_success = self.backup_manager.restore_backup(hostname)
73
- self.assertTrue(restore_success) # Currently returns True as placeholder
74
-
75
- @integration_test(scope="component")
76
- def test_multi_host_backup_management(self):
77
- """Test backup management across multiple hosts."""
78
- # Create backups for multiple hosts
79
- results = {}
80
- for hostname, config_file in self.test_configs.items():
81
- results[hostname] = self.backup_manager.create_backup(config_file, hostname)
82
- self.assertTrue(results[hostname].success)
83
-
84
- # Verify separate backup directories
85
- for hostname in self.test_configs.keys():
86
- backups = self.backup_manager.list_backups(hostname)
87
- self.assertEqual(len(backups), 1)
88
-
89
- # Verify backup isolation
90
- backup_dir = backups[0].file_path.parent
91
- self.assertEqual(backup_dir.name, hostname)
92
-
93
- # Verify no cross-contamination
94
- for other_hostname in self.test_configs.keys():
95
- if other_hostname != hostname:
96
- other_backups = self.backup_manager.list_backups(other_hostname)
97
- self.assertNotEqual(
98
- backups[0].file_path.parent,
99
- other_backups[0].file_path.parent
100
- )
101
-
102
- @integration_test(scope="end_to_end")
103
- def test_backup_with_configuration_update_workflow(self):
104
- """Test backup integration with configuration update operations."""
105
- hostname = 'vscode'
106
- config_file = self.test_configs[hostname]
107
-
108
- # Simulate configuration update with backup
109
- original_data = self.test_data.load_host_agnostic_config("simple_server")
110
- updated_data = self.test_data.load_host_agnostic_config("complex_server")
111
-
112
- # Ensure original data is in file
113
- with open(config_file, 'w') as f:
114
- json.dump(original_data, f)
115
-
116
- # Simulate update operation with backup
117
- backup_result = self.backup_manager.create_backup(config_file, hostname)
118
- self.assertTrue(backup_result.success)
119
-
120
- # Update configuration
121
- with open(config_file, 'w') as f:
122
- json.dump(updated_data, f)
123
-
124
- # Verify backup contains original data
125
- backups = self.backup_manager.list_backups(hostname)
126
- self.assertEqual(len(backups), 1)
127
-
128
- with open(backups[0].file_path) as f:
129
- backup_data = json.load(f)
130
- self.assertEqual(backup_data, original_data)
131
-
132
- # Verify current file has updated data
133
- with open(config_file) as f:
134
- current_data = json.load(f)
135
- self.assertEqual(current_data, updated_data)
136
-
137
- @integration_test(scope="service")
138
- def test_backup_system_with_existing_test_utilities(self):
139
- """Test backup system integration with existing test utilities."""
140
- # Use existing TestDataLoader patterns
141
- test_config = self.test_data.load_host_agnostic_config("complex_server")
142
-
143
- # Test backup creation with complex configuration
144
- config_path = self.temp_dir / "complex_config.json"
145
- with open(config_path, 'w') as f:
146
- json.dump(test_config, f)
147
-
148
- result = self.backup_manager.create_backup(config_path, "lmstudio")
149
- self.assertTrue(result.success)
150
-
151
- # Verify integration with existing test data patterns
152
- self.assertIsInstance(test_config, dict)
153
- self.assertIn("servers", test_config)
154
-
155
- # Verify backup content matches test data
156
- with open(result.backup_path) as f:
157
- backup_content = json.load(f)
158
- self.assertEqual(backup_content, test_config)
159
-
160
- @integration_test(scope="component")
161
- def test_backup_aware_operation_workflow(self):
162
- """Test backup-aware operation following environment manager patterns."""
163
- hostname = 'cursor'
164
- config_file = self.test_configs[hostname]
165
-
166
- # Test backup-aware operation following existing patterns
167
- operation = BackupAwareOperation(self.backup_manager)
168
-
169
- # Simulate environment manager update workflow
170
- backup_result = operation.prepare_backup(config_file, hostname, no_backup=False)
171
- self.assertTrue(backup_result.success)
172
-
173
- # Verify backup was created following existing patterns
174
- backups = self.backup_manager.list_backups(hostname)
175
- self.assertEqual(len(backups), 1)
176
- self.assertEqual(backups[0].hostname, hostname)
177
-
178
- # Test rollback capability
179
- rollback_success = operation.rollback_on_failure(backup_result, config_file, hostname)
180
- self.assertTrue(rollback_success)
181
-
182
-
183
- class TestMCPBackupPerformance(unittest.TestCase):
184
- """Test backup system performance characteristics."""
185
-
186
- def setUp(self):
187
- """Set up performance test environment."""
188
- self.temp_dir = Path(tempfile.mkdtemp(prefix="test_performance_"))
189
- self.backup_manager = MCPHostConfigBackupManager(backup_root=self.temp_dir / "backups")
190
- self.test_data = MCPBackupTestDataLoader()
191
-
192
- def tearDown(self):
193
- """Clean up performance test environment."""
194
- shutil.rmtree(self.temp_dir, ignore_errors=True)
195
-
196
- @slow_test
197
- @regression_test
198
- def test_backup_performance_large_config(self):
199
- """Test backup performance with larger configuration files."""
200
- # Create large host-agnostic configuration
201
- large_config = {"servers": {}}
202
- for i in range(1000):
203
- large_config["servers"][f"server_{i}"] = {
204
- "command": f"python_{i}",
205
- "args": [f"arg_{j}" for j in range(10)]
206
- }
207
-
208
- config_file = self.temp_dir / "large_config.json"
209
- with open(config_file, 'w') as f:
210
- json.dump(large_config, f)
211
-
212
- start_time = time.time()
213
- result = self.backup_manager.create_backup(config_file, "gemini")
214
- duration = time.time() - start_time
215
-
216
- self.assertTrue(result.success)
217
- self.assertLess(duration, 1.0) # Should complete within 1 second
218
-
219
- @regression_test
220
- def test_pydantic_validation_performance(self):
221
- """Test Pydantic model validation performance."""
222
- hostname = "claude-desktop"
223
- config_data = self.test_data.load_host_agnostic_config("simple_server")
224
- config_file = self.temp_dir / "test_config.json"
225
-
226
- with open(config_file, 'w') as f:
227
- json.dump(config_data, f)
228
-
229
- start_time = time.time()
230
-
231
- # Create backup (includes Pydantic validation)
232
- result = self.backup_manager.create_backup(config_file, hostname)
233
-
234
- # List backups (includes Pydantic model creation)
235
- backups = self.backup_manager.list_backups(hostname)
236
-
237
- duration = time.time() - start_time
238
-
239
- self.assertTrue(result.success)
240
- self.assertEqual(len(backups), 1)
241
- self.assertLess(duration, 0.1) # Pydantic operations should be fast
242
-
243
- @regression_test
244
- def test_concurrent_backup_operations(self):
245
- """Test concurrent backup operations for different hosts."""
246
- import threading
247
-
248
- results = {}
249
- config_files = {}
250
-
251
- # Create test configurations for different hosts
252
- for hostname in ['claude-desktop', 'vscode', 'cursor', 'lmstudio']:
253
- config_data = self.test_data.load_host_agnostic_config("simple_server")
254
- config_file = self.temp_dir / f"{hostname}_config.json"
255
- with open(config_file, 'w') as f:
256
- json.dump(config_data, f)
257
- config_files[hostname] = config_file
258
-
259
- def create_backup_thread(hostname, config_file):
260
- results[hostname] = self.backup_manager.create_backup(config_file, hostname)
261
-
262
- # Start concurrent backup operations
263
- threads = []
264
- for hostname, config_file in config_files.items():
265
- thread = threading.Thread(target=create_backup_thread, args=(hostname, config_file))
266
- threads.append(thread)
267
- thread.start()
268
-
269
- # Wait for all threads to complete
270
- for thread in threads:
271
- thread.join(timeout=5.0)
272
-
273
- # Verify all operations succeeded
274
- for hostname in config_files.keys():
275
- self.assertIn(hostname, results)
276
- self.assertTrue(results[hostname].success)
277
-
278
- @regression_test
279
- def test_backup_list_performance_many_backups(self):
280
- """Test backup listing performance with many backup files."""
281
- hostname = "claude-code"
282
- config_data = self.test_data.load_host_agnostic_config("simple_server")
283
- config_file = self.temp_dir / "test_config.json"
284
-
285
- with open(config_file, 'w') as f:
286
- json.dump(config_data, f)
287
-
288
- # Create many backups
289
- for i in range(50):
290
- result = self.backup_manager.create_backup(config_file, hostname)
291
- self.assertTrue(result.success)
292
-
293
- # Test listing performance
294
- start_time = time.time()
295
- backups = self.backup_manager.list_backups(hostname)
296
- duration = time.time() - start_time
297
-
298
- self.assertEqual(len(backups), 50)
299
- self.assertLess(duration, 0.1) # Should be fast even with many backups
300
-
301
- # Verify all backups are valid Pydantic models
302
- for backup in backups:
303
- self.assertIsInstance(backup, BackupInfo)
304
- self.assertEqual(backup.hostname, hostname)
305
-
306
-
307
- if __name__ == '__main__':
308
- unittest.main()