Files
houseplan-card/test/docs-freshness.test.mjs
Claude 7195ad1914 infra: heavy CI gates on the beta candidate, bundle rebase script, gate:small
Validate ran three smoke shards, golden and performance_smoke on every push,
check-docs went red on any src/** change until screenshots were re-captured,
and a parallel bundle build made every second task branch fail to rebase.
None of these gates ever failed at review time; they fail before betas.

- `heavy` output in job `changes` (scripts/classify-changes.mjs): smoke,
  smoke_done, golden, performance_smoke run only for a head commit with a
  `Release:` trailer, `workflow_dispatch full=true` and pull requests.
- nightly.yml dispatches Validate on dev with full=true every night.
- check-docs `--screenshots=warn|strict`: freshness of the screenshot index
  warns on a plain push, errors on the candidate; everything else still errors.
- publish-prerelease.yml and release.yml refuse a candidate without the
  `Release:` trailer and (prerelease) require fresh screenshots — a green
  Validate without the heavy jobs cannot pass for a release.
- scripts/rebase-on-dev.mjs: rebase on origin/dev taking dev's copy of the
  committed bundle, rebuild with bundle:sync, amend; any other conflict aborts.
- npm run gate:small: mandatory PROCESS §8 part in one parallel run.

Issue: #479
User-Visible: no
2026-09-06 15:39:34 +03:00

38 lines
2.4 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', 'external link returned']) {
const line = source.split('\n').find((l) => l.includes(always));
assert.ok(line && line.includes('errors.push'), `${always} остаётся ошибкой в обоих режимах`);
}
});