Fix: Remove broken getKlausurApiUrl and clean up empty lines
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
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
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>
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect, useCallback, useRef } from 'react'
|
||||
import type {
|
||||
ServiceTestInfo,
|
||||
TestRegistryStats,
|
||||
TestRun,
|
||||
CoverageData,
|
||||
TabType,
|
||||
Toast,
|
||||
FailedTest,
|
||||
BacklogItem,
|
||||
} from '../types'
|
||||
import { API_BASE, DEMO_SERVICES, DEMO_STATS } from '../_lib/constants'
|
||||
import type { ServiceProgress } from '../_components/ServiceTestCard'
|
||||
|
||||
export function useTestDashboard() {
|
||||
const [activeTab, setActiveTab] = useState<TabType>('overview')
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
// Toast state
|
||||
const [toasts, setToasts] = useState<Toast[]>([])
|
||||
const toastIdRef = useRef(0)
|
||||
|
||||
const addToast = useCallback((type: Toast['type'], message: string) => {
|
||||
const id = ++toastIdRef.current
|
||||
setToasts((prev) => [...prev, { id, type, message }])
|
||||
if (type !== 'loading') {
|
||||
setTimeout(() => {
|
||||
setToasts((prev) => prev.filter((t) => t.id !== id))
|
||||
}, 5000)
|
||||
}
|
||||
return id
|
||||
}, [])
|
||||
|
||||
const removeToast = useCallback((id: number) => {
|
||||
setToasts((prev) => prev.filter((t) => t.id !== id))
|
||||
}, [])
|
||||
|
||||
const updateToast = useCallback((id: number, type: Toast['type'], message: string) => {
|
||||
setToasts((prev) => prev.map((t) => (t.id === id ? { ...t, type, message } : t)))
|
||||
if (type !== 'loading') {
|
||||
setTimeout(() => {
|
||||
setToasts((prev) => prev.filter((t) => t.id !== id))
|
||||
}, 5000)
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Data states
|
||||
const [services, setServices] = useState<ServiceTestInfo[]>([])
|
||||
const [stats, setStats] = useState<TestRegistryStats | null>(null)
|
||||
const [coverage, setCoverage] = useState<CoverageData[]>([])
|
||||
const [testRuns, setTestRuns] = useState<TestRun[]>([])
|
||||
const [failedTests, setFailedTests] = useState<FailedTest[]>([])
|
||||
const [backlogItems, setBacklogItems] = useState<BacklogItem[]>([])
|
||||
const [usePostgres, setUsePostgres] = useState(false)
|
||||
|
||||
// Running states
|
||||
const [runningServices, setRunningServices] = useState<Set<string>>(new Set())
|
||||
|
||||
// Progress states fuer laufende Tests
|
||||
const [serviceProgress, setServiceProgress] = useState<Record<string, ServiceProgress>>({})
|
||||
|
||||
// Fetch data
|
||||
const fetchData = useCallback(async () => {
|
||||
setIsLoading(true)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
const registryResponse = await fetch(`${API_BASE}/registry`)
|
||||
if (registryResponse.ok) {
|
||||
const data = await registryResponse.json()
|
||||
setServices(data.services || DEMO_SERVICES)
|
||||
setStats(data.stats || DEMO_STATS)
|
||||
} else {
|
||||
setServices(DEMO_SERVICES)
|
||||
setStats(DEMO_STATS)
|
||||
}
|
||||
|
||||
const coverageResponse = await fetch(`${API_BASE}/coverage`)
|
||||
if (coverageResponse.ok) {
|
||||
const data = await coverageResponse.json()
|
||||
setCoverage(data.services || [])
|
||||
} else {
|
||||
setCoverage(DEMO_SERVICES.filter(s => s.coverage_percent).map(s => ({
|
||||
service: s.service,
|
||||
display_name: s.display_name,
|
||||
coverage_percent: s.coverage_percent!,
|
||||
language: s.language,
|
||||
})))
|
||||
}
|
||||
|
||||
const runsResponse = await fetch(`${API_BASE}/runs`)
|
||||
if (runsResponse.ok) {
|
||||
const data = await runsResponse.json()
|
||||
setTestRuns(data.runs || [])
|
||||
}
|
||||
|
||||
// Lade fehlgeschlagene Tests fuer Backlog
|
||||
const failedResponse = await fetch(`${API_BASE}/failed`)
|
||||
if (failedResponse.ok) {
|
||||
const data = await failedResponse.json()
|
||||
setFailedTests(data.tests || [])
|
||||
}
|
||||
|
||||
// Versuche PostgreSQL-Backlog zu laden (neue API)
|
||||
try {
|
||||
const backlogResponse = await fetch(`${API_BASE}/backlog`)
|
||||
if (backlogResponse.ok) {
|
||||
const data = await backlogResponse.json()
|
||||
if (data.items && data.items.length > 0) {
|
||||
setBacklogItems(data.items)
|
||||
setUsePostgres(true)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// PostgreSQL nicht verfuegbar, nutze Legacy
|
||||
setUsePostgres(false)
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch test registry data:', err)
|
||||
setServices(DEMO_SERVICES)
|
||||
setStats(DEMO_STATS)
|
||||
setCoverage(DEMO_SERVICES.filter(s => s.coverage_percent).map(s => ({
|
||||
service: s.service,
|
||||
display_name: s.display_name,
|
||||
coverage_percent: s.coverage_percent!,
|
||||
language: s.language,
|
||||
})))
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
fetchData()
|
||||
}, [fetchData])
|
||||
|
||||
// Update failed test status
|
||||
const updateTestStatus = async (testId: string, status: string) => {
|
||||
try {
|
||||
// Nutze PostgreSQL-Endpoint wenn verfuegbar
|
||||
const endpoint = usePostgres
|
||||
? `${API_BASE}/backlog/${testId}/status`
|
||||
: `${API_BASE}/failed/${encodeURIComponent(testId)}/status?status=${status}`
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: usePostgres ? { 'Content-Type': 'application/json' } : undefined,
|
||||
body: usePostgres ? JSON.stringify({ status }) : undefined,
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
// Aktualisiere lokalen State
|
||||
if (usePostgres) {
|
||||
setBacklogItems(prev =>
|
||||
prev.map(t => String(t.id) === testId ? { ...t, status: status as any } : t)
|
||||
)
|
||||
}
|
||||
setFailedTests(prev =>
|
||||
prev.map(t => t.id === testId ? { ...t, status: status as any } : t)
|
||||
)
|
||||
addToast('success', `Test-Status auf "${status}" gesetzt`)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to update test status:', err)
|
||||
// Trotzdem lokal aktualisieren fuer bessere UX
|
||||
setFailedTests(prev =>
|
||||
prev.map(t => t.id === testId ? { ...t, status: status as any } : t)
|
||||
)
|
||||
if (usePostgres) {
|
||||
setBacklogItems(prev =>
|
||||
prev.map(t => String(t.id) === testId ? { ...t, status: status as any } : t)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update failed test priority (nur PostgreSQL)
|
||||
const updateTestPriority = async (testId: string, priority: string) => {
|
||||
if (!usePostgres) return
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/backlog/${testId}/priority`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ priority }),
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
setBacklogItems(prev =>
|
||||
prev.map(t => String(t.id) === testId ? { ...t, priority: priority as any } : t)
|
||||
)
|
||||
addToast('success', `Prioritaet auf "${priority}" gesetzt`)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to update test priority:', err)
|
||||
// Trotzdem lokal aktualisieren
|
||||
setBacklogItems(prev =>
|
||||
prev.map(t => String(t.id) === testId ? { ...t, priority: priority as any } : t)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Run tests mit Progress-Polling
|
||||
const runTests = async (service: string) => {
|
||||
setRunningServices((prev) => new Set(prev).add(service))
|
||||
const loadingToast = addToast('loading', `Tests fuer ${service} werden gestartet...`)
|
||||
|
||||
// Progress-Polling starten
|
||||
let pollInterval: NodeJS.Timeout | null = null
|
||||
const pollProgress = async () => {
|
||||
try {
|
||||
const progressResponse = await fetch(`${API_BASE}/progress/${service}`)
|
||||
if (progressResponse.ok) {
|
||||
const progress = await progressResponse.json()
|
||||
setServiceProgress((prev) => ({
|
||||
...prev,
|
||||
[service]: progress,
|
||||
}))
|
||||
|
||||
// Toast-Message mit aktuellem Fortschritt aktualisieren
|
||||
if (progress.status === 'running' && progress.files_total > 0) {
|
||||
const toastMsg = `${service}: ${progress.current_file} (${progress.passed} bestanden, ${progress.failed} fehler)`
|
||||
updateToast(loadingToast, 'loading', toastMsg)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore polling errors
|
||||
}
|
||||
}
|
||||
|
||||
// Start polling (alle 1 Sekunde)
|
||||
pollInterval = setInterval(pollProgress, 1000)
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/run/${service}`, {
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
// Warte kurz und pruefe finalen Progress
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
await pollProgress()
|
||||
const finalProgress = serviceProgress[service]
|
||||
const passedMsg = finalProgress ? `${finalProgress.passed} bestanden, ${finalProgress.failed} fehler` : 'abgeschlossen'
|
||||
updateToast(loadingToast, 'success', `${service}: Tests ${passedMsg}`)
|
||||
await fetchData()
|
||||
} else {
|
||||
updateToast(loadingToast, 'info', `${service}: Demo-Modus (API nicht verfuegbar)`)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to run tests:', err)
|
||||
updateToast(loadingToast, 'info', `${service}: Demo-Modus (API nicht verfuegbar)`)
|
||||
} finally {
|
||||
// Polling stoppen
|
||||
if (pollInterval) {
|
||||
clearInterval(pollInterval)
|
||||
}
|
||||
setRunningServices((prev) => {
|
||||
const next = new Set(prev)
|
||||
next.delete(service)
|
||||
return next
|
||||
})
|
||||
// Progress-Daten entfernen nach Abschluss
|
||||
setServiceProgress((prev) => {
|
||||
const next = { ...prev }
|
||||
delete next[service]
|
||||
return next
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Filter services by category
|
||||
const unitServices = services.filter(s => !s.service.startsWith('bqas-'))
|
||||
const bqasServices = services.filter(s => s.service.startsWith('bqas-'))
|
||||
|
||||
return {
|
||||
// Tab
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
// Loading / Error
|
||||
isLoading,
|
||||
error,
|
||||
fetchData,
|
||||
// Toast
|
||||
toasts,
|
||||
removeToast,
|
||||
// Data
|
||||
services,
|
||||
stats,
|
||||
coverage,
|
||||
testRuns,
|
||||
failedTests,
|
||||
backlogItems,
|
||||
usePostgres,
|
||||
// Running
|
||||
runningServices,
|
||||
serviceProgress,
|
||||
// Actions
|
||||
updateTestStatus,
|
||||
updateTestPriority,
|
||||
runTests,
|
||||
// Derived
|
||||
unitServices,
|
||||
bqasServices,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user