diff --git a/internal/api/v1/admin/users/userstest/xpub_api_fixtures.go b/internal/api/v1/admin/users/userstest/xpub_api_fixtures.go index 37f7cdc..af20319 100644 --- a/internal/api/v1/admin/users/userstest/xpub_api_fixtures.go +++ b/internal/api/v1/admin/users/userstest/xpub_api_fixtures.go @@ -18,6 +18,14 @@ func NewBadRequestSPVError() models.SPVError { } } +func NewInternalServerSPVError() models.SPVError { + return models.SPVError{ + Message: http.StatusText(http.StatusInternalServerError), + StatusCode: http.StatusInternalServerError, + Code: models.UnknownErrorCode, + } +} + func ExpectedXPub(t *testing.T) *response.Xpub { return &response.Xpub{ Model: response.Model{ diff --git a/internal/api/v1/admin/users/xpubs_api_test.go b/internal/api/v1/admin/users/xpubs_api_test.go index a74c1da..a92e607 100644 --- a/internal/api/v1/admin/users/xpubs_api_test.go +++ b/internal/api/v1/admin/users/xpubs_api_test.go @@ -38,17 +38,19 @@ func TestXPubsAPI_CreateXPub(t *testing.T) { URL := spvwallettest.TestAPIAddr + "/api/v1/admin/users" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVAdminAPI(t) transport.RegisterResponder(http.MethodPost, URL, tc.responder) - // then: + // when: got, err := wallet.CreateXPub(context.Background(), &commands.CreateUserXpub{ Metadata: map[string]any{}, XPub: "", }) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } @@ -68,22 +70,24 @@ func TestXPubsAPI_XPubs(t *testing.T) { responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), }, "HTTP GET /api/v1/admin/users str response: 500": { - expectedErr: errors.ErrUnrecognizedAPIResponse, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: userstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, userstest.NewInternalServerSPVError()), }, } URL := spvwallettest.TestAPIAddr + "/api/v1/admin/users" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVAdminAPI(t) transport.RegisterResponder(http.MethodGet, URL, tc.responder) - // then: + // when: got, err := wallet.XPubs(context.Background()) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } diff --git a/internal/api/v1/querybuilders/querybuilderstest/querybuilderstest.go b/internal/api/v1/querybuilders/querybuilderstest/querybuilderstest.go index d7599b0..c24928e 100644 --- a/internal/api/v1/querybuilders/querybuilderstest/querybuilderstest.go +++ b/internal/api/v1/querybuilders/querybuilderstest/querybuilderstest.go @@ -1,11 +1,8 @@ package querybuilderstest import ( - "net/http" "testing" "time" - - "github.com/bitcoin-sv/spv-wallet/models" ) func ParseTime(t *testing.T, s string) time.Time { @@ -19,11 +16,3 @@ func ParseTime(t *testing.T, s string) time.Time { func Ptr[T any](value T) *T { return &value } - -func NewBadRequestSPVError() *models.SPVError { - return &models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - } -} diff --git a/internal/api/v1/user/configs/configs_api_test.go b/internal/api/v1/user/configs/configs_api_test.go index 8ba1aa3..35b4a6e 100644 --- a/internal/api/v1/user/configs/configs_api_test.go +++ b/internal/api/v1/user/configs/configs_api_test.go @@ -5,9 +5,8 @@ import ( "net/http" "testing" - "github.com/bitcoin-sv/spv-wallet-go-client/errors" + "github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/user/configs/configstest" "github.com/bitcoin-sv/spv-wallet-go-client/internal/spvwallettest" - "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/response" "github.com/jarcoal/httpmock" "github.com/stretchr/testify/require" @@ -15,7 +14,6 @@ import ( func TestConfigsAPI_SharedConfig_APIResponses(t *testing.T) { tests := map[string]struct { - statusCode int expectedResponse *response.SharedConfig expectedErr error responder httpmock.Responder @@ -28,38 +26,29 @@ func TestConfigsAPI_SharedConfig_APIResponses(t *testing.T) { "pikePaymentEnabled": true, }, }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("configstest/response_200_status_code.json")), + responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("configstest/response_200_status_code.json")), }, "HTTP GET /api/v1/configs/shared response: 400": { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, &models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }), + expectedErr: configstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, configstest.NewBadRequestSPVError()), }, "HTTP GET /api/v1/configs/shared str response: 500": { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: configstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, configstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/configs/shared" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodGet, url, tc.responder) - // then: + // when: got, err := wallet.SharedConfig(context.Background()) + + // then: require.ErrorIs(t, err, tc.expectedErr) require.Equal(t, tc.expectedResponse, got) }) diff --git a/internal/api/v1/user/configs/configstest/configs_api_fixtures.go b/internal/api/v1/user/configs/configstest/configs_api_fixtures.go new file mode 100644 index 0000000..09fb684 --- /dev/null +++ b/internal/api/v1/user/configs/configstest/configs_api_fixtures.go @@ -0,0 +1,23 @@ +package configstest + +import ( + "net/http" + + "github.com/bitcoin-sv/spv-wallet/models" +) + +func NewBadRequestSPVError() models.SPVError { + return models.SPVError{ + Message: http.StatusText(http.StatusBadRequest), + StatusCode: http.StatusBadRequest, + Code: "invalid-data-format", + } +} + +func NewInternalServerSPVError() models.SPVError { + return models.SPVError{ + Message: http.StatusText(http.StatusInternalServerError), + StatusCode: http.StatusInternalServerError, + Code: models.UnknownErrorCode, + } +} diff --git a/internal/api/v1/user/contacts/contacts_api_test.go b/internal/api/v1/user/contacts/contacts_api_test.go index bd1d53f..e713753 100644 --- a/internal/api/v1/user/contacts/contacts_api_test.go +++ b/internal/api/v1/user/contacts/contacts_api_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/bitcoin-sv/spv-wallet-go-client/commands" - "github.com/bitcoin-sv/spv-wallet-go-client/errors" "github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/user/contacts/contactstest" "github.com/bitcoin-sv/spv-wallet-go-client/internal/spvwallettest" "github.com/bitcoin-sv/spv-wallet-go-client/queries" @@ -20,42 +19,36 @@ import ( func TestContactsAPI_Contacts(t *testing.T) { tests := map[string]struct { responder httpmock.Responder - statusCode int expectedResponse *queries.UserContactsPage expectedErr error }{ "HTTP GET /api/v1/contacts response: 200": { - statusCode: http.StatusOK, expectedResponse: contactstest.ExpectedUserContactsPage(t), responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("contactstest/get_contacts_200.json")), }, "HTTP GET /api/v1/contacts response: 400": { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, contactstest.NewBadRequestSPVError()), + expectedErr: contactstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, contactstest.NewBadRequestSPVError()), }, "HTTP GET /api/v1/contacts str response: 500": { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: contactstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, contactstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/contacts" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodGet, url, tc.responder) - // then: + // when: got, err := wallet.Contacts(context.Background()) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } @@ -64,42 +57,36 @@ func TestContactsAPI_ContactWithPaymail(t *testing.T) { paymail := "john.doe.test5@john.doe.test.4chain.space" tests := map[string]struct { responder httpmock.Responder - statusCode int expectedResponse *response.Contact expectedErr error }{ fmt.Sprintf("HTTP GET /api/v1/contacts/%s response: 200", paymail): { - statusCode: http.StatusOK, expectedResponse: contactstest.ExpectedContactWithWithPaymail(t), responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("contactstest/get_contact_paymail_200.json")), }, fmt.Sprintf("HTTP GET /api/v1/contacts/%s response: 400", paymail): { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, contactstest.NewBadRequestSPVError()), + expectedErr: contactstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, contactstest.NewBadRequestSPVError()), }, fmt.Sprintf("HTTP GET /api/v1/contacts/%s str response: 500", paymail): { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: contactstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, contactstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/contacts/" + paymail for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodGet, url, tc.responder) - // then: + // when: got, err := wallet.ContactWithPaymail(context.Background(), paymail) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } @@ -108,46 +95,40 @@ func TestContactsAPI_UpsertContact(t *testing.T) { paymail := "john.doe.test@john.doe.test.4chain.space" tests := map[string]struct { responder httpmock.Responder - statusCode int expectedResponse *response.Contact expectedErr error }{ fmt.Sprintf("HTTP PUT /api/v1/contacts/%s response: 200", paymail): { - statusCode: http.StatusOK, expectedResponse: contactstest.ExpectedUpsertContact(t), responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("contactstest/put_contact_upsert_200.json")), }, fmt.Sprintf("HTTP PUT /api/v1/contacts/%s response: 400", paymail): { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, contactstest.NewBadRequestSPVError()), + expectedErr: contactstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, contactstest.NewBadRequestSPVError()), }, fmt.Sprintf("HTTP PUT /api/v1/contacts/%s str response: 500", paymail): { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: contactstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, contactstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/contacts/" + paymail for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodPut, url, tc.responder) - // then: + // when: got, err := wallet.UpsertContact(context.Background(), commands.UpsertContact{ FullName: "John Doe", Metadata: map[string]any{"example_key": "example_val"}, Paymail: paymail, }) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } @@ -156,38 +137,32 @@ func TestContactsAPI_RemoveContact(t *testing.T) { paymail := "john.doe.test@john.doe.test.4chain.space" tests := map[string]struct { responder httpmock.Responder - statusCode int expectedErr error }{ fmt.Sprintf("HTTP DELETE /api/v1/contacts/%s response: 200", paymail): { - statusCode: http.StatusOK, - responder: httpmock.NewStringResponder(http.StatusOK, http.StatusText(http.StatusOK)), + responder: httpmock.NewStringResponder(http.StatusOK, http.StatusText(http.StatusOK)), }, fmt.Sprintf("HTTP DELETE /api/v1/contacts/%s response: 400", paymail): { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, contactstest.NewBadRequestSPVError()), + expectedErr: contactstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, contactstest.NewBadRequestSPVError()), }, fmt.Sprintf("HTTP DELETE /api/v1/contacts/%s str response: 500", paymail): { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: contactstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, contactstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/contacts/" + paymail for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodDelete, url, tc.responder) - // then: + // when: err := wallet.RemoveContact(context.Background(), paymail) + + // then: require.ErrorIs(t, err, tc.expectedErr) }) } @@ -201,51 +176,41 @@ func TestContactsAPI_ConfirmContact(t *testing.T) { tests := map[string]struct { responder httpmock.Responder - statusCode int expectedErr error }{ fmt.Sprintf("HTTP POST /api/v1/contacts/%s/confirmation response: 200", contact.Paymail): { - statusCode: http.StatusOK, - responder: httpmock.NewStringResponder(http.StatusOK, http.StatusText(http.StatusOK)), + responder: httpmock.NewStringResponder(http.StatusOK, http.StatusText(http.StatusOK)), }, fmt.Sprintf("HTTP POST /api/v1/contacts/%s/confirmation response: 400", contact.Paymail): { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusBadRequest, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, contactstest.NewBadRequestSPVError()), + expectedErr: contactstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, contactstest.NewBadRequestSPVError()), }, fmt.Sprintf("HTTP POST /api/v1/contacts/%s/confirmation str response: 500", contact.Paymail): { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: contactstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, contactstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/contacts/" + contact.Paymail + "/confirmation" - for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: + const period = 3600 + const digits = 6 + wrappedTransport := spvwallettest.NewTransportWrapper() aliceClient, _ := spvwallettest.GivenSPVWalletClientWithTransport(t, wrappedTransport) wrappedTransport.RegisterResponder(http.MethodPost, url, tc.responder) - passcode, err := aliceClient.GenerateTotpForContact(contact, 3600, 6) - require.NoError(t, err) + // when: + passcode, err := aliceClient.GenerateTotpForContact(contact, period, digits) // then: - err = aliceClient.ConfirmContact(context.Background(), contact, passcode, contact.Paymail, 3600, 6) - require.ErrorIs(t, err, tc.expectedErr) - - // Assert status code: - resp, respErr := wrappedTransport.GetResponse() - require.NoError(t, respErr) - require.NotNil(t, resp, "response should not be nil") - require.Equal(t, tc.statusCode, resp.StatusCode, "unexpected HTTP status code") + require.NoError(t, err) + require.NotEmpty(t, passcode) + err = aliceClient.ConfirmContact(context.Background(), contact, passcode, contact.Paymail, period, digits) + require.ErrorIs(t, err, tc.expectedErr) }) } } @@ -254,38 +219,32 @@ func TestContactsAPI_UnconfirmContact(t *testing.T) { paymail := "john.doe.test@john.doe.test.4chain.space" tests := map[string]struct { responder httpmock.Responder - statusCode int expectedErr error }{ fmt.Sprintf("HTTP DELETE /api/v1/contacts/%s/confirmation response: 200", paymail): { - statusCode: http.StatusOK, - responder: httpmock.NewStringResponder(http.StatusOK, http.StatusText(http.StatusOK)), + responder: httpmock.NewStringResponder(http.StatusOK, http.StatusText(http.StatusOK)), }, fmt.Sprintf("HTTP DELETE /api/v1/contacts/%s/confirmation response: 400", paymail): { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, contactstest.NewBadRequestSPVError()), + expectedErr: contactstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, contactstest.NewBadRequestSPVError()), }, fmt.Sprintf("HTTP DELETE /api/v1/contacts/%s/confirmation str response: 500", paymail): { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: contactstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, contactstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/contacts/" + paymail + "/confirmation" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodDelete, url, tc.responder) - // then: + // when: err := wallet.UnconfirmContact(context.Background(), paymail) + + // then: require.ErrorIs(t, err, tc.expectedErr) }) } diff --git a/internal/api/v1/user/contacts/contactstest/contacts_api_fixtures.go b/internal/api/v1/user/contacts/contactstest/contacts_api_fixtures.go index 0bec7e5..093530c 100644 --- a/internal/api/v1/user/contacts/contactstest/contacts_api_fixtures.go +++ b/internal/api/v1/user/contacts/contactstest/contacts_api_fixtures.go @@ -88,10 +88,18 @@ func Ptr[T any](value T) *T { return &value } -func NewBadRequestSPVError() *models.SPVError { - return &models.SPVError{ +func NewBadRequestSPVError() models.SPVError { + return models.SPVError{ Message: http.StatusText(http.StatusBadRequest), StatusCode: http.StatusBadRequest, Code: "invalid-data-format", } } + +func NewInternalServerSPVError() models.SPVError { + return models.SPVError{ + Message: http.StatusText(http.StatusInternalServerError), + StatusCode: http.StatusInternalServerError, + Code: models.UnknownErrorCode, + } +} diff --git a/internal/api/v1/user/invitations/invitations_api_test.go b/internal/api/v1/user/invitations/invitations_api_test.go index 4f27319..393be71 100644 --- a/internal/api/v1/user/invitations/invitations_api_test.go +++ b/internal/api/v1/user/invitations/invitations_api_test.go @@ -5,11 +5,9 @@ import ( "fmt" "net/http" "testing" - "time" - "github.com/bitcoin-sv/spv-wallet-go-client/errors" + "github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/user/invitations/invitationstest" "github.com/bitcoin-sv/spv-wallet-go-client/internal/spvwallettest" - "github.com/bitcoin-sv/spv-wallet/models" "github.com/jarcoal/httpmock" "github.com/stretchr/testify/require" ) @@ -18,38 +16,32 @@ func TestInvitationsAPI_AcceptInvitation(t *testing.T) { paymail := "john.doe.test@john.doe.test.4chain.space" tests := map[string]struct { responder httpmock.Responder - statusCode int expectedErr error }{ fmt.Sprintf("HTTP POST /api/v1/invitations/%s/contacts response: 200", paymail): { - statusCode: http.StatusOK, - responder: httpmock.NewStringResponder(http.StatusOK, http.StatusText(http.StatusOK)), + responder: httpmock.NewStringResponder(http.StatusOK, http.StatusText(http.StatusOK)), }, fmt.Sprintf("HTTP POST /api/v1/invitations/%s/contacts response: 400", paymail): { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, NewBadRequestSPVError()), + expectedErr: invitationstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, invitationstest.NewBadRequestSPVError()), }, fmt.Sprintf("HTTP POST /api/v1/invitations/%s/contacts str response: 500", paymail): { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: invitationstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, invitationstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/invitations/" + paymail + "/contacts" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodPost, url, tc.responder) - // then: + // when: err := wallet.AcceptInvitation(context.Background(), paymail) + + // then: require.ErrorIs(t, err, tc.expectedErr) }) } @@ -59,55 +51,33 @@ func TestInvitationsAPI_RejectInvitation(t *testing.T) { paymail := "john.doe.test@john.doe.test.4chain.space" tests := map[string]struct { responder httpmock.Responder - statusCode int expectedErr error }{ fmt.Sprintf("HTTP POST /api/v1/invitations/%s response: 200", paymail): { - statusCode: http.StatusOK, - responder: httpmock.NewStringResponder(http.StatusOK, http.StatusText(http.StatusOK)), + responder: httpmock.NewStringResponder(http.StatusOK, http.StatusText(http.StatusOK)), }, fmt.Sprintf("HTTP POST /api/v1/invitations/%s response: 400", paymail): { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, NewBadRequestSPVError()), + expectedErr: invitationstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, invitationstest.NewBadRequestSPVError()), }, fmt.Sprintf("HTTP POST /api/v1/invitations/%s str response: 500", paymail): { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: invitationstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, invitationstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/invitations/" + paymail for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodDelete, url, tc.responder) - // then: + // when: err := wallet.RejectInvitation(context.Background(), paymail) + + // then: require.ErrorIs(t, err, tc.expectedErr) }) } } - -func ParseTime(s string) time.Time { - t, err := time.Parse(time.RFC3339Nano, s) - if err != nil { - panic(err) - } - return t -} - -func NewBadRequestSPVError() *models.SPVError { - return &models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - } -} diff --git a/internal/api/v1/user/invitations/invitationstest/invitations_api_fixtures.go b/internal/api/v1/user/invitations/invitationstest/invitations_api_fixtures.go new file mode 100644 index 0000000..888f34b --- /dev/null +++ b/internal/api/v1/user/invitations/invitationstest/invitations_api_fixtures.go @@ -0,0 +1,32 @@ +package invitationstest + +import ( + "net/http" + "time" + + "github.com/bitcoin-sv/spv-wallet/models" +) + +func ParseTime(s string) time.Time { + t, err := time.Parse(time.RFC3339Nano, s) + if err != nil { + panic(err) + } + return t +} + +func NewBadRequestSPVError() models.SPVError { + return models.SPVError{ + Message: http.StatusText(http.StatusBadRequest), + StatusCode: http.StatusBadRequest, + Code: "invalid-data-format", + } +} + +func NewInternalServerSPVError() models.SPVError { + return models.SPVError{ + Message: http.StatusText(http.StatusInternalServerError), + StatusCode: http.StatusInternalServerError, + Code: models.UnknownErrorCode, + } +} diff --git a/internal/api/v1/user/merkleroots/merkleroots_api_test.go b/internal/api/v1/user/merkleroots/merkleroots_api_test.go index 0e3a154..cd50b5c 100644 --- a/internal/api/v1/user/merkleroots/merkleroots_api_test.go +++ b/internal/api/v1/user/merkleroots/merkleroots_api_test.go @@ -18,42 +18,36 @@ import ( func TestMerkleRootsAPI_MerkleRoots(t *testing.T) { tests := map[string]struct { responder httpmock.Responder - statusCode int expectedResponse *queries.MerkleRootPage expectedErr error }{ "HTTP GET /api/v1/merkleroots response: 200": { - statusCode: http.StatusOK, expectedResponse: merklerootstest.ExpectedMerkleRootsPage(), responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("merklerootstest/get_merkleroots_200.json")), }, "HTTP GET /api/v1/merkleroots response: 400": { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, merklerootstest.NewBadRequestSPVError()), + expectedErr: merklerootstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, merklerootstest.NewBadRequestSPVError()), }, "HTTP GET /api/v1/merkleroots str response: 500": { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: merklerootstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, merklerootstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/merkleroots" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: spvWalletClient, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodGet, url, tc.responder) - // then: + // when: got, err := spvWalletClient.MerkleRoots(context.Background()) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } @@ -105,40 +99,32 @@ func TestMerkleRootsAPI_SyncMerkleRoots(t *testing.T) { expectedErr: errors.ErrStaleLastEvaluatedKey, }, "API Returns Error Response": { - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "Internal Server Error"), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, merklerootstest.NewInternalServerSPVError()), setupMock: func(mockRepo *MockMerkleRootsRepository) { mockRepo.On("GetLastMerkleRoot").Return("") // No data initially }, - expectedErr: errors.ErrUnrecognizedAPIResponse, + expectedErr: merklerootstest.NewInternalServerSPVError(), }, } url := spvwallettest.TestAPIAddr + "/api/v1/merkleroots" - for name, tc := range tests { t.Run(name, func(t *testing.T) { - // Arrange + // given: + mockRepo := new(MockMerkleRootsRepository) spvWalletClient, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodGet, url, tc.responder) - - mockRepo := new(MockMerkleRootsRepository) tc.setupMock(mockRepo) - // Act + // when: err := spvWalletClient.SyncMerkleRoots(context.Background(), mockRepo) - // Assert - if tc.expectedErr != nil { - require.Error(t, err) - require.ErrorIs(t, err, tc.expectedErr) - } else { - require.NoError(t, err) - } + // then: + require.ErrorIs(t, err, tc.expectedErr) }) } } -// TestMerkleRootsAPI_SyncMerkleRoots_PartialResponsesStoredSuccessfully tests the SyncMerkleRoots functionality func TestMerkleRootsAPI_SyncMerkleRoots_PartialResponsesStoredSuccessfully(t *testing.T) { // given: db := merklerootstest.CreateRepository([]models.MerkleRoot{}) diff --git a/internal/api/v1/user/merkleroots/merklerootstest/merkleroots_api_fixtures.go b/internal/api/v1/user/merkleroots/merklerootstest/merkleroots_api_fixtures.go index 588f5d6..bb50f0b 100644 --- a/internal/api/v1/user/merkleroots/merklerootstest/merkleroots_api_fixtures.go +++ b/internal/api/v1/user/merkleroots/merklerootstest/merkleroots_api_fixtures.go @@ -37,10 +37,18 @@ func Ptr[T any](value T) *T { return &value } -func NewBadRequestSPVError() *models.SPVError { - return &models.SPVError{ +func NewBadRequestSPVError() models.SPVError { + return models.SPVError{ Message: http.StatusText(http.StatusBadRequest), StatusCode: http.StatusBadRequest, Code: "invalid-data-format", } } + +func NewInternalServerSPVError() models.SPVError { + return models.SPVError{ + Message: http.StatusText(http.StatusInternalServerError), + StatusCode: http.StatusInternalServerError, + Code: models.UnknownErrorCode, + } +} diff --git a/internal/api/v1/user/transactions/transactions_api_test.go b/internal/api/v1/user/transactions/transactions_api_test.go index 45a264e..372319e 100644 --- a/internal/api/v1/user/transactions/transactions_api_test.go +++ b/internal/api/v1/user/transactions/transactions_api_test.go @@ -7,11 +7,9 @@ import ( "testing" "github.com/bitcoin-sv/spv-wallet-go-client/commands" - "github.com/bitcoin-sv/spv-wallet-go-client/errors" "github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/querybuilders" "github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/user/transactions/transactionstest" "github.com/bitcoin-sv/spv-wallet-go-client/internal/spvwallettest" - "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/response" "github.com/jarcoal/httpmock" "github.com/stretchr/testify/require" @@ -20,41 +18,32 @@ import ( func TestTransactionsAPI_UpdateTransactionMetadata(t *testing.T) { ID := "1024" tests := map[string]struct { - code int responder httpmock.Responder - statusCode int expectedResponse *response.Transaction expectedErr error }{ fmt.Sprintf("HTTP PATCH /api/v1/transactions/%s response: 200", ID): { expectedResponse: transactionstest.ExpectedTransactionWithUpdatedMetadata(t), - code: http.StatusOK, responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("transactionstest/transaction_update_metadata_200.json")), }, fmt.Sprintf("HTTP PATCH /api/v1/transactions/%s response: 400", ID): { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, transactionstest.NewBadRequestSPVError()), + expectedErr: transactionstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, transactionstest.NewBadRequestSPVError()), }, fmt.Sprintf("HTTP PATCH /api/v1/transactions/%s str response: 500", ID): { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: transactionstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, transactionstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/transactions/" + ID for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: spvWalletClient, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodPatch, url, tc.responder) - // then: + // when: got, err := spvWalletClient.UpdateTransactionMetadata(context.Background(), &commands.UpdateTransactionMetadata{ ID: ID, Metadata: querybuilders.Metadata{ @@ -62,8 +51,10 @@ func TestTransactionsAPI_UpdateTransactionMetadata(t *testing.T) { "example_key2": "example_key20_val", }, }) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } @@ -71,42 +62,36 @@ func TestTransactionsAPI_UpdateTransactionMetadata(t *testing.T) { func TestTransactionsAPI_RecordTransaction(t *testing.T) { tests := map[string]struct { responder httpmock.Responder - statusCode int expectedResponse *response.Transaction expectedErr error }{ "HTTP POST /api/v1/transactions response: 201": { - statusCode: http.StatusCreated, expectedResponse: transactionstest.ExpectedRecordTransaction(t), responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("transactionstest/transaction_record_201.json")), }, "HTTP GET /api/v1/transactions response: 400": { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, transactionstest.NewBadRequestSPVError()), + expectedErr: transactionstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, transactionstest.NewBadRequestSPVError()), }, "HTTP GET /api/v1/transactions str response: 500": { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: transactionstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, transactionstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/transactions" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: spvWalletClient, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodPost, url, tc.responder) - // then: + // when: got, err := spvWalletClient.RecordTransaction(context.Background(), &commands.RecordTransaction{}) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } @@ -114,45 +99,39 @@ func TestTransactionsAPI_RecordTransaction(t *testing.T) { func TestTransactionsAPI_DraftTransaction(t *testing.T) { tests := map[string]struct { responder httpmock.Responder - statusCode int expectedResponse *response.DraftTransaction expectedErr error }{ "HTTP POST /api/v1/transactions/drafts response: 200": { - statusCode: http.StatusOK, expectedResponse: transactionstest.ExpectedDraftTransaction(t), responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("transactionstest/transaction_draft_200.json")), }, "HTTP POST /api/v1/transactions/drafts response: 400": { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, transactionstest.NewBadRequestSPVError()), + expectedErr: transactionstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, transactionstest.NewBadRequestSPVError()), }, "HTTP POST /api/v1/transactions/drafts str response: 500": { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: transactionstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, transactionstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/transactions/drafts" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: spvWalletClient, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodPost, url, tc.responder) - // then: + // when: got, err := spvWalletClient.DraftTransaction(context.Background(), &commands.DraftTransaction{ Config: response.TransactionConfig{}, Metadata: map[string]any{}, }) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } @@ -161,42 +140,36 @@ func TestTransactionsAPI_Transaction(t *testing.T) { ID := "1024" tests := map[string]struct { responder httpmock.Responder - statusCode int expectedResponse *response.Transaction expectedErr error }{ fmt.Sprintf("HTTP PATCH /api/v1/transactions/%s response: 200", ID): { - statusCode: http.StatusOK, expectedResponse: transactionstest.ExpectedTransaction(t), responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("transactionstest/transaction_200.json")), }, fmt.Sprintf("HTTP PATCH /api/v1/transactions/%s response: 400", ID): { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, transactionstest.NewBadRequestSPVError()), + expectedErr: transactionstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, transactionstest.NewBadRequestSPVError()), }, fmt.Sprintf("HTTP PATCH /api/v1/transactions/%s str response: 500", ID): { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: transactionstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, transactionstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/transactions/" + ID for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: spvWalletClient, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodGet, url, tc.responder) - // then: + // when: got, err := spvWalletClient.Transaction(context.Background(), ID) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } @@ -204,42 +177,36 @@ func TestTransactionsAPI_Transaction(t *testing.T) { func TestTransactionsAPI_Transactions(t *testing.T) { tests := map[string]struct { responder httpmock.Responder - statusCode int expectedResponse *response.PageModel[response.Transaction] expectedErr error }{ "HTTP GET /api/v1/transactions response: 200": { - statusCode: http.StatusOK, expectedResponse: transactionstest.ExpectedTransactionsPage(t), responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("transactionstest/transactions_200.json")), }, "HTTP GET /api/v1/transactions response: 400": { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, transactionstest.NewBadRequestSPVError()), + expectedErr: transactionstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, transactionstest.NewBadRequestSPVError()), }, "HTTP GET /api/v1/transactions str response: 500": { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: transactionstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, transactionstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/transactions" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: spvWalletClient, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodGet, url, tc.responder) - // then: + // when: got, err := spvWalletClient.Transactions(context.Background()) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } diff --git a/internal/api/v1/user/transactions/transactionstest/transactionstest.go b/internal/api/v1/user/transactions/transactionstest/transactionstest.go index c8c2751..c3d97d4 100644 --- a/internal/api/v1/user/transactions/transactionstest/transactionstest.go +++ b/internal/api/v1/user/transactions/transactionstest/transactionstest.go @@ -311,10 +311,18 @@ func Ptr[T any](value T) *T { return &value } -func NewBadRequestSPVError() *models.SPVError { - return &models.SPVError{ +func NewBadRequestSPVError() models.SPVError { + return models.SPVError{ Message: http.StatusText(http.StatusBadRequest), StatusCode: http.StatusBadRequest, Code: "invalid-data-format", } } + +func NewInternalServerSPVError() models.SPVError { + return models.SPVError{ + Message: http.StatusText(http.StatusInternalServerError), + StatusCode: http.StatusInternalServerError, + Code: models.UnknownErrorCode, + } +} diff --git a/internal/api/v1/user/users/access_key_api_test.go b/internal/api/v1/user/users/access_key_api_test.go index f089dbf..aa7e360 100644 --- a/internal/api/v1/user/users/access_key_api_test.go +++ b/internal/api/v1/user/users/access_key_api_test.go @@ -7,11 +7,9 @@ import ( "testing" "github.com/bitcoin-sv/spv-wallet-go-client/commands" - "github.com/bitcoin-sv/spv-wallet-go-client/errors" "github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/user/users/userstest" "github.com/bitcoin-sv/spv-wallet-go-client/internal/spvwallettest" "github.com/bitcoin-sv/spv-wallet-go-client/queries" - "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/response" "github.com/jarcoal/httpmock" "github.com/stretchr/testify/require" @@ -19,48 +17,39 @@ import ( func TestAccessKeyAPI_GenerateAccessKey(t *testing.T) { tests := map[string]struct { - code int responder httpmock.Responder - statusCode int expectedResponse *response.AccessKey expectedErr error }{ "HTTP POST /api/v1/users/current/keys response: 200": { expectedResponse: userstest.ExpectedCreatedAccessKey(t), - code: http.StatusOK, responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("userstest/post_access_key_200.json")), }, "HTTP POST /api/v1/users/current/keys response: 400": { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), + expectedErr: userstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), }, "HTTP POST /api/v1/users/current/keys str response: 500": { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: userstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, userstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/users/current/keys" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodPost, url, tc.responder) - // then: + // when: got, err := wallet.GenerateAccessKey(context.Background(), &commands.GenerateAccessKey{ - Metadata: map[string]any{ - "example_key": "example_value", - }, + Metadata: map[string]any{"example_key": "example_value"}, }) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } @@ -68,88 +57,74 @@ func TestAccessKeyAPI_GenerateAccessKey(t *testing.T) { func TestAccessKeyAPI_AccessKey(t *testing.T) { ID := "1fb70cc2-e9d9-41a3-842e-f71cc58d9787" tests := map[string]struct { - code int responder httpmock.Responder - statusCode int expectedResponse *response.AccessKey expectedErr error }{ fmt.Sprintf("HTTP GET /api/v1/users/current/keys/%s response: 200", ID): { expectedResponse: userstest.ExpectedRertrivedAccessKey(t), - code: http.StatusOK, responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("userstest/get_access_key_200.json")), }, fmt.Sprintf("HTTP GET /api/v1/users/current/keys/%s response: 400", ID): { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), + expectedErr: userstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), }, fmt.Sprintf("HTTP GET /api/v1/users/current/keys/%s str response: 500", ID): { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: userstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, userstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/users/current/keys/" + ID for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodGet, url, tc.responder) - // then: + // when: got, err := wallet.AccessKey(context.Background(), ID) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } func TestAccessKeyAPI_AccessKeys(t *testing.T) { tests := map[string]struct { - code int responder httpmock.Responder - statusCode int expectedResponse *queries.AccessKeyPage expectedErr error }{ "HTTP GET /api/v1/users/current/keys response: 200": { expectedResponse: userstest.ExpectedAccessKeyPage(t), - code: http.StatusOK, responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("userstest/get_access_keys_200.json")), }, "HTTP GET /api/v1/users/current/keys response: 400": { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), + expectedErr: userstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), }, "HTTP GET /api/v1/users/current/keys str response: 500": { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: userstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, userstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/users/current/keys" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodGet, url, tc.responder) - // then: + // when: got, err := wallet.AccessKeys(context.Background()) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } @@ -157,40 +132,33 @@ func TestAccessKeyAPI_AccessKeys(t *testing.T) { func TestAccessKeyAPI_RevokeAccessKey(t *testing.T) { ID := "081743f7-040e-45a3-8c36-dde39001e20d" tests := map[string]struct { - code int responder httpmock.Responder - statusCode int expectedErr error }{ fmt.Sprintf("HTTP DELETE /api/v1/users/current/keys/%s response: 200", ID): { - code: http.StatusOK, responder: httpmock.NewStringResponder(http.StatusOK, http.StatusText(http.StatusOK)), }, fmt.Sprintf("HTTP DELETE /api/v1/users/current/keys/%s response: 400", ID): { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), + expectedErr: userstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), }, fmt.Sprintf("HTTP DELETE /api/v1/users/current/keys/%s str response: 500", ID): { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: userstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, userstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/users/current/keys/" + ID for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodDelete, url, tc.responder) - // then: + // when: err := wallet.RevokeAccessKey(context.Background(), ID) + + // then: require.ErrorIs(t, err, tc.expectedErr) }) } diff --git a/internal/api/v1/user/users/userstest/xpub_api_fixtures.go b/internal/api/v1/user/users/userstest/xpub_api_fixtures.go index 231eab7..6f92644 100644 --- a/internal/api/v1/user/users/userstest/xpub_api_fixtures.go +++ b/internal/api/v1/user/users/userstest/xpub_api_fixtures.go @@ -45,14 +45,6 @@ func ExpectedUserXPub(t *testing.T) *response.Xpub { } } -func NewBadRequestSPVError() *models.SPVError { - return &models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - } -} - func ParseTime(t *testing.T, s string) time.Time { ts, err := time.Parse(time.RFC3339Nano, s) if err != nil { @@ -64,3 +56,19 @@ func ParseTime(t *testing.T, s string) time.Time { func Ptr[T any](value T) *T { return &value } + +func NewBadRequestSPVError() models.SPVError { + return models.SPVError{ + Message: http.StatusText(http.StatusBadRequest), + StatusCode: http.StatusBadRequest, + Code: "invalid-data-format", + } +} + +func NewInternalServerSPVError() models.SPVError { + return models.SPVError{ + Message: http.StatusText(http.StatusInternalServerError), + StatusCode: http.StatusInternalServerError, + Code: models.UnknownErrorCode, + } +} diff --git a/internal/api/v1/user/users/xpub_api_test.go b/internal/api/v1/user/users/xpub_api_test.go index 3bd7934..793ce0a 100644 --- a/internal/api/v1/user/users/xpub_api_test.go +++ b/internal/api/v1/user/users/xpub_api_test.go @@ -6,10 +6,8 @@ import ( "testing" "github.com/bitcoin-sv/spv-wallet-go-client/commands" - "github.com/bitcoin-sv/spv-wallet-go-client/errors" "github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/user/users/userstest" "github.com/bitcoin-sv/spv-wallet-go-client/internal/spvwallettest" - "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/response" "github.com/jarcoal/httpmock" "github.com/stretchr/testify/require" @@ -17,90 +15,74 @@ import ( func TestXPubAPI_UpdateXPubMetadata(t *testing.T) { tests := map[string]struct { - code int responder httpmock.Responder - statusCode int expectedResponse *response.Xpub expectedErr error }{ "HTTP PATCH /api/v1/users/current response: 200": { expectedResponse: userstest.ExpectedUpdatedXPubMetadata(t), - code: http.StatusOK, responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("userstest/patch_xpub_metadata_200.json")), }, "HTTP PATCH /api/v1/users/current response: 400": { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), + expectedErr: userstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), }, "HTTP PATCH /api/v1/users/current str response: 500": { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: userstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, userstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/users/current" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodPatch, url, tc.responder) - // then: + // when: got, err := wallet.UpdateXPubMetadata(context.Background(), &commands.UpdateXPubMetadata{ - Metadata: map[string]any{ - "example_key": "example_value", - }, + Metadata: map[string]any{"example_key": "example_value"}, }) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } func TestXPubAPI_XPub(t *testing.T) { tests := map[string]struct { - code int responder httpmock.Responder - statusCode int expectedResponse *response.Xpub expectedErr error }{ "HTTP GET /api/v1/users/current/ response: 200": { expectedResponse: userstest.ExpectedUserXPub(t), - code: http.StatusOK, responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("userstest/get_xpub_200.json")), }, "HTTP GET /api/v1/users/current response: 400": { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), + expectedErr: userstest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, userstest.NewBadRequestSPVError()), }, "HTTP GET /api/v1/users/current str response: 500": { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: userstest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, userstest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/users/current" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodGet, url, tc.responder) - // then: + // when: got, err := wallet.XPub(context.Background()) + + // then: require.ErrorIs(t, err, tc.expectedErr) require.EqualValues(t, tc.expectedResponse, got) }) diff --git a/internal/api/v1/user/utxos/utxos_api_test.go b/internal/api/v1/user/utxos/utxos_api_test.go index cba4730..e9d64de 100644 --- a/internal/api/v1/user/utxos/utxos_api_test.go +++ b/internal/api/v1/user/utxos/utxos_api_test.go @@ -5,55 +5,46 @@ import ( "net/http" "testing" - "github.com/bitcoin-sv/spv-wallet-go-client/errors" "github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/user/utxos/utxostest" "github.com/bitcoin-sv/spv-wallet-go-client/internal/spvwallettest" "github.com/bitcoin-sv/spv-wallet-go-client/queries" - "github.com/bitcoin-sv/spv-wallet/models" "github.com/jarcoal/httpmock" "github.com/stretchr/testify/require" ) func TestUTXOAPI_UTXOs(t *testing.T) { tests := map[string]struct { - code int responder httpmock.Responder - statusCode int expectedResponse *queries.UtxosPage expectedErr error }{ "HTTP GET /api/v1/utxos response: 200": { expectedResponse: utxostest.ExpectedUtxosPage(t), - code: http.StatusOK, responder: httpmock.NewJsonResponderOrPanic(http.StatusOK, httpmock.File("utxostest/get_utxos_200.json")), }, "HTTP GET /api/v1/utxos response: 400": { - expectedErr: models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - }, - statusCode: http.StatusOK, - responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, utxostest.NewBadRequestSPVError()), + expectedErr: utxostest.NewBadRequestSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusBadRequest, utxostest.NewBadRequestSPVError()), }, "HTTP GET /api/v1/utxos str response: 500": { - expectedErr: errors.ErrUnrecognizedAPIResponse, - statusCode: http.StatusInternalServerError, - responder: httpmock.NewStringResponder(http.StatusInternalServerError, "unexpected internal server failure"), + expectedErr: utxostest.NewInternalServerSPVError(), + responder: httpmock.NewJsonResponderOrPanic(http.StatusInternalServerError, utxostest.NewInternalServerSPVError()), }, } url := spvwallettest.TestAPIAddr + "/api/v1/utxos" for name, tc := range tests { t.Run(name, func(t *testing.T) { - // when: + // given: wallet, transport := spvwallettest.GivenSPVUserAPI(t) transport.RegisterResponder(http.MethodGet, url, tc.responder) - // then: + // when: got, err := wallet.UTXOs(context.Background()) + + // then: require.ErrorIs(t, err, tc.expectedErr) - require.EqualValues(t, tc.expectedResponse, got) + require.Equal(t, tc.expectedResponse, got) }) } } diff --git a/internal/api/v1/user/utxos/utxostest/utxo_api_fixtures.go b/internal/api/v1/user/utxos/utxostest/utxo_api_fixtures.go index aabd4ed..7167179 100644 --- a/internal/api/v1/user/utxos/utxostest/utxo_api_fixtures.go +++ b/internal/api/v1/user/utxos/utxostest/utxo_api_fixtures.go @@ -10,14 +10,6 @@ import ( "github.com/bitcoin-sv/spv-wallet/models/response" ) -func NewBadRequestSPVError() *models.SPVError { - return &models.SPVError{ - Message: http.StatusText(http.StatusBadRequest), - StatusCode: http.StatusBadRequest, - Code: "invalid-data-format", - } -} - func ParseTime(t *testing.T, s string) time.Time { ts, err := time.Parse(time.RFC3339Nano, s) if err != nil { @@ -121,3 +113,19 @@ func ExpectedUtxosPage(t *testing.T) *queries.UtxosPage { }, } } + +func NewBadRequestSPVError() models.SPVError { + return models.SPVError{ + Message: http.StatusText(http.StatusBadRequest), + StatusCode: http.StatusBadRequest, + Code: "invalid-data-format", + } +} + +func NewInternalServerSPVError() models.SPVError { + return models.SPVError{ + Message: http.StatusText(http.StatusInternalServerError), + StatusCode: http.StatusInternalServerError, + Code: models.UnknownErrorCode, + } +} diff --git a/internal/spvwallettest/spvwallettest.go b/internal/spvwallettest/spvwallettest.go index 369f7b1..c307c72 100644 --- a/internal/spvwallettest/spvwallettest.go +++ b/internal/spvwallettest/spvwallettest.go @@ -116,7 +116,7 @@ func MockPKI(t *testing.T, xpub string) string { for i := 0; i < 3; i++ { //magicNumberOfInheritance is 3 -> 2+1; 2: because of the way spv-wallet stores xpubs in db; 1: to make a PKI xPub, err = xPub.Child(0) if err != nil { - panic(err) + t.Fatalf("test helper - retrieve a derived child extended key at index 0 failed: %s", err) } }