Adds electoral nuclei, leaderships, updates, and WhatsApp invite flows behind campaign auth, plus the consolidated schema migration that Vercel applies on deploy.
123 lines
4.4 KiB
TypeScript
123 lines
4.4 KiB
TypeScript
// @vitest-environment node
|
|
|
|
import { describe, expect, it, vi } from 'vitest'
|
|
import { z } from 'zod'
|
|
|
|
import {
|
|
boundedJsonFormValue,
|
|
FormDataBoundaryError,
|
|
nullableFormText,
|
|
requiredFormSecret,
|
|
requiredRelationshipFormValue,
|
|
repeatedRelationshipFormValues,
|
|
validationFieldErrors,
|
|
} from '@/lib/formData'
|
|
|
|
describe('FormData boundary', () => {
|
|
it('rejects File values for non-file fields', () => {
|
|
const formData = new FormData()
|
|
formData.set('name', new File(['unsafe'], 'unsafe.txt'))
|
|
|
|
expect(() => nullableFormText(formData, 'name')).toThrow(FormDataBoundaryError)
|
|
formData.set('strengths', new File(['[]'], 'payload.json'))
|
|
expect(() => boundedJsonFormValue(formData, 'strengths', 100)).toThrow(
|
|
'Arquivos não são aceitos neste campo.',
|
|
)
|
|
})
|
|
|
|
it('preserves secret whitespace while rejecting missing, empty, and File values', () => {
|
|
const formData = new FormData()
|
|
|
|
expect(() => requiredFormSecret(formData, 'password')).toThrow(FormDataBoundaryError)
|
|
|
|
formData.set('password', '')
|
|
expect(() => requiredFormSecret(formData, 'password')).toThrow(FormDataBoundaryError)
|
|
|
|
formData.set('password', new File(['secret'], 'secret.txt'))
|
|
expect(() => requiredFormSecret(formData, 'password')).toThrow(FormDataBoundaryError)
|
|
|
|
formData.set('password', ' exact secret ')
|
|
expect(requiredFormSecret(formData, 'password')).toBe(' exact secret ')
|
|
|
|
formData.set('password', 'ordinary-secret')
|
|
expect(requiredFormSecret(formData, 'password')).toBe('ordinary-secret')
|
|
})
|
|
|
|
it('preserves absent, blank, and normalized PATCH text states', () => {
|
|
const formData = new FormData()
|
|
expect(nullableFormText(formData, 'name')).toBeUndefined()
|
|
|
|
formData.set('name', ' ')
|
|
expect(nullableFormText(formData, 'name')).toBeNull()
|
|
|
|
formData.set('name', ' Maria ')
|
|
expect(nullableFormText(formData, 'name')).toBe('Maria')
|
|
})
|
|
|
|
it('rejects blank and non-decimal relationship identifiers before conversion', () => {
|
|
for (const value of ['', ' ', '1.5', '1e2', '+1', '0']) {
|
|
const formData = new FormData()
|
|
formData.set('id', value)
|
|
expect(() => requiredRelationshipFormValue(formData, 'id')).toThrow()
|
|
}
|
|
})
|
|
|
|
it('deduplicates repeated positive integer values deterministically', () => {
|
|
const formData = new FormData()
|
|
for (const value of ['12', '3', '12', '7']) formData.append('coordinators', value)
|
|
|
|
expect(repeatedRelationshipFormValues(formData, 'coordinators')).toEqual([3, 7, 12])
|
|
})
|
|
|
|
it('distinguishes absent JSON from explicit empty arrays and rejects invalid inputs', () => {
|
|
const formData = new FormData()
|
|
expect(boundedJsonFormValue(formData, 'strengths', 100)).toBeUndefined()
|
|
|
|
formData.set('strengths', '[]')
|
|
expect(boundedJsonFormValue(formData, 'strengths', 100)).toEqual([])
|
|
|
|
formData.set('strengths', '{')
|
|
expect(() => boundedJsonFormValue(formData, 'strengths', 100)).toThrow()
|
|
formData.set('strengths', JSON.stringify(['x'.repeat(101)]))
|
|
expect(() => boundedJsonFormValue(formData, 'strengths', 100)).toThrow()
|
|
})
|
|
|
|
it('bounds JSON by raw UTF-8 bytes before trimming or parsing', () => {
|
|
const formData = new FormData()
|
|
|
|
formData.set('strengths', ' [] ')
|
|
expect(() => boundedJsonFormValue(formData, 'strengths', 3)).toThrow(
|
|
'Conteúdo excede o tamanho permitido.',
|
|
)
|
|
|
|
formData.set('strengths', '"á"')
|
|
expect(boundedJsonFormValue(formData, 'strengths', 4)).toBe('á')
|
|
expect(() => boundedJsonFormValue(formData, 'strengths', 3)).toThrow(
|
|
'Conteúdo excede o tamanho permitido.',
|
|
)
|
|
})
|
|
|
|
it('rejects oversized raw JSON without invoking JSON.parse', () => {
|
|
const formData = new FormData()
|
|
formData.set('strengths', ' '.repeat(101))
|
|
const parse = vi.spyOn(JSON, 'parse')
|
|
|
|
expect(() => boundedJsonFormValue(formData, 'strengths', 100)).toThrow(
|
|
'Conteúdo excede o tamanho permitido.',
|
|
)
|
|
expect(parse).not.toHaveBeenCalled()
|
|
parse.mockRestore()
|
|
})
|
|
|
|
it('centralizes Zod issue mapping by top-level field', () => {
|
|
const result = z
|
|
.object({ estimate: z.number().positive(), nested: z.object({ value: z.string().min(2) }) })
|
|
.safeParse({ estimate: 0, nested: { value: '' } })
|
|
if (result.success) throw new Error('Validation error expected.')
|
|
|
|
expect(validationFieldErrors(result.error)).toEqual({
|
|
estimate: [expect.any(String)],
|
|
nested: [expect.any(String)],
|
|
})
|
|
})
|
|
})
|