mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
Some checks are pending
Deploy / preflight (push) Waiting to run
Deploy / verify (push) Blocked by required conditions
Deploy / deploy staging (teqo-staging) (push) Blocked by required conditions
Deploy / requeue when main advanced (push) Blocked by required conditions
Deploy / deploy production (teqo-1313) (push) Blocked by required conditions
Collection archivePhoto ganha o grupo catalog (legenda/descrição/cena/texto visível/temas/pessoas/município/origem/catalogedAt), curatedFields (o que a assessoria edita nunca é sobrescrito), takenOn e searchText com índice GIN trgm (migration aditiva add_archive_photo_catalog); hook beforeValidate deriva o índice e marca curadoria só em edição humana; pipeline transacional baixa os bytes privados, prepara JPEG <=1024px com sharp e mescla o VLM local com o gazetteer/catálogo curado (pessoas só do TEXTO, nunca de rosto); CLI pnpm archive:catalog (plan/--apply/--verify, recibo honesto, idempotente por catalogedAt) com engine OpenAI-compatible na rede privada atrás de guarda de host (ARCHIVE_VISION_ALLOW_REMOTE é o escape); client HTTP com retry/timeout e parser zod fail-closed; resolver de município extraído para owner compartilhado com o catálogo de peças delegando; unit/int novos e runbook staging-primeiro.
141 lines
4.7 KiB
TypeScript
141 lines
4.7 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
ARCHIVE_CATALOG_DEFAULT_OUT_DIR,
|
|
formatArchiveCatalogInventory,
|
|
formatArchiveCatalogReport,
|
|
parseArchiveCatalogCliArgs,
|
|
summarizeArchiveCatalogInventory,
|
|
summarizeArchiveCatalogResults,
|
|
} from '../../scripts/lib/archiveCatalogPlan.mjs'
|
|
|
|
// C232 — the pure CLI planning/reporting of the archive cataloguing: the argv
|
|
// contract, the run summaries and the `--verify` inventory the receipt and the
|
|
// stdout lines are built from.
|
|
|
|
describe('parseArchiveCatalogCliArgs (C232)', () => {
|
|
it('defaults to the plan mode and the canonical receipt dir', () => {
|
|
expect(parseArchiveCatalogCliArgs([])).toEqual({
|
|
apply: false,
|
|
verify: false,
|
|
limit: null,
|
|
out: ARCHIVE_CATALOG_DEFAULT_OUT_DIR,
|
|
help: false,
|
|
})
|
|
})
|
|
|
|
it('parses the modes and options', () => {
|
|
expect(
|
|
parseArchiveCatalogCliArgs(['--apply', '--limit', '5', '--out', 'data/x']),
|
|
).toMatchObject({
|
|
apply: true,
|
|
limit: 5,
|
|
out: 'data/x',
|
|
})
|
|
expect(parseArchiveCatalogCliArgs(['--verify'])).toMatchObject({ verify: true })
|
|
expect(parseArchiveCatalogCliArgs(['--help'])).toMatchObject({ help: true })
|
|
})
|
|
|
|
it('refuses conflicting, unknown and unsafe input', () => {
|
|
expect(() => parseArchiveCatalogCliArgs(['--apply', '--verify'])).toThrow(/mutuamente/)
|
|
expect(() => parseArchiveCatalogCliArgs(['--limit', '0'])).toThrow(/--limit/)
|
|
expect(() => parseArchiveCatalogCliArgs(['--limit'])).toThrow(/faltou valor/)
|
|
expect(() => parseArchiveCatalogCliArgs(['--force'])).toThrow(/desconhecido/)
|
|
expect(() => parseArchiveCatalogCliArgs(['--out', '../fora'])).toThrow(/\.\./)
|
|
})
|
|
})
|
|
|
|
describe('summarizeArchiveCatalogResults (C232)', () => {
|
|
it('counts the outcomes, the sources and names every failure', () => {
|
|
const summary = summarizeArchiveCatalogResults([
|
|
{ flickrId: '1', status: 'cataloged', source: 'ai' },
|
|
{ flickrId: '2', status: 'cataloged', source: 'none' },
|
|
{ flickrId: '3', status: 'skipped' },
|
|
{ flickrId: '4', status: 'failed', stage: 'analyze', error: 'engine fora do ar' },
|
|
])
|
|
|
|
expect(summary).toEqual({
|
|
cataloged: 2,
|
|
skipped: 1,
|
|
failed: 1,
|
|
bySource: { ai: 1, metadata: 0, none: 1 },
|
|
failures: [{ flickrId: '4', stage: 'analyze', error: 'engine fora do ar' }],
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('summarizeArchiveCatalogInventory (C232)', () => {
|
|
it('separates catalogued from pending and flags the missing file', () => {
|
|
const inventory = summarizeArchiveCatalogInventory([
|
|
{
|
|
flickrId: '1',
|
|
filename: 'a.jpg',
|
|
catalog: { catalogedAt: 'x', source: 'ai', scene: 'evento', municipality: 1 },
|
|
},
|
|
{ flickrId: '2', filename: 'b.jpg', catalog: { catalogedAt: 'x', source: 'metadata' } },
|
|
{ flickrId: '3', filename: 'c.jpg', catalog: {} },
|
|
{ flickrId: '4', filename: null, catalog: { catalogedAt: 'x', source: 'none' } },
|
|
])
|
|
|
|
expect(inventory).toMatchObject({
|
|
total: 4,
|
|
cataloged: 3,
|
|
pending: 1,
|
|
bySource: { ai: 1, metadata: 1, none: 1 },
|
|
withoutScene: 2,
|
|
withoutMunicipality: 2,
|
|
missingFilename: ['4'],
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('report lines (C232)', () => {
|
|
it('renders the plan line with the pending count', () => {
|
|
const lines = formatArchiveCatalogReport({
|
|
mode: 'plan',
|
|
target: '127.0.0.1/teqo_staging',
|
|
engine: { host: '100.94.122.26', model: 'qwen2.5vl:7b', scope: 'local' },
|
|
pending: 12,
|
|
durationMs: 1500,
|
|
})
|
|
|
|
expect(lines[0]).toContain('100.94.122.26 (qwen2.5vl:7b, local)')
|
|
expect(lines.join('\n')).toContain('pendentes na fila: 12')
|
|
})
|
|
|
|
it('renders the apply summary and every failure', () => {
|
|
const lines = formatArchiveCatalogReport({
|
|
mode: 'apply',
|
|
target: '127.0.0.1/teqo_staging',
|
|
durationMs: 2000,
|
|
summary: summarizeArchiveCatalogResults([
|
|
{ flickrId: '1', status: 'cataloged', source: 'ai' },
|
|
{ flickrId: '2', status: 'failed', stage: 'analyze', error: 'timeout' },
|
|
]),
|
|
})
|
|
|
|
const text = lines.join('\n')
|
|
expect(text).toContain('processadas: 1')
|
|
expect(text).toContain('falharam: 1')
|
|
expect(text).toContain('IA: 1')
|
|
expect(text).toContain('2 (analyze): timeout')
|
|
})
|
|
|
|
it('renders the verify inventory with the pending gap', () => {
|
|
const lines = formatArchiveCatalogInventory({
|
|
total: 3,
|
|
cataloged: 2,
|
|
pending: 1,
|
|
bySource: { ai: 1, metadata: 1, none: 0 },
|
|
withoutScene: 1,
|
|
withoutMunicipality: 1,
|
|
missingFilename: [],
|
|
})
|
|
|
|
const text = lines.join('\n')
|
|
expect(text).toContain('catalogadas: 2')
|
|
expect(text).toContain('pendentes: 1')
|
|
expect(text).toContain('IA: 1')
|
|
expect(text).toContain('ainda pendentes de catalogação: 1')
|
|
})
|
|
})
|