@@ -244,3 +244,66 @@ test('re-add comment → issue reappears', () => {
244244
245245 expect ( view . data ) . toEqual ( queryDelegate . materialize ( q ) . data ) ;
246246} ) ;
247+
248+ test ( 'join-level unordered overlay — remove comment triggers overlay for multiple parent issues' , ( ) => {
249+ // Uses ownerComments: issue.ownerId = comment.authorId
250+ // All 4 issues have ownerId='user1', all comments have authorId='user1'
251+ // So a single comment change matches ALL 4 issues as parents.
252+ // With flip: false, the planner builds a regular Join + Cap(limit=3, unordered).
253+ // When Cap pushes a remove+refill to Join, Join iterates all 4 parent issues,
254+ // and for issues 2-4, generateWithOverlayUnordered (join-utils.ts) is called.
255+ const q = issueQuery . whereExists ( 'ownerComments' , { flip : false } ) ;
256+ const view = queryDelegate . materialize ( q ) ;
257+
258+ // All 4 issues should be present (all have ownerComments via ownerId='user1')
259+ const initialData = view . data as ReadonlyArray < { readonly id : string } > ;
260+ const initialIds = initialData . map ( r => r . id ) ;
261+ expect ( initialIds ) . toContain ( 'issue1' ) ;
262+ expect ( initialIds ) . toContain ( 'issue2' ) ;
263+ expect ( initialIds ) . toContain ( 'issue3' ) ;
264+ expect ( initialIds ) . toContain ( 'issue4' ) ;
265+
266+ expect ( view . data ) . toEqual ( queryDelegate . materialize ( q ) . data ) ;
267+
268+ // Remove comments to ensure we hit a tracked one.
269+ // Cap tracks the first 3 it encounters (unordered). Removing multiple
270+ // guarantees at least one hits a tracked comment, triggering Cap refill → Join overlay.
271+ // After prior tests, the remaining comments are:
272+ // c1b (issue1), c2a (issue2), c3a/c3b/c3c/c3d (issue3), c4b/c4c/c4d (issue4)
273+ const commentsToRemove = [
274+ {
275+ id : 'c2a' ,
276+ authorId : 'user1' ,
277+ issue_id : 'issue2' ,
278+ text : 'Comment 2a' ,
279+ createdAt : 1009843200000 ,
280+ } ,
281+ {
282+ id : 'c3a' ,
283+ authorId : 'user1' ,
284+ issue_id : 'issue3' ,
285+ text : 'Comment 3a' ,
286+ createdAt : 1012521600000 ,
287+ } ,
288+ {
289+ id : 'c3b' ,
290+ authorId : 'user1' ,
291+ issue_id : 'issue3' ,
292+ text : 'Comment 3b' ,
293+ createdAt : 1012608000000 ,
294+ } ,
295+ {
296+ id : 'c4b' ,
297+ authorId : 'user1' ,
298+ issue_id : 'issue4' ,
299+ text : 'Comment 4b' ,
300+ createdAt : 1015113600000 ,
301+ } ,
302+ ] ;
303+
304+ const source = must ( queryDelegate . getSource ( 'comments' ) ) ;
305+ for ( const row of commentsToRemove ) {
306+ consume ( source . push ( { type : 'remove' , row} ) ) ;
307+ expect ( view . data ) . toEqual ( queryDelegate . materialize ( q ) . data ) ;
308+ }
309+ } ) ;
0 commit comments