@@ -3,7 +3,6 @@ package tests
3
3
import (
4
4
"fmt"
5
5
"math/rand"
6
- "sync"
7
6
"testing"
8
7
"time"
9
8
@@ -535,35 +534,42 @@ func TestMultiprocessNSEOlmSessionWedge(t *testing.T) {
535
534
})
536
535
}
537
536
538
- func TestMultiprocessDupeOTKUpload (t * testing.T ) {
537
+ // Test that the notification client doesn't cause duplicate OTK uploads.
538
+ // Regression test for https://github.com/matrix-org/matrix-rust-sdk/issues/1415
539
+ //
540
+ // This test creates a normal Alice rust client and lets it upload OTKs. It then:
541
+ // - hooks into /keys/upload requests and artificially delays them by adding 4s of latency
542
+ // - creates a Bob client who sends a message to Alice, consuming 1 OTK in the process
543
+ // - immediately calls GetNotification on Bob's event as soon as it 200 OKs, which creates
544
+ // a NotificationClient inside the same process.
545
+ //
546
+ // This means there are 2 sync loops: the main Client and the NotificationClient. Both clients
547
+ // will see the OTK count being lowered so both may try to upload a new OTK. Because we are
548
+ // delaying upload requests by 4s, this increases the chance of both uploads being in-flight at
549
+ // the same time. If they do not synchronise this operation, they will both try to upload
550
+ // _different keys_ with the _same_ key ID, which causes synapse to HTTP 400 with:
551
+ //
552
+ // > One time key signed_curve25519:AAAAAAAAADI already exists
553
+ //
554
+ // Which will fail the test.
555
+ func TestNotificationClientDupeOTKUpload (t * testing.T ) {
539
556
if ! Instance ().ShouldTest (api .ClientTypeRust ) {
540
557
t .Skipf ("rust only" )
541
558
return
542
559
}
543
- t .Skipf ("skipped until it is no longer flakey" )
544
560
tc , roomID := createAndJoinRoom (t )
545
561
546
562
// start the "main" app
547
563
alice := tc .MustLoginClient (t , & cc.ClientCreationRequest {
548
564
User : tc .Alice ,
549
565
Opts : api.ClientCreationOpts {
550
- PersistentStorage : true ,
551
- EnableCrossProcessRefreshLockProcessName : "main" ,
566
+ PersistentStorage : true ,
552
567
},
553
568
})
569
+ stopSyncing := alice .MustStartSyncing (t )
570
+ defer stopSyncing ()
554
571
aliceAccessToken := alice .Opts ().AccessToken
555
572
556
- // prep nse process
557
- nseAlice := tc .MustCreateClient (t , & cc.ClientCreationRequest {
558
- User : tc .Alice ,
559
- Multiprocess : true ,
560
- Opts : api.ClientCreationOpts {
561
- PersistentStorage : true ,
562
- EnableCrossProcessRefreshLockProcessName : api .ProcessNameNSE ,
563
- AccessToken : aliceAccessToken ,
564
- },
565
- })
566
-
567
573
aliceUploadedNewKeys := false
568
574
// artificially slow down the HTTP responses, such that we will potentially have 2 in-flight /keys/upload requests
569
575
// at once. If the NSE and main apps are talking to each other, they should be using the same key ID + key.
@@ -588,35 +594,21 @@ func TestMultiprocessDupeOTKUpload(t *testing.T) {
588
594
return nil
589
595
},
590
596
}, func () {
591
- var eventID string
592
597
// Bob appears and sends a message, causing Bob to claim one of Alice's OTKs.
593
598
// The main app will see this in /sync and then try to upload another OTK, which we will tarpit.
594
599
tc .WithClientSyncing (t , & cc.ClientCreationRequest {
595
600
User : tc .Bob ,
596
601
}, func (bob api.Client ) {
597
- eventID = bob .SendMessage (t , roomID , "Hello world!" )
598
- })
599
- var wg sync.WaitGroup
600
- wg .Add (2 )
601
- go func () { // nse process
602
- defer wg .Done ()
603
- // wake up NSE process as if it got a push notification. Calling this function
604
- // should cause the NSE process to upload a OTK as it would have seen 1 has been used.
605
- // The NSE and main app must talk to each other to ensure they use the same key.
606
- nseAlice .Logf (t , "GetNotification %s, %s" , roomID , eventID )
607
- notif , err := nseAlice .GetNotification (t , roomID , eventID )
602
+ eventID := bob .SendMessage (t , roomID , "Hello world!" )
603
+ // create a NotificationClient in the same process to fetch this "push notification".
604
+ // It might make the NotificationClient upload a OTK as it would have seen 1 has been used.
605
+ // The NotificationClient and main Client must talk to each other to ensure they use the same key.
606
+ alice .Logf (t , "GetNotification %s, %s" , roomID , eventID )
607
+ notif , err := alice .GetNotification (t , roomID , eventID )
608
608
must .NotError (t , "failed to get notification" , err )
609
609
must .Equal (t , notif .Text , "Hello world!" , "failed to decrypt msg body" )
610
610
must .Equal (t , notif .FailedToDecrypt , false , "FailedToDecrypt but we should be able to decrypt" )
611
- }()
612
- go func () { // app process
613
- defer wg .Done ()
614
- stopSyncing := alice .MustStartSyncing (t )
615
- // let alice upload new OTK
616
- time .Sleep (5 * time .Second )
617
- stopSyncing ()
618
- }()
619
- wg .Wait ()
611
+ })
620
612
})
621
613
if ! aliceUploadedNewKeys {
622
614
t .Errorf ("Alice did not upload new OTKs" )
0 commit comments