- Add frontend unit tests with Vitest for components, hooks, pages, and utilities - Add backend tests for webhooks, notifications, middleware, and edge cases - Add ForgotPassword, NotFound, and ResetPassword pages - Add migration for orphaned staff resources conversion - Add coverage directory to gitignore (generated reports) - Various bug fixes and improvements from previous work 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
160 lines
4.4 KiB
TypeScript
160 lines
4.4 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
// Mock apiClient
|
|
vi.mock('../client', () => ({
|
|
default: {
|
|
post: vi.fn(),
|
|
get: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
import {
|
|
login,
|
|
logout,
|
|
getCurrentUser,
|
|
refreshToken,
|
|
masquerade,
|
|
stopMasquerade,
|
|
} from '../auth';
|
|
import apiClient from '../client';
|
|
|
|
describe('auth API', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('login', () => {
|
|
it('sends credentials to login endpoint', async () => {
|
|
const mockResponse = {
|
|
data: {
|
|
access: 'access-token',
|
|
refresh: 'refresh-token',
|
|
user: { id: 1, email: 'test@example.com' },
|
|
},
|
|
};
|
|
vi.mocked(apiClient.post).mockResolvedValue(mockResponse);
|
|
|
|
const result = await login({ email: 'test@example.com', password: 'password' });
|
|
|
|
expect(apiClient.post).toHaveBeenCalledWith('/auth/login/', {
|
|
email: 'test@example.com',
|
|
password: 'password',
|
|
});
|
|
expect(result).toEqual(mockResponse.data);
|
|
});
|
|
|
|
it('returns MFA required response', async () => {
|
|
const mockResponse = {
|
|
data: {
|
|
mfa_required: true,
|
|
user_id: 1,
|
|
mfa_methods: ['TOTP', 'SMS'],
|
|
},
|
|
};
|
|
vi.mocked(apiClient.post).mockResolvedValue(mockResponse);
|
|
|
|
const result = await login({ email: 'test@example.com', password: 'password' });
|
|
|
|
expect(result.mfa_required).toBe(true);
|
|
expect(result.mfa_methods).toContain('TOTP');
|
|
});
|
|
});
|
|
|
|
describe('logout', () => {
|
|
it('calls logout endpoint', async () => {
|
|
vi.mocked(apiClient.post).mockResolvedValue({});
|
|
|
|
await logout();
|
|
|
|
expect(apiClient.post).toHaveBeenCalledWith('/auth/logout/');
|
|
});
|
|
});
|
|
|
|
describe('getCurrentUser', () => {
|
|
it('fetches current user from API', async () => {
|
|
const mockUser = {
|
|
id: 1,
|
|
email: 'test@example.com',
|
|
name: 'Test User',
|
|
role: 'owner',
|
|
};
|
|
vi.mocked(apiClient.get).mockResolvedValue({ data: mockUser });
|
|
|
|
const result = await getCurrentUser();
|
|
|
|
expect(apiClient.get).toHaveBeenCalledWith('/auth/me/');
|
|
expect(result).toEqual(mockUser);
|
|
});
|
|
});
|
|
|
|
describe('refreshToken', () => {
|
|
it('sends refresh token to API', async () => {
|
|
const mockResponse = { data: { access: 'new-access-token' } };
|
|
vi.mocked(apiClient.post).mockResolvedValue(mockResponse);
|
|
|
|
const result = await refreshToken('old-refresh-token');
|
|
|
|
expect(apiClient.post).toHaveBeenCalledWith('/auth/refresh/', {
|
|
refresh: 'old-refresh-token',
|
|
});
|
|
expect(result.access).toBe('new-access-token');
|
|
});
|
|
});
|
|
|
|
describe('masquerade', () => {
|
|
it('sends masquerade request with user_pk', async () => {
|
|
const mockResponse = {
|
|
data: {
|
|
access: 'masq-access',
|
|
refresh: 'masq-refresh',
|
|
user: { id: 2, email: 'other@example.com' },
|
|
masquerade_stack: [{ user_id: 1, username: 'admin', role: 'superuser' }],
|
|
},
|
|
};
|
|
vi.mocked(apiClient.post).mockResolvedValue(mockResponse);
|
|
|
|
const result = await masquerade(2);
|
|
|
|
expect(apiClient.post).toHaveBeenCalledWith('/auth/hijack/acquire/', {
|
|
user_pk: 2,
|
|
hijack_history: undefined,
|
|
});
|
|
expect(result.masquerade_stack).toHaveLength(1);
|
|
});
|
|
|
|
it('sends masquerade request with history', async () => {
|
|
const history = [{ user_id: 1, username: 'admin', role: 'superuser' as const }];
|
|
vi.mocked(apiClient.post).mockResolvedValue({ data: {} });
|
|
|
|
await masquerade(2, history);
|
|
|
|
expect(apiClient.post).toHaveBeenCalledWith('/auth/hijack/acquire/', {
|
|
user_pk: 2,
|
|
hijack_history: history,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('stopMasquerade', () => {
|
|
it('sends release request with masquerade stack', async () => {
|
|
const stack = [{ user_id: 1, username: 'admin', role: 'superuser' as const }];
|
|
const mockResponse = {
|
|
data: {
|
|
access: 'orig-access',
|
|
refresh: 'orig-refresh',
|
|
user: { id: 1 },
|
|
masquerade_stack: [],
|
|
},
|
|
};
|
|
vi.mocked(apiClient.post).mockResolvedValue(mockResponse);
|
|
|
|
const result = await stopMasquerade(stack);
|
|
|
|
expect(apiClient.post).toHaveBeenCalledWith('/auth/hijack/release/', {
|
|
masquerade_stack: stack,
|
|
});
|
|
expect(result.masquerade_stack).toHaveLength(0);
|
|
});
|
|
});
|
|
});
|