152 lines
3.8 KiB
TypeScript
152 lines
3.8 KiB
TypeScript
/**
|
|
* SessionGateway tests
|
|
*
|
|
* TDD: All tests mock cookies() from 'next/headers' and global.fetch
|
|
*/
|
|
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { SessionGateway } from './SessionGateway';
|
|
import type { AuthSessionDTO } from '../types/generated/AuthSessionDTO';
|
|
|
|
// Mock next/headers
|
|
vi.mock('next/headers', () => ({
|
|
cookies: vi.fn(),
|
|
}));
|
|
|
|
// Mock global.fetch
|
|
const mockFetch = vi.fn();
|
|
global.fetch = mockFetch;
|
|
|
|
describe('SessionGateway', () => {
|
|
let gateway: SessionGateway;
|
|
let mockCookies: ReturnType<typeof vi.mocked>;
|
|
|
|
beforeEach(async () => {
|
|
vi.clearAllMocks();
|
|
const nextHeaders = await import('next/headers');
|
|
mockCookies = vi.mocked(nextHeaders.cookies);
|
|
gateway = new SessionGateway();
|
|
});
|
|
|
|
describe('getSession()', () => {
|
|
it('should return null when no cookies are present', async () => {
|
|
// Arrange
|
|
mockCookies.mockReturnValue({
|
|
toString: () => '',
|
|
} as any);
|
|
|
|
// Act
|
|
const result = await gateway.getSession();
|
|
|
|
// Assert
|
|
expect(result).toBeNull();
|
|
expect(mockFetch).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should return session object when valid gp_session cookie exists', async () => {
|
|
// Arrange
|
|
const mockSession: AuthSessionDTO = {
|
|
token: 'valid-token',
|
|
user: {
|
|
userId: 'user-123',
|
|
email: 'test@example.com',
|
|
displayName: 'Test User',
|
|
role: 'driver',
|
|
},
|
|
};
|
|
|
|
mockCookies.mockReturnValue({
|
|
toString: () => 'gp_session=valid-token; other=value',
|
|
} as any);
|
|
|
|
mockFetch.mockResolvedValueOnce({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockSession,
|
|
} as Response);
|
|
|
|
// Act
|
|
const result = await gateway.getSession();
|
|
|
|
// Assert
|
|
expect(result).toEqual(mockSession);
|
|
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3101/auth/session', {
|
|
headers: { cookie: 'gp_session=valid-token; other=value' },
|
|
cache: 'no-store',
|
|
credentials: 'include',
|
|
});
|
|
});
|
|
|
|
it('should return null when session is invalid or expired', async () => {
|
|
// Arrange
|
|
mockCookies.mockReturnValue({
|
|
toString: () => 'gp_session=expired-token',
|
|
} as any);
|
|
|
|
mockFetch.mockResolvedValueOnce({
|
|
ok: false,
|
|
status: 401,
|
|
statusText: 'Unauthorized',
|
|
} as Response);
|
|
|
|
// Act
|
|
const result = await gateway.getSession();
|
|
|
|
// Assert
|
|
expect(result).toBeNull();
|
|
expect(mockFetch).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should return null on non-2xx response', async () => {
|
|
// Arrange
|
|
mockCookies.mockReturnValue({
|
|
toString: () => 'gp_session=some-token',
|
|
} as any);
|
|
|
|
mockFetch.mockResolvedValueOnce({
|
|
ok: false,
|
|
status: 500,
|
|
statusText: 'Internal Server Error',
|
|
} as Response);
|
|
|
|
// Act
|
|
const result = await gateway.getSession();
|
|
|
|
// Assert
|
|
expect(result).toBeNull();
|
|
expect(mockFetch).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should return null on network error', async () => {
|
|
// Arrange
|
|
mockCookies.mockReturnValue({
|
|
toString: () => 'gp_session=some-token',
|
|
} as any);
|
|
|
|
mockFetch.mockRejectedValueOnce(new Error('Network error'));
|
|
|
|
// Act
|
|
const result = await gateway.getSession();
|
|
|
|
// Assert
|
|
expect(result).toBeNull();
|
|
expect(mockFetch).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should return null when fetch throws any error', async () => {
|
|
// Arrange
|
|
mockCookies.mockReturnValue({
|
|
toString: () => 'gp_session=some-token',
|
|
} as any);
|
|
|
|
mockFetch.mockRejectedValueOnce(new Error('Connection timeout'));
|
|
|
|
// Act
|
|
const result = await gateway.getSession();
|
|
|
|
// Assert
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
});
|