teqo-backup/tests/int/municipalityDossierData.int.spec.ts
Francisco Ignácio Fontoura Solla 7b93c232ff C13: renomeia "Plano de Acao" para "Atividade" em toda a vertical /campanha
Collection actionPlan -> activity (labels "Atividade"/"Atividades"), campo
campaignDemand.actionPlan -> activity, rota /campanha/planos ->
/campanha/atividades (criacao em atividades/nova), sidebar "Atividades" e
rotulos alinhados a entidade ("Tipo de atividade", "Origem da atividade",
"Resultado da atividade"). Valores de enum kind/status/origin inalterados.

Schema movido por migration data-preserving escrita a mao
(20260725_213000_rename_action_plan_to_activity): 4 tabelas, 3 enums, 2
colunas, 39 indices (4 pkeys inclusos), 14 FKs e 2 sequences, cada statement
guardado na existencia do objeto de origem, com down() simetrico. O gerador do
Payload emitiria DROP+CREATE e nao ve o indice parcial escrito a mao
action_plan_upcoming_start_at_idx, entao a lista veio de
pg_indexes/pg_constraint/pg_type/pg_sequences e o snapshot .json foi derivado
do anterior por transformacao textual -- validado por migrate:create __probe
sem mudanca de schema. A mesma migration alinha os 8 indices fosseis %plaza%
que a remodelagem de municipios deixou (renomeou as colunas plaza_id, nao os
indices).

Guard: o describe "banned campaign terminology" de
codebaseConventions.unit.spec.ts virou tabela de termos varrendo src+tests+
scripts (migrations fora -- historia congelada), banindo os compostos
actionPlan, a copy "plano de acao" e a rota antiga, mas nao plan/plano isolado
(colidiria com os planos de assinatura da Fase 2). Allowlist: o proprio spec,
scripts/generate-remodel-municipalities-migration.mjs e o comentario historico
em campaignMigrationReconciliation.int.spec.ts.

Plano: docs/plans/renomear-plano-acao-para-atividade.md
2026-07-25 20:50:38 -03:00

113 lines
4.1 KiB
TypeScript

// @vitest-environment node
import type { Payload } from 'payload'
import { getPayload } from 'payload'
import { beforeAll, describe, expect, it, vi } from 'vitest'
// `loadMunicipalityElectoralBaseline` is wrapped in Next's `unstable_cache`,
// which needs the Next server runtime (incrementalCache) — unavailable under
// vitest. The dossier only forwards its result, so stub the module here.
vi.mock('@/utilities/municipalityElectoralBaseline', () => ({
loadMunicipalityElectoralBaseline: vi.fn().mockResolvedValue(null),
}))
import { createActivityRecord } from '@/app/(campaign)/campanha/actions/activity'
import { updateMunicipalityStrategyRecord } from '@/app/(campaign)/campanha/actions/municipality'
import config from '@/payload.config'
import {
DOSSIER_LEADERSHIP_LIMIT,
DOSSIER_SIGNAL_LIMIT,
loadMunicipalityDossierData,
} from '@/utilities/municipalityDossierData'
import {
getMunicipalityDetailViewModel,
resolveAccessibleMunicipalityContext,
} from '@/utilities/municipalityPageData'
import { installCampaignFixtures } from '../helpers/campaignFixtures'
let payload: Payload
const campaignFixtures = installCampaignFixtures({
getPayload: () => payload,
setPayload: (nextPayload) => {
payload = nextPayload
},
})
describe('municipality dossier data (E16)', () => {
beforeAll(async () => {
payload = await getPayload({ config: await config })
})
// The fixture builds ~20 rows sequentially (leaderships, updates, activities),
// so this test legitimately outlives the 5s default under parallel load.
it(
'composes the pre-visit dossier with section caps and full totals',
{ timeout: 30_000 },
async () => {
const fixtures = campaignFixtures()
const coordinator = await fixtures.createCampaignUser('coordinator')
const municipality = await fixtures.getMunicipality()
for (let index = 0; index < DOSSIER_LEADERSHIP_LIMIT + 1; index += 1) {
const contact = await fixtures.createContact()
await fixtures.createLeadership({
contact: contact.id,
municipalities: [municipality.id],
})
}
for (let index = 0; index < DOSSIER_SIGNAL_LIMIT + 1; index += 1) {
await fixtures.createMunicipalityUpdate({
municipality: municipality.id,
author: coordinator.id,
})
}
const upcomingActivity = await createActivityRecord(payload, coordinator, {
title: fixtures.value('Caminhada dossiê'),
kind: 'caminhada',
status: 'planejado',
startAt: new Date(Date.now() + 86_400_000).toISOString(),
municipality: municipality.id,
locality: 'Centro',
})
fixtures.own('activity', upcomingActivity.id)
await updateMunicipalityStrategyRecord(payload, coordinator, {
municipality: municipality.id,
priority: 'alta',
budgetNotes: 'Emenda de bancada 2024 destinada ao hospital municipal.',
})
fixtures.touchMunicipality(municipality.id)
const context = await resolveAccessibleMunicipalityContext(
payload,
coordinator,
municipality.slug,
)
const view = await getMunicipalityDetailViewModel(payload, context, coordinator)
const dossier = await loadMunicipalityDossierData(payload, coordinator, view)
expect(dossier.leaderships.rows).toHaveLength(DOSSIER_LEADERSHIP_LIMIT)
expect(dossier.leaderships.totalCount).toBe(DOSSIER_LEADERSHIP_LIMIT + 1)
expect(dossier.leaderships.rows.every((row) => row.updatedAt)).toBe(true)
expect(dossier.signals.rows).toHaveLength(DOSSIER_SIGNAL_LIMIT)
expect(dossier.signals.totalCount).toBe(DOSSIER_SIGNAL_LIMIT + 1)
expect(dossier.upcomingActivities.map((activity) => activity.id)).toContain(
upcomingActivity.id,
)
expect(dossier.recentActivities).toEqual([])
// Staff view model exists, so the E8 goal account block is present.
expect(dossier.goalAccount).not.toBeNull()
expect(dossier.demographics?.population).toBeGreaterThan(0)
expect(view.strategy?.budgetNotes).toBe(
'Emenda de bancada 2024 destinada ao hospital municipal.',
)
},
)
})