github.com/prebid/prebid-server/v2@v2.18.0/endpoints/events/vtrack_test.go (about)

     1  package events
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/prebid/prebid-server/v2/config"
    16  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    17  	"github.com/prebid/prebid-server/v2/prebid_cache_client"
    18  	"github.com/prebid/prebid-server/v2/stored_requests"
    19  	"github.com/prebid/prebid-server/v2/util/jsonutil"
    20  	"github.com/stretchr/testify/assert"
    21  )
    22  
    23  const (
    24  	maxSize = 1024 * 256
    25  
    26  	vastXmlWithImpressionWithContent    = "<VAST version=\"3.0\"><Ad><Wrapper><AdSystem>prebid.org wrapper</AdSystem><VASTAdTagURI><![CDATA[adm2]]></VASTAdTagURI><Impression>content</Impression><Creatives></Creatives></Wrapper></Ad></VAST>"
    27  	vastXmlWithImpressionWithoutContent = "<VAST version=\"3.0\"><Ad><Wrapper><AdSystem>prebid.org wrapper</AdSystem><VASTAdTagURI><![CDATA[adm2]]></VASTAdTagURI><Impression></Impression><Creatives></Creatives></Wrapper></Ad></VAST>"
    28  	vastXmlWithoutImpression            = "<VAST version=\"3.0\"><Ad><Wrapper><AdSystem>prebid.org wrapper</AdSystem><VASTAdTagURI><![CDATA[adm2]]></VASTAdTagURI><Creatives></Creatives></Wrapper></Ad></VAST>"
    29  )
    30  
    31  // Mock pbs cache client
    32  type vtrackMockCacheClient struct {
    33  	Fail   bool
    34  	Error  error
    35  	Uuids  []string
    36  	Values []prebid_cache_client.Cacheable
    37  }
    38  
    39  func (m *vtrackMockCacheClient) PutJson(ctx context.Context, values []prebid_cache_client.Cacheable) ([]string, []error) {
    40  	if m.Fail {
    41  		return []string{}, []error{m.Error}
    42  	}
    43  	m.Values = values
    44  	return m.Uuids, []error{}
    45  }
    46  func (m *vtrackMockCacheClient) GetExtCacheData() (scheme string, host string, path string) {
    47  	return
    48  }
    49  
    50  // Test
    51  func TestShouldRespondWithBadRequestWhenAccountParameterIsMissing(t *testing.T) {
    52  	// mock pbs cache client
    53  	mockCacheClient := &vtrackMockCacheClient{}
    54  
    55  	// mock AccountsFetcher
    56  	mockAccountsFetcher := &mockAccountsFetcher{}
    57  
    58  	// mock config
    59  	cfg := &config.Configuration{
    60  		AccountDefaults: config.Account{},
    61  	}
    62  	cfg.MarshalAccountDefaults()
    63  
    64  	// prepare
    65  	reqData := ""
    66  
    67  	req := httptest.NewRequest("POST", "/vtrack", strings.NewReader(reqData))
    68  	recorder := httptest.NewRecorder()
    69  
    70  	e := vtrackEndpoint{
    71  		Cfg:                 cfg,
    72  		BidderInfos:         nil,
    73  		Cache:               mockCacheClient,
    74  		Accounts:            mockAccountsFetcher,
    75  		normalizeBidderName: openrtb_ext.NormalizeBidderName,
    76  	}
    77  
    78  	// execute
    79  	e.Handle(recorder, req, nil)
    80  
    81  	d, err := io.ReadAll(recorder.Result().Body)
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  
    86  	// validate
    87  	assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 on request with missing account parameter")
    88  	assert.Equal(t, "Account 'a' is required query parameter and can't be empty", string(d))
    89  }
    90  
    91  func TestShouldRespondWithBadRequestWhenRequestBodyIsEmpty(t *testing.T) {
    92  	// mock pbs cache client
    93  	mockCacheClient := &vtrackMockCacheClient{}
    94  
    95  	// mock AccountsFetcher
    96  	mockAccountsFetcher := &mockAccountsFetcher{}
    97  
    98  	// config
    99  	cfg := &config.Configuration{
   100  		MaxRequestSize:  maxSize,
   101  		AccountDefaults: config.Account{},
   102  	}
   103  	cfg.MarshalAccountDefaults()
   104  
   105  	// prepare
   106  	reqData := ""
   107  
   108  	req := httptest.NewRequest("POST", "/vtrack?a=events_enabled", strings.NewReader(reqData))
   109  
   110  	recorder := httptest.NewRecorder()
   111  
   112  	e := vtrackEndpoint{
   113  		Cfg:                 cfg,
   114  		BidderInfos:         nil,
   115  		Cache:               mockCacheClient,
   116  		Accounts:            mockAccountsFetcher,
   117  		normalizeBidderName: openrtb_ext.NormalizeBidderName,
   118  	}
   119  
   120  	// execute
   121  	e.Handle(recorder, req, nil)
   122  
   123  	d, err := io.ReadAll(recorder.Result().Body)
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  
   128  	// validate
   129  	assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 on request with empty body")
   130  	assert.Equal(t, "Invalid request: request body is empty\n", string(d))
   131  }
   132  
   133  func TestShouldRespondWithBadRequestWhenRequestBodyIsInvalid(t *testing.T) {
   134  	// mock pbs cache client
   135  	mockCacheClient := &vtrackMockCacheClient{}
   136  
   137  	// mock AccountsFetcher
   138  	mockAccountsFetcher := &mockAccountsFetcher{}
   139  
   140  	// config
   141  	cfg := &config.Configuration{
   142  		MaxRequestSize:  maxSize,
   143  		AccountDefaults: config.Account{},
   144  	}
   145  	cfg.MarshalAccountDefaults()
   146  
   147  	// prepare
   148  	reqData := "invalid"
   149  
   150  	req := httptest.NewRequest("POST", "/vtrack?a=events_enabled", strings.NewReader(reqData))
   151  
   152  	recorder := httptest.NewRecorder()
   153  
   154  	e := vtrackEndpoint{
   155  		Cfg:                 cfg,
   156  		BidderInfos:         nil,
   157  		Cache:               mockCacheClient,
   158  		Accounts:            mockAccountsFetcher,
   159  		normalizeBidderName: openrtb_ext.NormalizeBidderName,
   160  	}
   161  
   162  	// execute
   163  	e.Handle(recorder, req, nil)
   164  
   165  	// validate
   166  	assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 on request with invalid body")
   167  }
   168  
   169  func TestShouldRespondWithBadRequestWhenBidIdIsMissing(t *testing.T) {
   170  	// mock pbs cache client
   171  	mockCacheClient := &vtrackMockCacheClient{}
   172  
   173  	// mock AccountsFetcher
   174  	mockAccountsFetcher := &mockAccountsFetcher{}
   175  
   176  	// config
   177  	cfg := &config.Configuration{
   178  		MaxRequestSize:  maxSize,
   179  		AccountDefaults: config.Account{},
   180  	}
   181  	cfg.MarshalAccountDefaults()
   182  
   183  	// prepare
   184  	data := &BidCacheRequest{
   185  		Puts: []prebid_cache_client.Cacheable{
   186  			{},
   187  		},
   188  	}
   189  
   190  	reqData, err := jsonutil.Marshal(data)
   191  	if err != nil {
   192  		t.Fatal(err)
   193  	}
   194  
   195  	req := httptest.NewRequest("POST", "/vtrack?a=events_enabled", strings.NewReader(string(reqData)))
   196  
   197  	recorder := httptest.NewRecorder()
   198  
   199  	e := vtrackEndpoint{
   200  		Cfg:                 cfg,
   201  		BidderInfos:         nil,
   202  		Cache:               mockCacheClient,
   203  		Accounts:            mockAccountsFetcher,
   204  		normalizeBidderName: openrtb_ext.NormalizeBidderName,
   205  	}
   206  
   207  	// execute
   208  	e.Handle(recorder, req, nil)
   209  
   210  	d, err := io.ReadAll(recorder.Result().Body)
   211  	if err != nil {
   212  		t.Fatal(err)
   213  	}
   214  
   215  	// validate
   216  	assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 on request with elements missing bidid")
   217  	assert.Equal(t, "Invalid request: 'bidid' is required field and can't be empty\n", string(d))
   218  }
   219  
   220  func TestShouldRespondWithBadRequestWhenBidderIsMissing(t *testing.T) {
   221  	// mock pbs cache client
   222  	mockCacheClient := &vtrackMockCacheClient{}
   223  
   224  	// mock AccountsFetcher
   225  	mockAccountsFetcher := &mockAccountsFetcher{}
   226  
   227  	// config
   228  	cfg := &config.Configuration{
   229  		MaxRequestSize:  maxSize,
   230  		AccountDefaults: config.Account{},
   231  	}
   232  	cfg.MarshalAccountDefaults()
   233  
   234  	// prepare
   235  	data := &BidCacheRequest{
   236  		Puts: []prebid_cache_client.Cacheable{
   237  			{
   238  				BidID: "test",
   239  			},
   240  		},
   241  	}
   242  
   243  	reqData, err := jsonutil.Marshal(data)
   244  	if err != nil {
   245  		t.Fatal(err)
   246  	}
   247  
   248  	req := httptest.NewRequest("POST", "/vtrack?a=events_enabled", strings.NewReader(string(reqData)))
   249  
   250  	recorder := httptest.NewRecorder()
   251  
   252  	e := vtrackEndpoint{
   253  		Cfg:                 cfg,
   254  		BidderInfos:         nil,
   255  		Cache:               mockCacheClient,
   256  		Accounts:            mockAccountsFetcher,
   257  		normalizeBidderName: openrtb_ext.NormalizeBidderName,
   258  	}
   259  
   260  	// execute
   261  	e.Handle(recorder, req, nil)
   262  
   263  	d, err := io.ReadAll(recorder.Result().Body)
   264  	if err != nil {
   265  		t.Fatal(err)
   266  	}
   267  
   268  	// validate
   269  	assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 on request with elements missing bidder")
   270  	assert.Equal(t, "Invalid request: 'bidder' is required field and can't be empty\n", string(d))
   271  }
   272  
   273  func TestShouldRespondWithInternalServerErrorWhenPbsCacheClientFails(t *testing.T) {
   274  	// mock pbs cache client
   275  	mockCacheClient := &vtrackMockCacheClient{
   276  		Fail:  true,
   277  		Error: fmt.Errorf("pbs cache client failed"),
   278  	}
   279  
   280  	// mock AccountsFetcher
   281  	mockAccountsFetcher := &mockAccountsFetcher{}
   282  
   283  	// config
   284  	cfg := &config.Configuration{
   285  		MaxRequestSize: maxSize, VTrack: config.VTrack{
   286  			TimeoutMS: int64(2000), AllowUnknownBidder: true,
   287  		},
   288  		AccountDefaults: config.Account{},
   289  	}
   290  	cfg.MarshalAccountDefaults()
   291  
   292  	// prepare
   293  	data, err := getValidVTrackRequestBody(false, false)
   294  	if err != nil {
   295  		t.Fatal(err)
   296  	}
   297  
   298  	req := httptest.NewRequest("POST", "/vtrack?a=events_enabled", strings.NewReader(data))
   299  
   300  	recorder := httptest.NewRecorder()
   301  
   302  	e := vtrackEndpoint{
   303  		Cfg:                 cfg,
   304  		BidderInfos:         nil,
   305  		Cache:               mockCacheClient,
   306  		Accounts:            mockAccountsFetcher,
   307  		normalizeBidderName: openrtb_ext.NormalizeBidderName,
   308  	}
   309  
   310  	// execute
   311  	e.Handle(recorder, req, nil)
   312  
   313  	d, err := io.ReadAll(recorder.Result().Body)
   314  	if err != nil {
   315  		t.Fatal(err)
   316  	}
   317  
   318  	// validate
   319  	assert.Equal(t, 500, recorder.Result().StatusCode, "Expected 500 when pbs cache client fails")
   320  	assert.Equal(t, "Error(s) updating vast: pbs cache client failed\n", string(d))
   321  }
   322  
   323  func TestShouldTolerateAccountNotFound(t *testing.T) {
   324  	// mock pbs cache client
   325  	mockCacheClient := &vtrackMockCacheClient{}
   326  
   327  	// mock AccountsFetcher
   328  	mockAccountsFetcher := &mockAccountsFetcher{
   329  		Fail:  true,
   330  		Error: stored_requests.NotFoundError{},
   331  	}
   332  
   333  	// config
   334  	cfg := &config.Configuration{
   335  		MaxRequestSize: maxSize, VTrack: config.VTrack{
   336  			TimeoutMS: int64(2000), AllowUnknownBidder: false,
   337  		},
   338  		AccountDefaults: config.Account{},
   339  	}
   340  	cfg.MarshalAccountDefaults()
   341  
   342  	// prepare
   343  	data, err := getValidVTrackRequestBody(true, false)
   344  	if err != nil {
   345  		t.Fatal(err)
   346  	}
   347  
   348  	req := httptest.NewRequest("POST", "/vtrack?a=1235", strings.NewReader(data))
   349  
   350  	recorder := httptest.NewRecorder()
   351  
   352  	e := vtrackEndpoint{
   353  		Cfg:                 cfg,
   354  		BidderInfos:         nil,
   355  		Cache:               mockCacheClient,
   356  		Accounts:            mockAccountsFetcher,
   357  		normalizeBidderName: openrtb_ext.NormalizeBidderName,
   358  	}
   359  
   360  	// execute
   361  	e.Handle(recorder, req, nil)
   362  
   363  	// validate
   364  	assert.Equal(t, 200, recorder.Result().StatusCode, "Expected 200 when account is not found and request is valid")
   365  	assert.Equal(t, "application/json", recorder.Header().Get("Content-Type"))
   366  }
   367  
   368  func TestShouldSendToCacheExpectedPutsAndUpdatableBiddersWhenBidderVastNotAllowed(t *testing.T) {
   369  	// mock pbs cache client
   370  	mockCacheClient := &vtrackMockCacheClient{
   371  		Fail:  false,
   372  		Uuids: []string{"uuid1"},
   373  	}
   374  
   375  	// mock AccountsFetcher
   376  	mockAccountsFetcher := &mockAccountsFetcher{
   377  		Fail: false,
   378  	}
   379  
   380  	// config
   381  	cfg := &config.Configuration{
   382  		MaxRequestSize: maxSize, VTrack: config.VTrack{
   383  			TimeoutMS: int64(2000), AllowUnknownBidder: false,
   384  		},
   385  		AccountDefaults: config.Account{},
   386  	}
   387  	cfg.MarshalAccountDefaults()
   388  
   389  	// bidder info
   390  	bidderInfos := make(config.BidderInfos)
   391  	bidderInfos["bidder"] = config.BidderInfo{
   392  		Disabled:                false,
   393  		ModifyingVastXmlAllowed: false,
   394  	}
   395  	bidderInfos["updatable_bidder"] = config.BidderInfo{
   396  		Disabled:                false,
   397  		ModifyingVastXmlAllowed: true,
   398  	}
   399  
   400  	// prepare
   401  	data, err := getValidVTrackRequestBody(false, false)
   402  	if err != nil {
   403  		t.Fatal(err)
   404  	}
   405  
   406  	req := httptest.NewRequest("POST", "/vtrack?a=events_enabled", strings.NewReader(data))
   407  
   408  	recorder := httptest.NewRecorder()
   409  
   410  	e := vtrackEndpoint{
   411  		Cfg:                 cfg,
   412  		BidderInfos:         bidderInfos,
   413  		Cache:               mockCacheClient,
   414  		Accounts:            mockAccountsFetcher,
   415  		normalizeBidderName: openrtb_ext.NormalizeBidderName,
   416  	}
   417  
   418  	// execute
   419  	e.Handle(recorder, req, nil)
   420  
   421  	d, err := io.ReadAll(recorder.Result().Body)
   422  	if err != nil {
   423  		t.Fatal(err)
   424  	}
   425  
   426  	// validate
   427  	assert.Equal(t, 200, recorder.Result().StatusCode, "Expected 200 when account is not found and request is valid")
   428  	assert.Equal(t, "{\"responses\":[{\"uuid\":\"uuid1\"}]}", string(d), "Expected 200 when account is found and request is valid")
   429  	assert.Equal(t, "application/json", recorder.Header().Get("Content-Type"))
   430  }
   431  
   432  func TestShouldSendToCacheExpectedPutsAndUpdatableBiddersWhenBidderVastAllowed(t *testing.T) {
   433  	// mock pbs cache client
   434  	mockCacheClient := &vtrackMockCacheClient{
   435  		Fail:  false,
   436  		Uuids: []string{"uuid1", "uuid2"},
   437  	}
   438  
   439  	// mock AccountsFetcher
   440  	mockAccountsFetcher := &mockAccountsFetcher{
   441  		Fail: false,
   442  	}
   443  
   444  	// config
   445  	cfg := &config.Configuration{
   446  		MaxRequestSize: maxSize, VTrack: config.VTrack{
   447  			TimeoutMS: int64(2000), AllowUnknownBidder: false,
   448  		},
   449  		AccountDefaults: config.Account{},
   450  	}
   451  	cfg.MarshalAccountDefaults()
   452  
   453  	// bidder info
   454  	bidderInfos := make(config.BidderInfos)
   455  	bidderInfos["bidder"] = config.BidderInfo{
   456  		Disabled:                false,
   457  		ModifyingVastXmlAllowed: true,
   458  	}
   459  	bidderInfos["updatable_bidder"] = config.BidderInfo{
   460  		Disabled:                false,
   461  		ModifyingVastXmlAllowed: true,
   462  	}
   463  
   464  	// prepare
   465  	data, err := getValidVTrackRequestBody(true, true)
   466  	if err != nil {
   467  		t.Fatal(err)
   468  	}
   469  
   470  	req := httptest.NewRequest("POST", "/vtrack?a=events_enabled", strings.NewReader(data))
   471  
   472  	recorder := httptest.NewRecorder()
   473  
   474  	var mockNormalizeBidderName normalizeBidderName = func(name string) (openrtb_ext.BidderName, bool) {
   475  		return openrtb_ext.BidderName(name), true
   476  	}
   477  	e := vtrackEndpoint{
   478  		Cfg:                 cfg,
   479  		BidderInfos:         bidderInfos,
   480  		Cache:               mockCacheClient,
   481  		Accounts:            mockAccountsFetcher,
   482  		normalizeBidderName: mockNormalizeBidderName,
   483  	}
   484  
   485  	// execute
   486  	e.Handle(recorder, req, nil)
   487  
   488  	d, err := io.ReadAll(recorder.Result().Body)
   489  	if err != nil {
   490  		t.Fatal(err)
   491  	}
   492  
   493  	// validate
   494  	assert.Equal(t, 200, recorder.Result().StatusCode, "Expected 200 when account is not found and request is valid")
   495  	assert.Equal(t, "{\"responses\":[{\"uuid\":\"uuid1\"},{\"uuid\":\"uuid2\"}]}", string(d), "Expected 200 when account is found and request is valid")
   496  	assert.Equal(t, "application/json", recorder.Header().Get("Content-Type"))
   497  	assert.Len(t, mockCacheClient.Values, 2)
   498  	assert.Contains(t, string(mockCacheClient.Values[0].Data), "bidder=bidder")
   499  	assert.Contains(t, string(mockCacheClient.Values[1].Data), "bidder=updatable_bidder")
   500  }
   501  
   502  func TestShouldSendToCacheExpectedPutsAndUpdatableCaseSensitiveBiddersWhenBidderVastAllowed(t *testing.T) {
   503  	// mock pbs cache client
   504  	mockCacheClient := &vtrackMockCacheClient{
   505  		Fail:  false,
   506  		Uuids: []string{"uuid1", "uuid2"},
   507  	}
   508  
   509  	// mock AccountsFetcher
   510  	mockAccountsFetcher := &mockAccountsFetcher{
   511  		Fail: false,
   512  	}
   513  
   514  	// config
   515  	cfg := &config.Configuration{
   516  		MaxRequestSize: maxSize, VTrack: config.VTrack{
   517  			TimeoutMS: int64(2000), AllowUnknownBidder: false,
   518  		},
   519  		AccountDefaults: config.Account{},
   520  	}
   521  	cfg.MarshalAccountDefaults()
   522  
   523  	// bidder info
   524  	bidderInfos := make(config.BidderInfos)
   525  	bidderInfos["appnexus"] = config.BidderInfo{
   526  		Disabled:                false,
   527  		ModifyingVastXmlAllowed: true,
   528  	}
   529  
   530  	d, err := getVTrackRequestData(true, true)
   531  	assert.NoError(t, err)
   532  
   533  	cacheReq := &BidCacheRequest{
   534  		Puts: []prebid_cache_client.Cacheable{
   535  			{
   536  				Type:       prebid_cache_client.TypeXML,
   537  				BidID:      "bidId1",
   538  				Bidder:     "APPNEXUS", // case sensitive name
   539  				Data:       d,
   540  				TTLSeconds: 3600,
   541  				Timestamp:  1000,
   542  			},
   543  			{
   544  				Type:       prebid_cache_client.TypeXML,
   545  				BidID:      "bidId2",
   546  				Bidder:     "ApPnExUs", // case sensitive name
   547  				Data:       d,
   548  				TTLSeconds: 3600,
   549  				Timestamp:  1000,
   550  			},
   551  		},
   552  	}
   553  	buf := &bytes.Buffer{}
   554  	enc := json.NewEncoder(buf)
   555  	enc.SetEscapeHTML(false)
   556  	err = enc.Encode(cacheReq)
   557  	assert.NoError(t, err)
   558  	data := buf.String()
   559  
   560  	req := httptest.NewRequest("POST", "/vtrack?a=events_enabled", strings.NewReader(data))
   561  
   562  	recorder := httptest.NewRecorder()
   563  	e := vtrackEndpoint{
   564  		Cfg:                 cfg,
   565  		BidderInfos:         bidderInfos,
   566  		Cache:               mockCacheClient,
   567  		Accounts:            mockAccountsFetcher,
   568  		normalizeBidderName: openrtb_ext.NormalizeBidderName,
   569  	}
   570  
   571  	// execute
   572  	e.Handle(recorder, req, nil)
   573  
   574  	d, err = io.ReadAll(recorder.Result().Body)
   575  	if err != nil {
   576  		t.Fatal(err)
   577  	}
   578  
   579  	// validate
   580  	assert.Equal(t, 200, recorder.Result().StatusCode, "Expected 200 when account is not found and request is valid")
   581  	assert.Equal(t, "{\"responses\":[{\"uuid\":\"uuid1\"},{\"uuid\":\"uuid2\"}]}", string(d), "Expected 200 when account is found and request is valid")
   582  	assert.Equal(t, "application/json", recorder.Header().Get("Content-Type"))
   583  	assert.Len(t, mockCacheClient.Values, 2)
   584  	assert.Contains(t, string(mockCacheClient.Values[0].Data), "bidder=APPNEXUS")
   585  	assert.Contains(t, string(mockCacheClient.Values[1].Data), "bidder=ApPnExUs")
   586  }
   587  
   588  func TestShouldSendToCacheExpectedPutsAndUpdatableUnknownBiddersWhenUnknownBidderIsAllowed(t *testing.T) {
   589  	// mock pbs cache client
   590  	mockCacheClient := &vtrackMockCacheClient{
   591  		Fail:  false,
   592  		Uuids: []string{"uuid1", "uuid2"},
   593  	}
   594  
   595  	// mock AccountsFetcher
   596  	mockAccountsFetcher := &mockAccountsFetcher{
   597  		Fail: false,
   598  	}
   599  
   600  	// config
   601  	cfg := &config.Configuration{
   602  		MaxRequestSize: maxSize, VTrack: config.VTrack{
   603  			TimeoutMS: int64(2000), AllowUnknownBidder: true,
   604  		},
   605  		AccountDefaults: config.Account{},
   606  	}
   607  	cfg.MarshalAccountDefaults()
   608  
   609  	// bidder info
   610  	bidderInfos := make(config.BidderInfos)
   611  
   612  	// prepare
   613  	data, err := getValidVTrackRequestBody(true, false)
   614  	if err != nil {
   615  		t.Fatal(err)
   616  	}
   617  
   618  	req := httptest.NewRequest("POST", "/vtrack?a=events_enabled", strings.NewReader(data))
   619  
   620  	recorder := httptest.NewRecorder()
   621  
   622  	e := vtrackEndpoint{
   623  		Cfg:                 cfg,
   624  		BidderInfos:         bidderInfos,
   625  		Cache:               mockCacheClient,
   626  		Accounts:            mockAccountsFetcher,
   627  		normalizeBidderName: openrtb_ext.NormalizeBidderName,
   628  	}
   629  
   630  	// execute
   631  	e.Handle(recorder, req, nil)
   632  
   633  	d, err := io.ReadAll(recorder.Result().Body)
   634  	if err != nil {
   635  		t.Fatal(err)
   636  	}
   637  
   638  	// validate
   639  	assert.Equal(t, 200, recorder.Result().StatusCode, "Expected 200 when account is not found and request is valid")
   640  	assert.Equal(t, "{\"responses\":[{\"uuid\":\"uuid1\"},{\"uuid\":\"uuid2\"}]}", string(d), "Expected 200 when account is found, request has unknown bidders but allowUnknownBidders is enabled")
   641  	assert.Equal(t, "application/json", recorder.Header().Get("Content-Type"))
   642  }
   643  
   644  func TestShouldReturnBadRequestWhenRequestExceedsMaxRequestSize(t *testing.T) {
   645  	// mock pbs cache client
   646  	mockCacheClient := &vtrackMockCacheClient{
   647  		Fail:  false,
   648  		Uuids: []string{"uuid1", "uuid2"},
   649  	}
   650  
   651  	// mock AccountsFetcher
   652  	mockAccountsFetcher := &mockAccountsFetcher{
   653  		Fail: false,
   654  	}
   655  
   656  	// config
   657  	cfg := &config.Configuration{
   658  		MaxRequestSize: 1,
   659  		VTrack: config.VTrack{
   660  			TimeoutMS: int64(2000), AllowUnknownBidder: true,
   661  		},
   662  		AccountDefaults: config.Account{},
   663  	}
   664  	cfg.MarshalAccountDefaults()
   665  
   666  	// bidder info
   667  	bidderInfos := make(config.BidderInfos)
   668  
   669  	// prepare
   670  	data, err := getValidVTrackRequestBody(true, false)
   671  	if err != nil {
   672  		t.Fatal(err)
   673  	}
   674  
   675  	req := httptest.NewRequest("POST", "/vtrack?a=events_enabled", strings.NewReader(data))
   676  
   677  	recorder := httptest.NewRecorder()
   678  
   679  	e := vtrackEndpoint{
   680  		Cfg:                 cfg,
   681  		BidderInfos:         bidderInfos,
   682  		Cache:               mockCacheClient,
   683  		Accounts:            mockAccountsFetcher,
   684  		normalizeBidderName: openrtb_ext.NormalizeBidderName,
   685  	}
   686  
   687  	// execute
   688  	e.Handle(recorder, req, nil)
   689  
   690  	d, err := io.ReadAll(recorder.Result().Body)
   691  	if err != nil {
   692  		t.Fatal(err)
   693  	}
   694  
   695  	// validate
   696  	assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 when request exceeds max request size")
   697  	assert.Equal(t, "Invalid request: request size exceeded max size of 1 bytes\n", string(d))
   698  }
   699  
   700  func TestShouldRespondWithInternalErrorPbsCacheIsNotConfigured(t *testing.T) {
   701  	// mock AccountsFetcher
   702  	mockAccountsFetcher := &mockAccountsFetcher{
   703  		Fail: false,
   704  	}
   705  
   706  	// config
   707  	cfg := &config.Configuration{
   708  		MaxRequestSize: maxSize, VTrack: config.VTrack{
   709  			TimeoutMS: int64(2000), AllowUnknownBidder: false,
   710  		},
   711  		AccountDefaults: config.Account{},
   712  	}
   713  	cfg.MarshalAccountDefaults()
   714  
   715  	// prepare
   716  	data, err := getValidVTrackRequestBody(true, true)
   717  	if err != nil {
   718  		t.Fatal(err)
   719  	}
   720  
   721  	req := httptest.NewRequest("POST", "/vtrack?a=events_enabled", strings.NewReader(data))
   722  	recorder := httptest.NewRecorder()
   723  
   724  	e := vtrackEndpoint{
   725  		Cfg:                 cfg,
   726  		BidderInfos:         nil,
   727  		Cache:               nil,
   728  		Accounts:            mockAccountsFetcher,
   729  		normalizeBidderName: openrtb_ext.NormalizeBidderName,
   730  	}
   731  
   732  	// execute
   733  	e.Handle(recorder, req, nil)
   734  
   735  	d, err := io.ReadAll(recorder.Result().Body)
   736  	if err != nil {
   737  		t.Fatal(err)
   738  	}
   739  
   740  	// validate
   741  	assert.Equal(t, 500, recorder.Result().StatusCode, "Expected 500 when pbs cache is not configured")
   742  	assert.Equal(t, "PBS Cache client is not configured", string(d))
   743  }
   744  
   745  func TestVastUrlShouldReturnExpectedUrl(t *testing.T) {
   746  	url := GetVastUrlTracking("http://external-url", "bidId", "bidder", "accountId", 1000, "integrationType")
   747  	assert.Equal(t, "http://external-url/event?t=imp&b=bidId&a=accountId&bidder=bidder&f=b&int=integrationType&ts=1000", url, "Invalid vast url")
   748  }
   749  
   750  func getValidVTrackRequestBody(withImpression bool, withContent bool) (string, error) {
   751  	d, e := getVTrackRequestData(withImpression, withContent)
   752  
   753  	if e != nil {
   754  		return "", e
   755  	}
   756  
   757  	req := &BidCacheRequest{
   758  		Puts: []prebid_cache_client.Cacheable{
   759  			{
   760  				Type:       prebid_cache_client.TypeXML,
   761  				BidID:      "bidId1",
   762  				Bidder:     "bidder",
   763  				Data:       d,
   764  				TTLSeconds: 3600,
   765  				Timestamp:  1000,
   766  			},
   767  			{
   768  				Type:       prebid_cache_client.TypeXML,
   769  				BidID:      "bidId2",
   770  				Bidder:     "updatable_bidder",
   771  				Data:       d,
   772  				TTLSeconds: 3600,
   773  				Timestamp:  1000,
   774  			},
   775  		},
   776  	}
   777  
   778  	buf := &bytes.Buffer{}
   779  	enc := json.NewEncoder(buf)
   780  	enc.SetEscapeHTML(false)
   781  
   782  	e = enc.Encode(req)
   783  
   784  	return buf.String(), e
   785  }
   786  
   787  func getVTrackRequestData(wi bool, wic bool) (db []byte, e error) {
   788  	data := &bytes.Buffer{}
   789  	enc := json.NewEncoder(data)
   790  	enc.SetEscapeHTML(false)
   791  
   792  	if wi && wic {
   793  		e = enc.Encode(vastXmlWithImpressionWithContent)
   794  		return data.Bytes(), e
   795  	} else if wi {
   796  		e = enc.Encode(vastXmlWithImpressionWithoutContent)
   797  	} else {
   798  		enc.Encode(vastXmlWithoutImpression)
   799  	}
   800  
   801  	return data.Bytes(), e
   802  }
   803  
   804  func TestGetIntegrationType(t *testing.T) {
   805  	testCases := []struct {
   806  		description             string
   807  		givenHttpRequest        *http.Request
   808  		expectedIntegrationType string
   809  		expectedError           error
   810  	}{
   811  		{
   812  			description:             "Integration type in http request is valid, expect same integration time and no errors",
   813  			givenHttpRequest:        httptest.NewRequest("GET", "/event?t=win&b=bidId&f=b&ts=1000&x=1&a=accountId&bidder=bidder&int=TestIntegrationType", strings.NewReader("")),
   814  			expectedIntegrationType: "TestIntegrationType",
   815  			expectedError:           nil,
   816  		},
   817  		{
   818  			description:      "Integration type in http request is too long, expect too long error",
   819  			givenHttpRequest: httptest.NewRequest("GET", "/event?t=win&b=bidId&f=b&ts=1000&x=1&a=accountId&bidder=bidder&int=TestIntegrationTypeTooLongTestIntegrationTypeTooLongTestIntegrationType", strings.NewReader("")),
   820  			expectedError:    errors.New("integration type length is too long"),
   821  		},
   822  		{
   823  			description:      "Integration type in http request contains invalid character, expect invalid character error",
   824  			givenHttpRequest: httptest.NewRequest("GET", "/event?t=win&b=bidId&f=b&ts=1000&x=1&a=accountId&bidder=bidder&int=Te$tIntegrationType", strings.NewReader("")),
   825  			expectedError:    errors.New("integration type can only contain numbers, letters and these characters '-', '_'"),
   826  		},
   827  	}
   828  
   829  	for _, test := range testCases {
   830  		integrationType, err := getIntegrationType(test.givenHttpRequest)
   831  		if test.expectedError != nil {
   832  			assert.Equal(t, test.expectedError, err, test.description)
   833  		} else {
   834  			assert.Empty(t, err, test.description)
   835  			assert.Equalf(t, test.expectedIntegrationType, integrationType, test.description)
   836  		}
   837  	}
   838  }