Skip to content

Commit

Permalink
Add tests and fix linter warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
Vadim Petrov committed Jul 14, 2017
1 parent 1718a0d commit 7512c94
Show file tree
Hide file tree
Showing 19 changed files with 241 additions and 76 deletions.
File renamed without changes.
6 changes: 3 additions & 3 deletions authorization.go
Expand Up @@ -25,15 +25,15 @@ type Authorization struct {
ProviderData ProviderData `json:"provider_data"`
ProviderSpecificData map[string]interface{} `json:"provider_specific_data"`
OriginatingPurchaseCountry string `json:"originating_purchase_country"`
IpAddress string `json:"ip_address"`
IPAddress string `json:"ip_address"`
Redirection *Redirection `json:"redirection"`
}

// AuthorizationParams is a set of params for creating entity.
type AuthorizationParams struct {
PaymentMethodToken string `json:"payment_method_token"`
CreditCardCvv string `json:"credit_card_cvv,omitempty"`
MerchantSiteUrl string `json:"merchant_site_url,omitempty"`
MerchantSiteURL string `json:"merchant_site_url,omitempty"`
ReconciliationID string `json:"reconciliation_id,omitempty"`
ThreeDSecureAttributes *ThreeDSecureAttributes `json:"three_d_secure_attributes,omitempty"`
Installments *Installments `json:"installments,omitempty"`
Expand All @@ -47,7 +47,7 @@ func (c *AuthorizationClient) New(ctx context.Context, idempotencyKey string, pa
headers := map[string]string{headerIdempotencyKey: idempotencyKey}

if clientInfo != nil {
headers[headerClientIpAddress] = clientInfo.IpAddress
headers[headerClientIPAddress] = clientInfo.IPAddress
headers[headerClientUserAgent] = clientInfo.UserAgent
}

Expand Down
11 changes: 11 additions & 0 deletions authorization_test.go
@@ -0,0 +1,11 @@
package zooz

import "testing"

func TestAuthorizationClient_authorizationPath(t *testing.T) {
c := &AuthorizationClient{}
p := c.authorizationPath("payment_id", "authorization_id")
if p != "payments/payment_id/authorizations/authorization_id" {
t.Errorf("Invalid authorization path: %s", p)
}
}
11 changes: 11 additions & 0 deletions capture_test.go
@@ -0,0 +1,11 @@
package zooz

import "testing"

func TestCaptureClient_capturePath(t *testing.T) {
c := &CaptureClient{}
p := c.capturePath("payment_id", "capture_id")
if p != "payments/payment_id/captures/capture_id" {
t.Errorf("Invalid capture path: %s", p)
}
}
6 changes: 3 additions & 3 deletions charge.go
Expand Up @@ -25,15 +25,15 @@ type Charge struct {
ProviderData ProviderData `json:"provider_data"`
ProviderSpecificData map[string]interface{} `json:"provider_specific_data"`
OriginatingPurchaseCountry string `json:"originating_purchase_country"`
IpAddress string `json:"ip_address"`
IPAddress string `json:"ip_address"`
Redirection *Redirection `json:"redirection"`
}

// ChargeParams is a set of params for creating entity.
type ChargeParams struct {
PaymentMethodToken string `json:"payment_method_token"`
CreditCardCvv string `json:"credit_card_cvv,omitempty"`
MerchantSiteUrl string `json:"merchant_site_url,omitempty"`
MerchantSiteURL string `json:"merchant_site_url,omitempty"`
ReconciliationID string `json:"reconciliation_id,omitempty"`
ThreeDSecureAttributes *ThreeDSecureAttributes `json:"three_d_secure_attributes,omitempty"`
Installments *Installments `json:"installments,omitempty"`
Expand All @@ -47,7 +47,7 @@ func (c *ChargeClient) New(ctx context.Context, idempotencyKey string, paymentID
headers := map[string]string{headerIdempotencyKey: idempotencyKey}

if clientInfo != nil {
headers[headerClientIpAddress] = clientInfo.IpAddress
headers[headerClientIPAddress] = clientInfo.IPAddress
headers[headerClientUserAgent] = clientInfo.UserAgent
}

Expand Down
11 changes: 11 additions & 0 deletions charge_test.go
@@ -0,0 +1,11 @@
package zooz

import "testing"

func TestChargeClient_chargePath(t *testing.T) {
c := &ChargeClient{}
p := c.chargePath("payment_id", "charge_id")
if p != "payments/payment_id/charges/charge_id" {
t.Errorf("Invalid charge path: %s", p)
}
}
31 changes: 18 additions & 13 deletions client.go
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/pkg/errors"
)

// Call makes HTTP call with given options and decode response into given struct.
// Caller makes HTTP call with given options and decode response into given struct.
// Client implements this interface and pass itself to entity clients. You may create entity clients with own caller for
// test purposes.
type Caller interface {
Expand All @@ -42,18 +42,19 @@ type env string

const (
apiVersion = "1.0.0"
apiUrl = "https://api.paymentsos.com/"
apiURL = "https://api.paymentsos.com/"

// Possible values for environment request header
// EnvTest is a value for test environment header
EnvTest env = "test"
// EnvLive is a value for live environment header
EnvLive env = "live"

headerApiVersion = "api-version"
headerAPIVersion = "api-version"
headerEnv = "x-payments-os-env"
headerIdempotencyKey = "idempotency_key"
headerAppID = "app_id"
headerPrivateKey = "private_key"
headerClientIpAddress = "x-client-ip-address"
headerClientIPAddress = "x-client-ip-address"
headerClientUserAgent = "x-client-user-agent"
headerRequestID = "X-Zooz-Request-Id"
)
Expand All @@ -72,8 +73,8 @@ func New(options ...Option) *Client {
return c
}

// OptHttpClient returns option with given HTTP client.
func OptHttpClient(httpClient httpClient) Option {
// OptHTTPClient returns option with given HTTP client.
func OptHTTPClient(httpClient httpClient) Option {
return func(c *Client) {
c.httpClient = httpClient
}
Expand Down Expand Up @@ -102,7 +103,7 @@ func OptEnv(env env) Option {

// Call does HTTP request with given params using set HTTP client. Response will be decoded into respObj.
// Error may be returned if something went wrong. If API return error as response, then Call returns error of type zooz.Error.
func (c *Client) Call(ctx context.Context, method, path string, headers map[string]string, reqObj interface{}, respObj interface{}) error {
func (c *Client) Call(ctx context.Context, method, path string, headers map[string]string, reqObj interface{}, respObj interface{}) (callErr error) {
var reqBody []byte
var err error

Expand All @@ -113,7 +114,7 @@ func (c *Client) Call(ctx context.Context, method, path string, headers map[stri
}
}

req, err := http.NewRequest(method, apiUrl+path, bytes.NewBuffer(reqBody))
req, err := http.NewRequest(method, apiURL+path, bytes.NewBuffer(reqBody))
if err != nil {
return errors.Wrap(err, "failed to create HTTP request")
}
Expand All @@ -127,7 +128,7 @@ func (c *Client) Call(ctx context.Context, method, path string, headers map[stri

// Set common client headers
req.Header.Set("Content-Type", "application/json")
req.Header.Set(headerApiVersion, apiVersion)
req.Header.Set(headerAPIVersion, apiVersion)
req.Header.Set(headerEnv, string(c.env))
req.Header.Set(headerAppID, c.appID)
req.Header.Set(headerPrivateKey, c.privateKey)
Expand All @@ -136,22 +137,26 @@ func (c *Client) Call(ctx context.Context, method, path string, headers map[stri
if err != nil {
return errors.Wrap(err, "failed to do request")
}
defer resp.Body.Close()
defer func() {
if err := resp.Body.Close(); err != nil {
callErr = err
}
}()

// Handle 4xx and 5xx statuses
if resp.StatusCode >= http.StatusBadRequest {
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return errors.Wrap(err, "failed to read response body")
}
var apiError ApiError
var apiError APIError
if err := json.Unmarshal(respBody, &apiError); err != nil {
return errors.Wrapf(err, "failed to unmarshal response error with status %d: %s", resp.StatusCode, string(respBody))
}
return &Error{
StatusCode: resp.StatusCode,
RequestID: resp.Header.Get(headerRequestID),
ApiError: apiError,
APIError: apiError,
}
}

Expand Down
41 changes: 33 additions & 8 deletions client_test.go
Expand Up @@ -22,6 +22,31 @@ func (c *httpClientMock) Do(r *http.Request) (*http.Response, error) {
return c.do(r)
}

func TestNew(t *testing.T) {
c := New(
OptAppID("app_id"),
OptEnv(EnvLive),
OptPrivateKey("private_key"),
OptHTTPClient(&httpClientMock{}),
)

if c == nil {
t.Errorf("Client is nil")
}
if c.appID != "app_id" {
t.Errorf("Invalid appID: %s", c.appID)
}
if c.env != EnvLive {
t.Errorf("Invalid env: %s", c.env)
}
if c.privateKey != "private_key" {
t.Errorf("Invalid privateKey: %s", c.privateKey)
}
if _, ok := c.httpClient.(*httpClientMock); !ok {
t.Errorf("Invalid httpClient: %T", c.httpClient)
}
}

func TestCall_WithApiResponse(t *testing.T) {
httpClientMock := &httpClientMock{
do: func(r *http.Request) (*http.Response, error) {
Expand Down Expand Up @@ -55,7 +80,7 @@ func TestCall_WithApiResponse(t *testing.T) {
},
}

request := request{
req := request{
Field: "request_value",
}

Expand All @@ -77,7 +102,7 @@ func TestCall_WithApiResponse(t *testing.T) {
map[string]string{
"test-header": "test-header-value",
},
&request,
&req,
&response,
)

Expand All @@ -100,7 +125,7 @@ func TestCall_WithApiError(t *testing.T) {
},
}

request := request{
req := request{
Field: "request_value",
}

Expand All @@ -113,7 +138,7 @@ func TestCall_WithApiError(t *testing.T) {
map[string]string{
"test-header": "test-header-value",
},
&request,
&req,
nil,
)

Expand All @@ -124,8 +149,8 @@ func TestCall_WithApiError(t *testing.T) {
if zoozErr.StatusCode != http.StatusBadRequest {
t.Errorf("Invalid error status code: %d", zoozErr.StatusCode)
}
if zoozErr.ApiError.Category != "category_test" {
t.Errorf("Invalid API error category: %d", zoozErr.ApiError.Category)
if zoozErr.APIError.Category != "category_test" {
t.Errorf("Invalid API error category: %s", zoozErr.APIError.Category)
}
} else {
t.Errorf("Call return invalid error type: %T", err)
Expand All @@ -139,7 +164,7 @@ func TestCall_WithTransportError(t *testing.T) {
},
}

request := request{
req := request{
Field: "request_value",
}

Expand All @@ -152,7 +177,7 @@ func TestCall_WithTransportError(t *testing.T) {
map[string]string{
"test-header": "test-header-value",
},
&request,
&req,
nil,
)

Expand Down
2 changes: 1 addition & 1 deletion common.go
Expand Up @@ -10,7 +10,7 @@ type Result struct {

// ClientInfo represents optional request params for some methods.
type ClientInfo struct {
IpAddress string
IPAddress string
UserAgent string
}

Expand Down
11 changes: 11 additions & 0 deletions customer_test.go
@@ -0,0 +1,11 @@
package zooz

import "testing"

func TestCustomerClient_customerPath(t *testing.T) {
c := &CustomerClient{}
p := c.customerPath("customer_id")
if p != "customers/customer_id" {
t.Errorf("Invalid customer path: %s", p)
}
}
10 changes: 5 additions & 5 deletions error.go
Expand Up @@ -9,24 +9,24 @@ import (
type Error struct {
StatusCode int
RequestID string
ApiError ApiError
APIError APIError
}

// ApiError represents API error response.
// APIError represents API error response.
// https://developers.paymentsos.com/docs/api#/introduction/responses/errors
type ApiError struct {
type APIError struct {
Category string `json:"category"`
Description string `json:"description"`
MoreInfo string `json:"more_info"`
}

// String implements stringer interface.
func (e ApiError) String() string {
func (e APIError) String() string {
str, _ := json.Marshal(e)
return string(str)
}

// Error implements error interface.
func (e *Error) Error() string {
return fmt.Sprintf("request: %s, status: %d, error: %s", e.RequestID, e.StatusCode, e.ApiError)
return fmt.Sprintf("request: %s, status: %d, error: %s", e.RequestID, e.StatusCode, e.APIError)
}
21 changes: 21 additions & 0 deletions error_test.go
@@ -0,0 +1,21 @@
package zooz

import "testing"

func TestError(t *testing.T) {
e := &Error{
StatusCode: 500,
RequestID: "request_id",
APIError: APIError{
Category: "cat",
Description: "desc",
MoreInfo: "info",
},
}

var err error = e

if err.Error() != `request: request_id, status: 500, error: {"category":"cat","description":"desc","more_info":"info"}` {
t.Errorf("Invalid error: %s", err.Error())
}
}

0 comments on commit 7512c94

Please sign in to comment.