teqo-backup/tests/unit/campaignInviteArchitecture.unit.spec.ts
Francisco Ignácio Fontoura Solla bcac665cb5 Ship /campanha nuclei MVP with RBAC, invites, and vote estimates.
Adds electoral nuclei, leaderships, updates, and WhatsApp invite flows behind campaign auth, plus the consolidated schema migration that Vercel applies on deploy.
2026-07-18 15:01:56 -03:00

92 lines
3.4 KiB
TypeScript

// @vitest-environment node
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import { resolveInviteConsent } from '@/utilities/campaignConsent'
const source = (relativePath: string): string =>
readFileSync(resolve(process.cwd(), relativePath), 'utf8')
const lineCount = (contents: string): number => contents.split(/\r?\n/).length
describe('campaign invite module boundaries', () => {
const actionPath = 'src/app/(campaign)/campanha/actions/invite.ts'
const modules = {
repository: 'src/utilities/campaignInviteRepository.ts',
creation: 'src/utilities/campaignInviteCreation.ts',
redemption: 'src/utilities/campaignInviteRedemption.ts',
} as const
it('keeps privileged invite modules server-only and focused', () => {
const repository = source(modules.repository)
const creation = source(modules.creation)
const redemption = source(modules.redemption)
for (const contents of [repository, creation, redemption]) {
expect(contents).toMatch(/^import ['"]server-only['"]/)
}
expect(repository).toContain('acquireTextAdvisoryLocks')
expect(repository).toContain('hashCampaignInviteToken')
expect(repository).toContain('UPDATE "campaign_invite"')
expect(repository).not.toContain('generateCampaignInviteToken')
expect(repository).not.toContain('buildCampaignInviteWhatsAppLink')
expect(creation).toContain('reloadCampaignActor')
expect(creation).toContain('requireLeadershipConsent')
expect(creation).toContain('buildCampaignInviteWhatsAppLink')
expect(creation).not.toContain('@payloadcms/db-postgres')
expect(redemption).toContain('resolveInviteConsent')
expect(redemption).toContain('assertContactPhoneAvailable')
expect(redemption).toContain("role: 'lideranca'")
expect(redemption).not.toContain('@payloadcms/db-postgres')
})
it('keeps the use-server entrypoint thin with only public actions exported', () => {
const action = source(actionPath)
const exports = [...action.matchAll(/^export const (\w+)/gm)]
.map(([, name]) => name)
.sort()
expect(action).toMatch(/^['"]use server['"]/)
expect(exports).toEqual([
'createCampaignInvite',
'redeemCampaignInviteAutofill',
'redeemCampaignInviteLogin',
])
expect(action).not.toContain('@payloadcms/db-postgres')
expect(action).not.toContain('overrideAccess')
expect(action).not.toContain('hashCampaignInviteToken')
expect(action).not.toContain('password:')
expect(lineCount(action)).toBeLessThanOrEqual(75)
})
it('keeps invite modules within their responsibility budgets', () => {
expect(lineCount(source(modules.repository))).toBeLessThanOrEqual(160)
expect(lineCount(source(modules.creation))).toBeLessThanOrEqual(150)
expect(lineCount(source(modules.redemption))).toBeLessThanOrEqual(340)
})
it('preserves consent only for the exact configured version', () => {
expect(
resolveInviteConsent({
existingConsentID: 7,
existingConsentContentHash: 'stable-hash',
configuredConsentID: 7,
configuredConsentContentHash: 'stable-hash',
}),
).toEqual({ shouldUpdate: false })
expect(() =>
resolveInviteConsent({
existingConsentID: 7,
existingConsentContentHash: 'old-hash',
configuredConsentID: 7,
configuredConsentContentHash: 'new-hash',
}),
).toThrow('É necessário aceitar o consentimento.')
})
})