test(forms): extract pure decision logic and add exhaustive coverage

The forms security/correctness logic was buried in DB-coupled server
actions and the builder component, so it had no real tests (the suite
is pure-logic only; nothing exercises Supabase). Extract the pure
parts behaviour-unchanged into lib/forms-logic.ts and test them for
real:

- csvCell (results-export escaping), parseClientIp (e-sign IP capture
  filtering), shouldBumpFormVersion (waiver non-blocking re-sign),
  evaluateRequiredFormStep (onboarding required/auto-satisfy/fail-open),
  slugify + deriveFieldKeys (builder key derivation + dedupe)
- forms.ts / onboarding.ts / form-builder.tsx now consume these; the
  local copies are removed (no behaviour change — verified by the full
  suite staying green)
- __tests__/forms.test.ts expanded 16 -> 62: every validateAnswers
  type/branch (type coercion, length caps, required, unknown-key
  stripping, first-error ordering), parseFormSchema, every new Zod
  schema incl. formSlug boundaries, and all extracted helpers

333/333 tests; build verified (Compiled successfully, exit 0). No deploy.
This commit is contained in:
Moheeb Zara
2026-05-16 16:40:09 -07:00
parent 40d4cdad98
commit 5c39317877
5 changed files with 564 additions and 138 deletions
+12 -20
View File
@@ -22,6 +22,11 @@ import {
getPublicFormSchema,
} from '@/lib/validations'
import { parseFormSchema, validateAnswers } from '@/lib/forms-schema'
import {
csvCell,
parseClientIp,
shouldBumpFormVersion,
} from '@/lib/forms-logic'
const SUBMISSIONS_CAP = 5000
@@ -117,9 +122,12 @@ export async function updateForm(input: unknown) {
const legalChanged = u.legal_text !== undefined && u.legal_text !== existing.legal_text
const schemaChanged = u.schema !== undefined
if (
existing.kind === 'waiver' &&
existing.status === 'published' &&
(legalChanged || schemaChanged)
shouldBumpFormVersion({
kind: existing.kind,
status: existing.status,
legalChanged,
schemaChanged,
})
) {
patch.version = (existing.version as number) + 1
}
@@ -237,13 +245,6 @@ export async function getFormResults(input: unknown) {
return { data: { form, submissions: submissions ?? [] } }
}
function csvCell(value: unknown): string {
if (value === null || value === undefined) return ''
const s = typeof value === 'string' ? value : JSON.stringify(value)
if (/[",\n\r]/.test(s)) return `"${s.replace(/"/g, '""')}"`
return s
}
export async function exportFormResultsCsv(input: unknown) {
const gate = await requireFormsManager()
if (!gate.ok) return { error: gate.error }
@@ -301,15 +302,6 @@ export async function exportFormResultsCsv(input: unknown) {
}
}
const IP_RE = /^[0-9a-fA-F:.]{3,45}$/
function clientIp(h: Headers): string | null {
const fwd = h.get('x-forwarded-for')
const candidate = (fwd ? fwd.split(',')[0] : h.get('x-real-ip'))?.trim()
if (!candidate || !IP_RE.test(candidate)) return null
return candidate
}
/**
* Public submission entry point. Callable by anonymous visitors, signed-in
* non-members, or members depending on the form's visibility. Reads the form
@@ -375,7 +367,7 @@ export async function submitForm(input: unknown) {
form_snapshot: form.schema,
legal_text_snapshot: form.legal_text ?? null,
form_version: form.version,
ip: clientIp(h),
ip: parseClientIp(h.get('x-forwarded-for'), h.get('x-real-ip')),
user_agent: h.get('user-agent'),
})
if (insErr) return { error: insErr.message }