Skip to content

Commit e9c9b0f

Browse files
committed
Add retries test
1 parent 2036d85 commit e9c9b0f

1 file changed

Lines changed: 86 additions & 60 deletions

File tree

internal/mirror/gateway/mirror_gateway_test.go

Lines changed: 86 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -44,68 +44,85 @@ func (f fakeWitnessGroup) WitnessEndpoints() map[string][]note.Verifier {
4444
}
4545

4646
func 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

Comments
 (0)