test(phase-02): add Nyquist validation tests — fill SEC-05, AUTH-08, SEC-03 and frontend gaps

8 test files, 60 new tests (14 backend + 46 frontend). All green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-05-31 12:04:21 +02:00
co-authored by Claude Sonnet 4.6
parent 6c79f92d70
commit d98e3ab7a1
8 changed files with 1381 additions and 0 deletions
@@ -0,0 +1,117 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import PasswordStrengthBar from '../PasswordStrengthBar.vue'
/**
* AUTH-01 (frontend): PasswordStrengthBar strength scoring.
*
* Score algorithm (04):
* +1 if length >= 12
* +1 if /[A-Z]/
* +1 if /[0-9]/
* +1 if /[^A-Za-z0-9]/ (special character)
*/
describe('PasswordStrengthBar — score and visibility', () => {
it('renders nothing when password is empty string', () => {
const w = mount(PasswordStrengthBar, { props: { password: '' } })
// v-if="password" — empty string is falsy, component is hidden
const bar = w.find('.mt-2')
expect(bar.exists()).toBe(false)
})
it('renders nothing when password prop is absent (default empty)', () => {
const w = mount(PasswordStrengthBar)
const bar = w.find('.mt-2')
expect(bar.exists()).toBe(false)
})
it('shows strength bar when password is non-empty', () => {
const w = mount(PasswordStrengthBar, { props: { password: 'a' } })
expect(w.find('.mt-2').exists()).toBe(true)
})
})
describe('PasswordStrengthBar — score 1 (weak)', () => {
it('score 1: long-enough uppercase-only password', () => {
// Only length >= 12 satisfied: "AAAAAAAAAAAA" — uppercase yes, but no digit, no special
// Actually uppercase satisfies /[A-Z]/ → score 2. Use lowercase only >= 12
// "aaaaaaaaaaaa" — only length passes → score 1
const w = mount(PasswordStrengthBar, { props: { password: 'aaaaaaaaaaaa' } })
expect(w.text()).toContain('Too weak')
})
it('score 1: short uppercase password with no digit or special', () => {
// "ABC" — only uppercase passes, no length, no digit, no special → score 1
const w = mount(PasswordStrengthBar, { props: { password: 'ABC' } })
expect(w.text()).toContain('Too weak')
})
})
describe('PasswordStrengthBar — score 2 (weak)', () => {
it('score 2: length + uppercase only', () => {
// "AAAAAAAAAAAAa" — length>=12 + uppercase: score 2 (no digit, no special)
// Wait: "AAAAAAAAAAAA" — length>=12 AND [A-Z] → score 2
const w = mount(PasswordStrengthBar, { props: { password: 'AAAAAAAAAAAA' } })
expect(w.text()).toContain('Weak')
})
})
describe('PasswordStrengthBar — score 3 (fair)', () => {
it('score 3: length + uppercase + digit', () => {
// "AAAAAAAAAAA1" (12 chars) — length>=12, [A-Z], [0-9] → score 3 (no special)
const w = mount(PasswordStrengthBar, { props: { password: 'AAAAAAAAAAA1' } })
expect(w.text()).toContain('Fair')
})
})
describe('PasswordStrengthBar — score 4 (strong)', () => {
it('score 4: length + uppercase + digit + special', () => {
// "Passw0rd123!" — all four criteria met
const w = mount(PasswordStrengthBar, { props: { password: 'Passw0rd123!' } })
expect(w.text()).toContain('Strong')
})
it('score 4: matches backend AUTH-01 strong password example', () => {
// StrongPass12! — exactly the password used in backend tests
const w = mount(PasswordStrengthBar, { props: { password: 'StrongPass12!' } })
expect(w.text()).toContain('Strong')
})
})
describe('PasswordStrengthBar — score boundary: short password', () => {
it('11-char password with all other criteria still misses +1 for length', () => {
// "Passw0rd12!" — 11 chars: no length bonus, but uppercase+digit+special → score 3
const w = mount(PasswordStrengthBar, { props: { password: 'Passw0rd12!' } })
// score should be 3 (Fair), not 4 (Strong)
expect(w.text()).toContain('Fair')
expect(w.text()).not.toContain('Strong')
})
it('12-char password with all criteria → score 4', () => {
// "Passw0rd123!" — exactly 12 chars with all criteria
const w = mount(PasswordStrengthBar, { props: { password: 'Passw0rd123!' } })
expect(w.text()).toContain('Strong')
})
})
describe('PasswordStrengthBar — visual segments', () => {
it('renders 4 bar segments', () => {
const w = mount(PasswordStrengthBar, { props: { password: 'something' } })
// 4 segment divs from v-for="i in 4"
const segments = w.findAll('.h-1.flex-1.rounded')
expect(segments.length).toBe(4)
})
it('score-1 label is red', () => {
const w = mount(PasswordStrengthBar, { props: { password: 'ABC' } })
const label = w.find('span.text-xs')
expect(label.classes()).toContain('text-red-500')
})
it('score-4 label is green', () => {
const w = mount(PasswordStrengthBar, { props: { password: 'StrongPass12!' } })
const label = w.find('span.text-xs')
expect(label.classes()).toContain('text-green-500')
})
})