@@ -93,6 +93,7 @@ import {
93
93
doc ,
94
94
docAddedRemoteEvent ,
95
95
docUpdateRemoteEvent ,
96
+ existenceFilterEvent ,
96
97
expectEqual ,
97
98
filter ,
98
99
key ,
@@ -130,6 +131,7 @@ class LocalStoreTester {
130
131
131
132
constructor (
132
133
public localStore : LocalStore ,
134
+ private readonly persistence : Persistence ,
133
135
private readonly queryEngine : CountingQueryEngine ,
134
136
readonly gcIsEager : boolean
135
137
) {
@@ -379,6 +381,30 @@ class LocalStoreTester {
379
381
return this ;
380
382
}
381
383
384
+ toContainTargetData (
385
+ target : Target ,
386
+ snapshotVersion : number ,
387
+ lastLimboFreeSnapshotVersion : number ,
388
+ resumeToken : ByteString
389
+ ) : LocalStoreTester {
390
+ this . promiseChain = this . promiseChain . then ( async ( ) => {
391
+ const targetData = await this . persistence . runTransaction (
392
+ 'getTargetData' ,
393
+ 'readonly' ,
394
+ txn => localStoreGetTargetData ( this . localStore , txn , target )
395
+ ) ;
396
+ expect ( targetData ! . snapshotVersion . isEqual ( version ( snapshotVersion ) ) ) . to
397
+ . be . true ;
398
+ expect (
399
+ targetData ! . lastLimboFreeSnapshotVersion . isEqual (
400
+ version ( lastLimboFreeSnapshotVersion )
401
+ )
402
+ ) . to . be . true ;
403
+ expect ( targetData ! . resumeToken . isEqual ( resumeToken ) ) . to . be . true ;
404
+ } ) ;
405
+ return this ;
406
+ }
407
+
382
408
toReturnChanged ( ...docs : Document [ ] ) : LocalStoreTester {
383
409
this . promiseChain = this . promiseChain . then ( ( ) => {
384
410
debugAssert (
@@ -583,7 +609,12 @@ function genericLocalStoreTests(
583
609
} ) ;
584
610
585
611
function expectLocalStore ( ) : LocalStoreTester {
586
- return new LocalStoreTester ( localStore , queryEngine , gcIsEager ) ;
612
+ return new LocalStoreTester (
613
+ localStore ,
614
+ persistence ,
615
+ queryEngine ,
616
+ gcIsEager
617
+ ) ;
587
618
}
588
619
589
620
it ( 'handles SetMutation' , ( ) => {
@@ -1876,6 +1907,47 @@ function genericLocalStoreTests(
1876
1907
}
1877
1908
} ) ;
1878
1909
1910
+ // eslint-disable-next-line no-restricted-properties
1911
+ ( gcIsEager ? it . skip : it ) (
1912
+ 'ignores target mapping after existence filter mismatch' ,
1913
+ async ( ) => {
1914
+ const query1 = query ( 'foo' , filter ( 'matches' , '==' , true ) ) ;
1915
+ const target = queryToTarget ( query1 ) ;
1916
+ const targetId = 2 ;
1917
+
1918
+ return (
1919
+ expectLocalStore ( )
1920
+ . afterAllocatingQuery ( query1 )
1921
+ . toReturnTargetId ( targetId )
1922
+ // Persist a mapping with a single document
1923
+ . after (
1924
+ docAddedRemoteEvent (
1925
+ doc ( 'foo/a' , 10 , { matches : true } ) ,
1926
+ [ targetId ] ,
1927
+ [ ] ,
1928
+ [ targetId ]
1929
+ )
1930
+ )
1931
+ . after ( noChangeEvent ( targetId , 10 , byteStringFromString ( 'foo' ) ) )
1932
+ . after ( localViewChanges ( targetId , /* fromCache= */ false , { } ) )
1933
+ . afterExecutingQuery ( query1 )
1934
+ . toReturnChanged ( doc ( 'foo/a' , 10 , { matches : true } ) )
1935
+ . toHaveRead ( { documentsByKey : 1 } )
1936
+ . toContainTargetData ( target , 10 , 10 , byteStringFromString ( 'foo' ) )
1937
+ // Create an existence filter mismatch and verify that the last limbo
1938
+ // free snapshot version is deleted
1939
+ . after ( existenceFilterEvent ( targetId , 2 , 20 ) )
1940
+ . after ( noChangeEvent ( targetId , 20 ) )
1941
+ . toContainTargetData ( target , 0 , 0 , ByteString . EMPTY_BYTE_STRING )
1942
+ // Re-run the query as a collection scan
1943
+ . afterExecutingQuery ( query1 )
1944
+ . toReturnChanged ( doc ( 'foo/a' , 10 , { matches : true } ) )
1945
+ . toHaveRead ( { documentsByQuery : 1 } )
1946
+ . finish ( )
1947
+ ) ;
1948
+ }
1949
+ ) ;
1950
+
1879
1951
// eslint-disable-next-line no-restricted-properties
1880
1952
( gcIsEager ? it . skip : it ) (
1881
1953
'queries include locally modified documents' ,
0 commit comments