mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 23:53:05 +00:00
Generaliza aditivamente o pipeline C186 (default município; specs verdes) via o descritor scripts/lib/dossieUnit.mjs (municipality|institution), extrai o emit A4/fit-guard para scripts/lib/buildPdf.mjs e a receita read-only para scripts/lib/readOnlyExtract.mjs. - Catálogo fail-closed src/lib/institutionCatalog.ts + institutionNameAliases.ts (seed UFBA/Correios/Enfermagem; escape one-off --slug/--name; dedup). - Skill/command/subagente /dossie-solla-instituicao; eras A/B/C reusadas. - Abrangência instituição × setor × rede nunca somada; era sem fonte vira página de lacuna explícita; lista capada declara "e mais N" (regra C188 no dono). - Boletim modelo 1×A4 com fase junto ao valor, sem fontes, sem CTA; herda apenas fatos com fonte do dossiê (incl. falas do acervo read-only). - Sem schema/migration/DB write; artefatos gitignored.
119 lines
3.8 KiB
TypeScript
119 lines
3.8 KiB
TypeScript
// @vitest-environment node
|
|
|
|
import { randomUUID } from 'node:crypto'
|
|
|
|
import type { Payload } from 'payload'
|
|
import { getPayload } from 'payload'
|
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
|
|
|
import config from '@/payload.config'
|
|
|
|
import { composeInstitutionSnapshot } from '../../scripts/institutionSnapshot.mjs'
|
|
import { installCampaignFixtures } from '../helpers/campaignFixtures'
|
|
|
|
// C187: the institutional snapshot recorte is theme→institution (the acervo has
|
|
// no institution relationship). Pins the read projection (no PII), the topic
|
|
// filter and the explicit gap when the recorte is empty.
|
|
|
|
let payload: Payload
|
|
const createdSpeechIds: number[] = []
|
|
const campaignFixtures = installCampaignFixtures({
|
|
getPayload: () => payload,
|
|
setPayload: (nextPayload) => {
|
|
payload = nextPayload
|
|
},
|
|
})
|
|
|
|
const identity = {
|
|
slug: 'ufba',
|
|
name: 'UFBA',
|
|
kind: 'universidade',
|
|
kindLabel: 'Universidade',
|
|
sphere: 'federal',
|
|
sphereLabel: 'Federal',
|
|
scope: 'BA',
|
|
scopeLabel: 'Abrangência BA',
|
|
aliases: ['Universidade Federal da Bahia'],
|
|
topics: ['ciencia-tecnologia'],
|
|
}
|
|
|
|
describe('institution snapshot (C187)', () => {
|
|
beforeAll(async () => {
|
|
payload = await getPayload({ config: await config })
|
|
})
|
|
|
|
afterAll(async () => {
|
|
for (const id of createdSpeechIds) {
|
|
try {
|
|
await payload.delete({ collection: 'speech', id, depth: 0, overrideAccess: true })
|
|
} catch {
|
|
// already deleted
|
|
}
|
|
}
|
|
})
|
|
|
|
it(
|
|
'projects the topic-matched speeches without PII and records the recorte',
|
|
{
|
|
timeout: 30_000,
|
|
},
|
|
async () => {
|
|
const fixtures = campaignFixtures()
|
|
const candidate = await fixtures.createCampaignUser('candidate')
|
|
|
|
const speech = await payload.create({
|
|
collection: 'speech',
|
|
data: {
|
|
sourceKey: `test-inst-${randomUUID()}`,
|
|
speechAt: '2024-05-10T17:28',
|
|
type: 'DISCURSO',
|
|
phase: 'Discurso',
|
|
summary: 'Defesa do financiamento das universidades federais.',
|
|
officialTranscript: 'O SR. JORGE SOLLA (Bloco/PT - BA) - Sr. Presidente...',
|
|
keywords: ['UFBA', 'universidade'],
|
|
topics: ['ciencia-tecnologia'],
|
|
classifiedBy: 'gazetteer',
|
|
},
|
|
depth: 0,
|
|
overrideAccess: true,
|
|
})
|
|
createdSpeechIds.push(speech.id)
|
|
|
|
const actor = { ...candidate, collection: 'campaignUser' as const }
|
|
const snapshot = await composeInstitutionSnapshot({
|
|
payload,
|
|
actor,
|
|
identity,
|
|
readAt: '2026-09-17T11:00:00.000Z',
|
|
codeSha: 'test-sha',
|
|
database: 'localhost:5432/teqo_test',
|
|
})
|
|
|
|
expect(snapshot.meta.kind).toBe('institution')
|
|
expect(snapshot.institution).toMatchObject({ slug: 'ufba', name: 'UFBA' })
|
|
expect(snapshot.institution.topics).toEqual(['ciencia-tecnologia'])
|
|
expect(snapshot.speeches.rows.some((row: { id: number }) => row.id === speech.id)).toBe(true)
|
|
const row = snapshot.speeches.rows.find(
|
|
(candidateRow: { id: number }) => candidateRow.id === speech.id,
|
|
)!
|
|
expect('officialTranscript' in row).toBe(false)
|
|
expect(JSON.stringify(snapshot)).not.toContain('@')
|
|
},
|
|
)
|
|
|
|
it('declares an explicit gap when the institution has no topic', async () => {
|
|
const fixtures = campaignFixtures()
|
|
const candidate = await fixtures.createCampaignUser('candidate')
|
|
const actor = { ...candidate, collection: 'campaignUser' as const }
|
|
|
|
const snapshot = await composeInstitutionSnapshot({
|
|
payload,
|
|
actor,
|
|
identity: { ...identity, slug: 'sem-tema', name: 'Sem Tema', topics: [] },
|
|
readAt: '2026-09-17T11:00:00.000Z',
|
|
})
|
|
|
|
expect(snapshot.speeches.rows).toEqual([])
|
|
expect(snapshot.gaps).toContainEqual(expect.objectContaining({ id: 'acervo_sem_tema' }))
|
|
})
|
|
})
|