test: prove coincident partition acceptance

Issue: #276
User-Visible: no
This commit is contained in:
Sergey Matyunin
2026-08-24 05:57:57 +03:00
parent 57fdfdbd83
commit 82fe98bcd3
13 changed files with 290 additions and 24 deletions
+89 -1
View File
@@ -1,9 +1,12 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { readFileSync, readdirSync } from 'node:fs';
import { join, relative } from 'node:path';
import { fileURLToPath } from 'node:url';
import { optimizePlans } from '../test-build/plan-optimizer.js';
import { checkOptimizeGeometry } from '../test-build/plan-geometry-preflight.js';
import { reconcileCoincidentPartitions } from '../test-build/coincident-partitions.js';
import { GRID_PITCH, GRID_STEP_N as S } from '../test-build/space-geometry.js';
import { wallIntervals } from '../test-build/wall-thickness.js';
@@ -68,6 +71,91 @@ test('issue 276 exact proof ignores endpoint direction and room order', () => {
}
});
test('issue 276 rehosts three non-overlapping door/window/gate openings atomically', () => {
const input = clone(fixture);
const space = input.spaces[0];
space.openings = [
{ id: 'hosted-door', type: 'door', x: 0, y: 0, angle: 0, length: 0.12,
contact: 'binary_sensor.door', host: { kind: 'partition', id: 'redundant', t: 59 / 238 } },
{ id: 'hosted-window', type: 'window', x: 0, y: 0, angle: 0, length: 0.12,
cover: 'cover.window', host: { kind: 'partition', id: 'redundant', t: 0.50 } },
{ id: 'hosted-gate', type: 'gate', x: 0, y: 0, angle: 0, length: 0.12,
future_field: { keep: true }, host: { kind: 'partition', id: 'redundant', t: 179 / 238 } },
];
const result = optimize(input);
assert.equal(result.report.partitionsReconciled, 1);
assert.equal(result.report.openingsRehosted, 3);
const openings = result.config.spaces[0].openings;
assert.deepEqual(openings.map((opening) => opening.id),
['hosted-door', 'hosted-window', 'hosted-gate']);
assert.deepEqual(openings.map((opening) => opening.type), ['door', 'window', 'gate']);
assert.ok(openings.every((opening) => opening.host === undefined));
assert.ok(openings.every((opening) => opening.x === 0.504166667));
assert.ok(openings[0].y < openings[1].y && openings[1].y < openings[2].y);
assert.equal(openings[0].contact, 'binary_sensor.door');
assert.equal(openings[1].cover, 'cover.window');
assert.deepEqual(openings[2].future_field, { keep: true });
assert.equal(checkOptimizeGeometry(result.config).ok, true);
const second = optimize(result.config);
assert.equal(second.changed, false);
assert.equal(second.report.openingsRehosted, 0);
assert.deepEqual(second.config, result.config);
});
test('issue 276 fails closed when two hosted openings would overlap after rehost', () => {
const input = clone(fixture);
const space = input.spaces[0];
space.openings = [
{ id: 'overlap-door', type: 'door', x: 0, y: 0, angle: 0, length: 0.2,
host: { kind: 'partition', id: 'redundant', t: 0.45 } },
{ id: 'overlap-window', type: 'window', x: 0, y: 0, angle: 0, length: 0.2,
host: { kind: 'partition', id: 'redundant', t: 0.55 } },
];
const result = optimize(input);
assert.equal(result.report.partitionsReconciled, 0);
assert.equal(result.report.openingsRehosted, 0);
assert.equal(result.config.spaces[0].partitions.length, 1);
assert.deepEqual(result.config.spaces[0].openings.map((opening) => opening.id),
['overlap-door', 'overlap-window']);
assert.ok(result.config.spaces[0].openings.every((opening) => opening.host?.id === 'redundant'));
});
test('issue 276 reconciliation is owned by explicit Optimize and called once per valid space', () => {
const input = clone(fixture);
const secondSpace = clone(input.spaces[0]);
secondSpace.id = 'offset-shared-wall-second';
input.spaces.push(secondSpace);
let calls = 0;
const instrumented = (...args) => {
calls++;
return reconcileCoincidentPartitions(...args);
};
const result = optimizePlans(input, {}, {}, {
reconcileCoincidentPartitions: instrumented,
});
assert.equal(calls, 2, 'one explicit pass per valid space in one Optimize candidate');
assert.equal(result.report.partitionsReconciled, 2);
const sourceRoot = fileURLToPath(new URL('../src/', import.meta.url));
const sourceFiles = [];
const walk = (directory) => {
for (const entry of readdirSync(directory, { withFileTypes: true })) {
const path = join(directory, entry.name);
if (entry.isDirectory()) walk(path);
else if (entry.isFile() && entry.name.endsWith('.ts')) sourceFiles.push(path);
}
};
walk(sourceRoot);
const owners = sourceFiles
.filter((path) => readFileSync(path, 'utf8').includes('reconcileCoincidentPartitions'))
.map((path) => relative(sourceRoot, path).replaceAll('\\', '/'))
.sort();
assert.deepEqual(owners, ['coincident-partitions.ts', 'plan-optimizer.ts'],
'render/pointer modules must not import or invoke the Optimize-only pass');
});
test('issue 276 fails closed for an orphan host, overlap, draft, column and unknown partition data', () => {
const variants = [];
+31 -1
View File
@@ -255,6 +255,36 @@ test('corner Split golden captures before, thin and thick facade states', () =>
}
});
test('issue 276 golden captures 5 cm offsets and hosted door before/after 10/30/virtual', () => {
const scenarios = GOLDEN_SCENARIOS.filter((scenario) => scenario.coincidentPartition);
assert.deepEqual(
scenarios.map((scenario) => scenario.coincidentPartition),
['before', 'thin', 'thick', 'virtual'],
);
for (const scenario of scenarios) {
const fixture = prepareGoldenFixture(scenario);
const space = fixture.config.spaces.find((item) => item.id === scenario.space);
assert.ok(space);
assert.equal(space.cell_cm, 5);
assert.ok(space.rooms[0].poly.some((point) => point[0] === 0.5
&& point[1] === 0.004166666666666667), 'short 5 cm offset must be visible');
assert.equal(space.openings.length, 1);
if (scenario.coincidentPartition === 'before') {
assert.equal(space.partitions.length, 1);
assert.equal(space.openings[0].host.id, 'redundant');
continue;
}
assert.equal(space.partitions, undefined);
assert.equal(space.openings[0].host, undefined);
assert.equal(space.openings[0].x, 0.504166667);
if (scenario.coincidentPartition === 'virtual') {
assert.equal(space.open_spans.length, 1);
} else {
assert.equal(space.walls[0].cm, scenario.coincidentPartition === 'thin' ? 10 : 30);
}
}
});
test('filled opening golden has a pixel-level seam detector', () => {
const scenario = GOLDEN_SCENARIOS.find((item) => item.id === 'openings-filled-tunnel-dark');
assert.ok(scenario);
@@ -322,7 +352,7 @@ test('sun-ray golden requires browser-painted light from a state-only sun entity
assert.ok(scenario);
const fixture = prepareGoldenFixture(scenario);
const space = fixture.config.spaces.find((item) => item.id === scenario.space);
assert.equal(GOLDEN_MATRIX_VERSION, 41);
assert.equal(GOLDEN_MATRIX_VERSION, 42);
assert.equal(space.settings.sun_rays, true);
assert.equal(scenario.northDeg, 90,
'the sign-sensitive golden must keep a non-zero north direction');