mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +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.
128 lines
4.8 KiB
TypeScript
128 lines
4.8 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
DOSSIER_RESEARCH_CHECKLIST_IDS,
|
|
dossierChecklistForEra,
|
|
dossierResearchReceipt,
|
|
mergeDossierResearch,
|
|
normalizeDossierResearchInput,
|
|
} from '../../scripts/lib/dossieResearch.mjs'
|
|
import { INSTITUTION_UNIT } from '../../scripts/lib/dossieUnit.mjs'
|
|
|
|
// C187: the institution branch of the shared research contract. Same eras,
|
|
// institution slug field and `instituicao|setor|rede` spheres; the C186
|
|
// municipality behaviour is untouched (see dossieResearch.unit.spec.ts).
|
|
|
|
const validItem = (id: string, overrides: Record<string, unknown> = {}) => ({
|
|
id,
|
|
answer: 'Entrega documentada.',
|
|
sphere: 'instituicao',
|
|
sourceUrl: 'https://ufba.test/ato',
|
|
sourceDate: '2024-02-01',
|
|
...overrides,
|
|
})
|
|
|
|
const eraResearch = (era: 'A' | 'B' | 'C', overrides: Record<string, unknown> = {}) => ({
|
|
institutionSlug: 'ufba',
|
|
era,
|
|
researchedAt: '2026-09-17T10:00:00.000Z',
|
|
items: dossierChecklistForEra(era, INSTITUTION_UNIT).map((item) => validItem(item.id)),
|
|
...overrides,
|
|
})
|
|
|
|
describe('institution checklist', () => {
|
|
it('exposes an institution checklist id per era (distinct ids)', () => {
|
|
const ids = [
|
|
...dossierChecklistForEra('A', INSTITUTION_UNIT),
|
|
...dossierChecklistForEra('B', INSTITUTION_UNIT),
|
|
...dossierChecklistForEra('C', INSTITUTION_UNIT),
|
|
].map((row) => row.id)
|
|
expect(new Set(ids).size).toBe(ids.length)
|
|
expect(ids).toContain('era_a_vinculo')
|
|
expect(ids).toContain('era_c_parcerias')
|
|
// Municipality ids remain the C186 public surface.
|
|
expect(DOSSIER_RESEARCH_CHECKLIST_IDS).toContain('era_a_formacao')
|
|
})
|
|
})
|
|
|
|
describe('normalizeDossierResearchInput (institution unit)', () => {
|
|
it('requires institutionSlug and keeps institution items', () => {
|
|
const research = normalizeDossierResearchInput(eraResearch('C'), { unit: INSTITUTION_UNIT })
|
|
expect(research.institutionSlug).toBe('ufba')
|
|
expect(research.items.length).toBe(dossierChecklistForEra('C', INSTITUTION_UNIT).length)
|
|
expect(research.items.every((item) => item.sphere === 'instituicao')).toBe(true)
|
|
})
|
|
|
|
it('rejects the municipality slug field', () => {
|
|
expect(() =>
|
|
normalizeDossierResearchInput(
|
|
{ municipalitySlug: 'ilheus', era: 'C', researchedAt: '2026-09-17T10:00:00.000Z' },
|
|
{ unit: INSTITUTION_UNIT },
|
|
),
|
|
).toThrow(/institutionSlug/)
|
|
})
|
|
|
|
it('turns an invalid sphere into a gap with the institution vocabulary', () => {
|
|
const items = dossierChecklistForEra('A', INSTITUTION_UNIT).map((item) => validItem(item.id))
|
|
items[0] = validItem(items[0].id, { sphere: 'municipio' })
|
|
const research = normalizeDossierResearchInput(eraResearch('A', { items }), {
|
|
unit: INSTITUTION_UNIT,
|
|
})
|
|
expect(research.gaps).toContainEqual(
|
|
expect.objectContaining({
|
|
id: items[0].id,
|
|
reason: expect.stringMatching(/instituicao\/setor\/rede/),
|
|
}),
|
|
)
|
|
})
|
|
|
|
it('defaults the sphere to instituicao and keeps setor/rede', () => {
|
|
const research = normalizeDossierResearchInput(
|
|
eraResearch('B', {
|
|
items: [
|
|
validItem('era_b_sesab', { sphere: undefined }),
|
|
validItem('era_b_equipamentos', { sphere: 'setor' }),
|
|
validItem('era_b_programas', { sphere: 'rede' }),
|
|
],
|
|
}),
|
|
{ unit: INSTITUTION_UNIT },
|
|
)
|
|
const byId = Object.fromEntries(research.items.map((item) => [item.id, item.sphere]))
|
|
expect(byId.era_b_sesab).toBe('instituicao')
|
|
expect(byId.era_b_equipamentos).toBe('setor')
|
|
expect(byId.era_b_programas).toBe('rede')
|
|
})
|
|
|
|
it('builds an institution receipt with the institution research path', () => {
|
|
const receipt = dossierResearchReceipt(
|
|
normalizeDossierResearchInput(eraResearch('A'), { unit: INSTITUTION_UNIT }),
|
|
{ unit: INSTITUTION_UNIT },
|
|
)
|
|
expect(receipt.slug).toBe('ufba')
|
|
expect(receipt.researchPath).toBe('data/dossie-solla-instituicao/ufba.a.research.json')
|
|
expect(receipt.itemCount).toBeGreaterThan(0)
|
|
})
|
|
})
|
|
|
|
describe('mergeDossierResearch (institution unit)', () => {
|
|
it('merges the institution eras', () => {
|
|
const merged = mergeDossierResearch(
|
|
(['A', 'B', 'C'] as const).map((era) =>
|
|
normalizeDossierResearchInput(eraResearch(era), { unit: INSTITUTION_UNIT }),
|
|
),
|
|
{ unit: INSTITUTION_UNIT },
|
|
)
|
|
expect(merged.institutionSlug).toBe('ufba')
|
|
expect(merged.eras).toEqual(['A', 'B', 'C'])
|
|
})
|
|
|
|
it('fails closed when institution slugs disagree', () => {
|
|
const a = normalizeDossierResearchInput(eraResearch('A'), { unit: INSTITUTION_UNIT })
|
|
const b = normalizeDossierResearchInput(eraResearch('B', { institutionSlug: 'correios' }), {
|
|
unit: INSTITUTION_UNIT,
|
|
})
|
|
expect(() => mergeDossierResearch([a, b], { unit: INSTITUTION_UNIT })).toThrow(
|
|
/instituições diferentes/,
|
|
)
|
|
})
|
|
})
|