mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
Fold duplicated campanha access, loaders, list/filter/form shells, and scripts into shared modules; reorganize utilities by domain; stabilize e2e; and document prevention-first audit workflow so the same smells cannot creep back in.
299 lines
10 KiB
TypeScript
299 lines
10 KiB
TypeScript
// @vitest-environment node
|
|
|
|
import type { Payload } from 'payload'
|
|
import { getPayload } from 'payload'
|
|
import { beforeAll, describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
declareVotesRecord,
|
|
estimateVotesRecord,
|
|
} from '@/app/(campaign)/campanha/actions/votePledge'
|
|
import config from '@/payload.config'
|
|
import { aggregatePledgesByMunicipality } from '@/utilities/votePledgeData'
|
|
import { aggregateMunicipalityPledgesFromRows } from '@/utilities/votePledgeViews'
|
|
|
|
import { installCampaignFixtures } from '../helpers/campaignFixtures'
|
|
|
|
let payload: Payload
|
|
const campaignFixtures = installCampaignFixtures({
|
|
getPayload: () => payload,
|
|
setPayload: (nextPayload) => {
|
|
payload = nextPayload
|
|
},
|
|
})
|
|
|
|
describe('vote pledges (declared by staff, estimated by staff)', () => {
|
|
beforeAll(async () => {
|
|
payload = await getPayload({ config: await config })
|
|
})
|
|
|
|
const createEngagedLeadership = async () => {
|
|
const fixtures = campaignFixtures()
|
|
const municipality = await fixtures.getMunicipality()
|
|
const advisor = await fixtures.createCampaignUser('advisor')
|
|
await fixtures.assignMunicipalityAdvisors(municipality.id, [advisor.id])
|
|
const contact = await fixtures.createContact()
|
|
const leadership = await fixtures.createLeadership({
|
|
contact: contact.id,
|
|
municipalities: [municipality.id],
|
|
supportStatus: 'engajado',
|
|
})
|
|
return { fixtures, municipality, advisor, contact, leadership }
|
|
}
|
|
|
|
it('lets staff declare and update votes for a linked leadership', async () => {
|
|
const { municipality, advisor, leadership } = await createEngagedLeadership()
|
|
|
|
const pledge = await declareVotesRecord(payload, advisor, {
|
|
municipality: municipality.id,
|
|
leadership: leadership.id,
|
|
declaredVotes: 150,
|
|
})
|
|
expect(pledge.declaredVotes).toBe(150)
|
|
expect(pledge.declaredAt).toBeTruthy()
|
|
campaignFixtures().own('votePledge', pledge.id)
|
|
|
|
const updated = await declareVotesRecord(payload, advisor, {
|
|
municipality: municipality.id,
|
|
leadership: leadership.id,
|
|
declaredVotes: 220,
|
|
})
|
|
expect(updated.id).toBe(pledge.id)
|
|
expect(updated.declaredVotes).toBe(220)
|
|
})
|
|
|
|
it('preserves the vote pledge trajectory in native Payload versions', async () => {
|
|
const { municipality, advisor, leadership } = await createEngagedLeadership()
|
|
const pledge = await declareVotesRecord(payload, advisor, {
|
|
municipality: municipality.id,
|
|
leadership: leadership.id,
|
|
declaredVotes: 80,
|
|
})
|
|
campaignFixtures().own('votePledge', pledge.id)
|
|
|
|
await declareVotesRecord(payload, advisor, {
|
|
municipality: municipality.id,
|
|
leadership: leadership.id,
|
|
declaredVotes: 125,
|
|
})
|
|
|
|
const versions = await payload.findVersions({
|
|
collection: 'votePledge',
|
|
where: { parent: { equals: pledge.id } },
|
|
depth: 0,
|
|
pagination: false,
|
|
sort: 'createdAt',
|
|
overrideAccess: true,
|
|
})
|
|
|
|
expect(versions.docs.map((version) => version.version.declaredVotes)).toEqual(
|
|
expect.arrayContaining([80, 125]),
|
|
)
|
|
})
|
|
|
|
it('rejects a declaration in a municipality the leadership is not linked to', async () => {
|
|
const { advisor, fixtures, leadership } = await createEngagedLeadership()
|
|
const otherMunicipality = await fixtures.getMunicipality()
|
|
|
|
await expect(
|
|
declareVotesRecord(payload, advisor, {
|
|
municipality: otherMunicipality.id,
|
|
leadership: leadership.id,
|
|
declaredVotes: 10,
|
|
}),
|
|
).rejects.toThrow('vinculada ao município')
|
|
})
|
|
|
|
it('blocks leaders from declaring votes', async () => {
|
|
const { municipality, fixtures } = await createEngagedLeadership()
|
|
const leaderAccount = await fixtures.createCampaignUser('leader')
|
|
const contact = await fixtures.createContact()
|
|
const leadership = await fixtures.createLeadership({
|
|
contact: contact.id,
|
|
municipalities: [municipality.id],
|
|
user: leaderAccount.id,
|
|
supportStatus: 'engajado',
|
|
})
|
|
|
|
await expect(
|
|
declareVotesRecord(payload, leaderAccount, {
|
|
municipality: municipality.id,
|
|
leadership: leadership.id,
|
|
declaredVotes: 50,
|
|
}),
|
|
).rejects.toThrow('coordenação e a assessoria')
|
|
})
|
|
|
|
it('never exposes staff estimates to leaders through read access', async () => {
|
|
const { fixtures, municipality, advisor, leadership } = await createEngagedLeadership()
|
|
const leaderAccount = await fixtures.createCampaignUser('leader')
|
|
await fixtures.createLeadership({
|
|
contact: await fixtures.createContact(),
|
|
municipalities: [municipality.id],
|
|
user: leaderAccount.id,
|
|
supportStatus: 'engajado',
|
|
})
|
|
|
|
const pledge = await declareVotesRecord(payload, advisor, {
|
|
municipality: municipality.id,
|
|
leadership: leadership.id,
|
|
declaredVotes: 300,
|
|
})
|
|
campaignFixtures().own('votePledge', pledge.id)
|
|
|
|
const estimated = await estimateVotesRecord(payload, advisor, {
|
|
pledge: pledge.id,
|
|
estimatedVotes: { pessimistic: null, central: 120, optimistic: null },
|
|
estimateNote: 'Base histórica indica menos da metade.',
|
|
})
|
|
expect(estimated.estimatedVotes?.central).toBe(120)
|
|
|
|
await expect(
|
|
payload.find({
|
|
collection: 'votePledge',
|
|
where: { id: { equals: pledge.id } },
|
|
depth: 0,
|
|
pagination: false,
|
|
user: leaderAccount,
|
|
overrideAccess: false,
|
|
}),
|
|
).rejects.toThrow(/permissão/i)
|
|
})
|
|
|
|
it('scopes an advisor to pledges of administered municipalities only', async () => {
|
|
const { fixtures, municipality, advisor, leadership } = await createEngagedLeadership()
|
|
const pledge = await declareVotesRecord(payload, advisor, {
|
|
municipality: municipality.id,
|
|
leadership: leadership.id,
|
|
declaredVotes: 50,
|
|
})
|
|
fixtures.own('votePledge', pledge.id)
|
|
|
|
const outsideAdvisor = await fixtures.createCampaignUser('advisor')
|
|
|
|
await expect(
|
|
estimateVotesRecord(payload, outsideAdvisor, {
|
|
pledge: pledge.id,
|
|
estimatedVotes: { pessimistic: null, central: 10, optimistic: null },
|
|
estimateNote: null,
|
|
}),
|
|
).rejects.toThrow()
|
|
|
|
const outsideRead = await payload.find({
|
|
collection: 'votePledge',
|
|
where: { id: { equals: pledge.id } },
|
|
depth: 0,
|
|
pagination: false,
|
|
user: outsideAdvisor,
|
|
overrideAccess: false,
|
|
})
|
|
expect(outsideRead.docs).toHaveLength(0)
|
|
})
|
|
|
|
it('lets the candidate read and estimate pledges across municipalities', async () => {
|
|
const { fixtures, municipality, advisor, leadership } = await createEngagedLeadership()
|
|
const candidate = await fixtures.createCampaignUser('candidate')
|
|
|
|
const pledge = await declareVotesRecord(payload, advisor, {
|
|
municipality: municipality.id,
|
|
leadership: leadership.id,
|
|
declaredVotes: 90,
|
|
})
|
|
fixtures.own('votePledge', pledge.id)
|
|
|
|
const candidateRead = await payload.find({
|
|
collection: 'votePledge',
|
|
where: { id: { equals: pledge.id } },
|
|
depth: 0,
|
|
pagination: false,
|
|
user: candidate,
|
|
overrideAccess: false,
|
|
})
|
|
expect(candidateRead.docs).toHaveLength(1)
|
|
|
|
const estimated = await estimateVotesRecord(payload, candidate, {
|
|
pledge: pledge.id,
|
|
estimatedVotes: { pessimistic: null, central: 45, optimistic: null },
|
|
estimateNote: null,
|
|
})
|
|
expect(estimated.estimatedVotes?.central).toBe(45)
|
|
})
|
|
|
|
it('aggregates effective votes as estimated ?? declared', async () => {
|
|
const { fixtures, municipality, advisor, leadership } = await createEngagedLeadership()
|
|
|
|
const firstPledge = await declareVotesRecord(payload, advisor, {
|
|
municipality: municipality.id,
|
|
leadership: leadership.id,
|
|
declaredVotes: 200,
|
|
})
|
|
fixtures.own('votePledge', firstPledge.id)
|
|
await estimateVotesRecord(payload, advisor, {
|
|
pledge: firstPledge.id,
|
|
estimatedVotes: { pessimistic: null, central: 80, optimistic: null },
|
|
estimateNote: null,
|
|
})
|
|
|
|
const secondContact = await fixtures.createContact()
|
|
const secondLeadership = await fixtures.createLeadership({
|
|
contact: secondContact.id,
|
|
municipalities: [municipality.id],
|
|
supportStatus: 'engajado',
|
|
})
|
|
const secondPledge = await fixtures.createVotePledge({
|
|
leadership: secondLeadership.id,
|
|
municipality: municipality.id,
|
|
declaredVotes: 40,
|
|
})
|
|
|
|
const aggregates = await aggregatePledgesByMunicipality(payload, [municipality.id])
|
|
const aggregate = aggregates.get(municipality.id)
|
|
expect(aggregate?.pledgeCount).toBe(2)
|
|
expect(aggregate?.declaredTotal).toBe(240)
|
|
expect(aggregate?.effectiveByScenario.central).toBe(120)
|
|
expect(aggregate?.missingEstimateCount).toBe(1)
|
|
expect(secondPledge.declaredVotes).toBe(40)
|
|
})
|
|
|
|
// P3-E pin: the LIST aggregate (aggregatePledgesByMunicipality, whose fold now
|
|
// delegates) and the DOSSIÊ fold (aggregateMunicipalityPledgesFromRows on the
|
|
// detail loaders' rows) must produce the same aggregate for the same pledges —
|
|
// the whole point of the fold delegation.
|
|
it('produces the dossiê aggregate and the list aggregate from one fold', async () => {
|
|
const { municipality, advisor, leadership } = await createEngagedLeadership()
|
|
const pledge = await declareVotesRecord(payload, advisor, {
|
|
municipality: municipality.id,
|
|
leadership: leadership.id,
|
|
declaredVotes: 150,
|
|
})
|
|
campaignFixtures().own('votePledge', pledge.id)
|
|
await estimateVotesRecord(payload, advisor, {
|
|
pledge: pledge.id,
|
|
estimatedVotes: { pessimistic: 120, central: 150, optimistic: 180 },
|
|
estimateNote: null,
|
|
})
|
|
|
|
const listAggregate = (await aggregatePledgesByMunicipality(payload, [municipality.id])).get(
|
|
municipality.id,
|
|
)
|
|
|
|
const { docs } = await payload.find({
|
|
collection: 'votePledge',
|
|
where: { municipality: { equals: municipality.id } },
|
|
depth: 0,
|
|
limit: 0,
|
|
pagination: false,
|
|
overrideAccess: true,
|
|
})
|
|
const dossierAggregate = aggregateMunicipalityPledgesFromRows(
|
|
docs.map((doc) => ({
|
|
declaredVotes: doc.declaredVotes ?? 0,
|
|
estimatedVotes: doc.estimatedVotes,
|
|
declaredAt: doc.declaredAt,
|
|
estimatedAt: doc.estimatedAt,
|
|
})),
|
|
)
|
|
|
|
expect(listAggregate).toEqual(dossierAggregate)
|
|
})
|
|
})
|