github.com/prebid/prebid-server/v2@v2.18.0/adservertargeting/respdataprocessor_test.go (about)

     1  package adservertargeting
     2  
     3  import (
     4  	"encoding/json"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/prebid/openrtb/v20/openrtb2"
    10  	"github.com/prebid/openrtb/v20/openrtb3"
    11  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  func TestProcessRequestTargetingData(t *testing.T) {
    16  
    17  	testCases := []struct {
    18  		description            string
    19  		inputAdServerTargeting adServerTargetingData
    20  		inputTargetingData     map[string]string
    21  		expectedTargetingData  map[string]string
    22  	}{
    23  
    24  		{
    25  			description: "Add single and targeting value by imp to not empty input targeting data",
    26  			inputAdServerTargeting: adServerTargetingData{
    27  				RequestTargetingData: map[string]RequestTargetingData{
    28  					"key1": {SingleVal: json.RawMessage(`val1`)},
    29  					"key2": {
    30  						TargetingValueByImpId: map[string][]byte{
    31  							"impId1": []byte(`impId1val`), "impId2": []byte(`impId2val`),
    32  						},
    33  					},
    34  				},
    35  			},
    36  			inputTargetingData: map[string]string{"inKey1": "inVal1"},
    37  			expectedTargetingData: map[string]string{
    38  				"inKey1": "inVal1", "key1": "val1", "key2": "impId1val",
    39  			},
    40  		},
    41  		{
    42  			description: "Add single and targeting value by imp to empty input targeting data",
    43  			inputAdServerTargeting: adServerTargetingData{
    44  				RequestTargetingData: map[string]RequestTargetingData{
    45  					"key1": {SingleVal: json.RawMessage(`val1`)},
    46  					"key2": {
    47  						TargetingValueByImpId: map[string][]byte{
    48  							"impId1": []byte(`impId1val`), "impId2": []byte(`impId2val`),
    49  						},
    50  					},
    51  				},
    52  			},
    53  			inputTargetingData: map[string]string{},
    54  			expectedTargetingData: map[string]string{
    55  				"key1": "val1", "key2": "impId1val",
    56  			},
    57  		},
    58  		{
    59  			description: "Add single value by imp to not empty input targeting data",
    60  			inputAdServerTargeting: adServerTargetingData{
    61  				RequestTargetingData: map[string]RequestTargetingData{
    62  					"key1": {SingleVal: json.RawMessage(`val1`)},
    63  				},
    64  			},
    65  			inputTargetingData: map[string]string{"inKey1": "inVal1"},
    66  			expectedTargetingData: map[string]string{
    67  				"inKey1": "inVal1", "key1": "val1",
    68  			},
    69  		},
    70  		{
    71  			description: "Add targeting value by imp to not empty input targeting data",
    72  			inputAdServerTargeting: adServerTargetingData{
    73  				RequestTargetingData: map[string]RequestTargetingData{
    74  					"key2": {
    75  						TargetingValueByImpId: map[string][]byte{
    76  							"impId1": []byte(`impId1val`), "impId2": []byte(`impId2val`),
    77  						},
    78  					},
    79  				},
    80  			},
    81  			inputTargetingData: map[string]string{"inKey1": "inVal1"},
    82  			expectedTargetingData: map[string]string{
    83  				"inKey1": "inVal1", "key2": "impId1val",
    84  			},
    85  		},
    86  	}
    87  
    88  	bidImpId := "impId1"
    89  	for _, test := range testCases {
    90  		processRequestTargetingData(&test.inputAdServerTargeting, test.inputTargetingData, bidImpId)
    91  		assert.Equal(t, test.expectedTargetingData, test.inputTargetingData, "incorrect targeting data")
    92  	}
    93  
    94  }
    95  
    96  func TestProcessResponseTargetingData(t *testing.T) {
    97  
    98  	testCases := []struct {
    99  		description            string
   100  		inputAdServerTargeting adServerTargetingData
   101  		inputTargetingData     map[string]string
   102  		inputBid               openrtb2.Bid
   103  		inputResponse          *openrtb2.BidResponse
   104  		inputSeatExt           json.RawMessage
   105  
   106  		expectedTargetingData map[string]string
   107  		expectedWarnings      []openrtb_ext.ExtBidderMessage
   108  	}{
   109  
   110  		{
   111  			description: "get value from seatbid.bid",
   112  			inputAdServerTargeting: adServerTargetingData{
   113  				ResponseTargetingData: []ResponseTargetingData{
   114  					{Key: "{{BIDDER}}_custom1", HasMacro: true, Path: "seatbid.bid.impid"},
   115  				},
   116  			},
   117  			inputTargetingData: map[string]string{"inKey1": "inVal1"},
   118  			inputBid:           openrtb2.Bid{ID: "testBidId", ImpID: "testBidImpId1"},
   119  			inputResponse:      nil,
   120  			inputSeatExt:       nil,
   121  
   122  			expectedTargetingData: map[string]string{
   123  				"inKey1": "inVal1", "bidderA_custom1": "testBidImpId1",
   124  			},
   125  			expectedWarnings: []openrtb_ext.ExtBidderMessage(nil),
   126  		},
   127  		{
   128  			description: "get value from seatbid.bid.ext.prebid.foo",
   129  			inputAdServerTargeting: adServerTargetingData{
   130  				ResponseTargetingData: []ResponseTargetingData{
   131  					{Key: "{{BIDDER}}_custom1", HasMacro: true, Path: "seatbid.bid.ext.prebid.foo"},
   132  				},
   133  			},
   134  			inputTargetingData: map[string]string{"inKey1": "inVal1"},
   135  			inputBid:           openrtb2.Bid{ID: "testBidId", ImpID: "testBidImpId1"},
   136  			inputResponse:      nil,
   137  			inputSeatExt:       nil,
   138  
   139  			expectedTargetingData: map[string]string{
   140  				"inKey1": "inVal1", "bidderA_custom1": "bar1",
   141  			},
   142  			expectedWarnings: []openrtb_ext.ExtBidderMessage(nil),
   143  		},
   144  		{
   145  			description: "get value from ext.testData.foo",
   146  			inputAdServerTargeting: adServerTargetingData{
   147  				ResponseTargetingData: []ResponseTargetingData{
   148  					{Key: "{{BIDDER}}_custom1", HasMacro: true, Path: "ext.testData.foo"},
   149  				},
   150  			},
   151  			inputTargetingData: map[string]string{"inKey1": "inVal1"},
   152  			inputBid:           openrtb2.Bid{ID: "testBidId", ImpID: "testBidImpId1"},
   153  			inputResponse:      &openrtb2.BidResponse{Cur: "UAH", Ext: json.RawMessage(`{"testData": {"foo": "barExt"}}`)},
   154  			inputSeatExt:       nil,
   155  
   156  			expectedTargetingData: map[string]string{
   157  				"inKey1": "inVal1", "bidderA_custom1": "barExt",
   158  			},
   159  			expectedWarnings: []openrtb_ext.ExtBidderMessage(nil),
   160  		},
   161  		{
   162  			description: "get value from resp",
   163  			inputAdServerTargeting: adServerTargetingData{
   164  				ResponseTargetingData: []ResponseTargetingData{
   165  					{Key: "{{BIDDER}}_custom1", HasMacro: true, Path: "cur"},
   166  				},
   167  			},
   168  			inputTargetingData: map[string]string{"inKey1": "inVal1"},
   169  			inputBid:           openrtb2.Bid{ID: "testBidId", ImpID: "testBidImpId1"},
   170  			inputResponse:      &openrtb2.BidResponse{Cur: "UAH", Ext: json.RawMessage(`{"testData": {"foo": "barExt"}}`)},
   171  			inputSeatExt:       nil,
   172  
   173  			expectedTargetingData: map[string]string{
   174  				"inKey1": "inVal1", "bidderA_custom1": "UAH",
   175  			},
   176  			expectedWarnings: []openrtb_ext.ExtBidderMessage(nil),
   177  		},
   178  		{
   179  			description: "get value from resp ext",
   180  			inputAdServerTargeting: adServerTargetingData{
   181  				ResponseTargetingData: []ResponseTargetingData{
   182  					{Key: "{{BIDDER}}_custom1", HasMacro: true, Path: "seatbid.ext.testData.foo"},
   183  				},
   184  			},
   185  			inputTargetingData: map[string]string{"inKey1": "inVal1"},
   186  			inputBid:           openrtb2.Bid{ID: "testBidId", ImpID: "testBidImpId1"},
   187  			inputResponse:      nil,
   188  			inputSeatExt:       json.RawMessage(`{"testData": {"foo": "barBidderA"}}`),
   189  
   190  			expectedTargetingData: map[string]string{
   191  				"inKey1": "inVal1", "bidderA_custom1": "barBidderA",
   192  			},
   193  			expectedWarnings: []openrtb_ext.ExtBidderMessage(nil),
   194  		},
   195  		{
   196  			description: "get value from resp ext with incorrect format",
   197  			inputAdServerTargeting: adServerTargetingData{
   198  				ResponseTargetingData: []ResponseTargetingData{
   199  					{Key: "{{BIDDER}}_custom1", HasMacro: true, Path: "seatbid.ext.testData"},
   200  				},
   201  			},
   202  			inputTargetingData: map[string]string{"inKey1": "inVal1"},
   203  			inputBid:           openrtb2.Bid{ID: "testBidId", ImpID: "testBidImpId1"},
   204  			inputResponse:      &openrtb2.BidResponse{Cur: "UAH", Ext: json.RawMessage(`{"testData": {"foo": "barExt"}}`)},
   205  			inputSeatExt:       json.RawMessage(`{"testData": {"foo": "barBidderA"}}`),
   206  
   207  			expectedTargetingData: map[string]string{
   208  				"inKey1": "inVal1",
   209  			},
   210  			expectedWarnings: []openrtb_ext.ExtBidderMessage{
   211  				{Code: 10007, Message: "incorrect value type for path: testData, value can only be string or number for bidder: bidderA, bid id: testBidId"},
   212  			},
   213  		},
   214  	}
   215  
   216  	bidderName := "bidderA"
   217  
   218  	inputBidsCache := bidsCache{
   219  		bids: map[string]map[string][]byte{
   220  			"bidderA": {"testBidId": []byte(`{"id":"testBidId","impid":"testBidImpId1","price":10,"cat":["cat11","cat12"],"ext":{"prebid":{"foo":"bar1"}}}`)},
   221  		},
   222  	}
   223  
   224  	for _, test := range testCases {
   225  		actualWarnings := processResponseTargetingData(&test.inputAdServerTargeting,
   226  			test.inputTargetingData, bidderName, test.inputBid, inputBidsCache,
   227  			test.inputResponse, test.inputSeatExt)
   228  		assert.Equal(t, test.expectedWarnings, actualWarnings, "incorrect warnings returned")
   229  		assert.Equal(t, test.expectedTargetingData, test.inputTargetingData, "incorrect targeting data")
   230  	}
   231  }
   232  
   233  func TestBuildBidExt(t *testing.T) {
   234  
   235  	testCases := []struct {
   236  		description             string
   237  		inputTargetingData      map[string]string
   238  		inputBid                openrtb2.Bid
   239  		inputWarnings           []openrtb_ext.ExtBidderMessage
   240  		truncateTargetAttribute int
   241  		expectedExt             json.RawMessage
   242  		expectedWarnings        []openrtb_ext.ExtBidderMessage
   243  	}{
   244  
   245  		{
   246  			description:        "build valid bid ext with existing warnings",
   247  			inputTargetingData: map[string]string{"inKey1": "inVal1"},
   248  			inputBid:           openrtb2.Bid{ID: "testBidId", ImpID: "testBidImpId1", Ext: json.RawMessage(`{"prebid": {"test": 1}}`)},
   249  			inputWarnings: []openrtb_ext.ExtBidderMessage{
   250  				{Code: 10007, Message: "incorrect value type for path: testData, value can only be string or number for bidder: bidderA, bid id: testBidId"},
   251  			},
   252  			truncateTargetAttribute: 20,
   253  			expectedExt:             json.RawMessage(`{"prebid":{"targeting":{"inKey1":"inVal1"},"test":1}}`),
   254  			expectedWarnings: []openrtb_ext.ExtBidderMessage{
   255  				{Code: 10007, Message: "incorrect value type for path: testData, value can only be string or number for bidder: bidderA, bid id: testBidId"},
   256  			},
   257  		},
   258  		{
   259  			description:             "build valid bid ext without existing warnings",
   260  			inputTargetingData:      map[string]string{"inKey1": "inVal1"},
   261  			inputBid:                openrtb2.Bid{ID: "testBidId", ImpID: "testBidImpId1", Ext: json.RawMessage(`{"prebid": {"test": 1}}`)},
   262  			inputWarnings:           []openrtb_ext.ExtBidderMessage(nil),
   263  			truncateTargetAttribute: 20,
   264  			expectedExt:             json.RawMessage(`{"prebid":{"targeting":{"inKey1":"inVal1"},"test":1}}`),
   265  			expectedWarnings:        []openrtb_ext.ExtBidderMessage(nil),
   266  		},
   267  	}
   268  
   269  	for _, test := range testCases {
   270  		actualExt := buildBidExt(test.inputTargetingData, test.inputBid, test.inputWarnings, &test.truncateTargetAttribute)
   271  		assert.Equal(t, test.expectedWarnings, test.inputWarnings, "incorrect warnings returned")
   272  		assert.JSONEq(t, string(test.expectedExt), string(actualExt), "incorrect result extension")
   273  	}
   274  }
   275  
   276  func TestResolveKey(t *testing.T) {
   277  
   278  	testCases := []struct {
   279  		description                string
   280  		inputResponseTargetingData ResponseTargetingData
   281  		inputBidderName            string
   282  		expectedKey                string
   283  	}{
   284  
   285  		{
   286  			description:                "resolve key with macro",
   287  			inputResponseTargetingData: ResponseTargetingData{Key: "{{BIDDER}}_custom1", HasMacro: true, Path: "ext"},
   288  			inputBidderName:            "bidderA",
   289  			expectedKey:                "bidderA_custom1",
   290  		},
   291  		{
   292  			description:                "resolve key without macro",
   293  			inputResponseTargetingData: ResponseTargetingData{Key: "key_custom1", HasMacro: true, Path: "ext"},
   294  			inputBidderName:            "bidderA",
   295  			expectedKey:                "key_custom1",
   296  		},
   297  		{
   298  			description:                "resolve key with macro only",
   299  			inputResponseTargetingData: ResponseTargetingData{Key: "{{BIDDER}}", HasMacro: true, Path: "ext"},
   300  			inputBidderName:            "bidderA",
   301  			expectedKey:                "bidderA",
   302  		},
   303  		{
   304  			description:                "resolve key with macro and empty bidder name",
   305  			inputResponseTargetingData: ResponseTargetingData{Key: "{{BIDDER}}_custom1", HasMacro: true, Path: "ext"},
   306  			inputBidderName:            "",
   307  			expectedKey:                "_custom1",
   308  		},
   309  	}
   310  
   311  	for _, test := range testCases {
   312  		actualKey := resolveKey(test.inputResponseTargetingData, test.inputBidderName)
   313  		assert.Equal(t, test.expectedKey, actualKey, "incorrect resolved key")
   314  	}
   315  }
   316  
   317  func TestTruncateTargetingKeys(t *testing.T) {
   318  
   319  	testCases := []struct {
   320  		description             string
   321  		inputTargetingData      map[string]string
   322  		truncateTargetAttribute int
   323  		expectedTargetingData   map[string]string
   324  	}{
   325  
   326  		{
   327  			description:             "truncate targeting keys",
   328  			inputTargetingData:      map[string]string{"inKey1": "inVal1"},
   329  			truncateTargetAttribute: 5,
   330  			expectedTargetingData:   map[string]string{"inKey": "inVal1"},
   331  		},
   332  		{
   333  			description:             "do not truncate targeting keys",
   334  			inputTargetingData:      map[string]string{"inKey1": "inVal1"},
   335  			truncateTargetAttribute: 10,
   336  			expectedTargetingData:   map[string]string{"inKey1": "inVal1"},
   337  		},
   338  		{
   339  			description:             "exceed truncate limit",
   340  			inputTargetingData:      map[string]string{"inKey1": "inVal1"},
   341  			truncateTargetAttribute: 100,
   342  			expectedTargetingData:   map[string]string{"inKey1": "inVal1"},
   343  		},
   344  		{
   345  			description:             "limit long key to default length",
   346  			inputTargetingData:      map[string]string{"very_long_targeting_key_should_be_truncated_to_default": "inVal1"},
   347  			truncateTargetAttribute: 0,
   348  			expectedTargetingData:   map[string]string{"very_long_targeting_": "inVal1"},
   349  		},
   350  	}
   351  
   352  	for _, test := range testCases {
   353  		actualTargetingData := truncateTargetingKeys(test.inputTargetingData, &test.truncateTargetAttribute)
   354  		assert.Equal(t, test.expectedTargetingData, actualTargetingData, "incorrect targeting data")
   355  	}
   356  }
   357  
   358  func TestGetValueFromSeatBidBid(t *testing.T) {
   359  
   360  	testCases := []struct {
   361  		description   string
   362  		inputPath     string
   363  		expectedValue string
   364  		expectError   bool
   365  	}{
   366  
   367  		{
   368  			description:   "get existing valid value from bid",
   369  			inputPath:     "seatbid.bid.price",
   370  			expectedValue: "10",
   371  			expectError:   false,
   372  		},
   373  		{
   374  			description:   "get existing invalid value from bid",
   375  			inputPath:     "seatbid.bid.cat",
   376  			expectedValue: "",
   377  			expectError:   true,
   378  		},
   379  		{
   380  			description:   "get non-existing value from bid",
   381  			inputPath:     "seatbid.bid.test",
   382  			expectedValue: "",
   383  			expectError:   true,
   384  		},
   385  	}
   386  
   387  	inputBidsCache := bidsCache{
   388  		bids: map[string]map[string][]byte{
   389  			"bidderA": {"testBidId": []byte(`{"id":"testBidId","impid":"testBidImpId1","price":10,"cat":["cat11","cat12"],"ext":{"prebid":{"foo":"bar1"}}}`)},
   390  		},
   391  	}
   392  
   393  	for _, test := range testCases {
   394  		actualValue, actualErr := getValueFromSeatBidBid(test.inputPath, inputBidsCache, "bidderA", openrtb2.Bid{ID: "testBidId"})
   395  		assert.Equal(t, test.expectedValue, actualValue, "incorrect value returned")
   396  		if test.expectError {
   397  			assert.Error(t, actualErr, "unexpected error returned")
   398  		} else {
   399  			assert.NoError(t, actualErr, "expected error not returned")
   400  		}
   401  	}
   402  }
   403  
   404  func TestGetValueFromExt(t *testing.T) {
   405  
   406  	testCases := []struct {
   407  		description    string
   408  		inputPath      string
   409  		inputSeparator string
   410  		expectedValue  string
   411  		expectError    bool
   412  	}{
   413  
   414  		{
   415  			description:    "get existing valid value from bid.ext",
   416  			inputPath:      "seatbid.ext.prebid.foo",
   417  			inputSeparator: "seatbid.ext.",
   418  			expectedValue:  "bar1",
   419  			expectError:    false,
   420  		},
   421  		{
   422  			description:    "get non-existing valid value from bid.ext",
   423  			inputPath:      "seatbid.ext.prebid.foo1",
   424  			inputSeparator: "seatbid.ext.",
   425  			expectedValue:  "",
   426  			expectError:    true,
   427  		},
   428  		{
   429  			description:    "get existing valid with incorrect type value from bid.ext",
   430  			inputPath:      "seatbid.ext.prebid",
   431  			inputSeparator: "seatbid.ext.",
   432  			expectedValue:  "",
   433  			expectError:    true,
   434  		},
   435  		{
   436  			description:    "get existing valid with unexpected separator from bid.ext",
   437  			inputPath:      "seatbid.ext.prebid",
   438  			inputSeparator: ".ext.",
   439  			expectedValue:  "",
   440  			expectError:    true,
   441  		},
   442  	}
   443  	inputExt := json.RawMessage(`{"prebid":{"foo":"bar1"}}`)
   444  
   445  	for _, test := range testCases {
   446  		actualValue, actualErr := getValueFromExt(test.inputPath, test.inputSeparator, inputExt)
   447  		assert.Equal(t, test.expectedValue, actualValue, "incorrect value returned")
   448  		if test.expectError {
   449  			assert.Error(t, actualErr, "unexpected error returned")
   450  		} else {
   451  			assert.NoError(t, actualErr, "expected error not returned")
   452  		}
   453  	}
   454  }
   455  
   456  func TestGetValueFromResp(t *testing.T) {
   457  
   458  	testCases := []struct {
   459  		description   string
   460  		inputPath     string
   461  		inputResponse *openrtb2.BidResponse
   462  		expectedValue string
   463  		expectError   bool
   464  	}{
   465  
   466  		{
   467  			description:   "get existing valid value from response",
   468  			inputPath:     "cur",
   469  			inputResponse: &openrtb2.BidResponse{Cur: "UAH"},
   470  			expectedValue: "UAH",
   471  			expectError:   false,
   472  		},
   473  		{
   474  			description:   "get empty valid value from response",
   475  			inputPath:     "id",
   476  			inputResponse: &openrtb2.BidResponse{},
   477  			expectedValue: "",
   478  			expectError:   false,
   479  		},
   480  		{
   481  			description:   "get non-existing valid value from response",
   482  			inputPath:     "test",
   483  			inputResponse: &openrtb2.BidResponse{},
   484  			expectedValue: "",
   485  			expectError:   true,
   486  		},
   487  	}
   488  
   489  	for _, test := range testCases {
   490  		actualValue, actualErr := getValueFromResp(test.inputPath, test.inputResponse)
   491  		assert.Equal(t, test.expectedValue, actualValue, "incorrect value returned")
   492  		if test.expectError {
   493  			assert.Error(t, actualErr, "unexpected error returned")
   494  		} else {
   495  			assert.NoError(t, actualErr, "expected error not returned")
   496  		}
   497  	}
   498  }
   499  
   500  func TestGetRespData(t *testing.T) {
   501  
   502  	nbr := openrtb3.NoBidProxy
   503  	testCases := []struct {
   504  		description   string
   505  		inputField    string
   506  		inputResponse *openrtb2.BidResponse
   507  		expectedValue string
   508  		expectError   bool
   509  	}{
   510  		{
   511  			description:   "get id from response",
   512  			inputField:    "id",
   513  			inputResponse: &openrtb2.BidResponse{ID: "testId"},
   514  			expectedValue: "testId",
   515  			expectError:   false,
   516  		},
   517  		{
   518  			description:   "get bidid from response",
   519  			inputField:    "bidid",
   520  			inputResponse: &openrtb2.BidResponse{BidID: "testBidId"},
   521  			expectedValue: "testBidId",
   522  			expectError:   false,
   523  		},
   524  		{
   525  			description:   "get cur from response",
   526  			inputField:    "cur",
   527  			inputResponse: &openrtb2.BidResponse{Cur: "UAH"},
   528  			expectedValue: "UAH",
   529  			expectError:   false,
   530  		},
   531  		{
   532  			description:   "get customdata from response",
   533  			inputField:    "customdata",
   534  			inputResponse: &openrtb2.BidResponse{CustomData: "testCustomdata"},
   535  			expectedValue: "testCustomdata",
   536  			expectError:   false,
   537  		},
   538  		{
   539  			description:   "get nbr from response",
   540  			inputField:    "nbr",
   541  			inputResponse: &openrtb2.BidResponse{NBR: &nbr},
   542  			expectedValue: "5",
   543  			expectError:   false,
   544  		},
   545  		{
   546  			description:   "get non-existing value from response",
   547  			inputField:    "test",
   548  			inputResponse: &openrtb2.BidResponse{ID: "testId"},
   549  			expectedValue: "",
   550  			expectError:   true,
   551  		},
   552  	}
   553  
   554  	for _, test := range testCases {
   555  		actualValue, actualErr := getRespData(test.inputResponse, test.inputField)
   556  		assert.Equal(t, test.expectedValue, actualValue, "incorrect value returned")
   557  		if test.expectError {
   558  			assert.Error(t, actualErr, "unexpected error returned")
   559  		} else {
   560  			assert.NoError(t, actualErr, "expected error not returned")
   561  		}
   562  	}
   563  
   564  }
   565  
   566  func TestResponseObjectStructure(t *testing.T) {
   567  	// in case BidResponse format will change in next versions this test will show the error
   568  	// current implementation is up to date with OpenRTB 2.5 and OpenRTB 2.6 formats
   569  	fieldsToCheck := map[string]reflect.Kind{
   570  		"id":         reflect.String,
   571  		"bidid":      reflect.String,
   572  		"cur":        reflect.String,
   573  		"customdata": reflect.String,
   574  		"nbr":        reflect.Pointer,
   575  	}
   576  
   577  	tt := reflect.TypeOf(openrtb2.BidResponse{})
   578  	fields := reflect.VisibleFields(tt)
   579  
   580  	for fieldName, fieldType := range fieldsToCheck {
   581  		fieldFound := false
   582  		for _, field := range fields {
   583  			if fieldName == strings.ToLower(field.Name) {
   584  				fieldFound = true
   585  				assert.Equal(t, fieldType, field.Type.Kind(), "incorrect type for field: %s", fieldName)
   586  				break
   587  			}
   588  		}
   589  		assert.True(t, fieldFound, "field %s is not found in bidResponse object", fieldName)
   590  	}
   591  }