mirror of
https://github.com/coleam00/Archon.git
synced 2026-01-02 12:48:54 -05:00
feat: TanStack Query Migration Phase 2 - Cleanup and Test Reorganization (#588)
* refactor: migrate layouts to TanStack Query and Radix UI patterns - Created new modern layout components in src/components/layout/ - Migrated from old MainLayout/SideNavigation to new system - Added BackendStatus component with proper separation of concerns - Fixed horizontal scrollbar issues in project list - Renamed old layouts folder to agent-chat for unused chat panel - Added layout directory to Biome configuration - Fixed all linting and TypeScript issues in new layout code - Uses TanStack Query for backend health monitoring - Temporarily imports old settings/credentials until full migration * test: reorganize test infrastructure with colocated tests in subdirectories - Move tests into dedicated tests/ subdirectories within each feature - Create centralized test utilities in src/features/testing/ - Update all import paths to match new structure - Configure tsconfig.prod.json to exclude test files - Remove legacy test files from old test/ directory - All 32 tests passing with proper provider wrapping * fix: use error boundary wrapper for ProjectPage - Export ProjectsViewWithBoundary from projects feature module - Update ProjectPage to use boundary-wrapped version - Provides proper error containment and recovery with TanStack Query integration * cleanup: remove unused MCP client components - Remove ToolTestingPanel, ClientCard, and MCPClients components - These were part of an unimplemented MCP clients feature - Clean up commented import in MCPPage - Preparing for proper MCP feature migration to features directory * cleanup: remove unused mcpService.ts - Remove duplicate/unused mcpService.ts (579 lines) - Keep mcpServerService.ts which is actively used by MCPPage and useMCPQueries - mcpService was never imported or used anywhere in the codebase * cleanup: remove unused mcpClientService and update deprecation comments - Remove mcpClientService.ts (445 lines) - no longer used after removing MCP client components - Update deprecation comments in mcpServerService to remove references to deleted service - This completes the MCP service cleanup * fix: correct test directory exclusion in coverage config Update coverage exclusion from 'test/' to 'tests/' to match actual project structure and ensure proper test file exclusion from coverage. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * docs: fix ArchonChatPanel import path in agent-chat.mdx Update import from deprecated layouts to agent-chat directory. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * refactor: improve backend health hook and types - Use existing ETag infrastructure in useBackendHealth for 70% bandwidth reduction - Honor React Query cancellation signals with proper timeout handling - Remove duplicate HealthResponse interface, import from shared types - Add React type import to fix potential strict TypeScript issues 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * fix: remove .d.ts exclusion from production TypeScript config Removing **/*.d.ts exclusion to fix import.meta.env type errors in production builds. The exclusion was preventing src/env.d.ts from being included, breaking ImportMetaEnv interface definitions. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * feat: implement modern MCP feature architecture - Add new /features/mcp with TanStack Query integration - Components: McpClientList, McpStatusBar, McpConfigSection - Services: mcpApi with ETag caching - Hooks: useMcpStatus, useMcpConfig, useMcpClients, useMcpSessionInfo - Views: McpView with error boundary wrapper - Full TypeScript types for MCP protocol Part of TanStack Query migration phase 2. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * refactor: complete MCP modernization and cleanup - Remove deprecated mcpServerService.ts (237 lines) - Remove unused useMCPQueries.ts hooks (77 lines) - Simplify MCPPage.tsx to use new feature architecture - Export useSmartPolling from ui/hooks for MCP feature - Add Python MCP API routes for backend integration This completes the MCP migration to TanStack Query with: - ETag caching for 70% bandwidth reduction - Smart polling with visibility awareness - Vertical slice architecture - Full TypeScript type safety 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * fix: correct MCP transport mode display and complete cleanup - Fix backend API to return correct "streamable-http" transport mode - Update frontend to dynamically display transport type from config - Remove unused MCP functions (startMCPServer, stopMCPServer, getMCPServerStatus) - Clean up unused MCPServerResponse interface - Update log messages to show accurate transport mode - Complete aggressive MCP cleanup with 75% code reduction (617 lines removed) Backend changes: - python/src/server/api_routes/mcp_api.py: Fix transport and logs - Reduced from 818 to 201 lines while preserving all functionality Frontend changes: - McpStatusBar: Dynamic transport display based on config - McpView: Pass config to status bar component - api.ts: Remove unused MCP management functions All MCP tools tested and verified working after cleanup. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * simplify MCP API to status-only endpoints - Remove Docker container management functionality - Remove start/stop/restart endpoints - Simplify to status and config endpoints only - Container is now managed entirely via docker-compose * feat: complete MCP feature migration to TanStack Query - Add MCP feature with TanStack Query hooks and services - Create useMcpQueries hook with smart polling for status/config - Implement mcpApi service with streamable-http transport - Add MCP page component with real-time updates - Export MCP hooks from features/ui for global access - Fix logging bug in mcp_api.py (invalid error kwarg) - Update docker command to v2 syntax (docker compose) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: clean up unused CSS and unify Tron-themed scrollbars - Remove 200+ lines of unused CSS classes (62% file size reduction) - Delete unused: glass classes, neon-dividers, card animations, screensaver animations - Remove unused knowledge-item-card and hide-scrollbar styles - Remove unused flip-card and card expansion animations - Update scrollbar-thin to match Tron theme with blue glow effects - Add gradient and glow effects to thin scrollbars for consistency - Keep only actively used styles: neon-grid, scrollbars, animation delays File reduced from 11.2KB to 4.3KB with no visual regressions 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: address CodeRabbit CSS review feedback - Fix neon-grid Tailwind @apply with arbitrary values (breaking build) - Convert hardcoded RGBA colors to HSL tokens using --blue-accent - Add prefers-reduced-motion accessibility support - Add Firefox dark mode scrollbar-color support - Optimize transitions to specific properties instead of 'all' 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * fix: properly close Docker client to prevent resource leak - Add finally block to ensure Docker client is closed - Prevents resource leak in get_container_status function - Fix linting issues (whitespace and newline) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { taskKeys, useProjectTasks, useCreateTask } from '../useTaskQueries';
|
||||
import type { Task } from '../../types';
|
||||
import React from 'react';
|
||||
|
||||
// Mock the services
|
||||
vi.mock('../../services', () => ({
|
||||
taskService: {
|
||||
getTasksByProject: vi.fn(),
|
||||
createTask: vi.fn(),
|
||||
updateTask: vi.fn(),
|
||||
deleteTask: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock the toast hook
|
||||
vi.mock('../../../../ui/hooks/useToast', () => ({
|
||||
useToast: () => ({
|
||||
showToast: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock smart polling
|
||||
vi.mock('../../../../ui/hooks', () => ({
|
||||
useSmartPolling: () => ({
|
||||
refetchInterval: 5000,
|
||||
isPaused: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
// Test wrapper with QueryClient
|
||||
const createWrapper = () => {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: { retry: false },
|
||||
mutations: { retry: false },
|
||||
},
|
||||
});
|
||||
|
||||
return ({ children }: { children: React.ReactNode }) =>
|
||||
React.createElement(QueryClientProvider, { client: queryClient }, children);
|
||||
};
|
||||
|
||||
describe('useTaskQueries', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('taskKeys', () => {
|
||||
it('should generate correct query keys', () => {
|
||||
expect(taskKeys.all('project-123')).toEqual(['projects', 'project-123', 'tasks']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useProjectTasks', () => {
|
||||
it('should fetch tasks for a project', async () => {
|
||||
const mockTasks: Task[] = [
|
||||
{
|
||||
id: 'task-1',
|
||||
project_id: 'project-123',
|
||||
title: 'Test Task',
|
||||
description: 'Test Description',
|
||||
status: 'todo',
|
||||
assignee: 'User',
|
||||
task_order: 100,
|
||||
created_at: '2024-01-01T00:00:00Z',
|
||||
updated_at: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
];
|
||||
|
||||
const { taskService } = await import('../../services');
|
||||
vi.mocked(taskService.getTasksByProject).mockResolvedValue(mockTasks);
|
||||
|
||||
const { result } = renderHook(() => useProjectTasks('project-123'), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
expect(result.current.data).toEqual(mockTasks);
|
||||
});
|
||||
|
||||
expect(taskService.getTasksByProject).toHaveBeenCalledWith('project-123');
|
||||
});
|
||||
|
||||
it('should not fetch tasks when projectId is undefined', () => {
|
||||
const { result } = renderHook(() => useProjectTasks(undefined), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.isFetching).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should respect enabled flag', () => {
|
||||
const { result } = renderHook(() => useProjectTasks('project-123', false), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.isFetching).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCreateTask', () => {
|
||||
it('should optimistically add task and replace with server response', async () => {
|
||||
const newTask: Task = {
|
||||
id: 'real-task-id',
|
||||
project_id: 'project-123',
|
||||
title: 'New Task',
|
||||
description: 'New Description',
|
||||
status: 'todo',
|
||||
assignee: 'User',
|
||||
task_order: 100,
|
||||
created_at: '2024-01-01T00:00:00Z',
|
||||
updated_at: '2024-01-01T00:00:00Z',
|
||||
};
|
||||
|
||||
const { taskService } = await import('../../services');
|
||||
vi.mocked(taskService.createTask).mockResolvedValue(newTask);
|
||||
|
||||
const wrapper = createWrapper();
|
||||
const { result } = renderHook(() => useCreateTask(), { wrapper });
|
||||
|
||||
await result.current.mutateAsync({
|
||||
project_id: 'project-123',
|
||||
title: 'New Task',
|
||||
description: 'New Description',
|
||||
status: 'todo',
|
||||
assignee: 'User',
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
expect(taskService.createTask).toHaveBeenCalledWith({
|
||||
project_id: 'project-123',
|
||||
title: 'New Task',
|
||||
description: 'New Description',
|
||||
status: 'todo',
|
||||
assignee: 'User',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should provide default values for optional fields', async () => {
|
||||
const newTask: Task = {
|
||||
id: 'real-task-id',
|
||||
project_id: 'project-123',
|
||||
title: 'Minimal Task',
|
||||
description: '',
|
||||
status: 'todo',
|
||||
assignee: 'User',
|
||||
task_order: 100,
|
||||
created_at: '2024-01-01T00:00:00Z',
|
||||
updated_at: '2024-01-01T00:00:00Z',
|
||||
};
|
||||
|
||||
const { taskService } = await import('../../services');
|
||||
vi.mocked(taskService.createTask).mockResolvedValue(newTask);
|
||||
|
||||
const wrapper = createWrapper();
|
||||
const { result } = renderHook(() => useCreateTask(), { wrapper });
|
||||
|
||||
await result.current.mutateAsync({
|
||||
project_id: 'project-123',
|
||||
title: 'Minimal Task',
|
||||
description: '',
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should rollback on error', async () => {
|
||||
const { taskService } = await import('../../services');
|
||||
vi.mocked(taskService.createTask).mockRejectedValue(new Error('Network error'));
|
||||
|
||||
const wrapper = createWrapper();
|
||||
const { result } = renderHook(() => useCreateTask(), { wrapper });
|
||||
|
||||
await expect(
|
||||
result.current.mutateAsync({
|
||||
project_id: 'project-123',
|
||||
title: 'Failed Task',
|
||||
description: 'This will fail',
|
||||
})
|
||||
).rejects.toThrow('Network error');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user