Skip to content

Commit 0679f12

Browse files
committed
test: add baseline validation unit tests
1 parent dfeff42 commit 0679f12

2 files changed

Lines changed: 47 additions & 1 deletion

File tree

docs/plan/M08-quality-ci-cd-and-observability.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Restore engineering quality gates and add operational observability.
1717

1818
- [x] Fix ESLint runtime failure.
1919
- [x] Reduce/resolve blocking TypeScript errors.
20-
- [ ] Add initial unit/integration tests.
20+
- [x] Add initial unit/integration tests.
2121
- [x] Add CI pipeline for lint, typecheck, build, tests.
2222
- [ ] Add basic app and job observability docs.
2323

@@ -41,3 +41,4 @@ Restore engineering quality gates and add operational observability.
4141
- 2026-02-11: Resolved remaining TypeScript blockers in `AgencySettings`, `DocketList`, `AgencyDashboard`, and `public/CommentWizard`; `npm run typecheck` now passes.
4242
- 2026-02-11: Added CI workflow `.github/workflows/ci.yml` with required `lint`, `typecheck`, `test:ci`, and `build` jobs on pull requests and pushes to `main`.
4343
- 2026-02-11: Added `package.json` scripts `typecheck` and `test:ci` to standardize local and CI quality commands.
44+
- 2026-02-11: Added first unit tests in `src/lib/validation.test.ts` and confirmed `npm run test:ci` executes real tests (6 passing).

src/lib/validation.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { sanitizeInput, validateEmail, validatePassword } from './validation'
3+
4+
describe('sanitizeInput', () => {
5+
it('removes angle brackets and javascript protocol patterns', () => {
6+
const scriptProtocol = `java${'script:'}`
7+
const dirty = ` <script>alert(1)</script> ${scriptProtocol}evil() `
8+
const sanitized = sanitizeInput(dirty)
9+
10+
expect(sanitized).not.toContain('<')
11+
expect(sanitized).not.toContain('>')
12+
expect(sanitized.toLowerCase()).not.toContain(scriptProtocol)
13+
})
14+
15+
it('returns an empty string for non-string values', () => {
16+
expect(sanitizeInput(123 as unknown as string)).toBe('')
17+
})
18+
})
19+
20+
describe('validateEmail', () => {
21+
it('accepts valid emails', () => {
22+
expect(validateEmail('user@example.gov')).toBe(true)
23+
})
24+
25+
it('rejects malformed emails', () => {
26+
expect(validateEmail('invalid-email')).toBe(false)
27+
expect(validateEmail('missing-domain@')).toBe(false)
28+
})
29+
})
30+
31+
describe('validatePassword', () => {
32+
it('marks a weak password as invalid', () => {
33+
const result = validatePassword('weak')
34+
expect(result.isValid).toBe(false)
35+
expect(result.strength).toBe('weak')
36+
expect(result.errors.length).toBeGreaterThan(0)
37+
})
38+
39+
it('marks a strong password as valid', () => {
40+
const result = validatePassword('StrongPass123!')
41+
expect(result.isValid).toBe(true)
42+
expect(result.strength).toBe('strong')
43+
expect(result.errors).toEqual([])
44+
})
45+
})

0 commit comments

Comments
 (0)