feat: add integration test framework and health check improvements

- Add test helper library with assertion functions (scripts/lib/test-helpers.sh)
- Add gateway integration test script (scripts/tests/gateway-test.sh)
- Add configuration validation tool (scripts/validate-config.ts)
- Add health-check.ts library with Tauri command wrappers
- Add HealthStatusIndicator component to ConnectionStatus.tsx
- Add E2E test specs for memory, settings, and team collaboration
- Update ZCLAW-DEEP-ANALYSIS.md to reflect actual project state

Key improvements:
- Store architecture now properly documented as migrated
- Tauri backend shown as 85-90% complete
- Component integration status clarified

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-03-21 00:09:47 +08:00
parent ce522de7e9
commit c5d91cf9f0
11 changed files with 4911 additions and 26 deletions

View File

@@ -3,13 +3,21 @@
*
* Displays the current Gateway connection status with visual indicators.
* Supports automatic reconnect and manual reconnect button.
* Includes health status indicator for OpenFang backend.
*/
import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Wifi, WifiOff, Loader2, RefreshCw } from 'lucide-react';
import { useGatewayStore } from '../store/gatewayStore';
import { Wifi, WifiOff, Loader2, RefreshCw, Heart, HeartPulse } from 'lucide-react';
import { useConnectionStore } from '../store/connectionStore';
import { getGatewayClient } from '../lib/gateway-client';
import {
createHealthCheckScheduler,
getHealthStatusLabel,
formatHealthCheckTime,
type HealthCheckResult,
type HealthStatus,
} from '../lib/health-check';
interface ConnectionStatusProps {
/** Show compact version (just icon and status text) */
@@ -75,7 +83,8 @@ export function ConnectionStatus({
showReconnectButton = true,
className = '',
}: ConnectionStatusProps) {
const { connectionState, connect } = useGatewayStore();
const connectionState = useConnectionStore((s) => s.connectionState);
const connect = useConnectionStore((s) => s.connect);
const [showPrompt, setShowPrompt] = useState(false);
const [reconnectInfo, setReconnectInfo] = useState<ReconnectInfo | null>(null);
@@ -188,7 +197,7 @@ export function ConnectionStatus({
* ConnectionIndicator - Minimal connection indicator for headers
*/
export function ConnectionIndicator({ className = '' }: { className?: string }) {
const { connectionState } = useGatewayStore();
const connectionState = useConnectionStore((s) => s.connectionState);
const isConnected = connectionState === 'connected';
const isReconnecting = connectionState === 'reconnecting';
@@ -221,4 +230,58 @@ export function ConnectionIndicator({ className = '' }: { className?: string })
);
}
/**
* HealthStatusIndicator - Displays OpenFang backend health status
*/
export function HealthStatusIndicator({
className = '',
showDetails = false,
}: {
className?: string;
showDetails?: boolean;
}) {
const [healthResult, setHealthResult] = useState<HealthCheckResult | null>(null);
useEffect(() => {
// Start periodic health checks
const cleanup = createHealthCheckScheduler((result) => {
setHealthResult(result);
}, 30000); // Check every 30 seconds
return cleanup;
}, []);
if (!healthResult) {
return (
<span className={`text-xs flex items-center gap-1 ${className}`}>
<Heart className="w-3.5 h-3.5 text-gray-400" />
<span className="text-gray-400">...</span>
</span>
);
}
const statusColors: Record<HealthStatus, { dot: string; text: string; icon: typeof Heart }> = {
healthy: { dot: 'bg-green-400', text: 'text-green-500', icon: Heart },
unhealthy: { dot: 'bg-red-400', text: 'text-red-500', icon: HeartPulse },
unknown: { dot: 'bg-gray-400', text: 'text-gray-500', icon: Heart },
};
const config = statusColors[healthResult.status];
const Icon = config.icon;
return (
<span className={`text-xs flex items-center gap-1 ${className}`}>
<Icon className={`w-3.5 h-3.5 ${config.text}`} />
<span className={config.text}>
{getHealthStatusLabel(healthResult.status)}
</span>
{showDetails && healthResult.message && (
<span className="text-gray-400 ml-1" title={healthResult.message}>
({formatHealthCheckTime(healthResult.timestamp)})
</span>
)}
</span>
);
}
export default ConnectionStatus;