Files
breakpilot-lehrer/admin-lehrer/app/(admin)/infrastructure/tests/page.tsx
Benjamin Admin 9ba420fa91
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 42s
CI / test-go-edu-search (push) Successful in 34s
CI / test-python-klausur (push) Failing after 2m51s
CI / test-python-agent-core (push) Successful in 21s
CI / test-nodejs-website (push) Successful in 29s
Fix: Remove broken getKlausurApiUrl and clean up empty lines
sed replacement left orphaned hostname references in story page
and empty lines in getApiBase functions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 16:02:04 +02:00

321 lines
12 KiB
TypeScript

'use client'
/**
* Test Dashboard - Zentrales Test-Registry
*
* Aggregiert alle 280+ Tests aus allen Services:
* - Go Unit Tests (~57)
* - Python Tests (~50)
* - BQAS Golden (97)
* - BQAS RAG (~20)
* - TypeScript Jest (~8)
* - SDK Vitest Unit Tests (~43)
* - SDK Playwright E2E (~25)
* - E2E Playwright (~5)
*/
import React from 'react'
import Link from 'next/link'
import { PagePurpose } from '@/components/common/PagePurpose'
import { DevOpsPipelineSidebarResponsive } from '@/components/infrastructure/DevOpsPipelineSidebar'
import type { TabType } from './types'
import { useTestDashboard } from './_hooks/useTestDashboard'
import { ToastContainer } from './_components/ToastContainer'
import { MetricCard } from './_components/MetricCard'
import { ServiceTestCard } from './_components/ServiceTestCard'
import { CoverageChart, FrameworkDistribution } from './_components/CoverageChart'
import { TestRunsTable } from './_components/TestRunsTable'
import { GuideTab } from './_components/GuideTab'
import { BacklogTab } from './_components/BacklogTab'
export default function TestDashboardPage() {
const {
activeTab,
setActiveTab,
isLoading,
error,
fetchData,
toasts,
removeToast,
services,
stats,
coverage,
testRuns,
failedTests,
backlogItems,
usePostgres,
runningServices,
serviceProgress,
updateTestStatus,
updateTestPriority,
runTests,
unitServices,
bqasServices,
} = useTestDashboard()
const renderTabContent = () => {
switch (activeTab) {
case 'overview':
return (
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<MetricCard
title="Gesamt Tests"
value={stats?.total_tests || 195}
subtitle={`${stats?.services_count || 8} Services`}
color="orange"
/>
<MetricCard
title="Pass Rate"
value={`${stats?.overall_pass_rate?.toFixed(1) || 92.3}%`}
subtitle={`${stats?.total_passed || 180} bestanden`}
trend="up"
color="green"
/>
<MetricCard
title="Fehlgeschlagen"
value={stats?.total_failed || 15}
subtitle="Tests mit Fehlern"
color="red"
/>
<MetricCard
title="Coverage"
value={`${stats?.average_coverage?.toFixed(1) || 76.8}%`}
subtitle="Durchschnitt"
color="purple"
/>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h3 className="text-lg font-semibold text-slate-900 mb-4">Framework-Verteilung</h3>
<FrameworkDistribution data={stats?.by_framework || {}} />
</div>
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h3 className="text-lg font-semibold text-slate-900 mb-4">Coverage nach Service</h3>
<CoverageChart data={coverage} />
</div>
</div>
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h3 className="text-lg font-semibold text-slate-900 mb-4">Service-Uebersicht</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{services.slice(0, 8).map((service) => (
<ServiceTestCard
key={service.service}
service={service}
onRun={runTests}
isRunning={runningServices.has(service.service)}
progress={serviceProgress[service.service]}
/>
))}
</div>
</div>
</div>
)
case 'unit':
return (
<div className="space-y-6">
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h3 className="text-lg font-semibold text-slate-900 mb-4">Unit Tests (Go & Python)</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{unitServices.map((service) => (
<ServiceTestCard
key={service.service}
service={service}
onRun={runTests}
isRunning={runningServices.has(service.service)}
progress={serviceProgress[service.service]}
/>
))}
</div>
</div>
</div>
)
case 'bqas':
return (
<div className="space-y-6">
<div className="bg-white rounded-xl border border-slate-200 p-6">
<div className="flex items-center justify-between mb-6">
<div>
<h3 className="text-lg font-semibold text-slate-900">BQAS (LLM Quality Assurance)</h3>
<p className="text-sm text-slate-500">Golden Suite, RAG Tests und Synthetic Tests</p>
</div>
<Link
href="/ai/test-quality"
className="px-4 py-2 bg-orange-100 text-orange-700 rounded-lg text-sm font-medium hover:bg-orange-200 transition-colors"
>
Vollstaendiges BQAS Dashboard
</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{bqasServices.map((service) => (
<ServiceTestCard
key={service.service}
service={service}
onRun={runTests}
isRunning={runningServices.has(service.service)}
progress={serviceProgress[service.service]}
/>
))}
</div>
</div>
<div className="bg-blue-50 border border-blue-200 rounded-xl p-4">
<div className="flex items-start gap-3">
<svg className="w-5 h-5 text-blue-600 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<div>
<p className="text-sm text-blue-800">
<strong>Tipp:</strong> Das vollstaendige BQAS Dashboard unter <Link href="/ai/test-quality" className="underline">/ai/test-quality</Link> bietet
detaillierte Metriken, Trend-Analyse und Intent-spezifische Scores.
</p>
</div>
</div>
</div>
</div>
)
case 'history':
return (
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h3 className="text-lg font-semibold text-slate-900 mb-6">Test Run Historie</h3>
<TestRunsTable runs={testRuns} />
</div>
)
case 'backlog':
return (
<BacklogTab
failedTests={failedTests}
onStatusChange={updateTestStatus}
onPriorityChange={usePostgres ? updateTestPriority : undefined}
isLoading={isLoading}
backlogItems={backlogItems}
usePostgres={usePostgres}
/>
)
case 'guide':
return <GuideTab />
default:
return null
}
}
return (
<div className="space-y-6">
<ToastContainer toasts={toasts} onDismiss={removeToast} />
<PagePurpose
title="Test Dashboard"
purpose="Zentrales Dashboard fuer alle 260+ Tests. Aggregiert Unit Tests (Go, Python), SDK Tests (Vitest), E2E Tests (Playwright) und BQAS Quality Tests aus allen Services ohne physische Migration."
audience={['Entwickler', 'QA', 'DevOps']}
architecture={{
services: ['Python Backend (Port 8000)', 'Voice Service (Port 8091)', 'SDK Backend (Port 8085)'],
databases: ['PostgreSQL', 'Qdrant'],
}}
relatedPages={[
{ name: 'BQAS Dashboard', href: '/ai/test-quality', description: 'Detaillierte LLM-Qualitaetsmetriken' },
{ name: 'CI/CD', href: '/infrastructure/ci-cd', description: 'Pipelines und Deployments' },
{ name: 'Security', href: '/infrastructure/security', description: 'DevSecOps Dashboard' },
{ name: 'Developer Portal', href: '/developers', description: 'SDK & API Dokumentation' },
]}
collapsible={true}
defaultCollapsed={true}
/>
{/* DevOps Pipeline Sidebar */}
<DevOpsPipelineSidebarResponsive currentTool="tests" />
{error && (
<div className="p-4 bg-red-50 border border-red-200 rounded-lg text-red-700 flex items-center gap-3">
<svg className="w-5 h-5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>{error}</span>
<button onClick={fetchData} className="ml-auto text-red-600 hover:text-red-800 text-sm font-medium">
Erneut versuchen
</button>
</div>
)}
<div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
<div className="border-b border-slate-200">
<nav className="flex gap-6 px-6">
{[
{ id: 'overview', label: 'Uebersicht' },
{ id: 'unit', label: 'Unit Tests' },
{ id: 'bqas', label: 'BQAS' },
{ id: 'backlog', label: `Backlog (${failedTests.filter(t => t.status === 'open').length})`, highlight: failedTests.filter(t => t.status === 'open').length > 0 },
{ id: 'history', label: 'Historie' },
{ id: 'guide', label: 'Anleitung' },
].map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id as TabType)}
className={`py-4 text-sm font-medium border-b-2 transition-colors ${
activeTab === tab.id
? 'border-orange-600 text-orange-600'
: 'highlight' in tab && tab.highlight
? 'border-transparent text-blue-500 hover:text-blue-600'
: 'border-transparent text-slate-500 hover:text-slate-700'
}`}
>
{tab.label}
</button>
))}
</nav>
</div>
<div className="p-6">
{isLoading ? (
<div className="flex items-center justify-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-orange-600"></div>
</div>
) : (
renderTabContent()
)}
</div>
</div>
<div className="flex items-center justify-between text-sm text-slate-500 px-2">
<div>
<span className="font-medium">Test Registry:</span> /api/tests
</div>
<div className="flex items-center gap-4">
<Link href="/ai/test-quality" className="text-orange-600 hover:text-orange-700">
BQAS Details
</Link>
<Link href="/infrastructure/ci-cd" className="text-orange-600 hover:text-orange-700">
CI/CD Pipelines
</Link>
</div>
</div>
<style jsx>{`
@keyframes slide-in {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.animate-slide-in {
animation: slide-in 0.3s ease-out;
}
`}</style>
</div>
)
}