/** * @jest-environment node */ import { NextRequest, NextResponse } from 'next/server'; import { middleware } from '../middleware'; // Mock NextResponse jest.mock('next/server', () => ({ ...jest.requireActual('next/server'), NextResponse: { next: jest.fn(() => ({ type: 'next' })), redirect: jest.fn((url: URL) => ({ type: 'redirect', url: url.toString() })), }, })); describe('Protected Route Middleware', () => { const baseUrl = 'http://localhost:3000'; // Helper to create mock request const createRequest = (pathname: string, hasToken = false): NextRequest => { const url = new URL(pathname, baseUrl); const request = new NextRequest(url); // Mock cookies if (hasToken) { Object.defineProperty(request, 'cookies', { value: { get: jest.fn((name: string) => { if (name === 'accessToken') { return { value: 'mock-token-123' }; } return undefined; }), }, writable: true, }); } else { Object.defineProperty(request, 'cookies', { value: { get: jest.fn(() => undefined), }, writable: true, }); } return request; }; beforeEach(() => { jest.clearAllMocks(); }); describe('Public Routes (Auth Routes)', () => { it('should allow access to /login without token', () => { const request = createRequest('/login', false); const response = middleware(request); expect(response).toEqual({ type: 'next' }); expect(NextResponse.next).toHaveBeenCalled(); }); it('should allow access to /verify-otp without token', () => { const request = createRequest('/verify-otp', false); const response = middleware(request); expect(response).toEqual({ type: 'next' }); expect(NextResponse.next).toHaveBeenCalled(); }); it('should redirect authenticated user from /login to /dashboard', () => { const request = createRequest('/login', true); const response = middleware(request); expect(response).toEqual({ type: 'redirect', url: `${baseUrl}/dashboard`, }); expect(NextResponse.redirect).toHaveBeenCalledWith( expect.objectContaining({ href: `${baseUrl}/dashboard`, }) ); }); it('should redirect authenticated user from /verify-otp to /dashboard', () => { const request = createRequest('/verify-otp', true); const response = middleware(request); expect(response).toEqual({ type: 'redirect', url: `${baseUrl}/dashboard`, }); }); }); describe('Protected Routes (Main Routes)', () => { it('should allow access to /dashboard with token', () => { const request = createRequest('/dashboard', true); const response = middleware(request); expect(response).toEqual({ type: 'next' }); expect(NextResponse.next).toHaveBeenCalled(); }); it('should redirect to /login when accessing /dashboard without token', () => { const request = createRequest('/dashboard', false); const response = middleware(request); expect(response).toEqual({ type: 'redirect', url: expect.stringContaining('/login?redirect=%2Fdashboard'), }); expect(NextResponse.redirect).toHaveBeenCalledWith( expect.objectContaining({ href: expect.stringContaining('/login?redirect=%2Fdashboard'), }) ); }); it('should redirect to /login when accessing /profile without token', () => { const request = createRequest('/profile', false); const response = middleware(request); expect(response).toEqual({ type: 'redirect', url: expect.stringContaining('/login?redirect=%2Fprofile'), }); }); }); describe('Root Path Handling', () => { it('should redirect to /dashboard when accessing / with token', () => { const request = createRequest('/', true); const response = middleware(request); expect(response).toEqual({ type: 'redirect', url: `${baseUrl}/dashboard`, }); }); it('should redirect to /login when accessing / without token', () => { const request = createRequest('/', false); const response = middleware(request); expect(response).toEqual({ type: 'redirect', url: `${baseUrl}/login`, }); }); }); describe('API and Static Routes', () => { it('should allow access to API routes without token', () => { const request = createRequest('/api/test', false); const response = middleware(request); expect(response).toEqual({ type: 'next' }); expect(NextResponse.next).toHaveBeenCalled(); }); it('should allow access to _next/static without token', () => { const request = createRequest('/_next/static/chunk.js', false); const response = middleware(request); expect(response).toEqual({ type: 'next' }); }); it('should allow access to favicon.ico without token', () => { const request = createRequest('/favicon.ico', false); const response = middleware(request); expect(response).toEqual({ type: 'next' }); }); it('should allow access to /images without token', () => { const request = createRequest('/images/logo.png', false); const response = middleware(request); expect(response).toEqual({ type: 'next' }); }); }); describe('Redirect Parameter Preservation', () => { it('should preserve original URL in redirect parameter', () => { const request = createRequest('/dashboard/settings', false); const response = middleware(request); expect(response).toEqual({ type: 'redirect', url: expect.stringContaining('/login?redirect=%2Fdashboard%2Fsettings'), }); }); it('should preserve query parameters in redirect', () => { const request = createRequest('/dashboard?tab=profile', false); const response = middleware(request); expect(response).toEqual({ type: 'redirect', url: expect.stringContaining('/login?redirect='), }); }); }); });