import { passwordMatchValidator, PasswordMatchGroupLike, } from '../../frontend/src/app/features/setup/setup-create-admin.validators'; function makeGroup(password: string | null | undefined, confirm: string | null | undefined): PasswordMatchGroupLike { return { get: (name) => { if (name === 'password') return { value: password }; if (name === 'passwordConfirm') return { value: confirm }; return null; }, }; } const USERNAME_PATTERN = /^[a-zA-Z0-9_.-]+$/; const USERNAME_MIN = 3; const USERNAME_MAX = 32; function usernameErrors(value: string): string | null { if (!value) return 'required'; if (value.length < USERNAME_MIN) return 'minlength'; if (value.length > USERNAME_MAX) return 'maxlength'; if (!USERNAME_PATTERN.test(value)) return 'pattern'; return null; } function isFormReadyForSubmit(username: string, password: string, confirm: string): boolean { if (usernameErrors(username) !== null) return false; if (!password) return false; if (!confirm) return false; const groupLike: PasswordMatchGroupLike = { get: (name) => (name === 'password' ? { value: password } : { value: confirm }), }; if (passwordMatchValidator(groupLike)?.passwordMismatch) return false; return true; } describe('passwordMatchValidator', () => { it('returns null when either field is empty (let other validators handle required)', () => { expect(passwordMatchValidator(makeGroup('', 'x'))).toBeNull(); expect(passwordMatchValidator(makeGroup('x', ''))).toBeNull(); expect(passwordMatchValidator(makeGroup(undefined, undefined))).toBeNull(); }); it('returns null when passwords match', () => { expect(passwordMatchValidator(makeGroup('Sup3r!', 'Sup3r!'))).toBeNull(); }); it('returns { passwordMismatch: true } when passwords differ', () => { expect(passwordMatchValidator(makeGroup('Sup3r!', 'Sup4r!'))).toEqual({ passwordMismatch: true, }); }); }); describe('Username field rules (mirroring component validators)', () => { const PATTERN = /^[a-zA-Z0-9_.-]+$/; const MIN = 3; const MAX = 32; function isValidUsername(value: string): boolean { return value.length >= MIN && value.length <= MAX && PATTERN.test(value); } it('accepts a 3-32 char username with allowed characters', () => { expect(isValidUsername('first.admin_01')).toBe(true); }); it('rejects usernames shorter than 3', () => { expect(isValidUsername('a')).toBe(false); }); it('rejects usernames with disallowed characters', () => { expect(isValidUsername('bad name')).toBe(false); expect(isValidUsername('user!')).toBe(false); expect(isValidUsername('user@')).toBe(false); }); it('rejects usernames longer than 32', () => { expect(isValidUsername('a'.repeat(33))).toBe(false); }); }); describe('Composed submit gate (mirrors SetupCreateAdminComponent.submit)', () => { it('rejects an all-empty form (no required field passes)', () => { expect(isFormReadyForSubmit('', '', '')).toBe(false); }); it('rejects a partial form (username only)', () => { expect(isFormReadyForSubmit('first.admin', '', '')).toBe(false); }); it('rejects when password and confirm are empty even with a valid username', () => { expect(isFormReadyForSubmit('first.admin', '', '')).toBe(false); }); it('rejects when passwordConfirm is empty', () => { expect(isFormReadyForSubmit('first.admin', 'Sup3rSecret!Pass', '')).toBe(false); }); it('rejects when password and confirm differ', () => { expect(isFormReadyForSubmit('first.admin', 'Sup3rSecret!Pass', 'Different!Pass1')).toBe(false); }); it('rejects when username violates the pattern', () => { expect(isFormReadyForSubmit('bad name!', 'Sup3rSecret!Pass', 'Sup3rSecret!Pass')).toBe(false); }); it('accepts a fully valid, matching form', () => { expect(isFormReadyForSubmit('first.admin', 'Sup3rSecret!Pass', 'Sup3rSecret!Pass')).toBe(true); }); });