@@ -154,9 +154,9 @@ const NON_BOOLEANS: ReadonlyArray<readonly [string, unknown, string]> = [
154154 [ '"false" (truthy)' , 'false' , 'string ("false")' ] ,
155155 [ '0' , 0 , 'number (0)' ] ,
156156 [ 'null' , null , 'null (null)' ] ,
157- // Not one of the card's five, but reaching the same gate: no earlier door
158- // refuses a list here, so it met the old `!!target` read like any other value.
159- [ '[true] (a list)' , [ true ] , 'array ([true])' ] ,
157+ // [#21448] `[true] (a list)` stood here, reaching this gate because no
158+ // earlier door refused a list at a flag. The shared comparand-shape face
159+ // does now, one door earlier, in its own words — pinned in the block below.
160160] ;
161161
162162describe ( '[#20981] a non-boolean $exists / $null — refused before any driver read, on both positions' , ( ) => {
@@ -184,6 +184,25 @@ describe('[#20981] a non-boolean $exists / $null — refused before any driver r
184184 }
185185 }
186186
187+ it ( '[#21448] a LIST flag is the shared comparand-shape face\'s refusal, one door earlier, at both positions — no read' , async ( ) => {
188+ for ( const op of OPS ) {
189+ const sentence = `Operator "${ op } " on field "name" requires a single comparable value, but received an array ([true])` ;
190+ const { engine, reads } = await makeEngine ( 'rows' , ROWS ) ;
191+ const filtered = await refusalOf ( ( ) => engine . aggregate ( OBJECT , filterQuery ( { name : { [ op ] : [ true ] } } ) ) ) ;
192+ expect ( { code : filtered . code , status : filtered . status } , op ) . toEqual ( { code : 'INVALID_FILTER' , status : 400 } ) ;
193+ expect ( filtered . message , op ) . toContain ( `${ sentence } at aggregations[1].filter.name.${ op } .` ) ;
194+ expect ( filtered . message , op ) . not . toContain ( 'requires a boolean comparand' ) ;
195+ expect ( reads , `${ op } : no row was read` ) . toEqual ( { aggregate : 0 , find : 0 } ) ;
196+ for ( const path of [ 'native' , 'rows' ] as const ) {
197+ const grouped = await makeEngine ( path , ROWS ) ;
198+ const having = await refusalOf ( ( ) => grouped . engine . aggregate ( OBJECT , havingQuery ( path , { name : { [ op ] : [ true ] } } ) ) ) ;
199+ expect ( { code : having . code , status : having . status } , `${ op } ${ path } ` ) . toEqual ( { code : 'INVALID_FILTER' , status : 400 } ) ;
200+ expect ( having . message , `${ op } ${ path } ` ) . toContain ( `${ sentence } at having.name.${ op } .` ) ;
201+ expect ( grouped . reads , `${ op } ${ path } : no row was read` ) . toEqual ( { aggregate : 0 , find : 0 } ) ;
202+ }
203+ }
204+ } ) ;
205+
187206 // Where the flag sits must not change the verdict: the walk is row-independent,
188207 // so a branch the per-row walk would short-circuit past is judged too.
189208 const POSITIONS : ReadonlyArray < readonly [ string , ( flag : Record < string , unknown > ) => Record < string , unknown > , string ] > = [
0 commit comments