teqo-backup/tests/unit/instagramContentPruneCli.unit.spec.ts
Francisco Solla 3d269725f8 feat(C230): expurgo de peças fora do período eleitoral na Central
O rascunho pré-campanha que o --publish-existing ressuscita precisa sair
do catálogo de vez: o novo CLI content:instagram:prune faz hard delete da
peça e da mídia privada (linha, arquivo e still do C226), então uma
importação futura não a recria. Compartilha o mesmo núcleo de corte da
retirada (data civil na Bahia, peça sem data no feed nunca é tocada) e
espelha as guardas dos irmãos: --before obrigatório, TEQO_ENV casando o
nome exato do banco, CONTENT_INSTAGRAM_PRUNE_CONFIRM=1 e recibos JSON.

Testes: planejador/parser/recibo em unit e guardas do CLI em subprocesso.
Runbook e changelog atualizados.
2026-10-01 02:29:19 -03:00

90 lines
3 KiB
TypeScript

import { spawnSync } from 'node:child_process'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
// C230-followup — the prune CLI's fail-closed write guards, exercised as a
// subprocess: each refusal must happen before any Payload import, feed call or
// DB connection (a delete is irreversible, so the guard is the last wall).
const repoRoot = process.cwd()
const scriptPath = join(repoRoot, 'scripts', 'prune-instagram-content.mjs')
const run = (args: string[], env: Record<string, string> = {}) =>
spawnSync(process.execPath, [scriptPath, ...args], {
cwd: repoRoot,
encoding: 'utf8',
timeout: 60_000,
env: {
...process.env,
NODE_OPTIONS: '--no-deprecation --import=tsx/esm --import=./scripts/seed-loader.mjs',
NODE_ENV: 'production',
VITEST: '',
DATABASE_URL: 'postgresql://teqo:teqo@127.0.0.1:5432/teqo_1313',
PAYLOAD_SECRET: 'test-secret',
CONTENT_INSTAGRAM_PRUNE_CONFIRM: '',
TEQO_ENV: '',
ALLOW_REMOTE_DB: '',
...env,
},
})
const output = (result: ReturnType<typeof run>) => `${result.stdout}${result.stderr}`
describe('content:instagram:prune write guards (C230-followup)', { timeout: 30_000 }, () => {
it('prints the help even without a database', () => {
const result = run(['--help'], { DATABASE_URL: '' })
expect(result.status).toBe(0)
expect(result.stdout).toContain('CONTENT_INSTAGRAM_PRUNE_CONFIRM')
expect(result.stdout).toContain('pnpm content:instagram:prune')
})
it('requires --before before touching anything', () => {
const result = run(['--apply'])
expect(result.status).toBe(1)
expect(output(result)).toContain('--before é obrigatório')
})
it('refuses an invalid cutoff before touching anything', () => {
const result = run(['--before', '16/08/2026'])
expect(result.status).toBe(1)
expect(output(result)).toContain('YYYY-MM-DD')
})
it('refuses --apply on a production target without the intent flag, before any DB import', () => {
const result = run(['--before', '2026-08-16', '--apply'])
expect(result.status).toBe(1)
expect(output(result)).toContain('CONTENT_INSTAGRAM_PRUNE_CONFIRM=1')
expect(output(result)).not.toContain('| modo:')
})
it('requires the declared TEQO_ENV once the intent flag is set', () => {
const result = run(['--before', '2026-08-16', '--apply'], {
CONTENT_INSTAGRAM_PRUNE_CONFIRM: '1',
})
expect(result.status).toBe(1)
expect(output(result)).toContain('TEQO_ENV')
})
it('refuses a target database that does not match the declared environment', () => {
const result = run(['--before', '2026-08-16', '--apply'], {
CONTENT_INSTAGRAM_PRUNE_CONFIRM: '1',
TEQO_ENV: 'staging',
})
expect(result.status).toBe(1)
expect(output(result)).toContain('≠ "teqo_staging"')
})
it('refuses an unknown argument before touching anything', () => {
const result = run(['--force'])
expect(result.status).toBe(1)
expect(output(result)).toContain('argumento desconhecido')
})
})