feat: Complete implementation of Tasks 1-5 - CIM Document Processor

Backend Infrastructure:
- Complete Express server setup with security middleware (helmet, CORS, rate limiting)
- Comprehensive error handling and logging with Winston
- Authentication system with JWT tokens and session management
- Database models and migrations for Users, Documents, Feedback, and Processing Jobs
- API routes structure for authentication and document management
- Integration tests for all server components (86 tests passing)

Frontend Infrastructure:
- React application with TypeScript and Vite
- Authentication UI with login form, protected routes, and logout functionality
- Authentication context with proper async state management
- Component tests with proper async handling (25 tests passing)
- Tailwind CSS styling and responsive design

Key Features:
- User registration, login, and authentication
- Protected routes with role-based access control
- Comprehensive error handling and user feedback
- Database schema with proper relationships
- Security middleware and validation
- Production-ready build configuration

Test Coverage: 111/111 tests passing
Tasks Completed: 1-5 (Project setup, Database, Auth system, Frontend UI, Backend infrastructure)

Ready for Task 6: File upload backend infrastructure
This commit is contained in:
Jon
2025-07-27 13:29:26 -04:00
commit 5a3c961bfc
72 changed files with 24326 additions and 0 deletions

View File

@@ -0,0 +1,132 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import ProtectedRoute from '../ProtectedRoute';
// Mock the useAuth hook to control its output in tests
const mockUseAuth = vi.fn();
vi.mock('../../contexts/AuthContext', () => ({
useAuth: () => mockUseAuth(),
}));
const TestComponent: React.FC = () => <div>Protected Content</div>;
const LoginComponent: React.FC = () => <div>Login Page</div>;
const UnauthorizedComponent: React.FC = () => <div>Unauthorized Page</div>;
const renderWithRouter = (ui: React.ReactNode, { initialEntries = ['/protected'] } = {}) => {
return render(
<MemoryRouter initialEntries={initialEntries}>
<Routes>
<Route path="/login" element={<LoginComponent />} />
<Route path="/unauthorized" element={<UnauthorizedComponent />} />
<Route path="/protected" element={ui} />
</Routes>
</MemoryRouter>
);
};
describe('ProtectedRoute', () => {
beforeEach(() => {
vi.resetAllMocks();
});
it('shows a loading spinner while authentication is in progress', () => {
mockUseAuth.mockReturnValue({
user: null,
isLoading: true,
isInitialized: false,
});
renderWithRouter(
<ProtectedRoute>
<TestComponent />
</ProtectedRoute>
);
expect(document.querySelector('.animate-spin')).toBeInTheDocument();
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
});
it('redirects to the login page if the user is not authenticated', () => {
mockUseAuth.mockReturnValue({
user: null,
isLoading: false,
isInitialized: true,
});
renderWithRouter(
<ProtectedRoute>
<TestComponent />
</ProtectedRoute>
);
expect(screen.getByText('Login Page')).toBeInTheDocument();
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
});
it('renders the protected content if the user is authenticated', () => {
mockUseAuth.mockReturnValue({
user: { id: '1', role: 'user' },
isLoading: false,
isInitialized: true,
});
renderWithRouter(
<ProtectedRoute>
<TestComponent />
</ProtectedRoute>
);
expect(screen.getByText('Protected Content')).toBeInTheDocument();
});
it('redirects to an unauthorized page if the user does not have the required role', () => {
mockUseAuth.mockReturnValue({
user: { id: '1', role: 'user' },
isLoading: false,
isInitialized: true,
});
renderWithRouter(
<ProtectedRoute requiredRole="admin">
<TestComponent />
</ProtectedRoute>
);
expect(screen.getByText('Unauthorized Page')).toBeInTheDocument();
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
});
it('renders the protected content if the user has the required admin role', () => {
mockUseAuth.mockReturnValue({
user: { id: '1', role: 'admin' },
isLoading: false,
isInitialized: true,
});
renderWithRouter(
<ProtectedRoute requiredRole="admin">
<TestComponent />
</ProtectedRoute>
);
expect(screen.getByText('Protected Content')).toBeInTheDocument();
});
it('renders the protected content if the user has the required user role', () => {
mockUseAuth.mockReturnValue({
user: { id: '1', role: 'user' },
isLoading: false,
isInitialized: true,
});
renderWithRouter(
<ProtectedRoute requiredRole="user">
<TestComponent />
</ProtectedRoute>
);
expect(screen.getByText('Protected Content')).toBeInTheDocument();
});
});