@@ -44,68 +44,85 @@ func (f fakeWitnessGroup) WitnessEndpoints() map[string][]note.Verifier {
4444}
4545
4646func TestGateway (t * testing.T ) {
47- opts := tessera .NewAppendOptions ().
48- WithCheckpointInterval (100 * time .Millisecond ).
49- WithCheckpointRepublishInterval (100 * time .Millisecond )
50-
51- testLog , shutdown := testonly .NewTestLog (t , opts )
52- defer func () {
53- _ = shutdown (t .Context ())
54- }()
55-
56- // Create a log with some entries.
57- const size = 5
58- var f tessera.IndexFuture
59- for i := range size {
60- entry := tessera .NewEntry (fmt .Appendf (nil , "entry-%d" , i ))
61- f = testLog .Appender .Add (t .Context (), entry )
62- }
63- a := tessera .NewPublicationAwaiter (t .Context (), testLog .LogReader .ReadCheckpoint , 100 * time .Millisecond )
64- if _ , _ , err := a .Await (t .Context (), f ); err != nil {
65- t .Fatalf ("failed to add entry: %v" , err )
66- }
67- goalCP , err := testLog .LogReader .ReadCheckpoint (t .Context ())
68- if err != nil {
69- t .Fatalf ("failed to read checkpoint: %v" , err )
70- }
47+ for _ , tc := range []struct {
48+ name string
49+ numMirrors int
50+ failCount int
51+ }{
52+ {
53+ name : "multiple mirrors" ,
54+ numMirrors : 3 ,
55+ },
56+ {
57+ name : "retry on transient error" ,
58+ numMirrors : 1 ,
59+ failCount : 2 ,
60+ },
61+ } {
62+ t .Run (tc .name , func (t * testing.T ) {
63+ opts := tessera .NewAppendOptions ().
64+ WithCheckpointInterval (100 * time .Millisecond ).
65+ WithCheckpointRepublishInterval (100 * time .Millisecond )
66+
67+ testLog , shutdown := testonly .NewTestLog (t , opts )
68+ defer func () {
69+ _ = shutdown (t .Context ())
70+ }()
71+
72+ // Create a log with some entries.
73+ const size = 5
74+ var f tessera.IndexFuture
75+ for i := range size {
76+ entry := tessera .NewEntry (fmt .Appendf (nil , "entry-%d" , i ))
77+ f = testLog .Appender .Add (t .Context (), entry )
78+ }
79+ a := tessera .NewPublicationAwaiter (t .Context (), testLog .LogReader .ReadCheckpoint , 100 * time .Millisecond )
80+ if _ , _ , err := a .Await (t .Context (), f ); err != nil {
81+ t .Fatalf ("failed to add entry: %v" , err )
82+ }
83+ goalCP , err := testLog .LogReader .ReadCheckpoint (t .Context ())
84+ if err != nil {
85+ t .Fatalf ("failed to read checkpoint: %v" , err )
86+ }
7187
72- const numMirrors = 3
73- var verifiers []note.Verifier
74- endpoints := make (map [string ][]note.Verifier )
88+ var verifiers []note.Verifier
89+ endpoints := make (map [string ][]note.Verifier )
7590
76- for i := range numMirrors {
77- signer , verifier := mustNewKeypair (t , fmt .Sprintf ("Mirror-%d" , i ))
78- server := startMockMirror (t , signer , testLog .SigVerifier )
79- defer server .Close ()
91+ for i := range tc . numMirrors {
92+ signer , verifier := mustNewKeypair (t , fmt .Sprintf ("Mirror-%d" , i ))
93+ server := startMockMirror (t , signer , testLog .SigVerifier , tc . failCount )
94+ defer server .Close ()
8095
81- endpoints [server .URL ] = []note.Verifier {verifier }
82- verifiers = append (verifiers , verifier )
83- }
96+ endpoints [server .URL ] = []note.Verifier {verifier }
97+ verifiers = append (verifiers , verifier )
98+ }
8499
85- policy := fakeWitnessGroup {
86- endpoints : endpoints ,
87- }
100+ policy := fakeWitnessGroup {
101+ endpoints : endpoints ,
102+ }
88103
89- g := gateway .NewGateway (t .Context (), http .DefaultClient , policy , testLog .LogReader , "test" )
104+ g := gateway .NewGateway (t .Context (), http .DefaultClient , policy , testLog .LogReader , "test" )
90105
91- // Call CosignCheckpoint and gather signatures.
92- sigCh := g .CosignCheckpoint (t .Context (), goalCP , size )
93- var cosigs []byte
94- for sig := range sigCh {
95- cosigs = append (cosigs , sig ... )
96- }
106+ // Call CosignCheckpoint and gather signatures.
107+ sigCh := g .CosignCheckpoint (t .Context (), goalCP , size )
108+ var cosigs []byte
109+ for sig := range sigCh {
110+ cosigs = append (cosigs , sig ... )
111+ }
97112
98- // Verify cosignatures.
99- fullCP := append (slices .Clone (goalCP ), cosigs ... )
100- cp , _ , n , err := log .ParseCheckpoint (fullCP , testLog .SigVerifier .Name (), testLog .SigVerifier , verifiers ... )
101- if err != nil {
102- t .Fatalf ("failed to verify cosigned checkpoint: %v" , err )
103- }
104- if got , want := len (n .Sigs ), 1 + numMirrors ; got != want {
105- t .Errorf ("note signatures: got %d, want %d" , got , want )
106- }
107- if got , want := uint64 (cp .Size ), uint64 (size ); got != want {
108- t .Errorf ("checkpoint size: got %d, want %d" , got , want )
113+ // Verify cosignatures.
114+ fullCP := append (slices .Clone (goalCP ), cosigs ... )
115+ cp , _ , n , err := log .ParseCheckpoint (fullCP , testLog .SigVerifier .Name (), testLog .SigVerifier , verifiers ... )
116+ if err != nil {
117+ t .Fatalf ("failed to verify cosigned checkpoint: %v" , err )
118+ }
119+ if got , want := len (n .Sigs ), 1 + tc .numMirrors ; got != want {
120+ t .Errorf ("note signatures: got %d, want %d" , got , want )
121+ }
122+ if got , want := uint64 (cp .Size ), uint64 (size ); got != want {
123+ t .Errorf ("checkpoint size: got %d, want %d" , got , want )
124+ }
125+ })
109126 }
110127}
111128
@@ -126,28 +143,37 @@ func mustNewKeypair(t *testing.T, name string) (f_note.Signer, note.Verifier) {
126143 return s , v
127144}
128145
129- func startMockMirror (t * testing.T , signer note.Signer , logVerifier note.Verifier ) * httptest.Server {
146+ func startMockMirror (t * testing.T , signer note.Signer , logVerifier note.Verifier , failCount int ) * httptest.Server {
130147 t .Helper ()
131148 var mu sync.Mutex
132149 var pendingCP []byte
150+ attempts := 0
133151
134152 server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
153+ mu .Lock ()
154+ defer mu .Unlock ()
155+
156+ if strings .HasSuffix (r .URL .Path , "/add-checkpoint" ) || strings .HasSuffix (r .URL .Path , "/add-entries" ) {
157+ if attempts < failCount {
158+ attempts ++
159+ w .WriteHeader (http .StatusInternalServerError )
160+ _ , _ = w .Write ([]byte ("mock error" ))
161+ return
162+ }
163+ }
164+
135165 if strings .HasSuffix (r .URL .Path , "/add-checkpoint" ) {
136166 body , _ := io .ReadAll (r .Body )
137167 parts := bytes .SplitN (body , []byte ("\n \n " ), 2 )
138168 if len (parts ) == 2 {
139- mu .Lock ()
140169 pendingCP = parts [1 ]
141- mu .Unlock ()
142170 }
143171 w .WriteHeader (http .StatusOK )
144172 return
145173 }
146174 if strings .HasSuffix (r .URL .Path , "/add-entries" ) {
147175 _ , _ = io .Copy (io .Discard , r .Body )
148- mu .Lock ()
149176 cp := pendingCP
150- mu .Unlock ()
151177
152178 if len (cp ) == 0 {
153179 w .WriteHeader (http .StatusBadRequest )
0 commit comments