teqo-backup/tests/int/electionResultsImport.int.spec.ts
Francisco Solla 8dd60cf962 A6 — Dobradinha 2026 automática quando o TSE publicar candidaturas (pós-15/08) (#82)
* docs(A6): freshness audit da #27 no plano de dobradinhas 2026

electionInsights.ts foi deletado no Pass 2 W4a (modulo puro novo),
electionAlliances.ts nao nasce (campoParties + electionPartySpectrum ja
carregam a taxonomia), questoes em aberto resolvidas (tiers, score,
proporcionais, lockdown de lideranca), card na aba Eleicoes, overview da
lista fora de escopo, cache election-tse com bust na conciliacao Fase 5.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* feat(A6): insight de dobradinhas 2026 na aba Eleicoes do municipio

Lista ranqueada de candidatos proporcionais que concorrem de novo em 2026
(runningAgain2026=sim) por geografia do municipio: score 0,6 alinhamento
(campo 2026 + espectro Bolognesi) + 0,4 forca local normalizada pelo maximo
do conjunto. Estado pendente ate a conciliacao pos-15/08; Solla excluido
(referencia, nao parceiro); leitura derivada sem escrita; loader cacheado
sob a tag election-tse como os irmaos; link para /campanha/dobradinhas
como caminho de registro operacional.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* test(A6): cobertura unit/int do insight + lease das colecoes eleitorais

Unit: tiers (campo/espectro/falha-para-o-meio), score, ordenacao, empates,
limite, zero votos. Int: pending2026 sem conciliacao (mesmo com votos
locais), so adversarios, misto com exclusao de Solla/nao e geografia
alheia. Lease advisory election-collections serializa os dois specs que
tratam as colecoes eleitorais como test-owned (o import wipe-all apagaria
as fixtures do novo spec no meio do assert, 48-way paralelo).

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* refactor(A6): fixes do /simplify (3 reviewers paralelos)

P1: campo coalition morto removido do input/select/mapping (sem consumidor).
P1: hooks do lease com timeout explicito de 120s nos dois specs eleitorais
(o wait advisory passa dos 10s default do Vitest sob contencao — flake em
ambas as direcoes). P2: sonda de conciliacao via payload.count (idioma da
casa), desempate final por candidateNumber (ordem deterministica), geography
como prop na secao (sem lookup duplicado), casing Dep. Federal/Estadual
alinhado, marcador 1313 via BASELINE_TICKET_2022, entrada no CHANGELOG.

Declinados com justificativa: zones como tupla nao-vazia (churn em tipo
compartilhado por assert provada no catalogo) e early-start da promise
antes do Promise.all (loader-dentro-do-Suspense e o idioma da casa; ganho
so em cache frio).

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-31 00:14:27 -03:00

238 lines
7.3 KiB
TypeScript

// @vitest-environment node
import { getPayload, type Payload } from 'payload'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { BASELINE_TICKET_2022 } from '@/lib/electionResults'
import { FEDERAL_ONLY_OFFICES } from '@/lib/electionResultsBuild'
import config from '@/payload.config'
import { buildImportBundles, importElectionBundles } from '@/utilities/electionResultsImport'
import {
acquireTestDatabaseLease,
ELECTION_COLLECTIONS_LEASE_KEY,
type TestDatabaseLease,
} from '../helpers/testDatabaseLease'
import {
loadTseFixtureResults,
loadTseFixtureResultsForYear,
TSE_FIXTURE_EXPECTED,
} from '../helpers/tseFixtures'
let payload: Payload
let electionCollectionsLease: TestDatabaseLease
const deleteAllElectionData = async () => {
for (const collection of [
'electionCandidateVote',
'electionTally',
'electionCandidate',
] as const) {
const existing = await payload.find({
collection,
depth: 0,
limit: 0,
pagination: false,
overrideAccess: true,
})
for (const doc of existing.docs) {
await payload.delete({
collection,
id: doc.id,
overrideAccess: true,
})
}
}
}
describe('election results import', () => {
// The lease acquisition blocks until the other election spec finishes its
// whole file — far past the 10s default hook timeout under pool contention.
beforeAll(async () => {
payload = await getPayload({ config: await config })
// This file deletes EVERY election row; the lease keeps that wipe from
// deleting another election spec's fixtures mid-run (48-way file
// parallelism, one Postgres).
electionCollectionsLease = await acquireTestDatabaseLease(
payload,
ELECTION_COLLECTIONS_LEASE_KEY,
)
await deleteAllElectionData()
}, 120_000)
afterAll(async () => {
await deleteAllElectionData()
await electionCollectionsLease.release()
}, 120_000)
it('imports fixture totals, winners, elected flags, and is idempotent', async () => {
const built = loadTseFixtureResults()
const bundles = buildImportBundles(built)
const first = await importElectionBundles(payload, bundles)
const second = await importElectionBundles(payload, bundles)
const firstVotes = first.reduce((sum, c) => sum + c.votesInserted, 0)
const secondVotes = second.reduce((sum, c) => sum + c.votesInserted, 0)
expect(firstVotes).toBe(TSE_FIXTURE_EXPECTED.voteRowCount)
expect(secondVotes).toBe(firstVotes)
expect(second.reduce((sum, c) => sum + c.votesDeleted, 0)).toBe(firstVotes)
const votes = await payload.find({
collection: 'electionCandidateVote',
depth: 0,
limit: 0,
pagination: false,
overrideAccess: true,
})
expect(votes.totalDocs).toBe(TSE_FIXTURE_EXPECTED.voteRowCount)
const sollaVotes = votes.docs
.filter(
(doc) =>
doc.office === 'deputado_federal' &&
Number(doc.candidateNumber) === BASELINE_TICKET_2022.candidate.candidateNumber,
)
.reduce((sum, doc) => sum + Number(doc.votes), 0)
expect(sollaVotes).toBe(TSE_FIXTURE_EXPECTED.sollaVotesTotal)
const tallies = await payload.find({
collection: 'electionTally',
where: {
and: [
{ office: { equals: 'deputado_federal' } },
{ cityCode: { equals: '38490' } },
{ zoneNumber: { equals: 1 } },
{ turn: { equals: '1' } },
],
},
depth: 0,
limit: 1,
overrideAccess: true,
})
expect(tallies.docs[0]?.winnerCandidateNumber).toBe(
TSE_FIXTURE_EXPECTED.federalWinnerSalvadorZ1.candidateNumber,
)
expect(Number(tallies.docs[0]?.winnerVotes)).toBe(
TSE_FIXTURE_EXPECTED.federalWinnerSalvadorZ1.votes,
)
const candidates = await payload.find({
collection: 'electionCandidate',
depth: 0,
limit: 0,
pagination: false,
overrideAccess: true,
})
const solla = candidates.docs.find(
(doc) => doc.office === 'deputado_federal' && Number(doc.candidateNumber) === 1313,
)
const lula = candidates.docs.find(
(doc) =>
doc.office === 'presidente' && Number(doc.candidateNumber) === 13 && doc.turn === '2',
)
const jeronimo = candidates.docs.find(
(doc) =>
doc.office === 'governador' && Number(doc.candidateNumber) === 13 && doc.turn === '2',
)
expect(solla?.elected).toBe(true)
expect(lula?.elected).toBe(true)
expect(jeronimo?.elected).toBe(true)
expect(lula?.identityKey).toBeTruthy()
})
it('imports a historical year without removing the 2022 scope', async () => {
const built2018 = loadTseFixtureResultsForYear(2018, FEDERAL_ONLY_OFFICES)
await importElectionBundles(payload, buildImportBundles(built2018))
const votes2022 = await payload.find({
collection: 'electionCandidateVote',
where: { year: { equals: 2022 } },
depth: 0,
limit: 0,
pagination: false,
overrideAccess: true,
})
const votes2018 = await payload.find({
collection: 'electionCandidateVote',
where: {
and: [{ year: { equals: 2018 } }, { office: { equals: 'deputado_federal' } }],
},
depth: 0,
limit: 0,
pagination: false,
overrideAccess: true,
})
const president2018 = await payload.find({
collection: 'electionCandidateVote',
where: {
and: [{ year: { equals: 2018 } }, { office: { equals: 'presidente' } }],
},
depth: 0,
limit: 0,
pagination: false,
overrideAccess: true,
})
expect(votes2022.totalDocs).toBe(TSE_FIXTURE_EXPECTED.voteRowCount)
expect(votes2018.totalDocs).toBeGreaterThan(0)
expect(president2018.totalDocs).toBe(0)
})
it('allows campaignUser read and denies campaignUser mutation', async () => {
// Avoid campaignFixtures here: teqo_test may already include territory migrations
// from other worktrees that diverge from this branch's electoralNucleus schema.
const campaignUser = await payload.create({
collection: 'campaignUser',
data: {
name: 'Election Access Tester',
role: 'coordinator',
email: `election-access-${Date.now()}@example.com`,
password: 'Password123!',
},
overrideAccess: true,
})
try {
const readable = await payload.find({
collection: 'electionTally',
depth: 0,
limit: 1,
user: campaignUser,
overrideAccess: false,
})
expect(readable.totalDocs).toBeGreaterThan(0)
await expect(
payload.create({
collection: 'electionTally',
data: {
year: 2022,
office: 'deputado_federal',
turn: '1',
state: 'BA',
cityCode: '99999',
cityName: 'Salvador',
zoneNumber: 99,
aptos: 1,
comparecimento: 1,
abstencoes: 0,
votosValidos: 1,
votosNominaisValidos: 1,
votosLegenda: 0,
votosBranco: 0,
votosNulo: 0,
votosAnulados: 0,
},
user: campaignUser,
overrideAccess: false,
}),
).rejects.toThrow()
} finally {
await payload.delete({
collection: 'campaignUser',
id: campaignUser.id,
overrideAccess: true,
})
}
})
})