@@ -424,7 +424,7 @@ function defineSyncTests(impl: SyncClientImplementation) {
424424
425425 mockSyncServiceTest ( 'interrupt and defrag' , async ( { syncService } ) => {
426426 let database = await syncService . createDatabase ( ) ;
427- database . connect ( new TestConnector ( ) , { connectionMethod : SyncStreamConnectionMethod . HTTP } ) ;
427+ database . connect ( new TestConnector ( ) , options ) ;
428428 await vi . waitFor ( ( ) => expect ( syncService . connectedListeners ) . toHaveLength ( 1 ) ) ;
429429
430430 syncService . pushLine ( {
@@ -442,7 +442,7 @@ function defineSyncTests(impl: SyncClientImplementation) {
442442 await database . close ( ) ;
443443 await vi . waitFor ( ( ) => expect ( syncService . connectedListeners ) . toHaveLength ( 0 ) ) ;
444444 database = await syncService . createDatabase ( ) ;
445- database . connect ( new TestConnector ( ) , { connectionMethod : SyncStreamConnectionMethod . HTTP } ) ;
445+ database . connect ( new TestConnector ( ) , options ) ;
446446 await vi . waitFor ( ( ) => expect ( syncService . connectedListeners ) . toHaveLength ( 1 ) ) ;
447447
448448 // A sync rule deploy could reset buckets, making the new bucket smaller than the existing one.
@@ -459,6 +459,142 @@ function defineSyncTests(impl: SyncClientImplementation) {
459459 await waitForSyncStatus ( database , ( s ) => s . downloadProgress == null ) ;
460460 } ) ;
461461 } ) ;
462+
463+ mockSyncServiceTest ( 'should upload after connecting' , async ( { syncService } ) => {
464+ let database = await syncService . createDatabase ( ) ;
465+
466+ await database . execute ( 'INSERT INTO lists (id, name) values (uuid(), ?)' , [ 'local write' ] ) ;
467+ const query = database . watchWithAsyncGenerator ( 'SELECT name FROM lists' ) [ Symbol . asyncIterator ] ( ) ;
468+ let rows = ( await query . next ( ) ) . value . rows . _array ;
469+ expect ( rows ) . toStrictEqual ( [ { name : 'local write' } ] ) ;
470+
471+ database . connect ( new TestConnector ( ) , options ) ;
472+ await vi . waitFor ( ( ) => expect ( syncService . connectedListeners ) . toHaveLength ( 1 ) ) ;
473+
474+ syncService . pushLine ( { checkpoint : { last_op_id : '1' , write_checkpoint : '1' , buckets : [ bucket ( 'a' , 1 ) ] } } ) ;
475+ syncService . pushLine ( {
476+ data : {
477+ bucket : 'a' ,
478+ data : [
479+ {
480+ checksum : 0 ,
481+ op_id : '1' ,
482+ op : 'PUT' ,
483+ object_id : '1' ,
484+ object_type : 'lists' ,
485+ data : '{"name": "from server"}'
486+ }
487+ ]
488+ }
489+ } ) ;
490+ syncService . pushLine ( { checkpoint_complete : { last_op_id : '1' } } ) ;
491+
492+ rows = ( await query . next ( ) ) . value . rows . _array ;
493+ expect ( rows ) . toStrictEqual ( [ { name : 'from server' } ] ) ;
494+ } ) ;
495+
496+ mockSyncServiceTest ( 'should update sync state incrementally' , async ( { syncService } ) => {
497+ const powersync = await syncService . createDatabase ( ) ;
498+ powersync . connect ( new TestConnector ( ) , options ) ;
499+ await vi . waitFor ( ( ) => expect ( syncService . connectedListeners ) . toHaveLength ( 1 ) ) ;
500+
501+ const buckets : BucketChecksum [ ] = [ ] ;
502+ for ( let prio = 0 ; prio <= 3 ; prio ++ ) {
503+ buckets . push ( { bucket : `prio${ prio } ` , priority : prio , checksum : 10 + prio } ) ;
504+ }
505+ syncService . pushLine ( {
506+ checkpoint : {
507+ last_op_id : '4' ,
508+ buckets
509+ }
510+ } ) ;
511+
512+ let operationId = 1 ;
513+ const addRow = ( prio : number ) => {
514+ syncService . pushLine ( {
515+ data : {
516+ bucket : `prio${ prio } ` ,
517+ data : [
518+ {
519+ checksum : prio + 10 ,
520+ data : JSON . stringify ( { name : 'row' } ) ,
521+ op : 'PUT' ,
522+ op_id : ( operationId ++ ) . toString ( ) ,
523+ object_id : `prio${ prio } ` ,
524+ object_type : 'lists'
525+ }
526+ ]
527+ }
528+ } ) ;
529+ } ;
530+
531+ const syncCompleted = vi . fn ( ) ;
532+ powersync . waitForFirstSync ( ) . then ( syncCompleted ) ;
533+
534+ // Emit partial sync complete for each priority but the last.
535+ for ( var prio = 0 ; prio < 3 ; prio ++ ) {
536+ const partialSyncCompleted = vi . fn ( ) ;
537+ powersync . waitForFirstSync ( { priority : prio } ) . then ( partialSyncCompleted ) ;
538+ expect ( powersync . currentStatus . statusForPriority ( prio ) . hasSynced ) . toBe ( false ) ;
539+ expect ( partialSyncCompleted ) . not . toHaveBeenCalled ( ) ;
540+ expect ( syncCompleted ) . not . toHaveBeenCalled ( ) ;
541+
542+ addRow ( prio ) ;
543+ syncService . pushLine ( {
544+ partial_checkpoint_complete : {
545+ last_op_id : operationId . toString ( ) ,
546+ priority : prio
547+ }
548+ } ) ;
549+
550+ await powersync . syncStreamImplementation ! . waitUntilStatusMatches ( ( status ) => {
551+ return status . statusForPriority ( prio ) . hasSynced === true ;
552+ } ) ;
553+ await new Promise ( ( r ) => setTimeout ( r ) ) ;
554+ expect ( partialSyncCompleted ) . toHaveBeenCalledOnce ( ) ;
555+
556+ expect ( await powersync . getAll ( 'select * from lists' ) ) . toHaveLength ( prio + 1 ) ;
557+ }
558+
559+ // Then, complete the sync.
560+ addRow ( 3 ) ;
561+ syncService . pushLine ( { checkpoint_complete : { last_op_id : operationId . toString ( ) } } ) ;
562+ await vi . waitFor ( ( ) => expect ( syncCompleted ) . toHaveBeenCalledOnce ( ) , 500 ) ;
563+ expect ( await powersync . getAll ( 'select * from lists' ) ) . toHaveLength ( 4 ) ;
564+ } ) ;
565+
566+ mockSyncServiceTest ( 'Should remember sync state' , async ( { syncService } ) => {
567+ const powersync = await syncService . createDatabase ( ) ;
568+ powersync . connect ( new TestConnector ( ) , options ) ;
569+ await vi . waitFor ( ( ) => expect ( syncService . connectedListeners ) . toHaveLength ( 1 ) ) ;
570+
571+ const buckets : BucketChecksum [ ] = [ ] ;
572+ for ( let prio = 0 ; prio <= 3 ; prio ++ ) {
573+ buckets . push ( { bucket : `prio${ prio } ` , priority : prio , checksum : 0 } ) ;
574+ }
575+ syncService . pushLine ( {
576+ checkpoint : {
577+ last_op_id : '0' ,
578+ buckets
579+ }
580+ } ) ;
581+ syncService . pushLine ( {
582+ partial_checkpoint_complete : {
583+ last_op_id : '0' ,
584+ priority : 0
585+ }
586+ } ) ;
587+
588+ await powersync . waitForFirstSync ( { priority : 0 } ) ;
589+
590+ // Open another database instance.
591+ const another = await syncService . createDatabase ( ) ;
592+ await another . init ( ) ;
593+
594+ expect ( another . currentStatus . priorityStatusEntries ) . toHaveLength ( 1 ) ;
595+ expect ( another . currentStatus . statusForPriority ( 0 ) . hasSynced ) . toBeTruthy ( ) ;
596+ await another . waitForFirstSync ( { priority : 0 } ) ;
597+ } ) ;
462598}
463599
464600function bucket ( name : string , count : number , options : { priority : number } = { priority : 3 } ) : BucketChecksum {
0 commit comments