Files
houseplan-card/test/backend-pins.test.mjs
Codex 98028a3093 ci: the backend gate now checks exactly what it promises (#399)
Three claims a green backend used to make, each slightly wider than the
truth — and #392 happened in exactly that gap.

The frontend pin said 20260826.1 next to homeassistant==2026.8.3, whose
package_constraints.txt requires 20260729.7: a combination that exists
in no HA release. It was never derived from anything — someone once
picked it. It is now taken from the constraints, the source is named in
the file, and a test holds both numbers together so raising HA cannot
quietly desync them.

ruff's include declared three trees while CI linted one. Narrowed the
declaration rather than widening CI: the debt in scripts/ and
tests_backend/ (56 findings, mostly E402/I001, plus 7 B023 and 5 B017)
has its own cost and its own decisions, and belongs in its own task, not
in a visibility fix. test/lint-scope.test.mjs now compares the two, so
they can only move together.

The pin check skipped a workflow when it found neither the package name
nor the requirements path — and both vanish together the moment someone
returns to Defaulting to user installation because normal site-packages is not writeable, i.e. the gate switched itself off
under precisely the change it exists to catch. It now walks the whole
.github/workflows directory and decides per file by a positive sign: if
a file installs python packages, it must install them from the pins
file. Verified by dropping a rogue workflow into the directory — it
reddens without touching any list.

Three mutants registered and each run by hand.

User-Visible: no
Issue: #399
2026-08-31 04:35:56 +03:00

42 lines
2.6 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
// #399: пин фронтенда не выводился ниоткуда — его однажды назначили руками, и
// получился набор, которого нет ни в одном релизе Home Assistant. Отказа это
// не вызывало (пакет — статика), но обещание #392 «по SHA видно, чем
// проверяли» переставало быть правдой ровно там, где его труднее всего
// заметить.
const read = (name) => readFileSync(fileURLToPath(new URL(`../${name}`, import.meta.url)), 'utf8');
// Ожидание живёт рядом с пином, а не в сети: тест обязан работать без
// GitHub. Источник — homeassistant/package_constraints.txt тега 2026.8.3.
// Поднимая HA, обновлять обе строки из того же файла.
const EXPECTED = { homeassistant: '2026.8.3', frontend: '20260729.7' };
const pinned = (name) => {
const line = read('tests_backend/requirements.txt')
.split('\n')
.find((row) => row.trimStart().startsWith(`${name}==`));
assert.ok(line, `${name} обязан быть закреплён в tests_backend/requirements.txt`);
return line.trim().split('==')[1].split('#')[0].trim();
};
test('#399 AC1: фронтенд закреплён так, как требует закреплённый Home Assistant', () => {
assert.equal(pinned('homeassistant'), EXPECTED.homeassistant,
'версия HA изменилась — ожидание фронтенда ниже больше не действует,'
+ ' возьмите новое из package_constraints.txt соответствующего тега');
assert.equal(pinned('home-assistant-frontend'), EXPECTED.frontend,
'пин фронтенда разошёлся с констрейнтами закреплённого HA:'
+ ' проверяется набор, которого не существует');
});
test('#399 AC1: источник версии назван в самом файле', () => {
const requirements = read('tests_backend/requirements.txt');
assert.match(requirements, /package_constraints\.txt/,
'следующий подъём HA не должен превращаться в угадывание: откуда взялась'
+ ' версия фронтенда, сказано рядом с ней');
});