mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-02 20:30:10 +00:00
94dbaf073b
Introduced a Vitest-based backend unit testing framework with supporting scripts, helpers, and GitHub Actions integration. Refactored the admin settings page to a modular architecture, splitting monolithic logic into feature-specific tabs and hooks for improved maintainability and testability. Updated documentation to reflect the new testing setup and settings architecture, and added new dependencies for testing utilities.
124 lines
4.0 KiB
TypeScript
124 lines
4.0 KiB
TypeScript
/**
|
|
* Component: Local Auth API Route Tests
|
|
* Documentation: documentation/testing.md
|
|
*/
|
|
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const localAuthProviderMock = {
|
|
handleCallback: vi.fn(),
|
|
register: vi.fn(),
|
|
};
|
|
|
|
vi.mock('@/lib/services/auth/LocalAuthProvider', () => ({
|
|
LocalAuthProvider: class {
|
|
handleCallback = localAuthProviderMock.handleCallback;
|
|
register = localAuthProviderMock.register;
|
|
},
|
|
}));
|
|
|
|
const makeRequest = (body: any, headers?: Record<string, string>) => ({
|
|
json: vi.fn().mockResolvedValue(body),
|
|
headers: {
|
|
get: (key: string) => headers?.[key.toLowerCase()] || null,
|
|
},
|
|
});
|
|
|
|
describe('Local auth routes', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
delete process.env.DISABLE_LOCAL_LOGIN;
|
|
});
|
|
|
|
it('rejects login when local auth is disabled', async () => {
|
|
process.env.DISABLE_LOCAL_LOGIN = 'true';
|
|
const { POST } = await import('@/app/api/auth/local/login/route');
|
|
|
|
const response = await POST(makeRequest({ username: 'user', password: 'pass' }) as any);
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(403);
|
|
expect(payload.error).toBe('Local login is disabled');
|
|
});
|
|
|
|
it('rejects login when username or password missing', async () => {
|
|
const { POST } = await import('@/app/api/auth/local/login/route');
|
|
|
|
const response = await POST(makeRequest({ username: 'user' }) as any);
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(payload.error).toBe('Username and password are required');
|
|
});
|
|
|
|
it('logs in successfully with local credentials', async () => {
|
|
localAuthProviderMock.handleCallback.mockResolvedValue({
|
|
success: true,
|
|
user: { id: 'u1' },
|
|
tokens: { accessToken: 'access', refreshToken: 'refresh' },
|
|
});
|
|
const { POST } = await import('@/app/api/auth/local/login/route');
|
|
|
|
const response = await POST(makeRequest({ username: 'user', password: 'pass' }) as any);
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(payload.success).toBe(true);
|
|
expect(payload.accessToken).toBe('access');
|
|
});
|
|
|
|
it('returns pending approval for local login', async () => {
|
|
localAuthProviderMock.handleCallback.mockResolvedValue({
|
|
success: false,
|
|
requiresApproval: true,
|
|
});
|
|
const { POST } = await import('@/app/api/auth/local/login/route');
|
|
|
|
const response = await POST(makeRequest({ username: 'user', password: 'pass' }) as any);
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(payload.pendingApproval).toBe(true);
|
|
});
|
|
|
|
it('registers a local user and returns tokens', async () => {
|
|
localAuthProviderMock.register.mockResolvedValue({
|
|
success: true,
|
|
user: { id: 'u2' },
|
|
tokens: { accessToken: 'access', refreshToken: 'refresh' },
|
|
});
|
|
const { POST } = await import('@/app/api/auth/register/route');
|
|
|
|
const request = makeRequest({ username: 'user', password: 'pass' }, { 'x-forwarded-for': 'ip-1' });
|
|
const response = await POST(request as any);
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(payload.success).toBe(true);
|
|
expect(payload.refreshToken).toBe('refresh');
|
|
});
|
|
|
|
it('rate limits repeated registration attempts by IP', async () => {
|
|
localAuthProviderMock.register.mockResolvedValue({
|
|
success: true,
|
|
user: { id: 'u3' },
|
|
tokens: { accessToken: 'access', refreshToken: 'refresh' },
|
|
});
|
|
const { POST } = await import('@/app/api/auth/register/route');
|
|
|
|
const request = makeRequest({ username: 'user', password: 'pass' }, { 'x-forwarded-for': 'ip-2' });
|
|
for (let i = 0; i < 5; i += 1) {
|
|
const response = await POST(request as any);
|
|
expect(response.status).toBe(200);
|
|
}
|
|
|
|
const blocked = await POST(request as any);
|
|
const payload = await blocked.json();
|
|
|
|
expect(blocked.status).toBe(429);
|
|
expect(payload.error).toMatch(/Too many registration attempts/);
|
|
});
|
|
});
|
|
|
|
|