github.com/prebid/prebid-server/v2@v2.18.0/prebid_cache_client/client_test.go (about)

     1  package prebid_cache_client
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"strconv"
    11  	"testing"
    12  
    13  	"github.com/prebid/prebid-server/v2/config"
    14  	"github.com/prebid/prebid-server/v2/metrics"
    15  	metricsConf "github.com/prebid/prebid-server/v2/metrics/config"
    16  	"github.com/prebid/prebid-server/v2/util/jsonutil"
    17  
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/mock"
    20  )
    21  
    22  func TestEmptyPut(t *testing.T) {
    23  	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    24  		t.Errorf("The server should not be called.")
    25  	})
    26  	server := httptest.NewServer(handler)
    27  	defer server.Close()
    28  
    29  	metricsMock := &metrics.MetricsEngineMock{}
    30  
    31  	client := &clientImpl{
    32  		httpClient: server.Client(),
    33  		putUrl:     server.URL,
    34  		metrics:    metricsMock,
    35  	}
    36  	ids, _ := client.PutJson(context.Background(), nil)
    37  	assertIntEqual(t, len(ids), 0)
    38  	ids, _ = client.PutJson(context.Background(), []Cacheable{})
    39  	assertIntEqual(t, len(ids), 0)
    40  
    41  	metricsMock.AssertNotCalled(t, "RecordPrebidCacheRequestTime")
    42  }
    43  
    44  func TestBadResponse(t *testing.T) {
    45  	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    46  		w.WriteHeader(500)
    47  	})
    48  	server := httptest.NewServer(handler)
    49  	defer server.Close()
    50  
    51  	metricsMock := &metrics.MetricsEngineMock{}
    52  	metricsMock.On("RecordPrebidCacheRequestTime", true, mock.Anything).Once()
    53  
    54  	client := &clientImpl{
    55  		httpClient: server.Client(),
    56  		putUrl:     server.URL,
    57  		metrics:    metricsMock,
    58  	}
    59  	ids, _ := client.PutJson(context.Background(), []Cacheable{
    60  		{
    61  			Type: TypeJSON,
    62  			Data: json.RawMessage("true"),
    63  		}, {
    64  			Type: TypeJSON,
    65  			Data: json.RawMessage("false"),
    66  		},
    67  	})
    68  	assertIntEqual(t, len(ids), 2)
    69  	assertStringEqual(t, ids[0], "")
    70  	assertStringEqual(t, ids[1], "")
    71  
    72  	metricsMock.AssertExpectations(t)
    73  }
    74  
    75  func TestCancelledContext(t *testing.T) {
    76  	testCases := []struct {
    77  		description         string
    78  		cacheable           []Cacheable
    79  		expectedItems       int
    80  		expectedPayloadSize int
    81  	}{
    82  		{
    83  			description: "1 Item",
    84  			cacheable: []Cacheable{
    85  				{
    86  					Type: TypeJSON,
    87  					Data: json.RawMessage("true"),
    88  				},
    89  			},
    90  			expectedItems:       1,
    91  			expectedPayloadSize: 39,
    92  		},
    93  		{
    94  			description: "2 Items",
    95  			cacheable: []Cacheable{
    96  				{
    97  					Type: TypeJSON,
    98  					Data: json.RawMessage("true"),
    99  				},
   100  				{
   101  					Type: TypeJSON,
   102  					Data: json.RawMessage("false"),
   103  				},
   104  			},
   105  			expectedItems:       2,
   106  			expectedPayloadSize: 69,
   107  		},
   108  	}
   109  
   110  	// Initialize Stub Server
   111  	stubHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   112  		w.WriteHeader(200)
   113  	})
   114  	stubServer := httptest.NewServer(stubHandler)
   115  	defer stubServer.Close()
   116  
   117  	// Run Tests
   118  	for _, testCase := range testCases {
   119  		metricsMock := &metrics.MetricsEngineMock{}
   120  		metricsMock.On("RecordPrebidCacheRequestTime", false, mock.Anything).Once()
   121  
   122  		client := &clientImpl{
   123  			httpClient: stubServer.Client(),
   124  			putUrl:     stubServer.URL,
   125  			metrics:    metricsMock,
   126  		}
   127  
   128  		ctx, cancel := context.WithCancel(context.Background())
   129  		cancel()
   130  		ids, errs := client.PutJson(ctx, testCase.cacheable)
   131  
   132  		expectedErrorMessage := fmt.Sprintf("Items=%v, Payload Size=%v", testCase.expectedItems, testCase.expectedPayloadSize)
   133  
   134  		assert.Equal(t, testCase.expectedItems, len(ids), testCase.description+":ids")
   135  		assert.Len(t, errs, 1)
   136  		assert.Contains(t, errs[0].Error(), "Error sending the request to Prebid Cache: context canceled", testCase.description+":error")
   137  		assert.Contains(t, errs[0].Error(), expectedErrorMessage, testCase.description+":error_dimensions")
   138  		metricsMock.AssertExpectations(t)
   139  	}
   140  }
   141  
   142  func TestSuccessfulPut(t *testing.T) {
   143  	server := httptest.NewServer(newHandler(2))
   144  	defer server.Close()
   145  
   146  	metricsMock := &metrics.MetricsEngineMock{}
   147  	metricsMock.On("RecordPrebidCacheRequestTime", true, mock.Anything).Once()
   148  
   149  	client := &clientImpl{
   150  		httpClient: server.Client(),
   151  		putUrl:     server.URL,
   152  		metrics:    metricsMock,
   153  	}
   154  
   155  	ids, _ := client.PutJson(context.Background(), []Cacheable{
   156  		{
   157  			Type:       TypeJSON,
   158  			Data:       json.RawMessage("true"),
   159  			TTLSeconds: 300,
   160  		}, {
   161  			Type: TypeJSON,
   162  			Data: json.RawMessage("false"),
   163  		},
   164  	})
   165  	assertIntEqual(t, len(ids), 2)
   166  	assertStringEqual(t, ids[0], "0")
   167  	assertStringEqual(t, ids[1], "1")
   168  
   169  	metricsMock.AssertExpectations(t)
   170  }
   171  
   172  func TestEncodeValueToBuffer(t *testing.T) {
   173  	buf := new(bytes.Buffer)
   174  	testCache := Cacheable{
   175  		Type:       TypeJSON,
   176  		Data:       json.RawMessage(`{}`),
   177  		TTLSeconds: 300,
   178  		BidID:      "bid",
   179  		Bidder:     "bdr",
   180  		Timestamp:  123456789,
   181  	}
   182  	expected := string(`{"type":"json","ttlseconds":300,"value":{},"bidid":"bid","bidder":"bdr","timestamp":123456789}`)
   183  	_ = encodeValueToBuffer(testCache, false, buf)
   184  	actual := buf.String()
   185  	assertStringEqual(t, expected, actual)
   186  }
   187  
   188  // The following test asserts that the cache client's GetExtCacheData() implementation is able to pull return the exact Path and Host that were
   189  // specified in Prebid-Server's configuration, no substitutions nor default values.
   190  func TestStripCacheHostAndPath(t *testing.T) {
   191  	inCacheURL := config.Cache{ExpectedTimeMillis: 10}
   192  	type aTest struct {
   193  		inExtCacheURL  config.ExternalCache
   194  		expectedScheme string
   195  		expectedHost   string
   196  		expectedPath   string
   197  	}
   198  	testInput := []aTest{
   199  		{
   200  			inExtCacheURL: config.ExternalCache{
   201  				Scheme: "",
   202  				Host:   "prebid-server.prebid.org",
   203  				Path:   "/pbcache/endpoint",
   204  			},
   205  			expectedScheme: "",
   206  			expectedHost:   "prebid-server.prebid.org",
   207  			expectedPath:   "/pbcache/endpoint",
   208  		},
   209  		{
   210  			inExtCacheURL: config.ExternalCache{
   211  				Scheme: "https",
   212  				Host:   "prebid-server.prebid.org",
   213  				Path:   "/pbcache/endpoint",
   214  			},
   215  			expectedScheme: "https",
   216  			expectedHost:   "prebid-server.prebid.org",
   217  			expectedPath:   "/pbcache/endpoint",
   218  		},
   219  		{
   220  			inExtCacheURL: config.ExternalCache{
   221  				Scheme: "",
   222  				Host:   "prebidcache.net",
   223  				Path:   "",
   224  			},
   225  			expectedScheme: "",
   226  			expectedHost:   "prebidcache.net",
   227  			expectedPath:   "",
   228  		},
   229  		{
   230  			inExtCacheURL: config.ExternalCache{
   231  				Scheme: "",
   232  				Host:   "",
   233  				Path:   "",
   234  			},
   235  			expectedScheme: "",
   236  			expectedHost:   "",
   237  			expectedPath:   "",
   238  		},
   239  		{
   240  			inExtCacheURL: config.ExternalCache{
   241  				Scheme: "",
   242  				Host:   "prebid-server.prebid.org",
   243  				Path:   "pbcache/endpoint",
   244  			},
   245  			expectedScheme: "",
   246  			expectedHost:   "prebid-server.prebid.org",
   247  			expectedPath:   "/pbcache/endpoint",
   248  		},
   249  		{
   250  			inExtCacheURL: config.ExternalCache{
   251  				Scheme: "",
   252  				Host:   "prebidcache.net",
   253  				Path:   "/",
   254  			},
   255  			expectedScheme: "",
   256  			expectedHost:   "prebidcache.net",
   257  			expectedPath:   "",
   258  		},
   259  	}
   260  	for _, test := range testInput {
   261  		cacheClient := NewClient(&http.Client{}, &inCacheURL, &test.inExtCacheURL, &metricsConf.NilMetricsEngine{})
   262  		scheme, host, path := cacheClient.GetExtCacheData()
   263  
   264  		assert.Equal(t, test.expectedScheme, scheme)
   265  		assert.Equal(t, test.expectedHost, host)
   266  		assert.Equal(t, test.expectedPath, path)
   267  	}
   268  }
   269  
   270  func assertIntEqual(t *testing.T, expected, actual int) {
   271  	t.Helper()
   272  	if expected != actual {
   273  		t.Errorf("Expected %d, got %d", expected, actual)
   274  	}
   275  }
   276  
   277  func assertStringEqual(t *testing.T, expected, actual string) {
   278  	t.Helper()
   279  	if expected != actual {
   280  		t.Errorf(`Expected "%s", got "%s"`, expected, actual)
   281  	}
   282  }
   283  
   284  type handlerResponseObject struct {
   285  	UUID string `json:"uuid"`
   286  }
   287  
   288  type handlerResponse struct {
   289  	Responses []handlerResponseObject `json:"responses"`
   290  }
   291  
   292  func newHandler(numResponses int) http.HandlerFunc {
   293  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   294  		resp := handlerResponse{
   295  			Responses: make([]handlerResponseObject, numResponses),
   296  		}
   297  		for i := 0; i < numResponses; i++ {
   298  			resp.Responses[i].UUID = strconv.Itoa(i)
   299  		}
   300  
   301  		respBytes, _ := jsonutil.Marshal(resp)
   302  		w.Write(respBytes)
   303  	})
   304  }