github.com/plutov/paypal/v4@v4.7.1/client_test.go (about)

     1  package paypal
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"math/rand"
     8  	"net/http"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  )
    15  
    16  // testClientID, testSecret imported from order_test.go
    17  
    18  // All test values are defined here
    19  // var testClientID = "AXy9orp-CDaHhBZ9C78QHW2BKZpACgroqo85_NIOa9mIfJ9QnSVKzY-X_rivR_fTUUr6aLjcJsj6sDur"
    20  // var testSecret = "EBoIiUSkCKeSk49hHSgTem1qnjzzJgRQHDEHvGpzlLEf_nIoJd91xu8rPOBDCdR_UYNKVxJE-UgS2iCw"
    21  var testUserID = "https://www.paypal.com/webapps/auth/identity/user/VBqgHcgZwb1PBs69ybjjXfIW86_Hr93aBvF_Rgbh2II"
    22  var testCardID = "CARD-54E6956910402550WKGRL6EA"
    23  
    24  var testProductId = ""   // will be fetched in  func TestProduct(t *testing.T)
    25  var testBillingPlan = "" // will be fetched in  func TestSubscriptionPlans(t *testing.T)
    26  
    27  const alphabet = "abcedfghijklmnopqrstuvwxyz"
    28  
    29  func RandomString(n int) string {
    30  	var sb strings.Builder
    31  	k := len(alphabet)
    32  
    33  	for i := 0; i < n; i++ {
    34  		c := alphabet[rand.Intn(k)]
    35  		sb.WriteByte(c)
    36  	}
    37  	return sb.String()
    38  }
    39  
    40  func createRandomProduct(t *testing.T) Product {
    41  	//create a product
    42  	productData := Product{
    43  		Name:        RandomString(10),
    44  		Description: RandomString(100),
    45  		Category:    ProductCategorySoftware,
    46  		Type:        ProductTypeService,
    47  		ImageUrl:    "https://example.com/image.png",
    48  		HomeUrl:     "https://example.com",
    49  	}
    50  	return productData
    51  }
    52  
    53  // this is a simple copy of the SendWithAuth method, used to
    54  // test the Lock and Unlock methods of the private mutex field
    55  // of Client structure.
    56  func (c *Client) sendWithAuth(req *http.Request, v interface{}) error {
    57  	// c.Lock()
    58  	c.mu.Lock()
    59  	// Note: Here we do not want to `defer c.Unlock()` because we need `c.Send(...)`
    60  	// to happen outside of the locked section.
    61  
    62  	if c.mu.TryLock() {
    63  		// if the code is able to acquire a lock
    64  		// despite the mutex of c being locked, throw an error
    65  		err := errors.New("TryLock succeeded inside sendWithAuth with mutex locked")
    66  		return err
    67  	}
    68  	if c.Token == nil || (!c.tokenExpiresAt.IsZero() && time.Until(c.tokenExpiresAt) < RequestNewTokenBeforeExpiresIn) {
    69  		// c.Token will be updated in GetAccessToken call
    70  		if _, err := c.GetAccessToken(req.Context()); err != nil {
    71  			// c.Unlock()
    72  			c.mu.Unlock()
    73  			return err
    74  		}
    75  	}
    76  	req.Header.Set("Authorization", "Bearer "+c.Token.Token)
    77  	// Unlock the client mutex before sending the request, this allows multiple requests
    78  	// to be in progress at the same time.
    79  	// c.Unlock()
    80  	c.mu.Unlock()
    81  
    82  	if !c.mu.TryLock() {
    83  		// if the code is unable to acquire a lock
    84  		// despite the mutex of c being unlocked, throw an error
    85  		err := errors.New("TryLock failed inside sendWithAuth with mutex unlocked")
    86  		return err
    87  	}
    88  	c.mu.Unlock() // undo changes from the previous TryLock
    89  
    90  	return c.Send(req, v)
    91  }
    92  
    93  // this method is used to invoke the sendWithAuth method, which will then check
    94  // operationally the privated mutex field of Client structure.
    95  func (c *Client) createProduct(ctx context.Context, product Product) (*CreateProductResponse, error) {
    96  	req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/catalogs/products"), product)
    97  	response := &CreateProductResponse{}
    98  	if err != nil {
    99  		return response, err
   100  	}
   101  	err = c.sendWithAuth(req, response)
   102  	return response, err
   103  }
   104  
   105  func TestClientMutex(t *testing.T) {
   106  	c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
   107  	c.GetAccessToken(context.Background())
   108  
   109  	// Basic Testing of the private mutex field
   110  	c.mu.Lock()
   111  	if c.mu.TryLock() {
   112  		t.Fatalf("TryLock succeeded with mutex locked")
   113  	}
   114  	c.mu.Unlock()
   115  	if !c.mu.TryLock() {
   116  		t.Fatalf("TryLock failed with mutex unlocked")
   117  	}
   118  	c.mu.Unlock() // undo changes from the previous TryLock
   119  
   120  	// Operational testing of the private mutex field
   121  	n_iter := 2
   122  
   123  	errs := make(chan error)
   124  
   125  	for i := 0; i < n_iter; i++ {
   126  		go func() {
   127  			_, err := c.createProduct(context.Background(), createRandomProduct(t))
   128  			errs <- err
   129  		}()
   130  	}
   131  
   132  	for i := 0; i < n_iter; i++ {
   133  		err := <-errs
   134  		assert.Equal(t, nil, err)
   135  	}
   136  
   137  }