mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-14 17:43:04 +00:00
- knip (files/deps at error level; unused exports ledgered as warn) with config for Payload/Next entries; deleted the dead modules it confirmed: RecentlyVisited + UpcomingActionPlansCard + actionPlanUpcomingPreview, lazy-dialog helpers, tseZone, federalCandidateTotalsAggregate (test-only), unused ui kit files, one-shot rename script; pruned unused radix deps and typescript-eslint; added pg/@types/pg (pinned to drizzle's 8.10.2) - GitHub Actions CI on the mirror: lint, typecheck, knip, unit, migrate, int against a Postgres 17 service - eslint: unused-vars with ^_ ignore patterns (migrations args exempt), repo-wide ban on `as never` (no-restricted-syntax), lint gate at --max-warnings=0; fixed all 54 pre-existing warnings (dead imports, hooks deps, tracking-pixel img disable with impeccable config exception) - tsconfig: include consolidated from 219 lines to 5 (wildcard covers all .next dist variants), noUnusedLocals on - typed replacements for every as-never: hookFilledCreateData (actions create data completed by collection hooks), drizzle scopeColumn narrowing, tests' stub<T> helper (property-level checking kept, completeness waived) - test infra: leased-consent generalization used by onda0 spec (shared leases over the stable consent keys; window race ledgered), purge fix - docs: IMPROVE-CODE-QUALITY-PLAN.md tracker, TECH-DEBT.md ledger, TESTING.md safety-net map (package.json also gains the Phase 2 build:election-aggregates script entry; the script lands next commit)
210 lines
7.3 KiB
TypeScript
210 lines
7.3 KiB
TypeScript
// @vitest-environment node
|
|
|
|
import { getPayload, type Payload, type RequiredDataFromCollectionSlug } from 'payload'
|
|
import { beforeAll, describe, expect, it } from 'vitest'
|
|
|
|
import { createLeaderSupporterRecord } from '@/app/(campaign)/campanha/actions/leaderSupporter'
|
|
import config from '@/payload.config'
|
|
import { SUPPORTER_REGISTRATION_CONSENT_KEY } from '@/utilities/campaignConsent'
|
|
import { relationshipId } from '@/utilities/relationship'
|
|
|
|
import { installCampaignFixtures } from '../helpers/campaignFixtures'
|
|
import { stub } from '../helpers/stub'
|
|
import {
|
|
SUPPORTER_REGISTRATION_CONSENT_LEASE_KEY,
|
|
withLeasedConsent,
|
|
} from '../helpers/testDatabaseLease'
|
|
|
|
let payload: Payload
|
|
const campaignFixtures = installCampaignFixtures({
|
|
getPayload: () => payload,
|
|
setPayload: (nextPayload) => {
|
|
payload = nextPayload
|
|
},
|
|
})
|
|
|
|
/** Runs `operation` with the shared supporter-registration consent guaranteed present. */
|
|
const withSupporterRegistrationConsent = <Result>(
|
|
operation: () => Promise<Result>,
|
|
): Promise<Result> =>
|
|
withLeasedConsent(
|
|
payload,
|
|
{
|
|
consentKey: SUPPORTER_REGISTRATION_CONSENT_KEY,
|
|
leaseKey: SUPPORTER_REGISTRATION_CONSENT_LEASE_KEY,
|
|
},
|
|
operation,
|
|
)
|
|
|
|
/**
|
|
* Collections without explicit `access` fall back to Payload's "any
|
|
* authenticated user" default — which includes campaign users presenting the
|
|
* `campaign-token` JWT against `/api/*`. These tests pin the lockdown added to
|
|
* the CMS/PII collections: campaign users (even the coordinator) must be
|
|
* denied, admins keep working, and the public keeps read-only access where
|
|
* intended.
|
|
*/
|
|
describe('CMS/PII collection access lockdown', () => {
|
|
beforeAll(async () => {
|
|
payload = await getPayload({ config: await config })
|
|
})
|
|
|
|
it('denies campaign users any access to admin accounts (users)', async () => {
|
|
const coordinator = await campaignFixtures().createCampaignUser('coordinator')
|
|
|
|
await expect(
|
|
payload.count({ collection: 'users', where: {}, user: coordinator, overrideAccess: false }),
|
|
).rejects.toThrow(/permissão|not allowed/i)
|
|
|
|
await expect(
|
|
payload.create({
|
|
collection: 'users',
|
|
data: { email: 'escalated@example.com', password: 'owned-by-attacker' },
|
|
user: coordinator,
|
|
overrideAccess: false,
|
|
}),
|
|
).rejects.toThrow(/permissão|not allowed/i)
|
|
})
|
|
|
|
it('lets admin users keep managing the users collection', async () => {
|
|
const fixtures = campaignFixtures()
|
|
const admin = await fixtures.createAdminUser()
|
|
|
|
const result = await payload.count({
|
|
collection: 'users',
|
|
where: { id: { equals: admin.id } },
|
|
user: admin,
|
|
overrideAccess: false,
|
|
})
|
|
expect(result.totalDocs).toBe(1)
|
|
})
|
|
|
|
it.each(['signature', 'subscription', 'consent'] as const)(
|
|
'denies campaign users and anonymous requests access to %s (PII/legal)',
|
|
async (collection) => {
|
|
const coordinator = await campaignFixtures().createCampaignUser('coordinator')
|
|
|
|
await expect(
|
|
payload.count({ collection, where: {}, user: coordinator, overrideAccess: false }),
|
|
).rejects.toThrow(/permissão|not allowed/i)
|
|
|
|
await expect(payload.count({ collection, where: {}, overrideAccess: false })).rejects.toThrow(
|
|
/permissão|not allowed/i,
|
|
)
|
|
},
|
|
)
|
|
|
|
it('lets admin users keep reading signatures and subscriptions', async () => {
|
|
const fixtures = campaignFixtures()
|
|
const admin = await fixtures.createAdminUser()
|
|
|
|
await expect(
|
|
payload.count({ collection: 'signature', where: {}, user: admin, overrideAccess: false }),
|
|
).resolves.toMatchObject({ totalDocs: expect.any(Number) })
|
|
await expect(
|
|
payload.count({ collection: 'subscription', where: {}, user: admin, overrideAccess: false }),
|
|
).resolves.toMatchObject({ totalDocs: expect.any(Number) })
|
|
})
|
|
|
|
it.each(['post', 'tag', 'petition', 'media'] as const)(
|
|
'denies campaign users create access to public-content collection %s',
|
|
async (collection) => {
|
|
const coordinator = await campaignFixtures().createCampaignUser('coordinator')
|
|
|
|
await expect(
|
|
payload.create({
|
|
collection,
|
|
// Denied before validation: an empty payload stands in for required fields.
|
|
data: stub<RequiredDataFromCollectionSlug<typeof collection>>({}),
|
|
user: coordinator,
|
|
overrideAccess: false,
|
|
}),
|
|
).rejects.toThrow(/permissão|not allowed/i)
|
|
},
|
|
)
|
|
|
|
it.each(['post', 'tag', 'petition', 'media'] as const)(
|
|
'keeps anonymous read access to public-content collection %s',
|
|
async (collection) => {
|
|
await expect(
|
|
payload.count({ collection, where: {}, overrideAccess: false }),
|
|
).resolves.toMatchObject({ totalDocs: expect.any(Number) })
|
|
},
|
|
)
|
|
})
|
|
|
|
describe('leader supporter creation scope', () => {
|
|
beforeAll(async () => {
|
|
payload = await getPayload({ config: await config })
|
|
})
|
|
|
|
it('lets an engaged leader register a supporter in a linked municipality', async () => {
|
|
const fixtures = campaignFixtures()
|
|
const leaderAccount = await fixtures.createCampaignUser('leader')
|
|
const leaderContact = await fixtures.createContact()
|
|
const linked = await fixtures.getMunicipality()
|
|
await fixtures.createLeadership({
|
|
contact: leaderContact.id,
|
|
municipalities: [linked.id],
|
|
user: leaderAccount.id,
|
|
supportStatus: 'engajado',
|
|
})
|
|
|
|
const supporter = await withSupporterRegistrationConsent(() =>
|
|
createLeaderSupporterRecord(payload, leaderAccount, {
|
|
name: fixtures.value('Apoiador'),
|
|
phone: fixtures.phone(),
|
|
city: 'Salvador',
|
|
municipality: linked.id,
|
|
consentAccepted: true,
|
|
}),
|
|
)
|
|
fixtures.own('supporter', supporter.id)
|
|
|
|
expect(supporter.source).toBe('lideranca')
|
|
expect(relationshipId(supporter.createdBy)).toBe(leaderAccount.id)
|
|
expect(relationshipId(supporter.municipality)).toBe(linked.id)
|
|
})
|
|
|
|
it('rejects a supporter outside the leadership municipalities', async () => {
|
|
const fixtures = campaignFixtures()
|
|
const leaderAccount = await fixtures.createCampaignUser('leader')
|
|
const leaderContact = await fixtures.createContact()
|
|
const linked = await fixtures.getMunicipality()
|
|
const outside = await fixtures.getMunicipality()
|
|
await fixtures.createLeadership({
|
|
contact: leaderContact.id,
|
|
municipalities: [linked.id],
|
|
user: leaderAccount.id,
|
|
supportStatus: 'engajado',
|
|
})
|
|
|
|
await expect(
|
|
withSupporterRegistrationConsent(() =>
|
|
createLeaderSupporterRecord(payload, leaderAccount, {
|
|
name: fixtures.value('Apoiador'),
|
|
phone: fixtures.phone(),
|
|
city: 'Salvador',
|
|
municipality: outside.id,
|
|
consentAccepted: true,
|
|
}),
|
|
),
|
|
).rejects.toThrow('Você só pode cadastrar contatos nos municípios da sua liderança.')
|
|
})
|
|
|
|
it('rejects staff accounts on the leader-only contact tool', async () => {
|
|
const fixtures = campaignFixtures()
|
|
const advisor = await fixtures.createCampaignUser('advisor')
|
|
const municipality = await fixtures.getMunicipality()
|
|
|
|
await expect(
|
|
createLeaderSupporterRecord(payload, advisor, {
|
|
name: fixtures.value('Apoiador'),
|
|
phone: fixtures.phone(),
|
|
city: 'Salvador',
|
|
municipality: municipality.id,
|
|
consentAccepted: true,
|
|
}),
|
|
).rejects.toThrow('Somente lideranças podem cadastrar contatos por aqui.')
|
|
})
|
|
})
|