1
1
package cmd
2
2
3
3
import (
4
+ "context"
4
5
"encoding/json"
5
6
"errors"
6
7
"fmt"
8
+ "io"
7
9
"os"
8
10
"path/filepath"
9
11
"strings"
@@ -12,6 +14,7 @@ import (
12
14
"github.com/google/uuid"
13
15
"github.com/rs/zerolog"
14
16
"github.com/stretchr/testify/require"
17
+ "google.golang.org/grpc"
15
18
"google.golang.org/grpc/codes"
16
19
"google.golang.org/grpc/status"
17
20
@@ -22,6 +25,7 @@ import (
22
25
"github.com/authzed/zed/internal/client"
23
26
"github.com/authzed/zed/internal/storage"
24
27
zedtesting "github.com/authzed/zed/internal/testing"
28
+ "github.com/authzed/zed/pkg/backupformat"
25
29
)
26
30
27
31
func init () {
@@ -606,3 +610,166 @@ func TestAddSizeErrInfo(t *testing.T) {
606
610
})
607
611
}
608
612
}
613
+
614
+ func TestTakeBackupMockWorksAsExpected (t * testing.T ) {
615
+ rels := []* v1.Relationship {
616
+ {
617
+ Resource : & v1.ObjectReference {
618
+ ObjectType : "resource" ,
619
+ ObjectId : "foo" ,
620
+ },
621
+ Relation : "view" ,
622
+ Subject : & v1.SubjectReference {
623
+ Object : & v1.ObjectReference {
624
+ ObjectType : "user" ,
625
+ ObjectId : "jim" ,
626
+ },
627
+ },
628
+ },
629
+ }
630
+ client := & mockClientForBackup {
631
+ t : t ,
632
+ recvCalls : []func () (* v1.ExportBulkRelationshipsResponse , error ){
633
+ func () (* v1.ExportBulkRelationshipsResponse , error ) {
634
+ return & v1.ExportBulkRelationshipsResponse {
635
+ Relationships : rels ,
636
+ }, nil
637
+ },
638
+ },
639
+ }
640
+
641
+ err := takeBackup (t .Context (), client , & v1.ExportBulkRelationshipsRequest {}, func (response * v1.ExportBulkRelationshipsResponse ) error {
642
+ require .Len (t , response .Relationships , 1 , "expecting 1 rel in the list" )
643
+ return nil
644
+ })
645
+ require .NoError (t , err )
646
+
647
+ client .assertAllRecvCalls ()
648
+ }
649
+
650
+ func TestTakeBackupRecoversFromRetryableErrors (t * testing.T ) {
651
+ firstRels := []* v1.Relationship {
652
+ {
653
+ Resource : & v1.ObjectReference {
654
+ ObjectType : "resource" ,
655
+ ObjectId : "foo" ,
656
+ },
657
+ Relation : "view" ,
658
+ Subject : & v1.SubjectReference {
659
+ Object : & v1.ObjectReference {
660
+ ObjectType : "user" ,
661
+ ObjectId : "jim" ,
662
+ },
663
+ },
664
+ },
665
+ }
666
+ cursor := & v1.Cursor {
667
+ Token : "an token" ,
668
+ }
669
+ secondRels := []* v1.Relationship {
670
+ {
671
+ Resource : & v1.ObjectReference {
672
+ ObjectType : "resource" ,
673
+ ObjectId : "bar" ,
674
+ },
675
+ Relation : "view" ,
676
+ Subject : & v1.SubjectReference {
677
+ Object : & v1.ObjectReference {
678
+ ObjectType : "user" ,
679
+ ObjectId : "jim" ,
680
+ },
681
+ },
682
+ },
683
+ }
684
+ client := & mockClientForBackup {
685
+ t : t ,
686
+ recvCalls : []func () (* v1.ExportBulkRelationshipsResponse , error ){
687
+ func () (* v1.ExportBulkRelationshipsResponse , error ) {
688
+ return & v1.ExportBulkRelationshipsResponse {
689
+ Relationships : firstRels ,
690
+ // Need to test that this cursor is supplied
691
+ AfterResultCursor : cursor ,
692
+ }, nil
693
+ },
694
+ func () (* v1.ExportBulkRelationshipsResponse , error ) {
695
+ // Return a retryable error
696
+ return nil , status .Error (codes .Unavailable , "i fell over" )
697
+ },
698
+ func () (* v1.ExportBulkRelationshipsResponse , error ) {
699
+ return & v1.ExportBulkRelationshipsResponse {
700
+ Relationships : secondRels ,
701
+ AfterResultCursor : & v1.Cursor {
702
+ Token : "some other token" ,
703
+ },
704
+ }, nil
705
+ },
706
+ },
707
+ exportCalls : []func (t * testing.T , req * v1.ExportBulkRelationshipsRequest ){
708
+ // Initial request
709
+ func (_ * testing.T , _ * v1.ExportBulkRelationshipsRequest ) {
710
+ },
711
+ // The retried request - asserting that it's called with the cursor
712
+ func (t * testing.T , req * v1.ExportBulkRelationshipsRequest ) {
713
+ require .Equal (t , req .OptionalCursor .Token , cursor .Token , "cursor token does not match expected" )
714
+ },
715
+ },
716
+ }
717
+
718
+ actualRels := make ([]* v1.Relationship , 0 )
719
+
720
+ err := takeBackup (t .Context (), client , & v1.ExportBulkRelationshipsRequest {}, func (response * v1.ExportBulkRelationshipsResponse ) error {
721
+ actualRels = append (actualRels , response .Relationships ... )
722
+ return nil
723
+ })
724
+ require .NoError (t , err )
725
+
726
+ require .Len (t , actualRels , 2 , "expecting two rels in the realized list" )
727
+ require .Equal (t , actualRels [0 ].Resource .ObjectId , "foo" )
728
+ require .Equal (t , actualRels [1 ].Resource .ObjectId , "bar" )
729
+
730
+ client .assertAllRecvCalls ()
731
+ }
732
+
733
+ type mockClientForBackup struct {
734
+ client.Client
735
+ grpc.ServerStreamingClient [v1.ExportBulkRelationshipsResponse ]
736
+ t * testing.T
737
+ backupformat.Encoder
738
+ recvCalls []func () (* v1.ExportBulkRelationshipsResponse , error )
739
+ recvCallIndex int
740
+ // exportCalls provides a handle on the calls made to ExportBulkRelationships,
741
+ // allowing for assertions to be made against those calls.
742
+ exportCalls []func (t * testing.T , req * v1.ExportBulkRelationshipsRequest )
743
+ exportCallsIndex int
744
+ }
745
+
746
+ func (m * mockClientForBackup ) Recv () (* v1.ExportBulkRelationshipsResponse , error ) {
747
+ // If we've run through all our calls, return an EOF
748
+ if m .recvCallIndex == len (m .recvCalls ) {
749
+ return nil , io .EOF
750
+ }
751
+ recvCall := m .recvCalls [m .recvCallIndex ]
752
+ m .recvCallIndex ++
753
+ return recvCall ()
754
+ }
755
+
756
+ func (m * mockClientForBackup ) ExportBulkRelationships (_ context.Context , req * v1.ExportBulkRelationshipsRequest , _ ... grpc.CallOption ) (grpc.ServerStreamingClient [v1.ExportBulkRelationshipsResponse ], error ) {
757
+ if m .exportCalls == nil {
758
+ // If the caller doesn't supply exportCalls, pass through
759
+ return m , nil
760
+ }
761
+ if m .exportCallsIndex == len (m .exportCalls ) {
762
+ // If invoked too many times, fail the test
763
+ m .t .FailNow ()
764
+ return m , nil
765
+ }
766
+ exportCall := m .exportCalls [m .exportCallsIndex ]
767
+ m .exportCallsIndex ++
768
+ exportCall (m .t , req )
769
+ return m , nil
770
+ }
771
+
772
+ // assertAllRecvCalls asserts that the number of invocations is as expected
773
+ func (m * mockClientForBackup ) assertAllRecvCalls () {
774
+ require .Equal (m .t , len (m .recvCalls ), m .recvCallIndex , "the number of provided recvCalls should match the number of invocations" )
775
+ }
0 commit comments