diff --git a/app/api/v1/admin/tests.py b/app/api/v1/admin/tests.py index cdda259a..914e081c 100644 --- a/app/api/v1/admin/tests.py +++ b/app/api/v1/admin/tests.py @@ -85,6 +85,12 @@ class TestDashboardStatsResponse(BaseModel): # Collection stats total_test_files: int + collected_tests: int + unit_tests: int + integration_tests: int + performance_tests: int + system_tests: int + last_collected: str | None # Trend and breakdown data trend: list[dict] diff --git a/app/services/test_runner_service.py b/app/services/test_runner_service.py index b01aeb46..275db6a3 100644 --- a/app/services/test_runner_service.py +++ b/app/services/test_runner_service.py @@ -375,6 +375,12 @@ class TestRunnerService: # Collection stats "total_test_files": collection.total_files if collection else 0, + "collected_tests": collection.total_tests if collection else 0, + "unit_tests": collection.unit_tests if collection else 0, + "integration_tests": collection.integration_tests if collection else 0, + "performance_tests": collection.performance_tests if collection else 0, + "system_tests": collection.system_tests if collection else 0, + "last_collected": collection.collected_at.isoformat() if collection else None, # Trend data "trend": [ @@ -410,47 +416,70 @@ class TestRunnerService: ) try: - # Run pytest --collect-only + # Run pytest --collect-only with JSON report + with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f: + json_report_path = f.name + result = subprocess.run( - ["python", "-m", "pytest", "--collect-only", "-q", "tests"], + [ + "python", "-m", "pytest", + "--collect-only", + "--json-report", + f"--json-report-file={json_report_path}", + "tests" + ], cwd=str(self.project_root), capture_output=True, text=True, - timeout=60, + timeout=120, ) - # Parse output - lines = result.stdout.strip().split('\n') - test_files = {} + # Parse JSON report + json_path = Path(json_report_path) + if json_path.exists(): + with open(json_path) as f: + report = json.load(f) - for line in lines: - if "::" in line: - file_path = line.split("::")[0] - if file_path not in test_files: - test_files[file_path] = 0 - test_files[file_path] += 1 + # Get total from summary + collection.total_tests = report.get("summary", {}).get("collected", 0) - # Count by category - for file_path, count in test_files.items(): - collection.total_tests += count - collection.total_files += 1 + # Parse collectors to get test files and counts + test_files = {} + for collector in report.get("collectors", []): + for item in collector.get("result", []): + if item.get("type") == "Function": + node_id = item.get("nodeid", "") + if "::" in node_id: + file_path = node_id.split("::")[0] + if file_path not in test_files: + test_files[file_path] = 0 + test_files[file_path] += 1 - if "unit" in file_path: - collection.unit_tests += count - elif "integration" in file_path: - collection.integration_tests += count - elif "performance" in file_path: - collection.performance_tests += count - elif "system" in file_path: - collection.system_tests += count + # Count files and categorize + for file_path, count in test_files.items(): + collection.total_files += 1 - collection.test_files = [ - {"file": f, "count": c} - for f, c in sorted(test_files.items(), key=lambda x: -x[1]) - ] + if "/unit/" in file_path or file_path.startswith("tests/unit"): + collection.unit_tests += count + elif "/integration/" in file_path or file_path.startswith("tests/integration"): + collection.integration_tests += count + elif "/performance/" in file_path or file_path.startswith("tests/performance"): + collection.performance_tests += count + elif "/system/" in file_path or file_path.startswith("tests/system"): + collection.system_tests += count + + collection.test_files = [ + {"file": f, "count": c} + for f, c in sorted(test_files.items(), key=lambda x: -x[1]) + ] + + # Cleanup + json_path.unlink(missing_ok=True) + + logger.info(f"Collected {collection.total_tests} tests from {collection.total_files} files") except Exception as e: - logger.error(f"Error collecting tests: {e}") + logger.error(f"Error collecting tests: {e}", exc_info=True) db.add(collection) return collection diff --git a/app/templates/admin/partials/sidebar.html b/app/templates/admin/partials/sidebar.html index 81ce0039..b0bcf87a 100644 --- a/app/templates/admin/partials/sidebar.html +++ b/app/templates/admin/partials/sidebar.html @@ -96,14 +96,19 @@ {% call section_content('devTools') %} {{ menu_item('components', '/admin/components', 'view-grid', 'Components') }} {{ menu_item('icons', '/admin/icons', 'photograph', 'Icons') }} + {% endcall %} + + + {{ section_header('Platform Health', 'platformHealth') }} + {% call section_content('platformHealth') %} {{ menu_item('testing', '/admin/testing', 'beaker', 'Testing Hub') }} {{ menu_item('code-quality', '/admin/code-quality', 'shield-check', 'Code Quality') }} + {{ menu_item('background-tasks', '/admin/background-tasks', 'collection', 'Background Tasks') }} {% endcall %} {{ section_header('Platform Monitoring', 'monitoring') }} {% call section_content('monitoring') %} - {{ menu_item('background-tasks', '/admin/background-tasks', 'collection', 'Background Tasks') }} {{ menu_item('imports', '/admin/imports', 'cube', 'Import Jobs') }} {{ menu_item('logs', '/admin/logs', 'document-text', 'Application Logs') }} {% endcall %} diff --git a/app/templates/admin/testing-dashboard.html b/app/templates/admin/testing-dashboard.html index 6c858ba9..77b67753 100644 --- a/app/templates/admin/testing-dashboard.html +++ b/app/templates/admin/testing-dashboard.html @@ -153,6 +153,53 @@ + +
+
+

+ Test Collection +

+ + Last collected: + +
+ + +
+
diff --git a/static/admin/js/init-alpine.js b/static/admin/js/init-alpine.js index 42fcafb6..5c75e35b 100644 --- a/static/admin/js/init-alpine.js +++ b/static/admin/js/init-alpine.js @@ -31,6 +31,7 @@ function data() { productCatalog: false, contentMgmt: false, devTools: false, + platformHealth: false, monitoring: false, settingsSection: false }; @@ -73,8 +74,10 @@ function data() { // Developer Tools components: 'devTools', icons: 'devTools', - testing: 'devTools', - 'code-quality': 'devTools', + // Platform Health + testing: 'platformHealth', + 'code-quality': 'platformHealth', + 'background-tasks': 'platformHealth', // Platform Monitoring imports: 'monitoring', logs: 'monitoring', diff --git a/static/admin/js/testing-dashboard.js b/static/admin/js/testing-dashboard.js index 9d651e45..d9e1888d 100644 --- a/static/admin/js/testing-dashboard.js +++ b/static/admin/js/testing-dashboard.js @@ -38,6 +38,12 @@ function testingDashboard() { last_run: null, last_run_status: null, total_test_files: 0, + collected_tests: 0, + unit_tests: 0, + integration_tests: 0, + performance_tests: 0, + system_tests: 0, + last_collected: null, trend: [], by_category: {}, top_failing: []