mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-06 22:49:16 +00:00
test: дерево бандла проверяется настоящее, а не синтетическое
Логика проверки существовала и была написана правильно: verifyBundleTree и
compareBundleTrees в scripts/bundle-tree.mjs. Но применялась только к фикстуре в
tmpdir(), поэтому манифест, ссылающийся на пять несуществующих файлов, прожил в
dev при 1444 зелёных тестах и зелёном check-docs. Установка через HACS получила
бы 404 на каждом ленивом импорте.
Второй тест спрашивает git, а не файловую систему, и это не перестраховка.
Дефект родился так: пересборка дала чанки с новыми хешами содержимого,
`git commit -a --amend` удалил старые (отслеживались) и не добавил новые (не
отслеживались). На машине автора проверка наличия файлов прошла бы — файлы там
были. Отличить «собрано» от «закоммичено» умеет только индекс.
Пропуск проверки при недоступном git — громкий: тихий пропуск это тот самый
класс, из-за которого задача и появилась.
Доказательство пользы исполнением: оба теста прогнаны на c665c7d3, коммите до
починки, и оба падают — первый с «manifest asset is missing:
houseplan-assets/editor-DMlizeQy.js», второй с перечислением десяти путей вне
индекса.
Мутанта не добавляю намеренно. Это утверждение о состоянии дерева, а не о
логике: на здоровом дереве ослабленная проверка проходит, то есть мутант
выживает, а выживающий мутант хуже отсутствующего. Логику verifyBundleTree
по-прежнему держат синтетические мутанты в bundle-assets.test.mjs.
Issue: #349
User-Visible: no
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
import { compareBundleTrees, readBundleManifest, verifyBundleTree } from '../scripts/bundle-tree.mjs';
|
||||
|
||||
// #349. Логика проверки дерева бандла существовала и была написана правильно —
|
||||
// но применялась только к синтетической фикстуре в tmpdir(). Поэтому манифест,
|
||||
// ссылающийся на пять несуществующих файлов, прожил в dev незамеченным: 1444
|
||||
// зелёных теста, зелёный check-docs, и установка через HACS получила бы 404 на
|
||||
// каждом ленивом импорте.
|
||||
//
|
||||
// Здесь те же функции спрашиваются о НАСТОЯЩЕМ дереве репозитория.
|
||||
|
||||
const ROOT = fileURLToPath(new URL('..', import.meta.url));
|
||||
const COPIES = ['dist', 'custom_components/houseplan/frontend'];
|
||||
|
||||
test('манифест бандла не ссылается в никуда, обе копии целы и равны (#349)', () => {
|
||||
for (const copy of COPIES) {
|
||||
// verifyBundleTree отвечает сразу на три вопроса: файл существует, его
|
||||
// sha256 совпадает с манифестом, путь не выходит за корень копии.
|
||||
assert.doesNotThrow(() => verifyBundleTree(resolve(ROOT, copy)), `${copy}: дерево бандла битое`);
|
||||
}
|
||||
assert.doesNotThrow(
|
||||
() => compareBundleTrees(resolve(ROOT, COPIES[0]), resolve(ROOT, COPIES[1])),
|
||||
'копии бандла разошлись: HACS ставит вторую, а сверяется первая',
|
||||
);
|
||||
});
|
||||
|
||||
test('каждый файл манифеста отслеживается git, а не только лежит на диске (#349)', () => {
|
||||
// Дефект #349 родился именно здесь. Пересборка дала чанки с новыми хешами
|
||||
// содержимого; `git commit -a --amend` удалил старые (они отслеживались) и
|
||||
// НЕ добавил новые (они не отслеживались). Проверка файловой системы на
|
||||
// машине автора прошла бы: файлы там были. Отличить «собрано» от
|
||||
// «закоммичено» умеет только git.
|
||||
const listed = spawnSync('git', ['-C', ROOT, 'ls-files', '-z', ...COPIES], { encoding: 'utf8' });
|
||||
if (listed.status !== 0) {
|
||||
// Громко, а не молча: тихий пропуск проверки — тот самый класс, из-за
|
||||
// которого эта задача и появилась.
|
||||
console.log('ПРОПУЩЕНО: git ls-files недоступен, отслеживаемость не проверена'
|
||||
+ ` (${(listed.stderr || '').trim() || 'нет вывода'})`);
|
||||
return;
|
||||
}
|
||||
const tracked = new Set(listed.stdout.split('\0').filter(Boolean));
|
||||
assert.ok(tracked.size, 'git ls-files не вернул ни одного файла — проверьте вызов');
|
||||
const missing = [];
|
||||
for (const copy of COPIES) {
|
||||
const manifest = readBundleManifest(resolve(ROOT, copy));
|
||||
assert.ok(manifest.files.length, `${copy}: манифест без файлов`);
|
||||
for (const file of manifest.files) {
|
||||
const relative = `${copy}/${file.path}`;
|
||||
if (!tracked.has(relative)) missing.push(relative);
|
||||
}
|
||||
}
|
||||
assert.deepEqual(missing, [],
|
||||
'манифест ссылается на файлы, которых нет в индексе git: собрано, но не закоммичено');
|
||||
});
|
||||
Reference in New Issue
Block a user