- Add intelligent-router.sh hook for automatic agent routing - Add AUTO-TRIGGER-SUMMARY.md documentation - Add FINAL-INTEGRATION-SUMMARY.md documentation - Complete Prometheus integration (6 commands + 4 tools) - Complete Dexto integration (12 commands + 5 tools) - Enhanced Ralph with access to all agents - Fix /clawd command (removed disable-model-invocation) - Update hooks.json to v5 with intelligent routing - 291 total skills now available - All 21 commands with automatic routing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
212 lines
8.7 KiB
TypeScript
212 lines
8.7 KiB
TypeScript
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
import { useAgentStore } from './agentStore.js';
|
|
|
|
describe('agentStore', () => {
|
|
beforeEach(() => {
|
|
// Reset store to default state
|
|
useAgentStore.setState({
|
|
status: 'idle',
|
|
connectionStatus: 'disconnected',
|
|
lastHeartbeat: null,
|
|
activeSessionId: null,
|
|
currentToolName: null,
|
|
connectionError: null,
|
|
reconnectAttempts: 0,
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
describe('status actions', () => {
|
|
it('should set status with setStatus', () => {
|
|
useAgentStore.getState().setStatus('thinking', 'session-1');
|
|
expect(useAgentStore.getState().status).toBe('thinking');
|
|
expect(useAgentStore.getState().activeSessionId).toBe('session-1');
|
|
});
|
|
|
|
it('should clear activeSessionId when setting to idle', () => {
|
|
useAgentStore.getState().setStatus('thinking', 'session-1');
|
|
useAgentStore.getState().setStatus('idle');
|
|
expect(useAgentStore.getState().activeSessionId).toBeNull();
|
|
});
|
|
|
|
it('should set thinking status', () => {
|
|
useAgentStore.getState().setThinking('session-1');
|
|
expect(useAgentStore.getState().status).toBe('thinking');
|
|
expect(useAgentStore.getState().activeSessionId).toBe('session-1');
|
|
expect(useAgentStore.getState().currentToolName).toBeNull();
|
|
});
|
|
|
|
it('should set executing tool status with tool name', () => {
|
|
useAgentStore.getState().setExecutingTool('session-1', 'read_file');
|
|
expect(useAgentStore.getState().status).toBe('executing_tool');
|
|
expect(useAgentStore.getState().activeSessionId).toBe('session-1');
|
|
expect(useAgentStore.getState().currentToolName).toBe('read_file');
|
|
});
|
|
|
|
it('should set awaiting approval status', () => {
|
|
useAgentStore.getState().setAwaitingApproval('session-1');
|
|
expect(useAgentStore.getState().status).toBe('awaiting_approval');
|
|
expect(useAgentStore.getState().activeSessionId).toBe('session-1');
|
|
});
|
|
|
|
it('should set idle and clear all', () => {
|
|
useAgentStore.getState().setExecutingTool('session-1', 'bash');
|
|
useAgentStore.getState().setIdle();
|
|
expect(useAgentStore.getState().status).toBe('idle');
|
|
expect(useAgentStore.getState().activeSessionId).toBeNull();
|
|
expect(useAgentStore.getState().currentToolName).toBeNull();
|
|
});
|
|
|
|
it('should clear tool name when transitioning from executing_tool to other status', () => {
|
|
useAgentStore.getState().setExecutingTool('session-1', 'bash');
|
|
useAgentStore.getState().setThinking('session-1');
|
|
expect(useAgentStore.getState().currentToolName).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('connection actions', () => {
|
|
it('should set connection status', () => {
|
|
useAgentStore.getState().setConnectionStatus('connected');
|
|
expect(useAgentStore.getState().connectionStatus).toBe('connected');
|
|
});
|
|
|
|
it('should handle setConnected', () => {
|
|
vi.useFakeTimers();
|
|
vi.setSystemTime(new Date('2024-01-01'));
|
|
|
|
useAgentStore.setState({
|
|
connectionError: 'Previous error',
|
|
reconnectAttempts: 5,
|
|
});
|
|
|
|
useAgentStore.getState().setConnected();
|
|
|
|
expect(useAgentStore.getState().connectionStatus).toBe('connected');
|
|
expect(useAgentStore.getState().connectionError).toBeNull();
|
|
expect(useAgentStore.getState().reconnectAttempts).toBe(0);
|
|
expect(useAgentStore.getState().lastHeartbeat).toBe(Date.now());
|
|
});
|
|
|
|
it('should handle setDisconnected without error', () => {
|
|
useAgentStore.getState().setDisconnected();
|
|
expect(useAgentStore.getState().connectionStatus).toBe('disconnected');
|
|
expect(useAgentStore.getState().connectionError).toBeNull();
|
|
});
|
|
|
|
it('should handle setDisconnected with error', () => {
|
|
useAgentStore.getState().setDisconnected('Network error');
|
|
expect(useAgentStore.getState().connectionStatus).toBe('disconnected');
|
|
expect(useAgentStore.getState().connectionError).toBe('Network error');
|
|
});
|
|
|
|
it('should handle setReconnecting', () => {
|
|
useAgentStore.getState().setReconnecting();
|
|
expect(useAgentStore.getState().connectionStatus).toBe('reconnecting');
|
|
});
|
|
|
|
it('should update heartbeat timestamp', () => {
|
|
vi.useFakeTimers();
|
|
vi.setSystemTime(new Date('2024-01-01T12:00:00'));
|
|
|
|
useAgentStore.getState().updateHeartbeat();
|
|
expect(useAgentStore.getState().lastHeartbeat).toBe(Date.now());
|
|
});
|
|
|
|
it('should increment reconnect attempts', () => {
|
|
useAgentStore.getState().incrementReconnectAttempts();
|
|
expect(useAgentStore.getState().reconnectAttempts).toBe(1);
|
|
useAgentStore.getState().incrementReconnectAttempts();
|
|
expect(useAgentStore.getState().reconnectAttempts).toBe(2);
|
|
});
|
|
|
|
it('should reset reconnect attempts', () => {
|
|
useAgentStore.setState({ reconnectAttempts: 5 });
|
|
useAgentStore.getState().resetReconnectAttempts();
|
|
expect(useAgentStore.getState().reconnectAttempts).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('selectors', () => {
|
|
it('isBusy should return true when not idle', () => {
|
|
useAgentStore.getState().setThinking('session-1');
|
|
expect(useAgentStore.getState().isBusy()).toBe(true);
|
|
});
|
|
|
|
it('isBusy should return false when idle', () => {
|
|
expect(useAgentStore.getState().isBusy()).toBe(false);
|
|
});
|
|
|
|
it('isConnected should return true when connected', () => {
|
|
useAgentStore.getState().setConnected();
|
|
expect(useAgentStore.getState().isConnected()).toBe(true);
|
|
});
|
|
|
|
it('isConnected should return false when disconnected', () => {
|
|
expect(useAgentStore.getState().isConnected()).toBe(false);
|
|
});
|
|
|
|
it('isConnected should return false when reconnecting', () => {
|
|
useAgentStore.getState().setReconnecting();
|
|
expect(useAgentStore.getState().isConnected()).toBe(false);
|
|
});
|
|
|
|
it('isActiveForSession should return true for matching session', () => {
|
|
useAgentStore.getState().setThinking('session-1');
|
|
expect(useAgentStore.getState().isActiveForSession('session-1')).toBe(true);
|
|
});
|
|
|
|
it('isActiveForSession should return false for different session', () => {
|
|
useAgentStore.getState().setThinking('session-1');
|
|
expect(useAgentStore.getState().isActiveForSession('session-2')).toBe(false);
|
|
});
|
|
|
|
it('isActiveForSession should return false when idle', () => {
|
|
expect(useAgentStore.getState().isActiveForSession('session-1')).toBe(false);
|
|
});
|
|
|
|
it('getHeartbeatAge should return null when no heartbeat', () => {
|
|
expect(useAgentStore.getState().getHeartbeatAge()).toBeNull();
|
|
});
|
|
|
|
it('getHeartbeatAge should return age in milliseconds', () => {
|
|
vi.useFakeTimers();
|
|
vi.setSystemTime(new Date('2024-01-01T12:00:00'));
|
|
|
|
useAgentStore.getState().updateHeartbeat();
|
|
|
|
// Advance time by 5 seconds
|
|
vi.advanceTimersByTime(5000);
|
|
|
|
expect(useAgentStore.getState().getHeartbeatAge()).toBe(5000);
|
|
});
|
|
});
|
|
|
|
describe('status transitions', () => {
|
|
it('should handle full lifecycle: idle -> thinking -> executing -> idle', () => {
|
|
expect(useAgentStore.getState().status).toBe('idle');
|
|
|
|
useAgentStore.getState().setThinking('session-1');
|
|
expect(useAgentStore.getState().status).toBe('thinking');
|
|
|
|
useAgentStore.getState().setExecutingTool('session-1', 'read_file');
|
|
expect(useAgentStore.getState().status).toBe('executing_tool');
|
|
|
|
useAgentStore.getState().setIdle();
|
|
expect(useAgentStore.getState().status).toBe('idle');
|
|
});
|
|
|
|
it('should handle approval flow: thinking -> awaiting_approval -> idle', () => {
|
|
useAgentStore.getState().setThinking('session-1');
|
|
useAgentStore.getState().setAwaitingApproval('session-1');
|
|
expect(useAgentStore.getState().status).toBe('awaiting_approval');
|
|
|
|
// After approval, back to thinking or idle
|
|
useAgentStore.getState().setThinking('session-1');
|
|
expect(useAgentStore.getState().status).toBe('thinking');
|
|
});
|
|
});
|
|
});
|