You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DateKeys/provider/drand/client_test.go

123 lines
4.4 KiB

package drand_test
import (
"context"
"encoding/hex"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
datekeys "g.activething.com/go/DateKeys"
"g.activething.com/go/DateKeys/internal/testkit"
"g.activething.com/go/DateKeys/profile"
"g.activething.com/go/DateKeys/provider"
"g.activething.com/go/DateKeys/provider/drand"
)
var sig1000 = hex.EncodeToString(testkit.Release(1000).Signature)
func serve(t *testing.T, h http.HandlerFunc) string {
t.Helper()
s := httptest.NewServer(h)
t.Cleanup(s.Close)
return s.URL
}
func TestRaceWaitsForAValidSignature(t *testing.T) {
p := profile.Quicknet()
bad := serve(t, func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{"round":1000,"signature":"%s"}`, strings.Repeat("0", 96))
})
good := serve(t, func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/v2/chains/"+p.ChainHashHex()+"/rounds/1000" {
t.Errorf("request not pinned to the chain: %s", r.URL.Path)
}
time.Sleep(25 * time.Millisecond)
fmt.Fprintf(w, `{"round":1000,"signature":"%s"}`, sig1000)
})
c := drand.NewWithHTTPClient(http.DefaultClient, bad, good)
rel, err := c.Fetch(context.Background(), p, provider.Condition{Round: 1000})
if err != nil || hex.EncodeToString(rel.Signature) != sig1000 || rel.Round != 1000 {
t.Fatalf("race failed: %+v %v", rel, err)
}
}
func TestRejectMalformedRelayResponses(t *testing.T) {
p := profile.Quicknet()
for _, payload := range []string{
`{"round":999,"signature":"` + sig1000 + `"}`,
`{"round":1000,"signature":"` + sig1000 + `","randomness":"fake"}`,
`{"round":1000,"signature":"fake"}`,
`{"round":1000,"signature":"` + sig1000 + `"} {}`,
`{"round":1000,"signature":"` + hex.EncodeToString(testkit.Release(1001).Signature) + `"}`,
strings.Repeat("x", 9000),
``,
} {
url := serve(t, func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, payload) })
c := drand.NewWithHTTPClient(http.DefaultClient, url)
_, err := c.Fetch(context.Background(), p, provider.Condition{Round: 1000})
if !errors.Is(err, datekeys.ErrReleaseUnavailable) {
t.Errorf("accepted or misclassified %.40q: %v", payload, err)
}
}
}
func TestRandomnessMustMatchWhenPresent(t *testing.T) {
p := profile.Quicknet()
sum := "e1f1cb5a9ddd0e2ae4a4a8c5bf42e8e1e1ed8b5a9f1f7da1a3f1d2bb0f1b2c3d"
url := serve(t, func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{"round":1000,"signature":"%s","randomness":"%s"}`, sig1000, sum)
})
_, err := drand.NewWithHTTPClient(http.DefaultClient, url).Fetch(context.Background(), p, provider.Condition{Round: 1000})
if !errors.Is(err, datekeys.ErrReleaseInvalid) {
t.Fatalf("wrong randomness accepted: %v", err)
}
}
func TestUnavailabilityAndCancellation(t *testing.T) {
p := profile.Quicknet()
for _, status := range []int{404, 425, 503} {
url := serve(t, func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(status) })
_, err := drand.NewWithHTTPClient(http.DefaultClient, url).Fetch(context.Background(), p, provider.Condition{Round: 1000})
if !errors.Is(err, datekeys.ErrReleaseUnavailable) {
t.Fatalf("HTTP %d: %v", status, err)
}
}
url := serve(t, func(w http.ResponseWriter, r *http.Request) { time.Sleep(time.Second) })
ctx, cancel := context.WithCancel(context.Background())
cancel()
if _, err := drand.NewWithHTTPClient(http.DefaultClient, url).Fetch(ctx, p, provider.Condition{Round: 1000}); !errors.Is(err, datekeys.ErrReleaseUnavailable) {
t.Fatalf("ignored cancellation: %v", err)
}
}
func TestRedirectsAreNotFollowed(t *testing.T) {
p := profile.Quicknet()
target := serve(t, func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{"round":1000,"signature":"%s"}`, sig1000)
})
redirect := serve(t, func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, target+r.URL.Path, http.StatusFound)
})
if _, err := drand.New(redirect).Fetch(context.Background(), p, provider.Condition{Round: 1000}); err == nil {
t.Fatal("followed a redirect")
}
}
func TestInvalidRequests(t *testing.T) {
c := drand.New("http://127.0.0.1:1")
p := profile.Quicknet()
if _, err := c.Fetch(context.Background(), p, provider.Condition{Round: 0}); !errors.Is(err, datekeys.ErrDateKeyInvalid) {
t.Fatalf("round 0: %v", err)
}
other := profile.Quicknet()
other.Provider = "other"
if _, err := c.Fetch(context.Background(), other, provider.Condition{Round: 1}); !errors.Is(err, datekeys.ErrUnknownProfile) {
t.Fatalf("non-drand profile: %v", err)
}
}

Powered by TurnKey Linux.