// SPDX-License-Identifier: AGPL-3.0-or-later import {afterAll, beforeAll, beforeEach, describe, expect, it} from 'vitest'; import type {ApiTestHarness} from '../../test/ApiTestHarness'; import {createBuilder, createBuilderWithoutAuth} from '../../test/TestRequestBuilder'; import {createAuthHarness, createTestAccount, createUniqueEmail, createUniqueUsername} from './AuthTestUtils'; interface ValidationErrorResponse { code: string; message: string; errors?: Array<{ path: string; code: string; message: string; }>; } describe('Registration validation', () => { let harness: ApiTestHarness; beforeAll(async () => { harness = await createAuthHarness(); }); beforeEach(async () => { await harness.reset(); }); afterAll(async () => { await harness?.shutdown(); }); it('rejects invalid email format', async () => { const json = await createBuilderWithoutAuth(harness) .post('/auth/register') .header('accept-language', 'fr') .body({ email: 'not-an-email', username: createUniqueUsername(), global_name: 'Test User', password: 'a-strong-password', date_of_birth: '2000-01-01', consent: true, }) .expect(400, 'INVALID_FORM_BODY') .execute(); const emailError = json.errors?.find((e) => e.path === 'email'); expect(emailError?.code).toBe('INVALID_EMAIL_FORMAT'); expect(emailError?.message).toBe("Format d'adresse e-mail invalide."); }); it('rejects blocked registration email TLDs as invalid email', async () => { await createBuilderWithoutAuth(harness) .post('/auth/register') .body({ email: 'holdermichael_6023@example.blocked', username: createUniqueUsername(), global_name: 'Test User', password: 'a-strong-password', date_of_birth: '2000-01-01', consent: true, }) .expect(400) .execute(); }); it('rejects weak password', async () => { const json = await createBuilderWithoutAuth(harness) .post('/auth/register') .body({ email: createUniqueEmail(), username: createUniqueUsername(), global_name: 'Test User', password: 'weak', date_of_birth: '2000-01-01', consent: true, }) .expect(400, 'INVALID_FORM_BODY') .execute(); const passwordError = json.errors?.find((e) => e.path === 'password'); expect(passwordError?.code).toBe('PASSWORD_LENGTH_INVALID'); expect(passwordError?.message).toBe('String length must be between 8 and 256 characters.'); }); it('includes bounds in username length validation message', async () => { const json = await createBuilderWithoutAuth(harness) .post('/auth/register') .body({ email: createUniqueEmail(), username: 'a'.repeat(33), global_name: 'Test User', password: 'a-strong-password', date_of_birth: '2000-01-01', consent: true, }) .expect(400, 'INVALID_FORM_BODY') .execute(); const usernameError = json.errors?.find((e) => e.path === 'username'); expect(usernameError?.code).toBe('USERNAME_LENGTH_INVALID'); expect(usernameError?.message).toBe('Username must be between 1 and 32 characters.'); }); it('rejects duplicate email', async () => { const account = await createTestAccount(harness); await createBuilderWithoutAuth(harness) .post('/auth/register') .body({ email: account.email, username: createUniqueUsername(), global_name: 'Test User', password: 'a-strong-password', date_of_birth: '2000-01-01', consent: true, }) .expect(400, 'INVALID_FORM_BODY') .execute(); }); it('rejects missing date of birth', async () => { await createBuilderWithoutAuth(harness) .post('/auth/register') .body({ email: createUniqueEmail(), username: createUniqueUsername(), global_name: 'Test User', password: 'a-strong-password', consent: true, }) .expect(400) .execute(); }); it('allows emoji in global name', async () => { const globalName = '🌻 Sunflower'; const reg = await createBuilderWithoutAuth<{ token: string; }>(harness) .post('/auth/register') .body({ email: createUniqueEmail('emoji'), username: createUniqueUsername('emoji'), global_name: globalName, password: 'a-strong-password', date_of_birth: '2000-01-01', consent: true, }) .execute(); const me = await createBuilder<{ global_name: string; }>(harness, reg.token) .get('/users/@me') .execute(); expect(me.global_name).toBe(globalName); }); it('derives username from global name when username is not provided', async () => { const reg = await createBuilderWithoutAuth<{ token: string; }>(harness) .post('/auth/register') .body({ email: createUniqueEmail('derived'), global_name: 'Magic Tester', password: 'a-strong-password', date_of_birth: '2000-01-01', consent: true, }) .execute(); const me = await createBuilder<{ username: string; }>(harness, reg.token) .get('/users/@me') .execute(); expect(me.username).toBe('Magic_Tester'); }); });