Skip to content

Commit 6643c22

Browse files
authored
test(query): pin fluent-api filter conjunction against a same-valued overlap (#3131)
Mutation sweep of packages/spatial and packages/query. QueryBuilder's ofType()/withProperty() each build currentFilter as `previousFilter(entity) && <own check>`, but no existing fixture ever had an entity that satisfied the new predicate while failing the prior one — so dropping `previousFilter` from any of the three call sites (ofType, withProperty-with-value, withProperty-without-value) left the full suite green. Added a fixture where a door shares a wall's exact pset/property/value, and a two-ofType() chain, so each conjunction is now independently observable. packages/spatial was already mutation-swept (#2147, #2158, #2312, #2764) — confirmed by running its existing bvh.test.ts suite against a hand-mutated tmax >= 0 -> tmax > 0 and watching it fail. No changes made there.
1 parent b5ba6d7 commit 6643c22

1 file changed

Lines changed: 55 additions & 0 deletions

File tree

packages/query/test/fluent-api.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ function setupFixtures() {
5353
return { entityTable, propertyTable };
5454
}
5555

56+
/**
57+
* Same as `setupFixtures`, but the door (id 3) also carries the SAME
58+
* pset/property/value as wall 2 (`Pset_WallCommon.IsExternal: false`). Used
59+
* only by the conjunction tests below: a filter chain that ANDs on type but
60+
* ORs (or drops) the property check would let this door leak into a
61+
* `.ofType('IFCWALL')` result, since the property half alone can't tell it
62+
* apart from wall 2.
63+
*/
64+
function setupOverlapFixture() {
65+
const fixtures = setupFixtures();
66+
fixtures.propertyTable.associatePropertySet(3, 101);
67+
return fixtures;
68+
}
69+
5670
describe('QueryBuilder', () => {
5771
it('should return all entities when no filter is applied', () => {
5872
const { entityTable, propertyTable } = setupFixtures();
@@ -110,6 +124,47 @@ describe('QueryBuilder', () => {
110124
expect(results[0].expressId).toBe(2);
111125
});
112126

127+
it('withProperty(value) ANDs with the prior filter instead of replacing it', () => {
128+
// Door 3 shares wall 2's exact pset/property/value (see fixture comment).
129+
// Without the prior filter also required, `withProperty(..., false)` alone
130+
// matches {2, 3} and `.ofType('IFCWALL')` would wrongly include the door.
131+
const { entityTable, propertyTable } = setupOverlapFixture();
132+
const results = new QueryBuilder(entityTable, propertyTable)
133+
.ofType('IFCWALL')
134+
.withProperty('Pset_WallCommon', 'IsExternal', false)
135+
.execute();
136+
expect(results.map(e => e.expressId)).toEqual([2]);
137+
// Property side alone (no type filter) legitimately reaches the door too,
138+
// confirming the fixture actually creates the overlap this test relies on.
139+
const propertyOnly = new QueryBuilder(entityTable, propertyTable)
140+
.withProperty('Pset_WallCommon', 'IsExternal', false)
141+
.execute();
142+
expect(propertyOnly.map(e => e.expressId).sort()).toEqual([2, 3]);
143+
});
144+
145+
it('withProperty(existence) ANDs with the prior filter instead of replacing it', () => {
146+
// Same overlap, but the value-less "does this property exist" branch.
147+
const { entityTable, propertyTable } = setupOverlapFixture();
148+
const results = new QueryBuilder(entityTable, propertyTable)
149+
.ofType('IFCWALL')
150+
.withProperty('Pset_WallCommon', 'IsExternal')
151+
.execute();
152+
// Both walls carry Pset_WallCommon.IsExternal; the door must not appear.
153+
expect(results.map(e => e.expressId).sort()).toEqual([1, 2]);
154+
});
155+
156+
it('ofType() ANDs with a prior ofType(), so two disagreeing types match nothing', () => {
157+
// Every entity has exactly one type, so IFCWALL and IFCDOOR never agree.
158+
// A filter that overwrote rather than ANDed with the previous one would
159+
// answer this with whichever `ofType()` call ran last (the doors).
160+
const { entityTable, propertyTable } = setupOverlapFixture();
161+
const results = new QueryBuilder(entityTable, propertyTable)
162+
.ofType('IFCWALL')
163+
.ofType('IFCDOOR')
164+
.execute();
165+
expect(results).toEqual([]);
166+
});
167+
113168
});
114169

115170
describe('QueryInterface', () => {

0 commit comments

Comments
 (0)