Files
gridpilot.gg/apps/website/lib/gateways/SessionGateway.test.ts
2026-01-17 15:46:55 +01:00

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(expect.stringContaining('/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();
});
});
});