TestServerPostWallet
This commit is contained in:
parent
d38fdf0d11
commit
db1c55dff0
3 changed files with 227 additions and 11 deletions
|
@ -27,13 +27,19 @@ func (a *TestAuth) NewToken(userId auth.UserId, deviceId auth.DeviceId, scope au
|
||||||
return &auth.AuthToken{Token: a.TestNewTokenString, UserId: userId, DeviceId: deviceId, Scope: scope}, nil
|
return &auth.AuthToken{Token: a.TestNewTokenString, UserId: userId, DeviceId: deviceId, Scope: scope}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SetWalletCall struct {
|
||||||
|
EncryptedWallet wallet.EncryptedWallet
|
||||||
|
Sequence wallet.Sequence
|
||||||
|
Hmac wallet.WalletHmac
|
||||||
|
}
|
||||||
|
|
||||||
// Whether functions are called, and sometimes what they're called with
|
// Whether functions are called, and sometimes what they're called with
|
||||||
type TestStoreFunctionsCalled struct {
|
type TestStoreFunctionsCalled struct {
|
||||||
SaveToken auth.TokenString
|
SaveToken auth.TokenString
|
||||||
GetToken auth.TokenString
|
GetToken auth.TokenString
|
||||||
GetUserId bool
|
GetUserId bool
|
||||||
CreateAccount bool
|
CreateAccount bool
|
||||||
SetWallet wallet.EncryptedWallet
|
SetWallet SetWalletCall
|
||||||
GetWallet bool
|
GetWallet bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +65,7 @@ type TestStore struct {
|
||||||
TestEncryptedWallet wallet.EncryptedWallet
|
TestEncryptedWallet wallet.EncryptedWallet
|
||||||
TestSequence wallet.Sequence
|
TestSequence wallet.Sequence
|
||||||
TestHmac wallet.WalletHmac
|
TestHmac wallet.WalletHmac
|
||||||
|
TestSequenceCorrect bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestStore) SaveToken(authToken *auth.AuthToken) error {
|
func (s *TestStore) SaveToken(authToken *auth.AuthToken) error {
|
||||||
|
@ -87,8 +94,14 @@ func (s *TestStore) SetWallet(
|
||||||
sequence wallet.Sequence,
|
sequence wallet.Sequence,
|
||||||
hmac wallet.WalletHmac,
|
hmac wallet.WalletHmac,
|
||||||
) (latestEncryptedWallet wallet.EncryptedWallet, latestSequence wallet.Sequence, latestHmac wallet.WalletHmac, sequenceCorrect bool, err error) {
|
) (latestEncryptedWallet wallet.EncryptedWallet, latestSequence wallet.Sequence, latestHmac wallet.WalletHmac, sequenceCorrect bool, err error) {
|
||||||
s.Called.SetWallet = encryptedWallet
|
s.Called.SetWallet = SetWalletCall{encryptedWallet, sequence, hmac}
|
||||||
err = s.Errors.SetWallet
|
err = s.Errors.SetWallet
|
||||||
|
if err == nil {
|
||||||
|
latestEncryptedWallet = s.TestEncryptedWallet
|
||||||
|
latestSequence = s.TestSequence
|
||||||
|
latestHmac = s.TestHmac
|
||||||
|
sequenceCorrect = s.TestSequenceCorrect
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ func (s *Server) postWallet(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
// Something other than sequence error
|
// Something other than sequence error
|
||||||
internalServiceErrorJson(w, err, "Error saving wallet")
|
internalServiceErrorJson(w, err, "Error saving or getting wallet")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ func (s *Server) postWallet(w http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
if !sequenceCorrect {
|
if !sequenceCorrect {
|
||||||
// TODO - should we even call this an error?
|
// TODO - should we even call this an error?
|
||||||
walletResponse.Error = "Bad sequence number"
|
walletResponse.Error = http.StatusText(http.StatusConflict) + ": " + "Bad sequence number"
|
||||||
}
|
}
|
||||||
response, err = json.Marshal(walletResponse)
|
response, err = json.Marshal(walletResponse)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -74,16 +75,15 @@ func TestServerGetWallet(t *testing.T) {
|
||||||
|
|
||||||
// Make sure we tried to get an auth based on the `token` param (whether or
|
// Make sure we tried to get an auth based on the `token` param (whether or
|
||||||
// not it was a valid `token`)
|
// not it was a valid `token`)
|
||||||
if testStore.Called.GetToken != testStore.TestAuthToken.Token {
|
if want, got := testStore.TestAuthToken.Token, testStore.Called.GetToken; want != got {
|
||||||
t.Errorf("Expected Store.GetToken to be called with %s. Got %s",
|
t.Errorf("testStore.Called.GetToken called with: expected %s, got %s", want, got)
|
||||||
testStore.TestAuthToken.Token,
|
|
||||||
testStore.Called.GetToken)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expectStatusCode(t, w, tc.expectedStatusCode)
|
expectStatusCode(t, w, tc.expectedStatusCode)
|
||||||
|
|
||||||
if len(tc.expectedErrorString) > 0 {
|
if len(tc.expectedErrorString) > 0 {
|
||||||
// Only check if we're expecting an error, since it reads the body
|
// Only do this check if we're expecting an error and only an error,
|
||||||
|
// since it reads the body and would break the ioutil.ReadAll below.
|
||||||
expectErrorString(t, w, tc.expectedErrorString)
|
expectErrorString(t, w, tc.expectedErrorString)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,211 @@ func TestServerGetWallet(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerPostWalletTooLate(t *testing.T) {
|
func TestServerPostWallet(t *testing.T) {
|
||||||
t.Fatalf("Test me: PostWallet fails for sequence being too low, returns the latest wallet")
|
tt := []struct {
|
||||||
|
name string
|
||||||
|
|
||||||
|
expectedStatusCode int
|
||||||
|
expectedErrorString string
|
||||||
|
|
||||||
|
// There is one case where we expect both the error field and the normal
|
||||||
|
// body fields. So, this needs to be separate.
|
||||||
|
expectWalletBody bool
|
||||||
|
|
||||||
|
// `new...` refers to what is being passed into the via POST request (and
|
||||||
|
// what gets passed into SetWallet for the *non-error* cases below)
|
||||||
|
// `returned...` refers to what the SetWallet function returns (and what
|
||||||
|
// gets returned in the request response for the *non-error* cases below)
|
||||||
|
newEncryptedWallet wallet.EncryptedWallet
|
||||||
|
returnedEncryptedWallet wallet.EncryptedWallet
|
||||||
|
newSequence wallet.Sequence
|
||||||
|
returnedSequence wallet.Sequence
|
||||||
|
newHmac wallet.WalletHmac
|
||||||
|
returnedHmac wallet.WalletHmac
|
||||||
|
|
||||||
|
// Passed-in sequence was correct. No conflict.
|
||||||
|
sequenceCorrect bool
|
||||||
|
|
||||||
|
storeErrors TestStoreFunctionsErrors
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "success",
|
||||||
|
expectedStatusCode: http.StatusOK,
|
||||||
|
expectWalletBody: true,
|
||||||
|
|
||||||
|
// Simulates a situation where the existing sequence is 1, the new
|
||||||
|
// sequence is 2. We don't see the existing data in this case because it
|
||||||
|
// successfully updates to and returns the new data. New and returned are
|
||||||
|
// the same here.
|
||||||
|
|
||||||
|
sequenceCorrect: true,
|
||||||
|
newEncryptedWallet: wallet.EncryptedWallet("my-encrypted-wallet"),
|
||||||
|
returnedEncryptedWallet: wallet.EncryptedWallet("my-encrypted-wallet"),
|
||||||
|
newSequence: wallet.Sequence(2),
|
||||||
|
returnedSequence: wallet.Sequence(2),
|
||||||
|
newHmac: wallet.WalletHmac("my-hmac"),
|
||||||
|
returnedHmac: wallet.WalletHmac("my-hmac"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "conflict",
|
||||||
|
expectedStatusCode: http.StatusConflict,
|
||||||
|
// In the special case of "conflict" where there *is* a stored wallet, we
|
||||||
|
// process the function in a normal way, but we still have the Error field.
|
||||||
|
// So, we can't rely on `tc.expectedErrorString == ""` to indicate that it
|
||||||
|
// is the type of error without a body. So, we need to specify this
|
||||||
|
// separately. In this case we will check the error string along with the
|
||||||
|
// body.
|
||||||
|
expectWalletBody: true,
|
||||||
|
|
||||||
|
// Simulates a situation where the existing sequence is 3, the new sequence
|
||||||
|
// is 2. This is a conflict, because the new sequence should be 4. A new
|
||||||
|
// sequence of 3 would also be a conflict, but we want two different
|
||||||
|
// sequence numbers for a clear test. We return the existing data in this
|
||||||
|
// case for the client to merge in. Note that we're passing in a sequence
|
||||||
|
// that makes sense for a conflict case, the actual behavior is triggered by
|
||||||
|
// sequenceCorrect=false
|
||||||
|
|
||||||
|
expectedErrorString: http.StatusText(http.StatusConflict) + ": Bad sequence number",
|
||||||
|
|
||||||
|
sequenceCorrect: false,
|
||||||
|
newEncryptedWallet: wallet.EncryptedWallet("my-encrypted-wallet-new"),
|
||||||
|
returnedEncryptedWallet: wallet.EncryptedWallet("my-encrypted-wallet-existing"),
|
||||||
|
newSequence: wallet.Sequence(2),
|
||||||
|
returnedSequence: wallet.Sequence(3),
|
||||||
|
newHmac: wallet.WalletHmac("my-hmac-new"),
|
||||||
|
returnedHmac: wallet.WalletHmac("my-hmac-existing"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "conflict with no stored wallet",
|
||||||
|
expectedStatusCode: http.StatusConflict,
|
||||||
|
|
||||||
|
// Simulates a situation where there is no stored wallet. In such a case the
|
||||||
|
// correct sequence would be 1, which implies the wallet should be inserted
|
||||||
|
// (as opposed to updated). We will be passing in a sequence of 2 for
|
||||||
|
// clarity, but what will actually trigger the desired error we are testing
|
||||||
|
// for is SetWallet returning ErrNoWallet, which is what the store is
|
||||||
|
// expected to return in this situation.
|
||||||
|
|
||||||
|
expectedErrorString: http.StatusText(http.StatusConflict) + ": Bad sequence number (No existing wallet)",
|
||||||
|
|
||||||
|
// In this case the "returned" values are blank because there will be
|
||||||
|
// nothing to return
|
||||||
|
sequenceCorrect: false,
|
||||||
|
newEncryptedWallet: wallet.EncryptedWallet("my-encrypted-wallet"),
|
||||||
|
returnedEncryptedWallet: wallet.EncryptedWallet(""),
|
||||||
|
newSequence: wallet.Sequence(2),
|
||||||
|
returnedSequence: wallet.Sequence(0),
|
||||||
|
newHmac: wallet.WalletHmac("my-hmac"),
|
||||||
|
returnedHmac: wallet.WalletHmac(""),
|
||||||
|
|
||||||
|
storeErrors: TestStoreFunctionsErrors{SetWallet: store.ErrNoWallet},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "auth error",
|
||||||
|
expectedStatusCode: http.StatusUnauthorized,
|
||||||
|
expectedErrorString: http.StatusText(http.StatusUnauthorized) + ": Token Not Found",
|
||||||
|
|
||||||
|
// Putting in valid data here so it's clear that this isn't what causes
|
||||||
|
// the error
|
||||||
|
sequenceCorrect: true,
|
||||||
|
newEncryptedWallet: wallet.EncryptedWallet("my-encrypted-wallet"),
|
||||||
|
returnedEncryptedWallet: wallet.EncryptedWallet("my-encrypted-wallet"),
|
||||||
|
newSequence: wallet.Sequence(2),
|
||||||
|
returnedSequence: wallet.Sequence(2),
|
||||||
|
newHmac: wallet.WalletHmac("my-hmac"),
|
||||||
|
returnedHmac: wallet.WalletHmac("my-hmac"),
|
||||||
|
|
||||||
|
// What causes the error
|
||||||
|
storeErrors: TestStoreFunctionsErrors{GetToken: store.ErrNoToken},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "db error setting or getting wallet",
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
expectedErrorString: http.StatusText(http.StatusInternalServerError),
|
||||||
|
|
||||||
|
// Putting in valid data here so it's clear that this isn't what causes
|
||||||
|
// the error
|
||||||
|
sequenceCorrect: true,
|
||||||
|
newEncryptedWallet: wallet.EncryptedWallet("my-encrypted-wallet"),
|
||||||
|
returnedEncryptedWallet: wallet.EncryptedWallet("my-encrypted-wallet"),
|
||||||
|
newSequence: wallet.Sequence(2),
|
||||||
|
returnedSequence: wallet.Sequence(2),
|
||||||
|
newHmac: wallet.WalletHmac("my-hmac"),
|
||||||
|
returnedHmac: wallet.WalletHmac("my-hmac"),
|
||||||
|
|
||||||
|
// What causes the error
|
||||||
|
storeErrors: TestStoreFunctionsErrors{SetWallet: fmt.Errorf("Some random db problem")},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tt {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
|
||||||
|
testAuth := TestAuth{}
|
||||||
|
testStore := TestStore{
|
||||||
|
TestAuthToken: auth.AuthToken{
|
||||||
|
Token: auth.TokenString("seekrit"),
|
||||||
|
Scope: auth.ScopeFull,
|
||||||
|
},
|
||||||
|
|
||||||
|
TestEncryptedWallet: tc.returnedEncryptedWallet,
|
||||||
|
TestSequence: tc.returnedSequence,
|
||||||
|
TestHmac: tc.returnedHmac,
|
||||||
|
TestSequenceCorrect: tc.sequenceCorrect,
|
||||||
|
|
||||||
|
Errors: tc.storeErrors,
|
||||||
|
}
|
||||||
|
|
||||||
|
s := Server{&testAuth, &testStore}
|
||||||
|
|
||||||
|
requestBody := []byte(
|
||||||
|
fmt.Sprintf(`{
|
||||||
|
"token": "%s",
|
||||||
|
"encryptedWallet": "%s",
|
||||||
|
"sequence": %d,
|
||||||
|
"hmac": "%s"
|
||||||
|
}`, testStore.TestAuthToken.Token, tc.newEncryptedWallet, tc.newSequence, tc.newHmac),
|
||||||
|
)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, PathWallet, bytes.NewBuffer(requestBody))
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// test handleWallet while we're at it, which is a dispatch for get and post
|
||||||
|
// wallet
|
||||||
|
s.handleWallet(w, req)
|
||||||
|
|
||||||
|
// Make sure we tried to get an auth based on the `token` param (whether or
|
||||||
|
// not it was a valid `token`)
|
||||||
|
if want, got := testStore.TestAuthToken.Token, testStore.Called.GetToken; want != got {
|
||||||
|
t.Errorf("testStore.Called.GetToken called with: expected %s, got %s", want, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectStatusCode(t, w, tc.expectedStatusCode)
|
||||||
|
|
||||||
|
if !tc.expectWalletBody {
|
||||||
|
// Only do this check if we're expecting an error and only an error,
|
||||||
|
// since it reads the body and would break the ioutil.ReadAll below.
|
||||||
|
expectErrorString(t, w, tc.expectedErrorString)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body, _ := ioutil.ReadAll(w.Body)
|
||||||
|
var result WalletResponse
|
||||||
|
err := json.Unmarshal(body, &result)
|
||||||
|
|
||||||
|
if err != nil ||
|
||||||
|
result.EncryptedWallet != tc.returnedEncryptedWallet ||
|
||||||
|
result.Hmac != tc.returnedHmac ||
|
||||||
|
result.Sequence != tc.returnedSequence ||
|
||||||
|
result.Error != tc.expectedErrorString {
|
||||||
|
|
||||||
|
t.Errorf("Expected wallet response to have the test wallet values and error string: result: %+v err: %+v", string(body), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if want, got := (SetWalletCall{tc.newEncryptedWallet, tc.newSequence, tc.newHmac}), testStore.Called.SetWallet; want != got {
|
||||||
|
t.Errorf("Store.SetWallet called with: expected %+v, got %+v", want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerPostWalletErrors(t *testing.T) {
|
func TestServerPostWalletErrors(t *testing.T) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue