Files
houseplan-card/test/docs-freshness.test.mjs
Claude e45bc87c6d process: preflight does not fail a task branch for foreign causes (#700)
11 of 85 returns in #600–#691 were the thin-workflow mirror check, and any
push could turn red because a foreign site behind a docs link was down.

- validate.yml preflight: on refs/heads/issue/* the workflow_sync mismatch
  is a warning in the summary, not a failed verdict; push to dev, the beta
  candidate and the release keep it red.
- On push to dev a mismatch opens one owner issue titled [workflow-sync]
  (or comments on the open one), like the nightly mutation gate (#472);
  preflight gets issues: write for that.
- check-docs --external=warn: external link failures become warnings; the
  docs step passes it on task branches only.

Canon: PROCESS.md §10.4 («Workflow из ветки по умолчанию»).

Issue: #700
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-29 01:22:14 +03:00

44 lines
3.0 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { freshnessSink, screenshotsMode } from '../scripts/docs-freshness.mjs';
// #479: свежесть скриншотов документации на обычном пуше — предупреждение, на
// кандидате беты — ошибка. Умолчание строгое, чтобы старый вызов не ослаб молча.
test('без флага режим строгий, warn и strict принимаются, мусор отвергается (#479)', () => {
assert.equal(screenshotsMode([]), 'strict');
assert.equal(screenshotsMode(['--external']), 'strict');
assert.equal(screenshotsMode(['--screenshots=warn']), 'warn');
assert.equal(screenshotsMode(['--screenshots=strict']), 'strict');
assert.throws(() => screenshotsMode(['--screenshots=off']), /warn\|strict/);
});
test('warn складывает находки свежести в предупреждения, strict — в ошибки (#479)', () => {
const errors = []; const warnings = [];
freshnessSink('warn', { errors, warnings }).push('stale');
freshnessSink('strict', { errors, warnings }).push('stale');
assert.deepEqual(warnings, ['stale']);
assert.deepEqual(errors, ['stale']);
assert.throws(() => freshnessSink('maybe', { errors, warnings }));
});
test('check-docs: только две проверки свежести идут через режим, остальное — всегда ошибка (#479)', () => {
const source = readFileSync(new URL('../scripts/check-docs.mjs', import.meta.url), 'utf8');
const viaMode = [...source.matchAll(/freshness\.push\(([^)]*)\)/g)].map((m) => m[1]);
assert.equal(viaMode.length, 2, 'ровно две проверки свежести: отпечаток и capture-скрипт');
assert.ok(viaMode[0].includes('fingerprint is stale'));
assert.ok(viaMode[1].includes('capture script changed'));
// Хеш картинки, полнота набора сцен и ссылки не имеют права ослабляться.
for (const always of ['image hash does not match manifest', 'scenario set is incomplete']) {
const line = source.split('\n').find((l) => l.includes(always));
assert.ok(line && line.includes('errors.push'), `${always} остаётся ошибкой в обоих режимах`);
}
// #700: внешние ссылки не зависят от режима скриншотов; предупреждением их
// делает только явный `--external=warn` на ветке задачи.
const external = source.split('\n').find((l) => l.includes('external link returned'));
assert.ok(external && external.includes('externalErrors.push'), 'внешняя ссылка идёт в свой сток');
assert.match(source, /const externalErrors = EXTERNAL_WARN \? warnings : errors;/);
assert.doesNotMatch(source, /externalErrors = freshness/);
});