Spaces:
Sleeping
Sleeping
| import { describe, it, expect } from 'vitest'; | |
| import { validatePassword } from '../../../src/services/passwordPolicy'; | |
| describe('validatePassword', () => { | |
| // AUTH-006 β Registration with weak password | |
| describe('length requirement', () => { | |
| it('AUTH-006: rejects passwords shorter than 8 characters', () => { | |
| expect(validatePassword('Ab1!')).toEqual({ ok: false, reason: expect.stringContaining('8 characters') }); | |
| expect(validatePassword('Ab1!456')).toEqual({ ok: false, reason: expect.stringContaining('8 characters') }); | |
| }); | |
| it('accepts passwords of exactly 8 characters that meet all requirements', () => { | |
| expect(validatePassword('Ab1!abcd')).toEqual({ ok: true }); | |
| }); | |
| }); | |
| describe('complexity requirements', () => { | |
| it('AUTH-006: rejects password missing uppercase letter', () => { | |
| const result = validatePassword('abcd1234!'); | |
| expect(result.ok).toBe(false); | |
| expect(result.reason).toContain('uppercase'); | |
| }); | |
| it('AUTH-006: rejects password missing lowercase letter', () => { | |
| const result = validatePassword('ABCD1234!'); | |
| expect(result.ok).toBe(false); | |
| expect(result.reason).toContain('lowercase'); | |
| }); | |
| it('AUTH-006: rejects password missing a number', () => { | |
| const result = validatePassword('Abcdefg!'); | |
| expect(result.ok).toBe(false); | |
| expect(result.reason).toContain('number'); | |
| }); | |
| it('AUTH-006: rejects password missing a special character', () => { | |
| // 'TrekApp1' β has upper, lower, number, NO special char, NOT in blocklist | |
| const result = validatePassword('TrekApp1'); | |
| expect(result.ok).toBe(false); | |
| expect(result.reason).toContain('special character'); | |
| }); | |
| }); | |
| // AUTH-007 β Registration with common password | |
| describe('common password blocklist', () => { | |
| it('AUTH-007: rejects password matching exact blocklist entry (case-insensitive)', () => { | |
| // 'password1' is in the blocklist. A capitalised+special variant still matches | |
| // because the check is COMMON_PASSWORDS.has(password.toLowerCase()). | |
| // However, 'Password1!' lowercased is 'password1!' which is NOT in the set. | |
| // We must use a password whose lowercase is exactly in the set: | |
| // 'Iloveyou1!' β lowercased: 'iloveyou1!' β NOT in set. | |
| // Use a password whose *lowercase* IS in set: 'changeme' β 'Changeme' is 8 chars | |
| // but lacks uppercase/number/special β test blocklist with full complex variants: | |
| // 'ILoveyou1!' lowercased = 'iloveyou1!' β not in set. | |
| // Just test exact matches that satisfy complexity: use blocklist entry itself. | |
| // 'Iloveyou' is 8 chars, no number/special β fails complexity, not blocklist. | |
| // Better: pick a blocklist entry that, when capitalised + special added, still matches. | |
| // The check is: COMMON_PASSWORDS.has(password.toLowerCase()) | |
| // So 'FOOTBALL!' lowercased = 'football!' β not in set ('football' is in set). | |
| // We need password.toLowerCase() to equal a set entry exactly: | |
| // 'football' β add uppercase β 'Football' is still 8 chars, no number, no special β fails complexity first | |
| // The blocklist check happens BEFORE complexity checks, after length + repetitive checks. | |
| // So any 8+ char string whose lowercase is in the blocklist gets caught first. | |
| // 'Password1' lowercased = 'password1' β in blocklist! β (length ok, not repetitive) | |
| expect(validatePassword('Password1')).toEqual({ | |
| ok: false, | |
| reason: expect.stringContaining('common'), | |
| }); | |
| }); | |
| it('AUTH-007: rejects "Changeme" whose lowercase is in the blocklist', () => { | |
| // 'changeme' is in the set; 'Changeme'.toLowerCase() === 'changeme' β | |
| expect(validatePassword('Changeme')).toEqual({ | |
| ok: false, | |
| reason: expect.stringContaining('common'), | |
| }); | |
| }); | |
| it('accepts a strong password that is not in the blocklist', () => { | |
| expect(validatePassword('MyUniq!1Trek')).toEqual({ ok: true }); | |
| }); | |
| }); | |
| describe('repetitive password', () => { | |
| it('rejects passwords made of a single repeated character', () => { | |
| const result = validatePassword('AAAAAAAA'); | |
| expect(result.ok).toBe(false); | |
| expect(result.reason).toContain('repetitive'); | |
| }); | |
| }); | |
| describe('valid passwords', () => { | |
| it('accepts a strong unique password', () => { | |
| expect(validatePassword('Tr3k!SecurePass')).toEqual({ ok: true }); | |
| }); | |
| it('accepts a strong password with special characters', () => { | |
| expect(validatePassword('MyP@ss#2024')).toEqual({ ok: true }); | |
| }); | |
| }); | |
| }); | |