github.com/prebid/prebid-server@v0.275.0/endpoints/openrtb2/video_auction_test.go (about)

     1  package openrtb2
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"os"
    10  	"regexp"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/prebid/prebid-server/analytics"
    15  	analyticsConf "github.com/prebid/prebid-server/analytics/config"
    16  	"github.com/prebid/prebid-server/config"
    17  	"github.com/prebid/prebid-server/errortypes"
    18  	"github.com/prebid/prebid-server/exchange"
    19  	"github.com/prebid/prebid-server/hooks"
    20  	"github.com/prebid/prebid-server/metrics"
    21  	metricsConfig "github.com/prebid/prebid-server/metrics/config"
    22  	"github.com/prebid/prebid-server/openrtb_ext"
    23  	"github.com/prebid/prebid-server/prebid_cache_client"
    24  	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
    25  	"github.com/prebid/prebid-server/util/ptrutil"
    26  
    27  	"github.com/prebid/openrtb/v19/adcom1"
    28  	"github.com/prebid/openrtb/v19/openrtb2"
    29  	gometrics "github.com/rcrowley/go-metrics"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  func TestVideoEndpointImpressionsNumber(t *testing.T) {
    35  	ex := &mockExchangeVideo{}
    36  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample.json")
    37  	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
    38  	recorder := httptest.NewRecorder()
    39  
    40  	deps := mockDeps(t, ex)
    41  	deps.VideoAuctionEndpoint(recorder, req, nil)
    42  
    43  	if ex.lastRequest == nil {
    44  		t.Fatalf("The request never made it into the Exchange.")
    45  	}
    46  
    47  	respBytes := recorder.Body.Bytes()
    48  	resp := &openrtb_ext.BidResponseVideo{}
    49  	if err := json.Unmarshal(respBytes, resp); err != nil {
    50  		t.Fatalf("Unable to unmarshal response.")
    51  	}
    52  
    53  	assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request")
    54  	assert.Equal(t, string(ex.lastRequest.Site.Page), "prebid.com", "Incorrect site page in request")
    55  	assert.Equal(t, ex.lastRequest.Site.Content.Series, "TvName", "Incorrect site content series in request")
    56  
    57  	assert.Len(t, resp.AdPods, 5, "Incorrect number of Ad Pods in response")
    58  	assert.Len(t, resp.AdPods[0].Targeting, 4, "Incorrect Targeting data in response")
    59  	assert.Len(t, resp.AdPods[1].Targeting, 3, "Incorrect Targeting data in response")
    60  	assert.Len(t, resp.AdPods[2].Targeting, 5, "Incorrect Targeting data in response")
    61  	assert.Len(t, resp.AdPods[3].Targeting, 1, "Incorrect Targeting data in response")
    62  	assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response")
    63  
    64  	assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s", "Incorrect number of Ad Pods in response")
    65  	assert.Equal(t, resp.AdPods[0].Targeting[0].HbDeal, "ABC_123", "If DealID exists in bid response, hb_deal targeting needs to be added to resp")
    66  }
    67  
    68  func TestVideoEndpointImpressionsDuration(t *testing.T) {
    69  	ex := &mockExchangeVideo{}
    70  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_different_durations.json")
    71  	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
    72  	recorder := httptest.NewRecorder()
    73  
    74  	deps := mockDeps(t, ex)
    75  	deps.VideoAuctionEndpoint(recorder, req, nil)
    76  
    77  	if ex.lastRequest == nil {
    78  		t.Fatalf("The request never made it into the Exchange.")
    79  	}
    80  
    81  	var extData openrtb_ext.ExtRequest
    82  	json.Unmarshal(ex.lastRequest.Ext, &extData)
    83  	assert.NotNil(t, extData.Prebid.Targeting.IncludeBidderKeys, "Request ext incorrect: IncludeBidderKeys should be true ")
    84  	assert.True(t, *extData.Prebid.Targeting.IncludeBidderKeys, "Request ext incorrect: IncludeBidderKeys should be true ")
    85  
    86  	assert.Len(t, ex.lastRequest.Imp, 22, "Incorrect number of impressions in request")
    87  	assert.Equal(t, ex.lastRequest.Imp[0].ID, "1_0", "Incorrect impression id in request")
    88  	assert.Equal(t, ex.lastRequest.Imp[0].Video.MaxDuration, int64(15), "Incorrect impression max duration in request")
    89  	assert.Equal(t, ex.lastRequest.Imp[0].Video.MinDuration, int64(15), "Incorrect impression min duration in request")
    90  
    91  	assert.Equal(t, ex.lastRequest.Imp[6].ID, "1_6", "Incorrect impression id in request")
    92  	assert.Equal(t, ex.lastRequest.Imp[6].Video.MaxDuration, int64(30), "Incorrect impression max duration in request")
    93  	assert.Equal(t, ex.lastRequest.Imp[6].Video.MinDuration, int64(30), "Incorrect impression min duration in request")
    94  
    95  	assert.Equal(t, ex.lastRequest.Imp[12].ID, "2_0", "Incorrect impression id in request")
    96  	assert.Equal(t, ex.lastRequest.Imp[12].Video.MaxDuration, int64(15), "Incorrect impression max duration in request")
    97  	assert.Equal(t, ex.lastRequest.Imp[12].Video.MinDuration, int64(15), "Incorrect impression min duration in request")
    98  
    99  	assert.Equal(t, ex.lastRequest.Imp[17].ID, "2_5", "Incorrect impression id in request")
   100  	assert.Equal(t, ex.lastRequest.Imp[17].Video.MaxDuration, int64(30), "Incorrect impression max duration in request")
   101  	assert.Equal(t, ex.lastRequest.Imp[17].Video.MinDuration, int64(30), "Incorrect impression min duration in request")
   102  }
   103  
   104  func TestCreateBidExtension(t *testing.T) {
   105  	durationRange := make([]int, 0)
   106  	durationRange = append(durationRange, 15)
   107  	durationRange = append(durationRange, 30)
   108  
   109  	priceGranRanges := make([]openrtb_ext.GranularityRange, 0)
   110  	priceGranRanges = append(priceGranRanges, openrtb_ext.GranularityRange{
   111  		Max:       30,
   112  		Min:       0,
   113  		Increment: 0.1,
   114  	})
   115  
   116  	translateCategories := true
   117  	videoRequest := openrtb_ext.BidRequestVideo{
   118  		IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{
   119  			PrimaryAdserver:     1,
   120  			Publisher:           "",
   121  			TranslateCategories: &translateCategories,
   122  		},
   123  		PodConfig: openrtb_ext.PodConfig{
   124  			DurationRangeSec:     durationRange,
   125  			RequireExactDuration: false,
   126  		},
   127  		PriceGranularity: &openrtb_ext.PriceGranularity{
   128  			Precision: ptrutil.ToPtr(2),
   129  			Ranges:    priceGranRanges,
   130  		},
   131  	}
   132  	res, err := createBidExtension(&videoRequest)
   133  	assert.NoError(t, err, "Error should be nil")
   134  
   135  	resExt := &openrtb_ext.ExtRequest{}
   136  
   137  	if err := json.Unmarshal(res, &resExt); err != nil {
   138  		assert.Fail(t, "Unable to unmarshal bid extension")
   139  	}
   140  	assert.Equal(t, resExt.Prebid.Targeting.DurationRangeSec, durationRange, "Duration range seconds is incorrect")
   141  	assert.Equal(t, resExt.Prebid.Targeting.PriceGranularity.Ranges, priceGranRanges, "Price granularity is incorrect")
   142  }
   143  
   144  func TestCreateBidExtensionTargeting(t *testing.T) {
   145  	ex := &mockExchangeVideo{}
   146  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample.json")
   147  	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
   148  	recorder := httptest.NewRecorder()
   149  
   150  	deps := mockDeps(t, ex)
   151  	deps.VideoAuctionEndpoint(recorder, req, nil)
   152  
   153  	require.NotNil(t, ex.lastRequest, "The request never made it into the Exchange.")
   154  
   155  	// assert targeting set to default
   156  	expectedRequestExt := `{"prebid":{"cache":{"vastxml":{}},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"mediatypepricegranularity":{},"includebidderkeys":true,"includewinners":true,"includebrandcategory":{"primaryadserver":1,"publisher":"","withcategory":true}}}}`
   157  	assert.JSONEq(t, expectedRequestExt, string(ex.lastRequest.Ext))
   158  }
   159  
   160  func TestVideoEndpointDebugQueryTrue(t *testing.T) {
   161  	ex := &mockExchangeVideo{
   162  		cache: &mockCacheClient{},
   163  	}
   164  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample.json")
   165  	req := httptest.NewRequest("POST", "/openrtb2/video?debug=true", strings.NewReader(reqBody))
   166  	recorder := httptest.NewRecorder()
   167  
   168  	deps := mockDeps(t, ex)
   169  	deps.VideoAuctionEndpoint(recorder, req, nil)
   170  
   171  	if ex.lastRequest == nil {
   172  		t.Fatalf("The request never made it into the Exchange.")
   173  	}
   174  	if !ex.cache.called {
   175  		t.Fatalf("Cache was not called when it should have been")
   176  	}
   177  
   178  	respBytes := recorder.Body.Bytes()
   179  	resp := &openrtb_ext.BidResponseVideo{}
   180  	if err := json.Unmarshal(respBytes, resp); err != nil {
   181  		t.Fatalf("Unable to unmarshal response.")
   182  	}
   183  
   184  	assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request")
   185  	assert.Equal(t, string(ex.lastRequest.Site.Page), "prebid.com", "Incorrect site page in request")
   186  	assert.Equal(t, ex.lastRequest.Site.Content.Series, "TvName", "Incorrect site content series in request")
   187  
   188  	assert.Len(t, resp.AdPods, 5, "Incorrect number of Ad Pods in response")
   189  	assert.Len(t, resp.AdPods[0].Targeting, 4, "Incorrect Targeting data in response")
   190  	assert.Len(t, resp.AdPods[1].Targeting, 3, "Incorrect Targeting data in response")
   191  	assert.Len(t, resp.AdPods[2].Targeting, 5, "Incorrect Targeting data in response")
   192  	assert.Len(t, resp.AdPods[3].Targeting, 1, "Incorrect Targeting data in response")
   193  	assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response")
   194  
   195  	assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s", "Incorrect number of Ad Pods in response")
   196  }
   197  
   198  func TestVideoEndpointDebugQueryFalse(t *testing.T) {
   199  	ex := &mockExchangeVideo{
   200  		cache: &mockCacheClient{},
   201  	}
   202  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample.json")
   203  	req := httptest.NewRequest("POST", "/openrtb2/video?debug=123", strings.NewReader(reqBody))
   204  	recorder := httptest.NewRecorder()
   205  
   206  	deps := mockDeps(t, ex)
   207  	deps.VideoAuctionEndpoint(recorder, req, nil)
   208  
   209  	if ex.lastRequest == nil {
   210  		t.Fatalf("The request never made it into the Exchange.")
   211  	}
   212  	if ex.cache.called {
   213  		t.Fatalf("Cache was called when it shouldn't have been")
   214  	}
   215  
   216  	respBytes := recorder.Body.Bytes()
   217  	resp := &openrtb_ext.BidResponseVideo{}
   218  	if err := json.Unmarshal(respBytes, resp); err != nil {
   219  		t.Fatalf("Unable to unmarshal response.")
   220  	}
   221  
   222  	assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request")
   223  	assert.Equal(t, string(ex.lastRequest.Site.Page), "prebid.com", "Incorrect site page in request")
   224  	assert.Equal(t, ex.lastRequest.Site.Content.Series, "TvName", "Incorrect site content series in request")
   225  
   226  	assert.Len(t, resp.AdPods, 5, "Incorrect number of Ad Pods in response")
   227  	assert.Len(t, resp.AdPods[0].Targeting, 4, "Incorrect Targeting data in response")
   228  	assert.Len(t, resp.AdPods[1].Targeting, 3, "Incorrect Targeting data in response")
   229  	assert.Len(t, resp.AdPods[2].Targeting, 5, "Incorrect Targeting data in response")
   230  	assert.Len(t, resp.AdPods[3].Targeting, 1, "Incorrect Targeting data in response")
   231  	assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response")
   232  
   233  	assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s", "Incorrect number of Ad Pods in response")
   234  }
   235  
   236  func TestVideoEndpointDebugError(t *testing.T) {
   237  	ex := &mockExchangeVideo{
   238  		cache: &mockCacheClient{},
   239  	}
   240  	reqBody := readVideoTestFile(t, "sample-requests/video/video_invalid_sample.json")
   241  	req := httptest.NewRequest("POST", "/openrtb2/video?debug=true", strings.NewReader(reqBody))
   242  	recorder := httptest.NewRecorder()
   243  
   244  	deps := mockDeps(t, ex)
   245  	deps.VideoAuctionEndpoint(recorder, req, nil)
   246  
   247  	if !ex.cache.called {
   248  		t.Fatalf("Cache was not called when it should have been")
   249  	}
   250  
   251  	assert.Equal(t, recorder.Code, 500, "Should catch error in request")
   252  }
   253  
   254  func TestVideoEndpointDebugNoAdPods(t *testing.T) {
   255  	ex := &mockExchangeVideoNoBids{
   256  		cache: &mockCacheClient{},
   257  	}
   258  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample.json")
   259  	req := httptest.NewRequest("POST", "/openrtb2/video?debug=true", strings.NewReader(reqBody))
   260  	recorder := httptest.NewRecorder()
   261  
   262  	deps := mockDepsNoBids(t, ex)
   263  	deps.VideoAuctionEndpoint(recorder, req, nil)
   264  
   265  	if ex.lastRequest == nil {
   266  		t.Fatalf("The request never made it into the Exchange.")
   267  	}
   268  	if !ex.cache.called {
   269  		t.Fatalf("Cache was not called when it should have been")
   270  	}
   271  
   272  	respBytes := recorder.Body.Bytes()
   273  	resp := &openrtb_ext.BidResponseVideo{}
   274  	if err := json.Unmarshal(respBytes, resp); err != nil {
   275  		t.Fatalf("Unable to unmarshal response.")
   276  	}
   277  
   278  	assert.Len(t, resp.AdPods, 1, "Debug AdPod should be added to response")
   279  	assert.Empty(t, resp.AdPods[0].Errors, "AdPod Errors should be empty")
   280  	assert.Empty(t, resp.AdPods[0].Targeting[0].HbPb, "Hb_pb should be empty")
   281  	assert.Empty(t, resp.AdPods[0].Targeting[0].HbPbCatDur, "Hb_pb_cat_dur should be empty")
   282  	assert.NotEmpty(t, resp.AdPods[0].Targeting[0].HbCacheID, "Hb_cache_id should not be empty")
   283  	assert.Equal(t, int64(0), resp.AdPods[0].PodId, "Pod ID should be 0")
   284  }
   285  
   286  func TestVideoEndpointNoPods(t *testing.T) {
   287  	ex := &mockExchangeVideo{}
   288  	reqBody := readVideoTestFile(t, "sample-requests/video/video_invalid_sample.json")
   289  	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
   290  	recorder := httptest.NewRecorder()
   291  
   292  	deps := mockDeps(t, ex)
   293  	deps.VideoAuctionEndpoint(recorder, req, nil)
   294  
   295  	errorMessage := string(recorder.Body.Bytes())
   296  
   297  	assert.Equal(t, recorder.Code, 500, "Should catch error in request")
   298  	assert.Equal(t, "Critical error while running the video endpoint:  request missing required field: PodConfig.DurationRangeSec request missing required field: PodConfig.Pods", errorMessage, "Incorrect request validation message")
   299  }
   300  
   301  func TestVideoEndpointValidationsPositive(t *testing.T) {
   302  	ex := &mockExchangeVideo{}
   303  	deps := mockDeps(t, ex)
   304  	deps.cfg.VideoStoredRequestRequired = true
   305  
   306  	durationRange := make([]int, 0)
   307  	durationRange = append(durationRange, 15)
   308  	durationRange = append(durationRange, 30)
   309  
   310  	pods := make([]openrtb_ext.Pod, 0)
   311  	pod1 := openrtb_ext.Pod{
   312  		PodId:            1,
   313  		AdPodDurationSec: 30,
   314  		ConfigId:         "qwerty",
   315  	}
   316  	pod2 := openrtb_ext.Pod{
   317  		PodId:            2,
   318  		AdPodDurationSec: 30,
   319  		ConfigId:         "qwerty",
   320  	}
   321  	pods = append(pods, pod1)
   322  	pods = append(pods, pod2)
   323  
   324  	mimes := make([]string, 0)
   325  	mimes = append(mimes, "mp4")
   326  	mimes = append(mimes, "")
   327  
   328  	videoProtocols := []adcom1.MediaCreativeSubtype{15, 30}
   329  
   330  	req := openrtb_ext.BidRequestVideo{
   331  		StoredRequestId: "123",
   332  		PodConfig: openrtb_ext.PodConfig{
   333  			DurationRangeSec:     durationRange,
   334  			RequireExactDuration: true,
   335  			Pods:                 pods,
   336  		},
   337  		App: &openrtb2.App{
   338  			Bundle: "pbs.com",
   339  		},
   340  		IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{
   341  			PrimaryAdserver: 1,
   342  		},
   343  		Video: &openrtb2.Video{
   344  			MIMEs:     mimes,
   345  			Protocols: videoProtocols,
   346  		},
   347  	}
   348  
   349  	errors, podErrors := deps.validateVideoRequest(&req)
   350  	assert.Len(t, errors, 0, "Errors should be empty")
   351  	assert.Len(t, podErrors, 0, "Pod errors should be empty")
   352  }
   353  
   354  func TestVideoEndpointValidationsCritical(t *testing.T) {
   355  	ex := &mockExchangeVideo{}
   356  	deps := mockDeps(t, ex)
   357  	deps.cfg.VideoStoredRequestRequired = true
   358  
   359  	durationRange := make([]int, 0)
   360  	durationRange = append(durationRange, 0)
   361  	durationRange = append(durationRange, -30)
   362  
   363  	pods := make([]openrtb_ext.Pod, 0)
   364  
   365  	mimes := make([]string, 0)
   366  	mimes = append(mimes, "")
   367  	mimes = append(mimes, "")
   368  
   369  	videoProtocols := []adcom1.MediaCreativeSubtype{}
   370  
   371  	req := openrtb_ext.BidRequestVideo{
   372  		StoredRequestId: "",
   373  		PodConfig: openrtb_ext.PodConfig{
   374  			DurationRangeSec:     durationRange,
   375  			RequireExactDuration: true,
   376  			Pods:                 pods,
   377  		},
   378  		IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{
   379  			PrimaryAdserver: 0,
   380  		},
   381  		Video: &openrtb2.Video{
   382  			MIMEs:     mimes,
   383  			Protocols: videoProtocols,
   384  		},
   385  	}
   386  
   387  	errors, podErrors := deps.validateVideoRequest(&req)
   388  	assert.Len(t, podErrors, 0, "Pod errors should be empty")
   389  	assert.Len(t, errors, 6, "Errors array should contain 6 error messages")
   390  
   391  	assert.Equal(t, "request missing required field: storedrequestid", errors[0].Error(), "Errors array should contain 6 error messages")
   392  	assert.Equal(t, "duration array cannot contain negative or zero values", errors[1].Error(), "Errors array should contain 6 error messages")
   393  	assert.Equal(t, "request missing required field: PodConfig.Pods", errors[2].Error(), "Errors array should contain 6 error messages")
   394  	assert.Equal(t, "request missing required field: site or app", errors[3].Error(), "Errors array should contain 6 error messages")
   395  	assert.Equal(t, "request missing required field: Video.Mimes, mime types contains empty strings only", errors[4].Error(), "Errors array should contain 6 error messages")
   396  	assert.Equal(t, "request missing required field: Video.Protocols", errors[5].Error(), "Errors array should contain 6 error messages")
   397  }
   398  
   399  func TestVideoEndpointValidationsPodErrors(t *testing.T) {
   400  	ex := &mockExchangeVideo{}
   401  	deps := mockDeps(t, ex)
   402  	deps.cfg.VideoStoredRequestRequired = true
   403  
   404  	durationRange := make([]int, 0)
   405  	durationRange = append(durationRange, 15)
   406  	durationRange = append(durationRange, 30)
   407  
   408  	pods := make([]openrtb_ext.Pod, 0)
   409  	pod1 := openrtb_ext.Pod{
   410  		PodId:            1,
   411  		AdPodDurationSec: 30,
   412  		ConfigId:         "qwerty",
   413  	}
   414  	pod2 := openrtb_ext.Pod{
   415  		PodId:            2,
   416  		AdPodDurationSec: 30,
   417  		ConfigId:         "qwerty",
   418  	}
   419  	pod3 := openrtb_ext.Pod{
   420  		PodId:            2,
   421  		AdPodDurationSec: 0,
   422  		ConfigId:         "",
   423  	}
   424  	pod4 := openrtb_ext.Pod{
   425  		PodId:            0,
   426  		AdPodDurationSec: -30,
   427  		ConfigId:         "",
   428  	}
   429  	pods = append(pods, pod1)
   430  	pods = append(pods, pod2)
   431  	pods = append(pods, pod3)
   432  	pods = append(pods, pod4)
   433  
   434  	mimes := make([]string, 0)
   435  	mimes = append(mimes, "mp4")
   436  	mimes = append(mimes, "")
   437  
   438  	videoProtocols := []adcom1.MediaCreativeSubtype{15, 30}
   439  
   440  	req := openrtb_ext.BidRequestVideo{
   441  		StoredRequestId: "123",
   442  		PodConfig: openrtb_ext.PodConfig{
   443  			DurationRangeSec:     durationRange,
   444  			RequireExactDuration: true,
   445  			Pods:                 pods,
   446  		},
   447  		App: &openrtb2.App{
   448  			Bundle: "pbs.com",
   449  		},
   450  		IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{
   451  			PrimaryAdserver: 1,
   452  		},
   453  		Video: &openrtb2.Video{
   454  			MIMEs:     mimes,
   455  			Protocols: videoProtocols,
   456  		},
   457  	}
   458  
   459  	errors, podErrors := deps.validateVideoRequest(&req)
   460  	assert.Len(t, errors, 0, "Errors should be empty")
   461  
   462  	assert.Len(t, podErrors, 2, "Pod errors should contain 2 elements")
   463  
   464  	assert.Equal(t, 2, podErrors[0].PodId, "Pod error ind 0, incorrect id should be 2")
   465  	assert.Equal(t, 2, podErrors[0].PodIndex, "Pod error ind 0, incorrect index should be 2")
   466  	assert.Len(t, podErrors[0].ErrMsgs, 3, "Pod error ind 0 should contain 3 errors")
   467  	assert.Equal(t, "request duplicated required field: PodConfig.Pods.PodId, Pod id: 2", podErrors[0].ErrMsgs[0], "Pod error ind 0 should have duplicated pod id")
   468  	assert.Equal(t, "request missing or incorrect required field: PodConfig.Pods.AdPodDurationSec, Pod index: 2", podErrors[0].ErrMsgs[1], "Pod error ind 0 should have missing AdPodDuration")
   469  	assert.Equal(t, "request missing or incorrect required field: PodConfig.Pods.ConfigId, Pod index: 2", podErrors[0].ErrMsgs[2], "Pod error ind 0 should have missing config id")
   470  
   471  	assert.Equal(t, 0, podErrors[1].PodId, "Pod error ind 1, incorrect id should be 0")
   472  	assert.Equal(t, 3, podErrors[1].PodIndex, "Pod error ind 1, incorrect index should be 3")
   473  	assert.Len(t, podErrors[1].ErrMsgs, 3, "Pod error ind 1 should contain 3 errors")
   474  	assert.Equal(t, "request missing required field: PodConfig.Pods.PodId, Pod index: 3", podErrors[1].ErrMsgs[0], "Pod error ind 1 should have missed pod id")
   475  	assert.Equal(t, "request incorrect required field: PodConfig.Pods.AdPodDurationSec is negative, Pod index: 3", podErrors[1].ErrMsgs[1], "Pod error ind 1 should have negative AdPodDurationSec")
   476  	assert.Equal(t, "request missing or incorrect required field: PodConfig.Pods.ConfigId, Pod index: 3", podErrors[1].ErrMsgs[2], "Pod error ind 1 should have missing config id")
   477  }
   478  
   479  func TestVideoEndpointValidationsSiteAndApp(t *testing.T) {
   480  	ex := &mockExchangeVideo{}
   481  	deps := mockDeps(t, ex)
   482  	deps.cfg.VideoStoredRequestRequired = true
   483  
   484  	durationRange := make([]int, 0)
   485  	durationRange = append(durationRange, 15)
   486  	durationRange = append(durationRange, 30)
   487  
   488  	pods := make([]openrtb_ext.Pod, 0)
   489  	pod1 := openrtb_ext.Pod{
   490  		PodId:            1,
   491  		AdPodDurationSec: 30,
   492  		ConfigId:         "qwerty",
   493  	}
   494  	pod2 := openrtb_ext.Pod{
   495  		PodId:            2,
   496  		AdPodDurationSec: 30,
   497  		ConfigId:         "qwerty",
   498  	}
   499  	pods = append(pods, pod1)
   500  	pods = append(pods, pod2)
   501  
   502  	mimes := make([]string, 0)
   503  	mimes = append(mimes, "mp4")
   504  	mimes = append(mimes, "")
   505  
   506  	videoProtocols := []adcom1.MediaCreativeSubtype{15, 30}
   507  
   508  	req := openrtb_ext.BidRequestVideo{
   509  		StoredRequestId: "123",
   510  		PodConfig: openrtb_ext.PodConfig{
   511  			DurationRangeSec:     durationRange,
   512  			RequireExactDuration: true,
   513  			Pods:                 pods,
   514  		},
   515  		App: &openrtb2.App{
   516  			Bundle: "pbs.com",
   517  		},
   518  		Site: &openrtb2.Site{
   519  			ID: "pbs.com",
   520  		},
   521  		IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{
   522  			PrimaryAdserver: 1,
   523  		},
   524  		Video: &openrtb2.Video{
   525  			MIMEs:     mimes,
   526  			Protocols: videoProtocols,
   527  		},
   528  	}
   529  
   530  	errors, podErrors := deps.validateVideoRequest(&req)
   531  	assert.Equal(t, "request.site or request.app must be defined, but not both", errors[0].Error(), "Site and App error should be present")
   532  	assert.Len(t, podErrors, 0, "Pod errors should be empty")
   533  }
   534  
   535  func TestVideoEndpointValidationsSiteMissingRequiredField(t *testing.T) {
   536  	ex := &mockExchangeVideo{}
   537  	deps := mockDeps(t, ex)
   538  	deps.cfg.VideoStoredRequestRequired = true
   539  
   540  	durationRange := make([]int, 0)
   541  	durationRange = append(durationRange, 15)
   542  	durationRange = append(durationRange, 30)
   543  
   544  	pods := make([]openrtb_ext.Pod, 0)
   545  	pod1 := openrtb_ext.Pod{
   546  		PodId:            1,
   547  		AdPodDurationSec: 30,
   548  		ConfigId:         "qwerty",
   549  	}
   550  	pod2 := openrtb_ext.Pod{
   551  		PodId:            2,
   552  		AdPodDurationSec: 30,
   553  		ConfigId:         "qwerty",
   554  	}
   555  	pods = append(pods, pod1)
   556  	pods = append(pods, pod2)
   557  
   558  	mimes := make([]string, 0)
   559  	mimes = append(mimes, "mp4")
   560  	mimes = append(mimes, "")
   561  
   562  	videoProtocols := []adcom1.MediaCreativeSubtype{15, 30}
   563  
   564  	req := openrtb_ext.BidRequestVideo{
   565  		StoredRequestId: "123",
   566  		PodConfig: openrtb_ext.PodConfig{
   567  			DurationRangeSec:     durationRange,
   568  			RequireExactDuration: true,
   569  			Pods:                 pods,
   570  		},
   571  		Site: &openrtb2.Site{
   572  			Domain: "pbs.com",
   573  		},
   574  		IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{
   575  			PrimaryAdserver: 1,
   576  		},
   577  		Video: &openrtb2.Video{
   578  			MIMEs:     mimes,
   579  			Protocols: videoProtocols,
   580  		},
   581  	}
   582  
   583  	errors, podErrors := deps.validateVideoRequest(&req)
   584  	assert.Equal(t, "request.site missing required field: id or page", errors[0].Error(), "Site required fields error should be present")
   585  	assert.Len(t, podErrors, 0, "Pod errors should be empty")
   586  }
   587  
   588  func TestVideoEndpointValidationsMissingVideo(t *testing.T) {
   589  	ex := &mockExchangeVideo{}
   590  	deps := mockDeps(t, ex)
   591  	deps.cfg.VideoStoredRequestRequired = true
   592  
   593  	req := openrtb_ext.BidRequestVideo{
   594  		StoredRequestId: "123",
   595  		PodConfig: openrtb_ext.PodConfig{
   596  			DurationRangeSec:     []int{15, 30},
   597  			RequireExactDuration: true,
   598  			Pods: []openrtb_ext.Pod{
   599  				{
   600  					PodId:            1,
   601  					AdPodDurationSec: 30,
   602  					ConfigId:         "qwerty",
   603  				},
   604  				{
   605  					PodId:            2,
   606  					AdPodDurationSec: 30,
   607  					ConfigId:         "qwerty",
   608  				},
   609  			},
   610  		},
   611  		App: &openrtb2.App{
   612  			Bundle: "pbs.com",
   613  		},
   614  		IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{
   615  			PrimaryAdserver: 1,
   616  		},
   617  	}
   618  
   619  	errors, podErrors := deps.validateVideoRequest(&req)
   620  	assert.Len(t, podErrors, 0, "Pod errors should be empty")
   621  	assert.Len(t, errors, 1, "Errors array should contain 1 error message")
   622  	assert.Equal(t, "request missing required field: Video", errors[0].Error(), "Errors array should contain message regarding missing Video field")
   623  }
   624  
   625  func TestVideoBuildVideoResponseMissedCacheForOneBid(t *testing.T) {
   626  	openRtbBidResp := openrtb2.BidResponse{}
   627  	podErrors := make([]PodError, 0)
   628  
   629  	seatBids := make([]openrtb2.SeatBid, 0)
   630  	seatBid := openrtb2.SeatBid{}
   631  
   632  	bids := make([]openrtb2.Bid, 0)
   633  	bid1 := openrtb2.Bid{}
   634  	bid2 := openrtb2.Bid{}
   635  	bid3 := openrtb2.Bid{}
   636  
   637  	extBid1 := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"17.00","hb_pb_cat_dur_appnex":"17.00_123_30s","hb_size":"1x1","hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"}}}`)
   638  	extBid2 := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"17.00","hb_pb_cat_dur_appnex":"17.00_456_30s","hb_size":"1x1","hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"}}}`)
   639  	extBid3 := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"17.00","hb_pb_cat_dur_appnex":"17.00_406_30s","hb_size":"1x1"}}}`)
   640  
   641  	bid1.Ext = extBid1
   642  	bids = append(bids, bid1)
   643  
   644  	bid2.Ext = extBid2
   645  	bids = append(bids, bid2)
   646  
   647  	bid3.Ext = extBid3
   648  	bids = append(bids, bid3)
   649  
   650  	seatBid.Bid = bids
   651  	seatBid.Seat = "appnexus"
   652  	seatBids = append(seatBids, seatBid)
   653  	openRtbBidResp.SeatBid = seatBids
   654  
   655  	bidRespVideo, err := buildVideoResponse(&openRtbBidResp, podErrors)
   656  	assert.NoError(t, err, "Should be no error")
   657  	assert.Len(t, bidRespVideo.AdPods, 1, "AdPods length should be 1")
   658  	assert.Len(t, bidRespVideo.AdPods[0].Targeting, 2, "AdPod Targeting length should be 2")
   659  	assert.Equal(t, "17.00_123_30s", bidRespVideo.AdPods[0].Targeting[0].HbPbCatDur, "AdPod Targeting first element hb_pb_cat_dur should be 17.00_123_30s")
   660  	assert.Equal(t, "17.00_456_30s", bidRespVideo.AdPods[0].Targeting[1].HbPbCatDur, "AdPod Targeting first element hb_pb_cat_dur should be 17.00_456_30s")
   661  }
   662  
   663  func TestVideoBuildVideoResponseMissedCacheForAllBids(t *testing.T) {
   664  	openRtbBidResp := openrtb2.BidResponse{}
   665  	podErrors := make([]PodError, 0)
   666  
   667  	seatBids := make([]openrtb2.SeatBid, 0)
   668  	seatBid := openrtb2.SeatBid{}
   669  
   670  	bids := make([]openrtb2.Bid, 0)
   671  	bid1 := openrtb2.Bid{}
   672  	bid2 := openrtb2.Bid{}
   673  	bid3 := openrtb2.Bid{}
   674  
   675  	extBid1 := []byte(`{"prebid":{"targeting":{"hb_bidder":"appnexus","hb_pb":"17.00","hb_pb_cat_dur":"17.00_123_30s","hb_size":"1x1"}}}`)
   676  	extBid2 := []byte(`{"prebid":{"targeting":{"hb_bidder":"appnexus","hb_pb":"17.00","hb_pb_cat_dur":"17.00_456_30s","hb_size":"1x1"}}}`)
   677  	extBid3 := []byte(`{"prebid":{"targeting":{"hb_bidder":"appnexus","hb_pb":"17.00","hb_pb_cat_dur":"17.00_406_30s","hb_size":"1x1"}}}`)
   678  
   679  	bid1.Ext = extBid1
   680  	bids = append(bids, bid1)
   681  
   682  	bid2.Ext = extBid2
   683  	bids = append(bids, bid2)
   684  
   685  	bid3.Ext = extBid3
   686  	bids = append(bids, bid3)
   687  
   688  	seatBid.Bid = bids
   689  	seatBids = append(seatBids, seatBid)
   690  	openRtbBidResp.SeatBid = seatBids
   691  
   692  	bidRespVideo, err := buildVideoResponse(&openRtbBidResp, podErrors)
   693  	assert.Nil(t, bidRespVideo, "bid response should be nil")
   694  	assert.Equal(t, "caching failed for all bids", err.Error(), "error should be caching failed for all bids")
   695  }
   696  
   697  func TestVideoBuildVideoResponsePodErrors(t *testing.T) {
   698  	openRtbBidResp := openrtb2.BidResponse{}
   699  	podErrors := make([]PodError, 0, 2)
   700  
   701  	seatBids := make([]openrtb2.SeatBid, 0)
   702  	seatBid := openrtb2.SeatBid{}
   703  
   704  	bids := make([]openrtb2.Bid, 0)
   705  	bid1 := openrtb2.Bid{}
   706  	bid2 := openrtb2.Bid{}
   707  
   708  	extBid1 := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"17.00","hb_pb_cat_dur_appnex":"17.00_123_30s","hb_size":"1x1","hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"}}}`)
   709  	extBid2 := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"17.00","hb_pb_cat_dur_appnex":"17.00_456_30s","hb_size":"1x1","hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"}}}`)
   710  
   711  	bid1.Ext = extBid1
   712  	bids = append(bids, bid1)
   713  
   714  	bid2.Ext = extBid2
   715  	bids = append(bids, bid2)
   716  
   717  	seatBid.Bid = bids
   718  	seatBid.Seat = "appnexus"
   719  	seatBids = append(seatBids, seatBid)
   720  	openRtbBidResp.SeatBid = seatBids
   721  
   722  	podErr1 := PodError{}
   723  	podErr1.PodId = 222
   724  	podErr1.PodIndex = 1
   725  	podErrors = append(podErrors, podErr1)
   726  
   727  	podErr2 := PodError{}
   728  	podErr2.PodId = 333
   729  	podErr2.PodIndex = 2
   730  	podErrors = append(podErrors, podErr2)
   731  
   732  	bidRespVideo, err := buildVideoResponse(&openRtbBidResp, podErrors)
   733  	assert.NoError(t, err, "Error should be nil")
   734  	assert.Len(t, bidRespVideo.AdPods, 3, "AdPods length should be 3")
   735  	assert.Len(t, bidRespVideo.AdPods[0].Targeting, 2, "First ad pod should be correct and contain 2 targeting elements")
   736  	assert.Equal(t, int64(222), bidRespVideo.AdPods[1].PodId, "AdPods should contain error element at index 1")
   737  	assert.Equal(t, int64(333), bidRespVideo.AdPods[2].PodId, "AdPods should contain error element at index 2")
   738  }
   739  
   740  func TestVideoBuildVideoResponseNoBids(t *testing.T) {
   741  	openRtbBidResp := openrtb2.BidResponse{}
   742  	podErrors := make([]PodError, 0, 0)
   743  	openRtbBidResp.SeatBid = make([]openrtb2.SeatBid, 0)
   744  	bidRespVideo, err := buildVideoResponse(&openRtbBidResp, podErrors)
   745  	assert.NoError(t, err, "Error should be nil")
   746  	assert.Len(t, bidRespVideo.AdPods, 0, "AdPods length should be 0")
   747  }
   748  
   749  func TestMergeOpenRTBToVideoRequest(t *testing.T) {
   750  	var bidReq = &openrtb2.BidRequest{}
   751  	var videoReq = &openrtb_ext.BidRequestVideo{}
   752  
   753  	videoReq.App = &openrtb2.App{
   754  		Domain: "test.com",
   755  		Bundle: "test.bundle",
   756  	}
   757  
   758  	videoReq.Site = &openrtb2.Site{
   759  		Page: "site.com/index",
   760  	}
   761  
   762  	var dnt int8 = 4
   763  	var lmt int8 = 5
   764  	videoReq.Device = openrtb2.Device{
   765  		DNT: &dnt,
   766  		Lmt: &lmt,
   767  	}
   768  
   769  	videoReq.BCat = []string{"test1", "test2"}
   770  	videoReq.BAdv = []string{"test3", "test4"}
   771  
   772  	videoReq.Regs = &openrtb2.Regs{
   773  		Ext: json.RawMessage(`{"gdpr":1,"us_privacy":"1NYY","existing":"any","consent":"anyConsent"}`),
   774  	}
   775  
   776  	videoReq.User = &openrtb2.User{
   777  		BuyerUID: "test UID",
   778  		Yob:      1980,
   779  		Keywords: "test keywords",
   780  		Ext:      json.RawMessage(`{"consent":"test string"}`),
   781  	}
   782  
   783  	mergeData(videoReq, bidReq)
   784  
   785  	assert.Equal(t, videoReq.BCat, bidReq.BCat, "BCat is incorrect")
   786  	assert.Equal(t, videoReq.BAdv, bidReq.BAdv, "BAdv is incorrect")
   787  
   788  	assert.Equal(t, videoReq.App.Domain, bidReq.App.Domain, "App.Domain is incorrect")
   789  	assert.Equal(t, videoReq.App.Bundle, bidReq.App.Bundle, "App.Bundle is incorrect")
   790  
   791  	assert.Equal(t, videoReq.Device.Lmt, bidReq.Device.Lmt, "Device.Lmt is incorrect")
   792  	assert.Equal(t, videoReq.Device.DNT, bidReq.Device.DNT, "Device.DNT is incorrect")
   793  
   794  	assert.Equal(t, videoReq.Site.Page, bidReq.Site.Page, "Device.Site.Page is incorrect")
   795  
   796  	assert.Equal(t, videoReq.Regs, bidReq.Regs, "Regs is incorrect")
   797  
   798  	assert.Equal(t, videoReq.User, bidReq.User, "User is incorrect")
   799  }
   800  
   801  func TestHandleError(t *testing.T) {
   802  	tests := []struct {
   803  		description       string
   804  		giveErrors        []error
   805  		wantCode          int
   806  		wantMetricsStatus metrics.RequestStatus
   807  	}{
   808  		{
   809  			description: "Blocked account - return 503 with blocked metrics status",
   810  			giveErrors: []error{
   811  				&errortypes.BlacklistedAcct{},
   812  			},
   813  			wantCode:          503,
   814  			wantMetricsStatus: metrics.RequestStatusBlacklisted,
   815  		},
   816  		{
   817  			description: "Blocked app - return 503 with blocked metrics status",
   818  			giveErrors: []error{
   819  				&errortypes.BlacklistedApp{},
   820  			},
   821  			wantCode:          503,
   822  			wantMetricsStatus: metrics.RequestStatusBlacklisted,
   823  		},
   824  		{
   825  			description: "Account required error - return 400 with bad input metrics status",
   826  			giveErrors: []error{
   827  				&errortypes.AcctRequired{},
   828  			},
   829  			wantCode:          400,
   830  			wantMetricsStatus: metrics.RequestStatusBadInput,
   831  		},
   832  		{
   833  			description: "Malformed account config error - return 500 with account config error metrics status",
   834  			giveErrors: []error{
   835  				&errortypes.MalformedAcct{},
   836  			},
   837  			wantCode:          500,
   838  			wantMetricsStatus: metrics.RequestStatusAccountConfigErr,
   839  		},
   840  		{
   841  			description: "Multiple generic errors - return 500 with generic error metrics status",
   842  			giveErrors: []error{
   843  				errors.New("Error for testing handleError 1"),
   844  				errors.New("Error for testing handleError 2"),
   845  			},
   846  			wantCode:          500,
   847  			wantMetricsStatus: metrics.RequestStatusErr,
   848  		},
   849  	}
   850  
   851  	for _, tt := range tests {
   852  		vo := analytics.VideoObject{
   853  			Status: 200,
   854  			Errors: make([]error, 0),
   855  		}
   856  
   857  		labels := metrics.Labels{
   858  			Source:        metrics.DemandUnknown,
   859  			RType:         metrics.ReqTypeVideo,
   860  			PubID:         metrics.PublisherUnknown,
   861  			CookieFlag:    metrics.CookieFlagUnknown,
   862  			RequestStatus: metrics.RequestStatusOK,
   863  		}
   864  
   865  		recorder := httptest.NewRecorder()
   866  		handleError(&labels, recorder, tt.giveErrors, &vo, nil)
   867  
   868  		assert.Equal(t, tt.wantMetricsStatus, labels.RequestStatus, tt.description)
   869  		assert.Equal(t, tt.wantCode, recorder.Code, tt.description)
   870  		assert.Equal(t, tt.wantCode, vo.Status, tt.description)
   871  		assert.ElementsMatch(t, tt.giveErrors, vo.Errors, tt.description)
   872  	}
   873  }
   874  
   875  func TestHandleErrorMetrics(t *testing.T) {
   876  	ex := &mockExchangeVideo{}
   877  	reqBody := readVideoTestFile(t, "sample-requests/video/video_invalid_sample.json")
   878  	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
   879  	recorder := httptest.NewRecorder()
   880  
   881  	deps, met, mod := mockDepsWithMetrics(t, ex)
   882  	deps.VideoAuctionEndpoint(recorder, req, nil)
   883  
   884  	assert.Equal(t, int64(0), met.RequestStatuses[metrics.ReqTypeVideo][metrics.RequestStatusOK].Count(), "OK requests count should be 0")
   885  	assert.Equal(t, int64(1), met.RequestStatuses[metrics.ReqTypeVideo][metrics.RequestStatusErr].Count(), "Error requests count should be 1")
   886  	assert.Equal(t, 1, len(mod.videoObjects), "Mock AnalyticsModule should have 1 AuctionObject")
   887  	assert.Equal(t, 500, mod.videoObjects[0].Status, "AnalyticsObject should have 500 status")
   888  	assert.Equal(t, 2, len(mod.videoObjects[0].Errors), "AnalyticsObject should have Errors length of 2")
   889  	assert.Equal(t, "request missing required field: PodConfig.DurationRangeSec", mod.videoObjects[0].Errors[0].Error(), "First error in AnalyticsObject should have message regarding DurationRangeSec")
   890  	assert.Equal(t, "request missing required field: PodConfig.Pods", mod.videoObjects[0].Errors[1].Error(), "Second error in AnalyticsObject should have message regarding Pods")
   891  }
   892  
   893  func TestParseVideoRequestWithUserAgentAndHeader(t *testing.T) {
   894  	ex := &mockExchangeVideo{}
   895  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_with_device_user_agent.json")
   896  	headers := http.Header{}
   897  	headers.Add("User-Agent", "TestHeader")
   898  
   899  	deps := mockDeps(t, ex)
   900  	req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers)
   901  
   902  	assert.Equal(t, "TestHeaderSample", req.Device.UA, "Header should be taken from original request")
   903  	assert.Equal(t, []error(nil), valErr, "No validation errors should be returned")
   904  	assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned")
   905  }
   906  
   907  func TestParseVideoRequestWithUserAgentAndEmptyHeader(t *testing.T) {
   908  	ex := &mockExchangeVideo{}
   909  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_with_device_user_agent.json")
   910  
   911  	headers := http.Header{}
   912  
   913  	deps := mockDeps(t, ex)
   914  	req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers)
   915  
   916  	assert.Equal(t, "TestHeaderSample", req.Device.UA, "Header should be taken from original request")
   917  	assert.Equal(t, []error(nil), valErr, "No validation errors should be returned")
   918  	assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned")
   919  }
   920  
   921  func TestParseVideoRequestWithoutUserAgentWithHeader(t *testing.T) {
   922  	ex := &mockExchangeVideo{}
   923  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_without_device_user_agent.json")
   924  	headers := http.Header{}
   925  	headers.Add("User-Agent", "TestHeader")
   926  
   927  	deps := mockDeps(t, ex)
   928  	req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers)
   929  
   930  	assert.Equal(t, "TestHeader", req.Device.UA, "Device.ua should be taken from request header")
   931  	assert.Equal(t, []error(nil), valErr, "No validation errors should be returned")
   932  	assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned")
   933  }
   934  
   935  func TestParseVideoRequestWithoutUserAgentAndEmptyHeader(t *testing.T) {
   936  	ex := &mockExchangeVideo{}
   937  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_without_device_user_agent.json")
   938  
   939  	headers := http.Header{}
   940  
   941  	deps := mockDeps(t, ex)
   942  
   943  	req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers)
   944  
   945  	assert.Equal(t, "", req.Device.UA, "Device.ua should be empty")
   946  	assert.Equal(t, []error(nil), valErr, "No validation errors should be returned")
   947  	assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned")
   948  }
   949  
   950  func TestParseVideoRequestWithEncodedUserAgentInHeader(t *testing.T) {
   951  	ex := &mockExchangeVideo{}
   952  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_without_device_user_agent.json")
   953  
   954  	uaEncoded := "Mozilla%2F5.0%20%28Macintosh%3B%20Intel%20Mac%20OS%20X%2010_14_6%29%20AppleWebKit%2F537.36%20%28KHTML%2C%20like%20Gecko%29%20Chrome%2F78.0.3904.87%20Safari%2F537.36"
   955  	uaDecoded := "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36"
   956  
   957  	headers := http.Header{}
   958  	headers.Add("User-Agent", uaEncoded)
   959  
   960  	deps := mockDeps(t, ex)
   961  	req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers)
   962  
   963  	assert.Equal(t, uaDecoded, req.Device.UA, "Device.ua should be taken from request header")
   964  	assert.Equal(t, []error(nil), valErr, "No validation errors should be returned")
   965  	assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned")
   966  }
   967  
   968  func TestParseVideoRequestWithDecodedUserAgentInHeader(t *testing.T) {
   969  	ex := &mockExchangeVideo{}
   970  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_without_device_user_agent.json")
   971  
   972  	uaDecoded := "Mozilla/5.0+(Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36"
   973  
   974  	headers := http.Header{}
   975  	headers.Add("User-Agent", uaDecoded)
   976  
   977  	deps := mockDeps(t, ex)
   978  	req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers)
   979  
   980  	assert.Equal(t, uaDecoded, req.Device.UA, "Device.ua should be taken from request header")
   981  	assert.Equal(t, []error(nil), valErr, "No validation errors should be returned")
   982  	assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned")
   983  }
   984  
   985  func TestHandleErrorDebugLog(t *testing.T) {
   986  	vo := analytics.VideoObject{
   987  		Status: 200,
   988  		Errors: make([]error, 0),
   989  	}
   990  
   991  	labels := metrics.Labels{
   992  		Source:        metrics.DemandUnknown,
   993  		RType:         metrics.ReqTypeVideo,
   994  		PubID:         metrics.PublisherUnknown,
   995  		CookieFlag:    metrics.CookieFlagUnknown,
   996  		RequestStatus: metrics.RequestStatusOK,
   997  	}
   998  
   999  	recorder := httptest.NewRecorder()
  1000  	err1 := errors.New("Error for testing handleError 1")
  1001  	err2 := errors.New("Error for testing handleError 2")
  1002  	debugLog := exchange.DebugLog{
  1003  		Enabled:   true,
  1004  		CacheType: prebid_cache_client.TypeXML,
  1005  		Data: exchange.DebugData{
  1006  			Request:  "test request string",
  1007  			Headers:  "test headers string",
  1008  			Response: "test response string",
  1009  		},
  1010  		TTL:                      int64(3600),
  1011  		Regexp:                   regexp.MustCompile(`[<>]`),
  1012  		DebugOverride:            false,
  1013  		DebugEnabledOrOverridden: true,
  1014  	}
  1015  	handleError(&labels, recorder, []error{err1, err2}, &vo, &debugLog)
  1016  
  1017  	assert.Equal(t, metrics.RequestStatusErr, labels.RequestStatus, "labels.RequestStatus should indicate an error")
  1018  	assert.Equal(t, 500, recorder.Code, "Error status should be written to writer")
  1019  	assert.Equal(t, 500, vo.Status, "Analytics object should have error status")
  1020  	assert.Equal(t, 3, len(vo.Errors), "New errors including debug cache ID should be appended to Analytics object Errors")
  1021  	assert.Equal(t, "Error for testing handleError 1", vo.Errors[0].Error(), "Error in Analytics object should have test error message for first error")
  1022  	assert.Equal(t, "Error for testing handleError 2", vo.Errors[1].Error(), "Error in Analytics object should have test error message for second error")
  1023  	assert.NotEmpty(t, debugLog.CacheKey, "DebugLog CacheKey value should have been set")
  1024  }
  1025  
  1026  func TestCreateImpressionTemplate(t *testing.T) {
  1027  
  1028  	imp := openrtb2.Imp{}
  1029  	imp.Video = &openrtb2.Video{}
  1030  	imp.Video.Protocols = []adcom1.MediaCreativeSubtype{1, 2}
  1031  	imp.Video.MIMEs = []string{"video/mp4"}
  1032  	imp.Video.H = 200
  1033  	imp.Video.W = 400
  1034  	imp.Video.PlaybackMethod = []adcom1.PlaybackMethod{5, 6}
  1035  
  1036  	video := openrtb2.Video{}
  1037  	video.Protocols = []adcom1.MediaCreativeSubtype{3, 4}
  1038  	video.MIMEs = []string{"video/flv"}
  1039  	video.H = 300
  1040  	video.W = 0
  1041  	video.PlaybackMethod = []adcom1.PlaybackMethod{7, 8}
  1042  
  1043  	res := createImpressionTemplate(imp, &video)
  1044  	assert.Equal(t, res.Video.Protocols, []adcom1.MediaCreativeSubtype{3, 4}, "Incorrect video protocols")
  1045  	assert.Equal(t, res.Video.MIMEs, []string{"video/flv"}, "Incorrect video MIMEs")
  1046  	assert.Equal(t, int(res.Video.H), 300, "Incorrect video height")
  1047  	assert.Equal(t, int(res.Video.W), 0, "Incorrect video width")
  1048  	assert.Equal(t, res.Video.PlaybackMethod, []adcom1.PlaybackMethod{7, 8}, "Incorrect video playback method")
  1049  }
  1050  
  1051  func TestCCPA(t *testing.T) {
  1052  	testCases := []struct {
  1053  		description         string
  1054  		testFilePath        string
  1055  		expectConsentString bool
  1056  		expectEmptyConsent  bool
  1057  	}{
  1058  		{
  1059  			description:         "Missing Consent",
  1060  			testFilePath:        "sample-requests/video/video_valid_sample.json",
  1061  			expectConsentString: false,
  1062  			expectEmptyConsent:  true,
  1063  		},
  1064  		{
  1065  			description:         "Valid Consent",
  1066  			testFilePath:        "sample-requests/video/video_valid_sample_ccpa_valid.json",
  1067  			expectConsentString: true,
  1068  		},
  1069  		{
  1070  			description:         "Malformed Consent",
  1071  			testFilePath:        "sample-requests/video/video_valid_sample_ccpa_malformed.json",
  1072  			expectConsentString: false,
  1073  		},
  1074  	}
  1075  
  1076  	for _, test := range testCases {
  1077  		reqBody := readVideoTestFile(t, test.testFilePath)
  1078  
  1079  		// Create HTTP Request + Response Recorder
  1080  		httpRequest := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
  1081  		httpResponseRecorder := httptest.NewRecorder()
  1082  
  1083  		// Run Test
  1084  		ex := &mockExchangeVideo{}
  1085  		mockDeps(t, ex).VideoAuctionEndpoint(httpResponseRecorder, httpRequest, nil)
  1086  
  1087  		// Validate Request To Exchange
  1088  		// - An error should never be generated for CCPA problems.
  1089  		if ex.lastRequest == nil {
  1090  			t.Fatalf("%s: The request never made it into the exchange.", test.description)
  1091  		}
  1092  		extRegs := &openrtb_ext.ExtRegs{}
  1093  		if err := json.Unmarshal(ex.lastRequest.Regs.Ext, extRegs); err != nil {
  1094  			t.Fatalf("%s: Failed to unmarshal reg.ext in request to the exchange: %v", test.description, err)
  1095  		}
  1096  		if test.expectConsentString {
  1097  			assert.Len(t, extRegs.USPrivacy, 4, test.description+":consent")
  1098  		} else if test.expectEmptyConsent {
  1099  			assert.Empty(t, extRegs.USPrivacy, test.description+":consent")
  1100  		}
  1101  
  1102  		// Validate HTTP Response
  1103  		responseBytes := httpResponseRecorder.Body.Bytes()
  1104  		response := &openrtb_ext.BidResponseVideo{}
  1105  		if err := json.Unmarshal(responseBytes, response); err != nil {
  1106  			t.Fatalf("%s: Unable to unmarshal response.", test.description)
  1107  		}
  1108  		assert.Len(t, ex.lastRequest.Imp, 11, test.description+":imps")
  1109  		assert.Len(t, response.AdPods, 5, test.description+":adpods")
  1110  	}
  1111  }
  1112  
  1113  func TestVideoEndpointAppendBidderNames(t *testing.T) {
  1114  	ex := &mockExchangeAppendBidderNames{}
  1115  	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_appendbiddernames.json")
  1116  	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
  1117  	recorder := httptest.NewRecorder()
  1118  
  1119  	deps := mockDepsAppendBidderNames(t, ex)
  1120  	deps.VideoAuctionEndpoint(recorder, req, nil)
  1121  
  1122  	if ex.lastRequest == nil {
  1123  		t.Fatalf("The request never made it into the Exchange.")
  1124  	}
  1125  
  1126  	var extData openrtb_ext.ExtRequest
  1127  	json.Unmarshal(ex.lastRequest.Ext, &extData)
  1128  	assert.True(t, extData.Prebid.Targeting.AppendBidderNames, "Request ext incorrect: AppendBidderNames should be true ")
  1129  
  1130  	respBytes := recorder.Body.Bytes()
  1131  	resp := &openrtb_ext.BidResponseVideo{}
  1132  	if err := json.Unmarshal(respBytes, resp); err != nil {
  1133  		t.Fatalf("Unable to unmarshal response.")
  1134  	}
  1135  
  1136  	assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request")
  1137  	assert.Equal(t, string(ex.lastRequest.Site.Page), "prebid.com", "Incorrect site page in request")
  1138  	assert.Equal(t, ex.lastRequest.Site.Content.Series, "TvName", "Incorrect site content series in request")
  1139  
  1140  	assert.Len(t, resp.AdPods, 5, "Incorrect number of Ad Pods in response")
  1141  	assert.Len(t, resp.AdPods[0].Targeting, 4, "Incorrect Targeting data in response")
  1142  	assert.Len(t, resp.AdPods[1].Targeting, 3, "Incorrect Targeting data in response")
  1143  	assert.Len(t, resp.AdPods[2].Targeting, 5, "Incorrect Targeting data in response")
  1144  	assert.Len(t, resp.AdPods[3].Targeting, 1, "Incorrect Targeting data in response")
  1145  	assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response")
  1146  
  1147  	assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s_appnexus", "Incorrect number of Ad Pods in response")
  1148  }
  1149  
  1150  func TestFormatTargetingKey(t *testing.T) {
  1151  	res := formatTargetingKey(openrtb_ext.HbCategoryDurationKey, "appnexus")
  1152  	assert.Equal(t, "hb_pb_cat_dur_appnex", res, "Tergeting key constructed incorrectly")
  1153  }
  1154  
  1155  func TestFormatTargetingKeyLongKey(t *testing.T) {
  1156  	res := formatTargetingKey(openrtb_ext.HbpbConstantKey, "20.00")
  1157  	assert.Equal(t, "hb_pb_20.00", res, "Tergeting key constructed incorrectly")
  1158  }
  1159  
  1160  func TestVideoAuctionResponseHeaders(t *testing.T) {
  1161  	testCases := []struct {
  1162  		description     string
  1163  		givenTestFile   string
  1164  		expectedStatus  int
  1165  		expectedHeaders func(http.Header)
  1166  	}{
  1167  		{
  1168  			description:    "Success Response",
  1169  			givenTestFile:  "sample-requests/video/video_valid_sample.json",
  1170  			expectedStatus: 200,
  1171  			expectedHeaders: func(h http.Header) {
  1172  				h.Set("X-Prebid", "pbs-go/unknown")
  1173  				h.Set("Content-Type", "application/json")
  1174  			},
  1175  		}, {
  1176  			description:    "Failure Response",
  1177  			givenTestFile:  "sample-requests/video/video_invalid_sample.json",
  1178  			expectedStatus: 500,
  1179  			expectedHeaders: func(h http.Header) {
  1180  				h.Set("X-Prebid", "pbs-go/unknown")
  1181  			},
  1182  		},
  1183  	}
  1184  
  1185  	exchange := &mockExchangeVideo{}
  1186  	endpoint := mockDeps(t, exchange)
  1187  
  1188  	for _, test := range testCases {
  1189  		requestBody := readVideoTestFile(t, test.givenTestFile)
  1190  
  1191  		httpReq := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(requestBody))
  1192  		recorder := httptest.NewRecorder()
  1193  
  1194  		endpoint.VideoAuctionEndpoint(recorder, httpReq, nil)
  1195  
  1196  		expectedHeaders := http.Header{}
  1197  		test.expectedHeaders(expectedHeaders)
  1198  
  1199  		assert.Equal(t, test.expectedStatus, recorder.Result().StatusCode, test.description+":statuscode")
  1200  		assert.Equal(t, expectedHeaders, recorder.Result().Header, test.description+":statuscode")
  1201  	}
  1202  }
  1203  
  1204  func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *metrics.Metrics, *mockAnalyticsModule) {
  1205  	mockModule := &mockAnalyticsModule{}
  1206  
  1207  	metrics := metrics.NewMetrics(gometrics.NewRegistry(), openrtb_ext.CoreBidderNames(), config.DisabledMetrics{}, nil, nil)
  1208  
  1209  	deps := &endpointDeps{
  1210  		fakeUUIDGenerator{},
  1211  		ex,
  1212  		mockBidderParamValidator{},
  1213  		&mockVideoStoredReqFetcher{},
  1214  		&mockVideoStoredReqFetcher{},
  1215  		&mockAccountFetcher{data: mockVideoAccountData},
  1216  		&config.Configuration{MaxRequestSize: maxSize},
  1217  		metrics,
  1218  		mockModule,
  1219  		map[string]string{},
  1220  		false,
  1221  		[]byte{},
  1222  		openrtb_ext.BuildBidderMap(),
  1223  		nil,
  1224  		nil,
  1225  		hardcodedResponseIPValidator{response: true},
  1226  		empty_fetcher.EmptyFetcher{},
  1227  		hooks.EmptyPlanBuilder{},
  1228  		nil,
  1229  	}
  1230  	return deps, metrics, mockModule
  1231  }
  1232  
  1233  type mockAnalyticsModule struct {
  1234  	auctionObjects []*analytics.AuctionObject
  1235  	videoObjects   []*analytics.VideoObject
  1236  }
  1237  
  1238  func (m *mockAnalyticsModule) LogAuctionObject(ao *analytics.AuctionObject) {
  1239  	m.auctionObjects = append(m.auctionObjects, ao)
  1240  }
  1241  
  1242  func (m *mockAnalyticsModule) LogVideoObject(vo *analytics.VideoObject) {
  1243  	m.videoObjects = append(m.videoObjects, vo)
  1244  }
  1245  
  1246  func (m *mockAnalyticsModule) LogCookieSyncObject(cso *analytics.CookieSyncObject) {}
  1247  
  1248  func (m *mockAnalyticsModule) LogSetUIDObject(so *analytics.SetUIDObject) {}
  1249  
  1250  func (m *mockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject) {}
  1251  
  1252  func (m *mockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent) {}
  1253  
  1254  func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
  1255  	return &endpointDeps{
  1256  		fakeUUIDGenerator{},
  1257  		ex,
  1258  		mockBidderParamValidator{},
  1259  		&mockVideoStoredReqFetcher{},
  1260  		&mockVideoStoredReqFetcher{},
  1261  		&mockAccountFetcher{data: mockVideoAccountData},
  1262  		&config.Configuration{MaxRequestSize: maxSize},
  1263  		&metricsConfig.NilMetricsEngine{},
  1264  		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
  1265  		map[string]string{},
  1266  		false,
  1267  		[]byte{},
  1268  		openrtb_ext.BuildBidderMap(),
  1269  		ex.cache,
  1270  		regexp.MustCompile(`[<>]`),
  1271  		hardcodedResponseIPValidator{response: true},
  1272  		empty_fetcher.EmptyFetcher{},
  1273  		hooks.EmptyPlanBuilder{},
  1274  		nil,
  1275  	}
  1276  }
  1277  
  1278  func mockDepsInvalidPrivacy(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
  1279  	return &endpointDeps{
  1280  		fakeUUIDGenerator{},
  1281  		ex,
  1282  		mockBidderParamValidator{},
  1283  		&mockVideoStoredReqFetcher{},
  1284  		&mockVideoStoredReqFetcher{},
  1285  		&mockAccountFetcher{data: mockVideoAccountData},
  1286  		&config.Configuration{MaxRequestSize: maxSize,
  1287  			AccountDefaults: config.Account{
  1288  				Privacy: config.AccountPrivacy{
  1289  					AllowActivities: &config.AllowActivities{
  1290  						TransmitPreciseGeo: config.Activity{Rules: []config.ActivityRule{
  1291  							{Condition: config.ActivityCondition{ComponentName: []string{"bidderA.BidderB.bidderC"}}},
  1292  						}},
  1293  					},
  1294  				},
  1295  			},
  1296  		},
  1297  		&metricsConfig.NilMetricsEngine{},
  1298  		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
  1299  		map[string]string{},
  1300  		false,
  1301  		[]byte{},
  1302  		openrtb_ext.BuildBidderMap(),
  1303  		ex.cache,
  1304  		regexp.MustCompile(`[<>]`),
  1305  		hardcodedResponseIPValidator{response: true},
  1306  		empty_fetcher.EmptyFetcher{},
  1307  		hooks.EmptyPlanBuilder{},
  1308  		nil,
  1309  	}
  1310  }
  1311  
  1312  func mockDepsAppendBidderNames(t *testing.T, ex *mockExchangeAppendBidderNames) *endpointDeps {
  1313  	deps := &endpointDeps{
  1314  		fakeUUIDGenerator{},
  1315  		ex,
  1316  		mockBidderParamValidator{},
  1317  		&mockVideoStoredReqFetcher{},
  1318  		&mockVideoStoredReqFetcher{},
  1319  		empty_fetcher.EmptyFetcher{},
  1320  		&config.Configuration{MaxRequestSize: maxSize},
  1321  		&metricsConfig.NilMetricsEngine{},
  1322  		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
  1323  		map[string]string{},
  1324  		false,
  1325  		[]byte{},
  1326  		openrtb_ext.BuildBidderMap(),
  1327  		ex.cache,
  1328  		regexp.MustCompile(`[<>]`),
  1329  		hardcodedResponseIPValidator{response: true},
  1330  		empty_fetcher.EmptyFetcher{},
  1331  		hooks.EmptyPlanBuilder{},
  1332  		nil,
  1333  	}
  1334  
  1335  	return deps
  1336  }
  1337  
  1338  func mockDepsNoBids(t *testing.T, ex *mockExchangeVideoNoBids) *endpointDeps {
  1339  	edep := &endpointDeps{
  1340  		fakeUUIDGenerator{},
  1341  		ex,
  1342  		mockBidderParamValidator{},
  1343  		&mockVideoStoredReqFetcher{},
  1344  		&mockVideoStoredReqFetcher{},
  1345  		empty_fetcher.EmptyFetcher{},
  1346  		&config.Configuration{MaxRequestSize: maxSize},
  1347  		&metricsConfig.NilMetricsEngine{},
  1348  		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
  1349  		map[string]string{},
  1350  		false,
  1351  		[]byte{},
  1352  		openrtb_ext.BuildBidderMap(),
  1353  		ex.cache,
  1354  		regexp.MustCompile(`[<>]`),
  1355  		hardcodedResponseIPValidator{response: true},
  1356  		empty_fetcher.EmptyFetcher{},
  1357  		hooks.EmptyPlanBuilder{},
  1358  		nil,
  1359  	}
  1360  
  1361  	return edep
  1362  }
  1363  
  1364  type mockCacheClient struct {
  1365  	called bool
  1366  }
  1367  
  1368  func (m *mockCacheClient) PutJson(ctx context.Context, values []prebid_cache_client.Cacheable) ([]string, []error) {
  1369  	if !m.called {
  1370  		m.called = true
  1371  	}
  1372  	return []string{}, []error{}
  1373  }
  1374  
  1375  func (m *mockCacheClient) GetExtCacheData() (scheme string, host string, path string) {
  1376  	return "", "", ""
  1377  }
  1378  
  1379  type mockVideoStoredReqFetcher struct {
  1380  }
  1381  
  1382  func (cf mockVideoStoredReqFetcher) FetchRequests(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage, errs []error) {
  1383  	return testVideoStoredRequestData, testVideoStoredImpData, nil
  1384  }
  1385  
  1386  func (cf mockVideoStoredReqFetcher) FetchResponses(ctx context.Context, ids []string) (data map[string]json.RawMessage, errs []error) {
  1387  	return nil, nil
  1388  }
  1389  
  1390  type mockExchangeVideo struct {
  1391  	lastRequest *openrtb2.BidRequest
  1392  	cache       *mockCacheClient
  1393  }
  1394  
  1395  func (m *mockExchangeVideo) HoldAuction(ctx context.Context, r *exchange.AuctionRequest, debugLog *exchange.DebugLog) (*exchange.AuctionResponse, error) {
  1396  	if err := r.BidRequestWrapper.RebuildRequest(); err != nil {
  1397  		return nil, err
  1398  	}
  1399  
  1400  	m.lastRequest = r.BidRequestWrapper.BidRequest
  1401  	if debugLog != nil && debugLog.Enabled {
  1402  		m.cache.called = true
  1403  	}
  1404  	ext := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"20.00","hb_pb_cat_dur_appnex":"20.00_395_30s","hb_size":"1x1", "hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1", "hb_deal_appnexus": "ABC_123"},"type":"video","dealpriority":0,"dealtiersatisfied":false},"bidder":{"appnexus":{"brand_id":1,"auction_id":7840037870526938650,"bidder_id":2,"bid_ad_type":1,"creative_info":{"video":{"duration":30,"mimes":["video\/mp4"]}}}}}`)
  1405  	return &exchange.AuctionResponse{BidResponse: &openrtb2.BidResponse{
  1406  		SeatBid: []openrtb2.SeatBid{{
  1407  			Seat: "appnexus",
  1408  			Bid: []openrtb2.Bid{
  1409  				{ID: "01", ImpID: "1_0", Ext: ext},
  1410  				{ID: "02", ImpID: "1_1", Ext: ext},
  1411  				{ID: "03", ImpID: "1_2", Ext: ext},
  1412  				{ID: "04", ImpID: "1_3", Ext: ext},
  1413  				{ID: "05", ImpID: "2_0", Ext: ext},
  1414  				{ID: "06", ImpID: "2_1", Ext: ext},
  1415  				{ID: "07", ImpID: "2_2", Ext: ext},
  1416  				{ID: "08", ImpID: "3_0", Ext: ext},
  1417  				{ID: "09", ImpID: "3_1", Ext: ext},
  1418  				{ID: "10", ImpID: "3_2", Ext: ext},
  1419  				{ID: "11", ImpID: "3_3", Ext: ext},
  1420  				{ID: "12", ImpID: "3_5", Ext: ext},
  1421  				{ID: "13", ImpID: "4_0", Ext: ext},
  1422  				{ID: "14", ImpID: "5_0", Ext: ext},
  1423  				{ID: "15", ImpID: "5_1", Ext: ext},
  1424  				{ID: "16", ImpID: "5_2", Ext: ext},
  1425  			},
  1426  		}},
  1427  	}}, nil
  1428  }
  1429  
  1430  type mockExchangeAppendBidderNames struct {
  1431  	lastRequest *openrtb2.BidRequest
  1432  	cache       *mockCacheClient
  1433  }
  1434  
  1435  func (m *mockExchangeAppendBidderNames) HoldAuction(ctx context.Context, r *exchange.AuctionRequest, debugLog *exchange.DebugLog) (*exchange.AuctionResponse, error) {
  1436  	m.lastRequest = r.BidRequestWrapper.BidRequest
  1437  	if debugLog != nil && debugLog.Enabled {
  1438  		m.cache.called = true
  1439  	}
  1440  	ext := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"20.00","hb_pb_cat_dur_appnex":"20.00_395_30s_appnexus","hb_size":"1x1", "hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"},"type":"video"},"bidder":{"appnexus":{"brand_id":1,"auction_id":7840037870526938650,"bidder_id":2,"bid_ad_type":1,"creative_info":{"video":{"duration":30,"mimes":["video\/mp4"]}}}}}`)
  1441  	return &exchange.AuctionResponse{BidResponse: &openrtb2.BidResponse{
  1442  		SeatBid: []openrtb2.SeatBid{{
  1443  			Seat: "appnexus",
  1444  			Bid: []openrtb2.Bid{
  1445  				{ID: "01", ImpID: "1_0", Ext: ext},
  1446  				{ID: "02", ImpID: "1_1", Ext: ext},
  1447  				{ID: "03", ImpID: "1_2", Ext: ext},
  1448  				{ID: "04", ImpID: "1_3", Ext: ext},
  1449  				{ID: "05", ImpID: "2_0", Ext: ext},
  1450  				{ID: "06", ImpID: "2_1", Ext: ext},
  1451  				{ID: "07", ImpID: "2_2", Ext: ext},
  1452  				{ID: "08", ImpID: "3_0", Ext: ext},
  1453  				{ID: "09", ImpID: "3_1", Ext: ext},
  1454  				{ID: "10", ImpID: "3_2", Ext: ext},
  1455  				{ID: "11", ImpID: "3_3", Ext: ext},
  1456  				{ID: "12", ImpID: "3_5", Ext: ext},
  1457  				{ID: "13", ImpID: "4_0", Ext: ext},
  1458  				{ID: "14", ImpID: "5_0", Ext: ext},
  1459  				{ID: "15", ImpID: "5_1", Ext: ext},
  1460  				{ID: "16", ImpID: "5_2", Ext: ext},
  1461  			},
  1462  		}}},
  1463  	}, nil
  1464  }
  1465  
  1466  type mockExchangeVideoNoBids struct {
  1467  	lastRequest *openrtb2.BidRequest
  1468  	cache       *mockCacheClient
  1469  }
  1470  
  1471  func (m *mockExchangeVideoNoBids) HoldAuction(ctx context.Context, r *exchange.AuctionRequest, debugLog *exchange.DebugLog) (*exchange.AuctionResponse, error) {
  1472  	m.lastRequest = r.BidRequestWrapper.BidRequest
  1473  	return &exchange.AuctionResponse{BidResponse: &openrtb2.BidResponse{
  1474  		SeatBid: []openrtb2.SeatBid{{}},
  1475  	}}, nil
  1476  }
  1477  
  1478  var mockVideoAccountData = map[string]json.RawMessage{
  1479  	"valid_acct":     json.RawMessage(`{"disabled":false}`),
  1480  	"disabled_acct":  json.RawMessage(`{"disabled":true}`),
  1481  	"malformed_acct": json.RawMessage(`{"disabled":"invalid type"}`),
  1482  }
  1483  
  1484  var testVideoStoredImpData = map[string]json.RawMessage{
  1485  	"fba10607-0c12-43d1-ad07-b8a513bc75d6": json.RawMessage(`{"ext": {"appnexus": {"placementId": 14997137}}}`),
  1486  	"8b452b41-2681-4a20-9086-6f16ffad7773": json.RawMessage(`{"ext": {"appnexus": {"placementId": 15016213}}}`),
  1487  	"87d82a45-35c3-46cc-9315-2e3eeb91d0f2": json.RawMessage(`{"ext": {"appnexus": {"placementId": 15062775}}}`),
  1488  }
  1489  
  1490  var testVideoStoredRequestData = map[string]json.RawMessage{
  1491  	"80ce30c53c16e6ede735f123ef6e32361bfc7b22": json.RawMessage(`{"accountid": "11223344", "site": {"page": "mygame.foo.com"}}`),
  1492  }
  1493  
  1494  func readVideoTestFile(t *testing.T, filename string) string {
  1495  	requestData, err := os.ReadFile(filename)
  1496  	if err != nil {
  1497  		t.Fatalf("Failed to fetch a valid request: %v", err)
  1498  	}
  1499  
  1500  	return string(getRequestPayload(t, requestData))
  1501  }