New tests to ensure that the correct error is returned when the server signature is invalid.
182 lines
4.8 KiB
Go
182 lines
4.8 KiB
Go
package client
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ed25519"
|
|
"encoding/base64"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/decred/slog"
|
|
"github.com/decred/vspd/types"
|
|
)
|
|
|
|
// TestErrorDetails ensures errors returned by client.do contain adequate
|
|
// information for debugging (HTTP status and response body).
|
|
func TestErrorDetails(t *testing.T) {
|
|
|
|
tests := map[string]struct {
|
|
respHTTPStatus int
|
|
respBodyBytes []byte
|
|
expectedErr string
|
|
vspdError bool
|
|
vspdErrCode int64
|
|
}{
|
|
"500, vspd error (generic bad request)": {
|
|
respHTTPStatus: 500,
|
|
respBodyBytes: []byte(`{"code": 1, "message": "bad request"}`),
|
|
expectedErr: `bad request`,
|
|
vspdError: true,
|
|
vspdErrCode: 1,
|
|
},
|
|
"428, vspd error (cannot broadcast fee)": {
|
|
respHTTPStatus: 428,
|
|
respBodyBytes: []byte(`{"code": 16, "message": "fee transaction could not be broadcast due to unknown outputs"}`),
|
|
expectedErr: `fee transaction could not be broadcast due to unknown outputs`,
|
|
vspdError: true,
|
|
vspdErrCode: 16,
|
|
},
|
|
"500, no body": {
|
|
respHTTPStatus: 500,
|
|
respBodyBytes: nil,
|
|
expectedErr: `http status 500 (Internal Server Error) with no body`,
|
|
vspdError: false,
|
|
},
|
|
"500, non vspd error": {
|
|
respHTTPStatus: 500,
|
|
respBodyBytes: []byte(`an error occurred`),
|
|
expectedErr: `http status 500 (Internal Server Error) with body "an error occurred"`,
|
|
vspdError: false,
|
|
},
|
|
"500, non vspd error (json)": {
|
|
respHTTPStatus: 500,
|
|
respBodyBytes: []byte(`{"some": "json"}`),
|
|
expectedErr: `http status 500 (Internal Server Error) with body "{\"some\": \"json\"}"`,
|
|
vspdError: false,
|
|
},
|
|
}
|
|
|
|
for testName, testData := range tests {
|
|
t.Run(testName, func(t *testing.T) {
|
|
|
|
testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
res.WriteHeader(testData.respHTTPStatus)
|
|
_, err := res.Write(testData.respBodyBytes)
|
|
if err != nil {
|
|
t.Fatalf("writing response body failed: %v", err)
|
|
}
|
|
}))
|
|
|
|
client := Client{
|
|
URL: testServer.URL,
|
|
PubKey: []byte("fake pubkey"),
|
|
Log: slog.Disabled,
|
|
}
|
|
|
|
var resp interface{}
|
|
err := client.do(context.TODO(), http.MethodGet, "", nil, &resp, nil)
|
|
|
|
testServer.Close()
|
|
|
|
if err == nil {
|
|
t.Fatalf("client.do did not return an error")
|
|
}
|
|
|
|
if err.Error() != testData.expectedErr {
|
|
t.Fatalf("client.do returned incorrect error, expected %q, got %q",
|
|
testData.expectedErr, err.Error())
|
|
}
|
|
|
|
if testData.vspdError {
|
|
// Error should be unwrappable as a vspd error response.
|
|
var e types.ErrorResponse
|
|
if !errors.As(err, &e) {
|
|
t.Fatal("unable to unwrap vspd error")
|
|
}
|
|
|
|
if e.Code != testData.vspdErrCode {
|
|
t.Fatalf("incorrect vspd error code, expected %d, got %d",
|
|
testData.vspdErrCode, e.Code)
|
|
}
|
|
}
|
|
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestSignatureValidation ensures that responses with invalid signatures are
|
|
// flagged.
|
|
func TestSignatureValidation(t *testing.T) {
|
|
|
|
// Generate some test data for the valid signature case.
|
|
privKey := ed25519.NewKeyFromSeed([]byte("00000000000000000000000000000000"))
|
|
pubKey, _ := privKey.Public().(ed25519.PublicKey)
|
|
emptyJSON := []byte("{}")
|
|
validSig := base64.StdEncoding.EncodeToString(ed25519.Sign(privKey, emptyJSON))
|
|
|
|
tests := map[string]struct {
|
|
responseSig string
|
|
expectErr bool
|
|
expectErrStr string
|
|
}{
|
|
"valid signature": {
|
|
responseSig: validSig,
|
|
expectErr: false,
|
|
},
|
|
"invalid signature": {
|
|
responseSig: "1234",
|
|
expectErr: true,
|
|
expectErrStr: "authenticate server response: invalid signature",
|
|
},
|
|
"no signature": {
|
|
responseSig: "",
|
|
expectErr: true,
|
|
expectErrStr: "authenticate server response: no signature provided",
|
|
},
|
|
"failed to decode signature": {
|
|
responseSig: "0xp",
|
|
expectErr: true,
|
|
expectErrStr: "authenticate server response: failed to decode signature: illegal base64 data at input byte 0",
|
|
},
|
|
}
|
|
|
|
for testName, testData := range tests {
|
|
t.Run(testName, func(t *testing.T) {
|
|
|
|
testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
res.Header().Add("VSP-Server-Signature", testData.responseSig)
|
|
res.WriteHeader(http.StatusOK)
|
|
_, err := res.Write(emptyJSON)
|
|
if err != nil {
|
|
t.Fatalf("writing response body failed: %v", err)
|
|
}
|
|
}))
|
|
|
|
client := Client{
|
|
URL: testServer.URL,
|
|
PubKey: pubKey,
|
|
Log: slog.Disabled,
|
|
}
|
|
|
|
var resp interface{}
|
|
err := client.do(context.TODO(), http.MethodGet, "", nil, &resp, nil)
|
|
|
|
testServer.Close()
|
|
|
|
if testData.expectErr {
|
|
if err.Error() != testData.expectErrStr {
|
|
t.Fatalf("client.do returned incorrect error, expected %q, got %q",
|
|
testData.expectErrStr, err.Error())
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Fatalf("client.do returned unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
})
|
|
}
|
|
}
|