kailash 0.9.15__py3-none-any.whl → 0.9.16__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.
- kailash/middleware/database/base_models.py +7 -1
- kailash/migration/__init__.py +30 -0
- kailash/migration/cli.py +340 -0
- kailash/migration/compatibility_checker.py +662 -0
- kailash/migration/configuration_validator.py +837 -0
- kailash/migration/documentation_generator.py +1828 -0
- kailash/migration/examples/__init__.py +5 -0
- kailash/migration/examples/complete_migration_example.py +692 -0
- kailash/migration/migration_assistant.py +715 -0
- kailash/migration/performance_comparator.py +760 -0
- kailash/migration/regression_detector.py +1141 -0
- kailash/migration/tests/__init__.py +6 -0
- kailash/migration/tests/test_compatibility_checker.py +403 -0
- kailash/migration/tests/test_integration.py +463 -0
- kailash/migration/tests/test_migration_assistant.py +397 -0
- kailash/migration/tests/test_performance_comparator.py +433 -0
- kailash/nodes/data/async_sql.py +1507 -6
- kailash/runtime/local.py +1255 -8
- kailash/runtime/monitoring/__init__.py +1 -0
- kailash/runtime/monitoring/runtime_monitor.py +780 -0
- kailash/runtime/resource_manager.py +3033 -0
- kailash/sdk_exceptions.py +21 -0
- kailash/workflow/cyclic_runner.py +18 -2
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/METADATA +1 -1
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/RECORD +30 -12
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/WHEEL +0 -0
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/entry_points.txt +0 -0
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/licenses/NOTICE +0 -0
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,6 @@
|
|
1
|
+
"""Test suite for LocalRuntime migration tools.
|
2
|
+
|
3
|
+
This package contains comprehensive tests for all migration tools and utilities,
|
4
|
+
including compatibility checking, migration assistance, performance comparison,
|
5
|
+
configuration validation, documentation generation, and regression detection.
|
6
|
+
"""
|
@@ -0,0 +1,403 @@
|
|
1
|
+
"""Tests for the CompatibilityChecker class."""
|
2
|
+
|
3
|
+
import tempfile
|
4
|
+
import textwrap
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
import pytest
|
8
|
+
|
9
|
+
from kailash.migration.compatibility_checker import (
|
10
|
+
AnalysisResult,
|
11
|
+
CompatibilityChecker,
|
12
|
+
CompatibilityIssue,
|
13
|
+
IssueSeverity,
|
14
|
+
IssueType,
|
15
|
+
)
|
16
|
+
|
17
|
+
|
18
|
+
@pytest.fixture
|
19
|
+
def checker():
|
20
|
+
"""Create a CompatibilityChecker instance for testing."""
|
21
|
+
return CompatibilityChecker()
|
22
|
+
|
23
|
+
|
24
|
+
@pytest.fixture
|
25
|
+
def temp_project_dir():
|
26
|
+
"""Create a temporary project directory with test files."""
|
27
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
28
|
+
temp_path = Path(temp_dir)
|
29
|
+
|
30
|
+
# Create test Python files
|
31
|
+
(temp_path / "main.py").write_text(
|
32
|
+
textwrap.dedent(
|
33
|
+
"""
|
34
|
+
from kailash.runtime.local import LocalRuntime
|
35
|
+
from kailash.workflow.builder import WorkflowBuilder
|
36
|
+
|
37
|
+
# Legacy configuration
|
38
|
+
runtime = LocalRuntime(
|
39
|
+
enable_parallel=True,
|
40
|
+
thread_pool_size=10,
|
41
|
+
debug_mode=True
|
42
|
+
)
|
43
|
+
|
44
|
+
# Legacy method usage
|
45
|
+
workflow = WorkflowBuilder().build()
|
46
|
+
runtime.execute_sync(workflow)
|
47
|
+
results = runtime.get_results()
|
48
|
+
"""
|
49
|
+
).strip()
|
50
|
+
)
|
51
|
+
|
52
|
+
(temp_path / "config.py").write_text(
|
53
|
+
textwrap.dedent(
|
54
|
+
"""
|
55
|
+
# Configuration with deprecated parameters
|
56
|
+
RUNTIME_CONFIG = {
|
57
|
+
'memory_limit': 1024,
|
58
|
+
'timeout': 300,
|
59
|
+
'log_level': 'DEBUG'
|
60
|
+
}
|
61
|
+
|
62
|
+
def get_runtime():
|
63
|
+
from kailash.runtime.local import LocalRuntime
|
64
|
+
return LocalRuntime(**RUNTIME_CONFIG)
|
65
|
+
"""
|
66
|
+
).strip()
|
67
|
+
)
|
68
|
+
|
69
|
+
(temp_path / "modern.py").write_text(
|
70
|
+
textwrap.dedent(
|
71
|
+
"""
|
72
|
+
from kailash.runtime.local import LocalRuntime
|
73
|
+
from kailash.access_control import UserContext
|
74
|
+
|
75
|
+
# Modern configuration
|
76
|
+
user_context = UserContext(user_id="test")
|
77
|
+
runtime = LocalRuntime(
|
78
|
+
debug=True,
|
79
|
+
max_concurrency=10,
|
80
|
+
enable_security=True,
|
81
|
+
user_context=user_context
|
82
|
+
)
|
83
|
+
"""
|
84
|
+
).strip()
|
85
|
+
)
|
86
|
+
|
87
|
+
# Create subdirectory with test file
|
88
|
+
subdir = temp_path / "subdir"
|
89
|
+
subdir.mkdir()
|
90
|
+
(subdir / "nested.py").write_text(
|
91
|
+
textwrap.dedent(
|
92
|
+
"""
|
93
|
+
from kailash.runtime.local import LocalRuntime
|
94
|
+
|
95
|
+
# More deprecated usage
|
96
|
+
runtime = LocalRuntime(
|
97
|
+
enable_parallel=False,
|
98
|
+
cache_enabled=True
|
99
|
+
)
|
100
|
+
"""
|
101
|
+
).strip()
|
102
|
+
)
|
103
|
+
|
104
|
+
yield temp_path
|
105
|
+
|
106
|
+
|
107
|
+
class TestCompatibilityChecker:
|
108
|
+
"""Test cases for CompatibilityChecker."""
|
109
|
+
|
110
|
+
def test_initialization(self, checker):
|
111
|
+
"""Test CompatibilityChecker initialization."""
|
112
|
+
assert checker is not None
|
113
|
+
assert isinstance(checker.deprecated_parameters, dict)
|
114
|
+
assert isinstance(checker.breaking_changes, dict)
|
115
|
+
assert isinstance(checker.parameter_migrations, dict)
|
116
|
+
assert isinstance(checker.enterprise_patterns, dict)
|
117
|
+
|
118
|
+
def test_analyze_codebase(self, checker, temp_project_dir):
|
119
|
+
"""Test comprehensive codebase analysis."""
|
120
|
+
result = checker.analyze_codebase(temp_project_dir)
|
121
|
+
|
122
|
+
assert isinstance(result, AnalysisResult)
|
123
|
+
assert result.total_files_analyzed > 0
|
124
|
+
assert len(result.issues) > 0
|
125
|
+
assert isinstance(result.summary, dict)
|
126
|
+
assert result.migration_complexity in [
|
127
|
+
"trivial",
|
128
|
+
"low",
|
129
|
+
"medium",
|
130
|
+
"high",
|
131
|
+
"very_high",
|
132
|
+
]
|
133
|
+
assert result.estimated_effort_days >= 0
|
134
|
+
|
135
|
+
def test_deprecated_parameter_detection(self, checker, temp_project_dir):
|
136
|
+
"""Test detection of deprecated parameters."""
|
137
|
+
result = checker.analyze_codebase(temp_project_dir)
|
138
|
+
|
139
|
+
# Should detect deprecated parameters
|
140
|
+
deprecated_issues = [
|
141
|
+
issue
|
142
|
+
for issue in result.issues
|
143
|
+
if issue.issue_type == IssueType.DEPRECATED_PARAMETER
|
144
|
+
]
|
145
|
+
|
146
|
+
assert len(deprecated_issues) > 0
|
147
|
+
|
148
|
+
# Check specific deprecated parameters
|
149
|
+
deprecated_params = [issue.parameter for issue in deprecated_issues]
|
150
|
+
assert any("enable_parallel" in param for param in deprecated_params)
|
151
|
+
assert any("thread_pool_size" in param for param in deprecated_params)
|
152
|
+
|
153
|
+
def test_breaking_change_detection(self, checker, temp_project_dir):
|
154
|
+
"""Test detection of breaking changes."""
|
155
|
+
result = checker.analyze_codebase(temp_project_dir)
|
156
|
+
|
157
|
+
breaking_issues = [
|
158
|
+
issue
|
159
|
+
for issue in result.issues
|
160
|
+
if issue.breaking_change or issue.issue_type == IssueType.BREAKING_CHANGE
|
161
|
+
]
|
162
|
+
|
163
|
+
assert len(breaking_issues) > 0
|
164
|
+
|
165
|
+
# Should detect execute_sync usage
|
166
|
+
execute_sync_issues = [
|
167
|
+
issue for issue in breaking_issues if "execute_sync" in issue.description
|
168
|
+
]
|
169
|
+
assert len(execute_sync_issues) > 0
|
170
|
+
|
171
|
+
def test_parameter_migration_detection(self, checker, temp_project_dir):
|
172
|
+
"""Test detection of parameters that need migration."""
|
173
|
+
result = checker.analyze_codebase(temp_project_dir)
|
174
|
+
|
175
|
+
migration_issues = [
|
176
|
+
issue
|
177
|
+
for issue in result.issues
|
178
|
+
if issue.issue_type == IssueType.CONFIGURATION_UPDATE
|
179
|
+
]
|
180
|
+
|
181
|
+
assert len(migration_issues) > 0
|
182
|
+
|
183
|
+
def test_enterprise_feature_detection(self, checker, temp_project_dir):
|
184
|
+
"""Test detection of enterprise features."""
|
185
|
+
result = checker.analyze_codebase(temp_project_dir)
|
186
|
+
|
187
|
+
enterprise_issues = [
|
188
|
+
issue
|
189
|
+
for issue in result.issues
|
190
|
+
if issue.enterprise_feature
|
191
|
+
or issue.issue_type == IssueType.ENTERPRISE_UPGRADE
|
192
|
+
]
|
193
|
+
|
194
|
+
# Modern.py has UserContext which is an enterprise feature
|
195
|
+
assert len(enterprise_issues) > 0
|
196
|
+
|
197
|
+
def test_complexity_assessment(self, checker, temp_project_dir):
|
198
|
+
"""Test migration complexity assessment."""
|
199
|
+
result = checker.analyze_codebase(temp_project_dir)
|
200
|
+
|
201
|
+
# With deprecated parameters and breaking changes, should not be trivial
|
202
|
+
assert result.migration_complexity != "trivial"
|
203
|
+
assert result.estimated_effort_days > 0
|
204
|
+
|
205
|
+
def test_summary_generation(self, checker, temp_project_dir):
|
206
|
+
"""Test summary statistics generation."""
|
207
|
+
result = checker.analyze_codebase(temp_project_dir)
|
208
|
+
|
209
|
+
assert "total_issues" in result.summary
|
210
|
+
assert "critical_issues" in result.summary
|
211
|
+
assert "high_issues" in result.summary
|
212
|
+
assert "breaking_changes" in result.summary
|
213
|
+
assert "automated_fixes" in result.summary
|
214
|
+
|
215
|
+
# Verify counts make sense
|
216
|
+
total = result.summary["total_issues"]
|
217
|
+
critical = result.summary["critical_issues"]
|
218
|
+
high = result.summary["high_issues"]
|
219
|
+
medium = result.summary["medium_issues"]
|
220
|
+
low = result.summary["low_issues"]
|
221
|
+
|
222
|
+
assert total == len(result.issues)
|
223
|
+
assert critical + high + medium + low <= total
|
224
|
+
|
225
|
+
def test_text_report_generation(self, checker, temp_project_dir):
|
226
|
+
"""Test text format report generation."""
|
227
|
+
result = checker.analyze_codebase(temp_project_dir)
|
228
|
+
report = checker.generate_report(result, "text")
|
229
|
+
|
230
|
+
assert isinstance(report, str)
|
231
|
+
assert len(report) > 0
|
232
|
+
assert "Migration Compatibility Report" in report
|
233
|
+
assert "SUMMARY" in report
|
234
|
+
assert "ISSUE BREAKDOWN" in report
|
235
|
+
|
236
|
+
def test_json_report_generation(self, checker, temp_project_dir):
|
237
|
+
"""Test JSON format report generation."""
|
238
|
+
result = checker.analyze_codebase(temp_project_dir)
|
239
|
+
report = checker.generate_report(result, "json")
|
240
|
+
|
241
|
+
assert isinstance(report, str)
|
242
|
+
|
243
|
+
# Should be valid JSON
|
244
|
+
import json
|
245
|
+
|
246
|
+
data = json.loads(report)
|
247
|
+
|
248
|
+
assert "summary" in data
|
249
|
+
assert "migration_complexity" in data
|
250
|
+
assert "issues" in data
|
251
|
+
assert isinstance(data["issues"], list)
|
252
|
+
|
253
|
+
def test_markdown_report_generation(self, checker, temp_project_dir):
|
254
|
+
"""Test markdown format report generation."""
|
255
|
+
result = checker.analyze_codebase(temp_project_dir)
|
256
|
+
report = checker.generate_report(result, "markdown")
|
257
|
+
|
258
|
+
assert isinstance(report, str)
|
259
|
+
assert len(report) > 0
|
260
|
+
assert "# LocalRuntime Migration Compatibility Report" in report
|
261
|
+
assert "## Summary" in report
|
262
|
+
assert "| Metric | Value |" in report
|
263
|
+
|
264
|
+
def test_include_exclude_patterns(self, checker, temp_project_dir):
|
265
|
+
"""Test file inclusion and exclusion patterns."""
|
266
|
+
# Test inclusion patterns
|
267
|
+
result_py_only = checker.analyze_codebase(
|
268
|
+
temp_project_dir, include_patterns=["*.py"]
|
269
|
+
)
|
270
|
+
|
271
|
+
# Test exclusion patterns
|
272
|
+
result_exclude_subdir = checker.analyze_codebase(
|
273
|
+
temp_project_dir, exclude_patterns=["subdir"]
|
274
|
+
)
|
275
|
+
|
276
|
+
assert (
|
277
|
+
result_py_only.total_files_analyzed
|
278
|
+
>= result_exclude_subdir.total_files_analyzed
|
279
|
+
)
|
280
|
+
|
281
|
+
def test_error_handling(self, checker):
|
282
|
+
"""Test error handling for invalid inputs."""
|
283
|
+
# Non-existent directory
|
284
|
+
result = checker.analyze_codebase("/non/existent/path")
|
285
|
+
|
286
|
+
assert result.total_files_analyzed == 0
|
287
|
+
assert len(result.issues) == 0
|
288
|
+
|
289
|
+
def test_syntax_error_handling(self, checker):
|
290
|
+
"""Test handling of Python files with syntax errors."""
|
291
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
292
|
+
temp_path = Path(temp_dir)
|
293
|
+
|
294
|
+
# Create file with syntax error
|
295
|
+
(temp_path / "broken.py").write_text(
|
296
|
+
"def broken_function(\n # Missing closing parenthesis"
|
297
|
+
)
|
298
|
+
|
299
|
+
result = checker.analyze_codebase(temp_path)
|
300
|
+
|
301
|
+
# Should detect the syntax error
|
302
|
+
syntax_errors = [
|
303
|
+
issue
|
304
|
+
for issue in result.issues
|
305
|
+
if "syntax error" in issue.description.lower()
|
306
|
+
]
|
307
|
+
assert len(syntax_errors) > 0
|
308
|
+
|
309
|
+
def test_enterprise_opportunities_identification(self, checker, temp_project_dir):
|
310
|
+
"""Test identification of enterprise upgrade opportunities."""
|
311
|
+
result = checker.analyze_codebase(temp_project_dir)
|
312
|
+
|
313
|
+
# Should identify opportunities based on patterns in the code
|
314
|
+
assert len(result.enterprise_opportunities) >= 0
|
315
|
+
|
316
|
+
if result.enterprise_opportunities:
|
317
|
+
# Should be meaningful suggestions
|
318
|
+
for opportunity in result.enterprise_opportunities:
|
319
|
+
assert isinstance(opportunity, str)
|
320
|
+
assert len(opportunity) > 10 # Reasonable description length
|
321
|
+
|
322
|
+
def test_issue_severity_assignment(self, checker, temp_project_dir):
|
323
|
+
"""Test proper severity assignment to issues."""
|
324
|
+
result = checker.analyze_codebase(temp_project_dir)
|
325
|
+
|
326
|
+
# Check that issues have appropriate severities
|
327
|
+
for issue in result.issues:
|
328
|
+
assert issue.severity in [
|
329
|
+
IssueSeverity.CRITICAL,
|
330
|
+
IssueSeverity.HIGH,
|
331
|
+
IssueSeverity.MEDIUM,
|
332
|
+
IssueSeverity.LOW,
|
333
|
+
IssueSeverity.INFO,
|
334
|
+
]
|
335
|
+
|
336
|
+
# Breaking changes should be high or critical severity
|
337
|
+
if issue.breaking_change:
|
338
|
+
assert issue.severity in [IssueSeverity.CRITICAL, IssueSeverity.HIGH]
|
339
|
+
|
340
|
+
def test_automated_fix_identification(self, checker, temp_project_dir):
|
341
|
+
"""Test identification of issues that can be automatically fixed."""
|
342
|
+
result = checker.analyze_codebase(temp_project_dir)
|
343
|
+
|
344
|
+
automated_issues = [issue for issue in result.issues if issue.automated_fix]
|
345
|
+
|
346
|
+
# Should have at least some automated fixes for deprecated parameters
|
347
|
+
assert len(automated_issues) > 0
|
348
|
+
|
349
|
+
# Automated fixes should typically be for configuration/parameter issues
|
350
|
+
for issue in automated_issues:
|
351
|
+
assert issue.issue_type in [
|
352
|
+
IssueType.DEPRECATED_PARAMETER,
|
353
|
+
IssueType.CONFIGURATION_UPDATE,
|
354
|
+
IssueType.PERFORMANCE_OPTIMIZATION,
|
355
|
+
]
|
356
|
+
|
357
|
+
|
358
|
+
class TestCompatibilityIssue:
|
359
|
+
"""Test cases for CompatibilityIssue dataclass."""
|
360
|
+
|
361
|
+
def test_creation(self):
|
362
|
+
"""Test CompatibilityIssue creation."""
|
363
|
+
issue = CompatibilityIssue(
|
364
|
+
issue_type=IssueType.DEPRECATED_PARAMETER,
|
365
|
+
severity=IssueSeverity.HIGH,
|
366
|
+
description="Test issue",
|
367
|
+
file_path="/test/file.py",
|
368
|
+
line_number=10,
|
369
|
+
code_snippet="test = True",
|
370
|
+
recommendation="Update parameter",
|
371
|
+
migration_effort="low",
|
372
|
+
)
|
373
|
+
|
374
|
+
assert issue.issue_type == IssueType.DEPRECATED_PARAMETER
|
375
|
+
assert issue.severity == IssueSeverity.HIGH
|
376
|
+
assert issue.description == "Test issue"
|
377
|
+
assert issue.file_path == "/test/file.py"
|
378
|
+
assert issue.line_number == 10
|
379
|
+
assert issue.code_snippet == "test = True"
|
380
|
+
assert issue.recommendation == "Update parameter"
|
381
|
+
assert issue.migration_effort == "low"
|
382
|
+
assert issue.automated_fix is False
|
383
|
+
assert issue.breaking_change is False
|
384
|
+
assert issue.enterprise_feature is False
|
385
|
+
|
386
|
+
|
387
|
+
class TestAnalysisResult:
|
388
|
+
"""Test cases for AnalysisResult dataclass."""
|
389
|
+
|
390
|
+
def test_creation(self):
|
391
|
+
"""Test AnalysisResult creation."""
|
392
|
+
result = AnalysisResult(total_files_analyzed=5)
|
393
|
+
|
394
|
+
assert result.total_files_analyzed == 5
|
395
|
+
assert isinstance(result.issues, list)
|
396
|
+
assert isinstance(result.summary, dict)
|
397
|
+
assert result.migration_complexity == "unknown"
|
398
|
+
assert result.estimated_effort_days == 0.0
|
399
|
+
assert isinstance(result.enterprise_opportunities, list)
|
400
|
+
|
401
|
+
|
402
|
+
if __name__ == "__main__":
|
403
|
+
pytest.main([__file__])
|