mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Карточка и редакторский рантайм держали ≈380 неиспользуемых импортов, 56 мёртвых объявлений и дублей типов (warm-boot, LS_*, GLOW_*, debounce, navigate, lruRead — копии карточки в рантайме) и 112 приватных членов карточки, которых не читал никто — делегаты `_editorRuntimeOrThrow()._x()`, оставшиеся от выноса #425, и аксессоры glow-состояния. Всё это снято; в 9 других файлах — по одиночной ошибке. Делегаты и поля, которых касаются браузерные смоки (`card._x(...)`), оставлены и посчитаны отдельно. Гейт `npm run lint:unused` (scripts/unused-locals-gate.mjs, в gate:small и Validate после сборки): `tsc --noUnusedLocals` чист, кроме приватных членов карточки из порта HouseplanEditorHostPort / `host.` (portPrivates) и членов, которых зовёт харнесс (harnessPrivates); храповик по шести числам scripts/monolith-metrics.mjs против scripts/monolith-baseline.json — delegates 260→159, portMembers 350, hostRefs 4948, portPrivates 96, harnessPrivates 107, bundleBytes 2 510 141→2 500 387. `npm run inventory` печатает те же числа. Заморозка 54 тестов, читающих монолит как текст (test/monolith-text-anchors.test.mjs); PROCESS.md §2.7 — правило. Логический исходник для контрактных тестов (test/houseplan-source.mjs) дописывает члены рантайма без делегата в карточке — контракт продукта не зависит от наличия заглушки. Потолки ядер и initial gzip опущены на выигрыш (292 000 → 290 400). Бандл пересобран, три копии синхронны. Issue: #624 User-Visible: no
147 lines
6.8 KiB
JavaScript
147 lines
6.8 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
|
|
import {
|
|
NEAR_AXIS_MAX_DEGREES,
|
|
NEAR_AXIS_MAX_SLOPE,
|
|
classifyNearAxisSegment,
|
|
repairNearAxisRoomWalls,
|
|
snapNearAxisEndpoint,
|
|
} from '../test-build/near-axis.js';
|
|
import { optimizePlans } from '../test-build/plan-optimizer.js';
|
|
import { MULTI_WALL_NEAR_ORTHOGONAL_MAX_DEGREES } from '../test-build/wall-thickness.js';
|
|
import { nearAxisProfile } from '../scripts/model-invariants.mjs';
|
|
import { readHouseplanProductionSource } from './houseplan-source.mjs';
|
|
|
|
const fixture = JSON.parse(readFileSync(
|
|
new URL('./fixtures/279-near-orthogonal-junction.json', import.meta.url), 'utf8',
|
|
));
|
|
const clone = (value) => JSON.parse(JSON.stringify(value));
|
|
const sourceOf = (file) => readFileSync(new URL(`../src/${file}`, import.meta.url), 'utf8');
|
|
|
|
test('#290 authoring, Optimize and renderer use one near-axis threshold source', () => {
|
|
const renderer = sourceOf('wall-thickness.ts');
|
|
const authoring = readHouseplanProductionSource();
|
|
const optimizer = sourceOf('plan-optimizer.ts');
|
|
|
|
assert.match(renderer, /import \{ NEAR_AXIS_MAX_DEGREES \} from '\.\/near-axis';/);
|
|
assert.match(renderer,
|
|
/export const MULTI_WALL_NEAR_ORTHOGONAL_MAX_DEGREES = NEAR_AXIS_MAX_DEGREES;/);
|
|
// #624: импорт живёт в редакторском рантайме — карточка держала мёртвую копию.
|
|
assert.match(sourceOf('houseplan-editor-runtime.ts'), /import \{ snapNearAxisEndpoint \} from '\.\/near-axis';/);
|
|
assert.match(authoring, /snapNearAxisEndpoint\(/);
|
|
assert.match(optimizer, /import \{ repairNearAxisRoomWalls \} from '\.\/near-axis';/);
|
|
assert.match(optimizer, /repairNearAxisRoomWalls\(space\)/);
|
|
});
|
|
|
|
test('#290 near-axis boundary is shared, inclusive and bounded', () => {
|
|
assert.equal(NEAR_AXIS_MAX_DEGREES, 0.25);
|
|
assert.equal(MULTI_WALL_NEAR_ORTHOGONAL_MAX_DEGREES, NEAR_AXIS_MAX_DEGREES);
|
|
assert.equal(classifyNearAxisSegment([0, 0], [316, 0]), null);
|
|
assert.equal(classifyNearAxisSegment([0, 0], [316, 1])?.axis, 'horizontal');
|
|
assert.equal(classifyNearAxisSegment([0, 0], [316, 2]), null);
|
|
assert.equal(classifyNearAxisSegment([0, 0], [1, NEAR_AXIS_MAX_SLOPE])?.axis, 'horizontal');
|
|
assert.equal(classifyNearAxisSegment([0, 0], [1, NEAR_AXIS_MAX_SLOPE * 1.000001]), null);
|
|
assert.equal(classifyNearAxisSegment([0, 0], [1, Math.tan(Math.PI / 6)]), null);
|
|
assert.equal(classifyNearAxisSegment([0, 0], [1, 316])?.axis, 'vertical');
|
|
assert.equal(classifyNearAxisSegment([1, 316], [0, 0])?.axis, 'vertical');
|
|
});
|
|
|
|
test('#290 Walls rule moves only the free endpoint', () => {
|
|
assert.deepEqual(snapNearAxisEndpoint([10, 20], [326, 21]), [326, 20]);
|
|
assert.deepEqual(snapNearAxisEndpoint([10, 20], [11, 336]), [10, 336]);
|
|
assert.deepEqual(snapNearAxisEndpoint([10, 20], [326, 22]), [326, 22]);
|
|
assert.deepEqual(snapNearAxisEndpoint([10, 20], [110, 120]), [110, 120]);
|
|
});
|
|
|
|
test('#290 repairs a duplicated 316x1 physical wall once without mutating input', () => {
|
|
const input = clone(fixture);
|
|
const before = JSON.stringify(input);
|
|
const result = repairNearAxisRoomWalls(input);
|
|
assert.equal(JSON.stringify(input), before);
|
|
assert.equal(result.report.wallsStraightened, 1);
|
|
assert.equal(result.report.wallsStraightenSkipped, 0);
|
|
assert.ok(Math.abs(result.report.maxStraightenShift - 1 / 240) < 1e-9);
|
|
const north = result.space.rooms.find((room) => room.id === 'north-west');
|
|
const south = result.space.rooms.find((room) => room.id === 'south-west');
|
|
assert.deepEqual(north.poly[1], south.poly[0]);
|
|
assert.deepEqual(north.poly[2], south.poly[3]);
|
|
assert.equal(north.poly[1][1], north.poly[2][1]);
|
|
});
|
|
|
|
test('#290 repairs ordinary independent walls but preserves true diagonals', () => {
|
|
const input = {
|
|
rooms: [],
|
|
partitions: [
|
|
{ id: 'former-draft-a', a: [0, 0], b: [316, 1], cm: 15 },
|
|
{ id: 'former-draft-b', a: [316, 1], b: [316, 20], cm: 15 },
|
|
{ id: 'near', a: [0, 100], b: [316, 101], cm: 15 },
|
|
{ id: 'diagonal', a: [0, 200], b: [100, 300], cm: 15 },
|
|
],
|
|
};
|
|
const result = repairNearAxisRoomWalls(input);
|
|
assert.equal(result.report.wallsStraightened, 2);
|
|
assert.equal(result.space.partitions[0].a[1], result.space.partitions[0].b[1]);
|
|
assert.deepEqual(result.space.partitions[1], input.partitions[1]);
|
|
assert.equal(result.space.partitions[2].a[1], result.space.partitions[2].b[1]);
|
|
assert.deepEqual(result.space.partitions[3], input.partitions[3]);
|
|
});
|
|
|
|
test('#290 skips a repair that would no longer fit a hosted opening', () => {
|
|
const input = {
|
|
rooms: [],
|
|
partitions: [{ id: 'host', a: [0, 0], b: [316, 1], cm: 15 }],
|
|
openings: [{
|
|
id: 'opening',
|
|
length: 316.001,
|
|
host: { kind: 'partition', id: 'host', t: 0.5 },
|
|
}],
|
|
};
|
|
const result = repairNearAxisRoomWalls(input);
|
|
assert.equal(result.report.wallsStraightened, 0);
|
|
assert.equal(result.report.wallsStraightenSkipped, 1);
|
|
assert.deepEqual(result.space.partitions, input.partitions);
|
|
});
|
|
|
|
test('#290 Optimize reports physical count, exact max centimetres and is idempotent', () => {
|
|
const config = { spaces: [{ id: 'floor', title: 'Floor', ...clone(fixture) }] };
|
|
const first = optimizePlans(config, {});
|
|
assert.equal(first.report.wallsStraightened, 1);
|
|
assert.equal(first.report.wallsStraightenSkipped, 0);
|
|
assert.ok(Math.abs(first.report.maxStraightenShiftCm - 1) < 1e-6);
|
|
assert.equal(first.report.maxStraightenSpace, 'floor');
|
|
assert.equal(first.changed, true);
|
|
const second = optimizePlans(first.config, first.layout);
|
|
assert.equal(second.changed, false);
|
|
assert.equal(second.report.wallsStraightened, 0);
|
|
assert.equal(second.report.maxStraightenShiftCm, 0);
|
|
});
|
|
|
|
test('#290 Optimize reduces the tracked real-plan near-axis profile and stays idempotent', () => {
|
|
for (const file of ['real-plan-first-floor.json', 'real-plan-second-floor.json']) {
|
|
const { space } = JSON.parse(readFileSync(
|
|
new URL(`./fixtures/${file}`, import.meta.url), 'utf8',
|
|
));
|
|
const config = { spaces: [clone(space)] };
|
|
const before = nearAxisProfile(config);
|
|
const first = optimizePlans(config, {});
|
|
const after = nearAxisProfile(first.config);
|
|
const second = optimizePlans(first.config, first.layout);
|
|
|
|
assert.equal(after.total, before.total - first.report.wallsStraightened, file);
|
|
assert.equal(after.total <= before.total, true, file);
|
|
assert.equal(second.report.wallsStraightened, 0, file);
|
|
assert.equal(nearAxisProfile(second.config).total, after.total, file);
|
|
}
|
|
const { space } = JSON.parse(readFileSync(
|
|
new URL('./fixtures/real-plan-second-floor.json', import.meta.url), 'utf8',
|
|
));
|
|
const config = { spaces: [clone(space)] };
|
|
const before = nearAxisProfile(config);
|
|
const repaired = optimizePlans(config, {});
|
|
assert.equal(before.total, 1);
|
|
assert.equal(repaired.report.wallsStraightened, 1);
|
|
assert.equal(nearAxisProfile(repaired.config).total, 0);
|
|
});
|