github.com/prebid/prebid-server@v0.275.0/openrtb_ext/request_wrapper_test.go (about) 1 package openrtb_ext 2 3 import ( 4 "encoding/json" 5 "testing" 6 7 "github.com/prebid/openrtb/v19/openrtb2" 8 "github.com/prebid/prebid-server/util/ptrutil" 9 "github.com/stretchr/testify/assert" 10 ) 11 12 func TestCloneRequestWrapper(t *testing.T) { 13 testCases := []struct { 14 name string 15 reqWrap *RequestWrapper 16 reqWrapCopy *RequestWrapper // manual copy of above ext object to verify against 17 mutator func(t *testing.T, reqWrap *RequestWrapper) // function to modify the Ext object 18 }{ 19 { 20 name: "Nil", // Verify the nil case 21 reqWrap: nil, 22 reqWrapCopy: nil, 23 mutator: func(t *testing.T, reqWrap *RequestWrapper) {}, 24 }, 25 { 26 name: "NoMutate", 27 reqWrap: &RequestWrapper{ 28 impWrappers: []*ImpWrapper{ 29 { 30 impExt: &ImpExt{prebid: &ExtImpPrebid{Options: &Options{EchoVideoAttrs: true}}, prebidDirty: true, tid: "fun"}, 31 }, 32 { 33 impExt: &ImpExt{tid: "star"}, 34 }, 35 }, 36 userExt: &UserExt{consentDirty: true}, 37 deviceExt: &DeviceExt{extDirty: true}, 38 requestExt: &RequestExt{ 39 prebid: &ExtRequestPrebid{Integration: "derivative"}, 40 }, 41 appExt: &AppExt{prebidDirty: true}, 42 regExt: &RegExt{usPrivacy: "foo"}, 43 siteExt: &SiteExt{amp: ptrutil.ToPtr[int8](1)}, 44 sourceExt: &SourceExt{schainDirty: true}, 45 }, 46 reqWrapCopy: &RequestWrapper{ 47 impWrappers: []*ImpWrapper{ 48 { 49 impExt: &ImpExt{prebid: &ExtImpPrebid{Options: &Options{EchoVideoAttrs: true}}, prebidDirty: true, tid: "fun"}, 50 }, 51 { 52 impExt: &ImpExt{tid: "star"}, 53 }, 54 }, 55 userExt: &UserExt{consentDirty: true}, 56 deviceExt: &DeviceExt{extDirty: true}, 57 requestExt: &RequestExt{ 58 prebid: &ExtRequestPrebid{Integration: "derivative"}, 59 }, 60 appExt: &AppExt{prebidDirty: true}, 61 regExt: &RegExt{usPrivacy: "foo"}, 62 siteExt: &SiteExt{amp: ptrutil.ToPtr[int8](1)}, 63 sourceExt: &SourceExt{schainDirty: true}, 64 }, 65 mutator: func(t *testing.T, reqWrap *RequestWrapper) {}, 66 }, 67 { 68 name: "General", 69 reqWrap: &RequestWrapper{ 70 impWrappers: []*ImpWrapper{ 71 { 72 impExt: &ImpExt{prebid: &ExtImpPrebid{Options: &Options{EchoVideoAttrs: true}}, prebidDirty: true, tid: "fun"}, 73 }, 74 { 75 impExt: &ImpExt{tid: "star"}, 76 }, 77 }, 78 userExt: &UserExt{consentDirty: true}, 79 deviceExt: &DeviceExt{extDirty: true}, 80 requestExt: &RequestExt{ 81 prebid: &ExtRequestPrebid{Integration: "derivative"}, 82 }, 83 appExt: &AppExt{prebidDirty: true}, 84 regExt: &RegExt{usPrivacy: "foo"}, 85 siteExt: &SiteExt{amp: ptrutil.ToPtr[int8](1)}, 86 sourceExt: &SourceExt{schainDirty: true}, 87 }, 88 reqWrapCopy: &RequestWrapper{ 89 impWrappers: []*ImpWrapper{ 90 { 91 impExt: &ImpExt{prebid: &ExtImpPrebid{Options: &Options{EchoVideoAttrs: true}}, prebidDirty: true, tid: "fun"}, 92 }, 93 { 94 impExt: &ImpExt{tid: "star"}, 95 }, 96 }, 97 userExt: &UserExt{consentDirty: true}, 98 deviceExt: &DeviceExt{extDirty: true}, 99 requestExt: &RequestExt{ 100 prebid: &ExtRequestPrebid{Integration: "derivative"}, 101 }, 102 appExt: &AppExt{prebidDirty: true}, 103 regExt: &RegExt{usPrivacy: "foo"}, 104 siteExt: &SiteExt{amp: ptrutil.ToPtr[int8](1)}, 105 sourceExt: &SourceExt{schainDirty: true}, 106 }, 107 mutator: func(t *testing.T, reqWrap *RequestWrapper) { 108 reqWrap.impWrappers[1].impExt.prebidDirty = true 109 reqWrap.impWrappers[0] = nil 110 reqWrap.impWrappers = append(reqWrap.impWrappers, &ImpWrapper{impExt: &ImpExt{tid: "star"}}) 111 reqWrap.impWrappers = nil 112 reqWrap.userExt = nil 113 reqWrap.deviceExt = nil 114 reqWrap.requestExt = nil 115 reqWrap.appExt = nil 116 reqWrap.regExt = nil 117 reqWrap.siteExt = nil 118 reqWrap.sourceExt = nil 119 }, 120 }, 121 } 122 123 for _, test := range testCases { 124 t.Run(test.name, func(t *testing.T) { 125 clone := test.reqWrap.Clone() 126 test.mutator(t, test.reqWrap) 127 assert.Equal(t, test.reqWrapCopy, clone) 128 }) 129 } 130 } 131 132 func TestUserExt(t *testing.T) { 133 userExt := &UserExt{} 134 135 userExt.unmarshal(nil) 136 assert.Equal(t, false, userExt.Dirty(), "New UserExt should not be dirty.") 137 assert.Nil(t, userExt.GetConsent(), "Empty UserExt should have nil consent") 138 assert.Nil(t, userExt.GetEid(), "Empty UserExt should have nil eid") 139 assert.Nil(t, userExt.GetPrebid(), "Empty UserExt should have nil prebid") 140 assert.Nil(t, userExt.GetConsentedProvidersSettingsIn(), "Empty UserExt should have nil consentedProvidersSettings") 141 142 newConsent := "NewConsent" 143 userExt.SetConsent(&newConsent) 144 assert.Equal(t, "NewConsent", *userExt.GetConsent(), "UserExt consent is incorrect") 145 146 eid := openrtb2.EID{Source: "source", UIDs: []openrtb2.UID{{ID: "id"}}} 147 newEid := []openrtb2.EID{eid} 148 userExt.SetEid(&newEid) 149 assert.Equal(t, []openrtb2.EID{eid}, *userExt.GetEid(), "UserExt eid is incorrect") 150 151 buyerIDs := map[string]string{"buyer": "id"} 152 newPrebid := ExtUserPrebid{BuyerUIDs: buyerIDs} 153 userExt.SetPrebid(&newPrebid) 154 assert.Equal(t, ExtUserPrebid{BuyerUIDs: buyerIDs}, *userExt.GetPrebid(), "UserExt prebid is incorrect") 155 156 consentedProvidersSettings := &ConsentedProvidersSettingsIn{ConsentedProvidersString: "1~X.X.X"} 157 userExt.SetConsentedProvidersSettingsIn(consentedProvidersSettings) 158 assert.Equal(t, &ConsentedProvidersSettingsIn{ConsentedProvidersString: "1~X.X.X"}, userExt.GetConsentedProvidersSettingsIn(), "UserExt consentedProvidersSettings is incorrect") 159 assert.Equal(t, true, userExt.Dirty(), "UserExt should be dirty after field updates") 160 cpsIn := userExt.GetConsentedProvidersSettingsIn() 161 assert.Equal(t, "1~X.X.X", cpsIn.ConsentedProvidersString, "UserExt consentedProviders is incorrect") 162 163 consentedProvidersString := "1~1.35.41.101" 164 cpsIn.ConsentedProvidersString = consentedProvidersString 165 userExt.SetConsentedProvidersSettingsIn(cpsIn) 166 cpsIn = userExt.GetConsentedProvidersSettingsIn() 167 assert.Equal(t, "1~1.35.41.101", cpsIn.ConsentedProvidersString, "UserExt consentedProviders is incorrect") 168 169 cpsOut := &ConsentedProvidersSettingsOut{} 170 //cpsOut.ConsentedProvidersList = make([]int, 0, 1) 171 cpsOut.ConsentedProvidersList = append(cpsOut.ConsentedProvidersList, ParseConsentedProvidersString(consentedProvidersString)...) 172 assert.Len(t, cpsOut.ConsentedProvidersList, 4, "UserExt consentedProvidersList is incorrect") 173 userExt.SetConsentedProvidersSettingsOut(cpsOut) 174 175 updatedUserExt, err := userExt.marshal() 176 assert.Nil(t, err, "Marshalling UserExt after updating should not cause an error") 177 178 expectedUserExt := json.RawMessage(`{"consent":"NewConsent","prebid":{"buyeruids":{"buyer":"id"}},"consented_providers_settings":{"consented_providers":[1,35,41,101]},"ConsentedProvidersSettings":{"consented_providers":"1~1.35.41.101"},"eids":[{"source":"source","uids":[{"id":"id"}]}]}`) 179 assert.JSONEq(t, string(expectedUserExt), string(updatedUserExt), "Marshalled UserExt is incorrect") 180 181 assert.Equal(t, false, userExt.Dirty(), "UserExt should not be dirty after marshalling") 182 } 183 184 func TestRebuildImp(t *testing.T) { 185 var ( 186 prebid = &ExtImpPrebid{IsRewardedInventory: openrtb2.Int8Ptr(1)} 187 prebidJson = json.RawMessage(`{"prebid":{"is_rewarded_inventory":1}}`) 188 ) 189 190 testCases := []struct { 191 description string 192 request openrtb2.BidRequest 193 requestImpWrapper []*ImpWrapper 194 expectedRequest openrtb2.BidRequest 195 expectedError string 196 }{ 197 { 198 description: "Empty - Never Accessed", 199 request: openrtb2.BidRequest{}, 200 requestImpWrapper: nil, 201 expectedRequest: openrtb2.BidRequest{}, 202 }, 203 { 204 description: "One - Never Accessed", 205 request: openrtb2.BidRequest{Imp: []openrtb2.Imp{{ID: "1"}}}, 206 requestImpWrapper: nil, 207 expectedRequest: openrtb2.BidRequest{Imp: []openrtb2.Imp{{ID: "1"}}}, 208 }, 209 { 210 description: "One - Accessed - Dirty", 211 request: openrtb2.BidRequest{Imp: []openrtb2.Imp{{ID: "1"}}}, 212 requestImpWrapper: []*ImpWrapper{{Imp: &openrtb2.Imp{ID: "2"}, impExt: &ImpExt{prebid: prebid, prebidDirty: true}}}, 213 expectedRequest: openrtb2.BidRequest{Imp: []openrtb2.Imp{{ID: "2", Ext: prebidJson}}}, 214 }, 215 { 216 description: "One - Accessed - Error", 217 request: openrtb2.BidRequest{Imp: []openrtb2.Imp{{ID: "1"}}}, 218 requestImpWrapper: []*ImpWrapper{{Imp: nil, impExt: &ImpExt{}}}, 219 expectedError: "ImpWrapper RebuildImp called on a nil Imp", 220 }, 221 { 222 description: "Many - Accessed - Dirty / Not Dirty", 223 request: openrtb2.BidRequest{Imp: []openrtb2.Imp{{ID: "1"}, {ID: "2"}}}, 224 requestImpWrapper: []*ImpWrapper{{Imp: &openrtb2.Imp{ID: "1"}, impExt: &ImpExt{}}, {Imp: &openrtb2.Imp{ID: "2"}, impExt: &ImpExt{prebid: prebid, prebidDirty: true}}}, 225 expectedRequest: openrtb2.BidRequest{Imp: []openrtb2.Imp{{ID: "1"}, {ID: "2", Ext: prebidJson}}}, 226 }, 227 } 228 229 for _, test := range testCases { 230 // create required filed in the test loop to keep test declaration easier to read 231 for _, w := range test.requestImpWrapper { 232 w.impExt.ext = make(map[string]json.RawMessage) 233 } 234 235 w := RequestWrapper{BidRequest: &test.request, impWrappers: test.requestImpWrapper, impWrappersAccessed: test.requestImpWrapper != nil} 236 err := w.RebuildRequest() 237 238 if test.expectedError != "" { 239 assert.EqualError(t, err, test.expectedError, test.description) 240 } else { 241 assert.NoError(t, err, test.description) 242 assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description) 243 } 244 } 245 } 246 247 func TestRebuildUserExt(t *testing.T) { 248 strA := "a" 249 strB := "b" 250 251 type testCase struct { 252 description string 253 request openrtb2.BidRequest 254 requestUserExtWrapper UserExt 255 expectedRequest openrtb2.BidRequest 256 } 257 testGroups := []struct { 258 groupDesc string 259 tests []testCase 260 }{ 261 { 262 groupDesc: "Consent string tests", 263 tests: []testCase{ 264 // Nil req.User 265 { 266 description: "Nil req.User - UserExt Not Dirty", 267 request: openrtb2.BidRequest{}, 268 requestUserExtWrapper: UserExt{}, 269 expectedRequest: openrtb2.BidRequest{}, 270 }, 271 { 272 description: "Nil req.User - Dirty UserExt", 273 request: openrtb2.BidRequest{}, 274 requestUserExtWrapper: UserExt{consent: &strA, consentDirty: true}, 275 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}}, 276 }, 277 { 278 description: "Nil req.User - Dirty UserExt but consent is nil - No Change", 279 request: openrtb2.BidRequest{}, 280 requestUserExtWrapper: UserExt{consent: nil, consentDirty: true}, 281 expectedRequest: openrtb2.BidRequest{}, 282 }, 283 // Nil req.User.Ext 284 { 285 description: "Nil req.User.Ext - Not Dirty - No Change", 286 request: openrtb2.BidRequest{User: &openrtb2.User{}}, 287 requestUserExtWrapper: UserExt{}, 288 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}}, 289 }, 290 { 291 description: "Nil req.User.Ext - Dirty with valid consent string - Expect consent string to be added", 292 request: openrtb2.BidRequest{User: &openrtb2.User{}}, 293 requestUserExtWrapper: UserExt{consent: &strB, consentDirty: true}, 294 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"b"}`)}}, 295 }, 296 { 297 description: "Nil req.User.Ext - Dirty UserExt with nil consent string- No Change", 298 request: openrtb2.BidRequest{User: &openrtb2.User{}}, 299 requestUserExtWrapper: UserExt{consent: nil, consentDirty: true}, 300 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}}, 301 }, 302 // Not-nil req.User.Ext 303 { 304 description: "Populated - Not Dirty - No Change", 305 request: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}}, 306 requestUserExtWrapper: UserExt{}, 307 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}}, 308 }, 309 { 310 description: "Populated - Dirty - Consent string overriden", 311 request: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}}, 312 requestUserExtWrapper: UserExt{consent: &strB, consentDirty: true}, 313 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"b"}`)}}, 314 }, 315 { 316 description: "Populated - Dirty but consent string is equal to the one already set - No Change", 317 request: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}}, 318 requestUserExtWrapper: UserExt{consent: &strA, consentDirty: true}, 319 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}}, 320 }, 321 { 322 description: "Populated - Dirty UserExt with nil consent string - Cleared", 323 request: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}}, 324 requestUserExtWrapper: UserExt{consent: nil, consentDirty: true}, 325 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}}, 326 }, 327 }, 328 }, 329 { 330 groupDesc: "Consented provider settings string form", 331 tests: []testCase{ 332 // Nil req.User 333 { 334 description: "Nil req.User - Dirty UserExt with nil consentedProviderSettings - No Change", 335 request: openrtb2.BidRequest{}, 336 requestUserExtWrapper: UserExt{}, 337 expectedRequest: openrtb2.BidRequest{}, 338 }, 339 { 340 description: "Nil req.User - Dirty UserExt with empty consentedProviderSettings - No Change", 341 request: openrtb2.BidRequest{}, 342 requestUserExtWrapper: UserExt{consentedProvidersSettingsIn: &ConsentedProvidersSettingsIn{}, consentedProvidersSettingsInDirty: true}, 343 expectedRequest: openrtb2.BidRequest{}, 344 }, 345 { 346 description: "Nil req.User - Dirty UserExt with populated consentedProviderSettings - ConsentedProvidersSettings are added", 347 request: openrtb2.BidRequest{}, 348 requestUserExtWrapper: UserExt{consentedProvidersSettingsIn: &ConsentedProvidersSettingsIn{ConsentedProvidersString: "ConsentedProvidersString"}, consentedProvidersSettingsInDirty: true}, 349 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"ConsentedProvidersSettings":{"consented_providers":"ConsentedProvidersString"}}`)}}, 350 }, 351 // Nil req.User.Ext 352 { 353 description: "Nil req.User.Ext - Dirty UserExt with nil consentedProviderSettings - No Change", 354 request: openrtb2.BidRequest{User: &openrtb2.User{}}, 355 requestUserExtWrapper: UserExt{consentedProvidersSettingsIn: nil, consentedProvidersSettingsInDirty: true}, 356 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}}, 357 }, 358 { 359 description: "Nil req.User.Ext - Dirty UserExt with empty consentedProviderSettings - No Change", 360 request: openrtb2.BidRequest{User: &openrtb2.User{}}, 361 requestUserExtWrapper: UserExt{consentedProvidersSettingsIn: &ConsentedProvidersSettingsIn{}, consentedProvidersSettingsInDirty: true}, 362 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}}, 363 }, 364 { 365 description: "Nil req.User.Ext - Dirty UserExt with populated consentedProviderSettings - ConsentedProvidersSettings are added", 366 request: openrtb2.BidRequest{User: &openrtb2.User{}}, 367 requestUserExtWrapper: UserExt{consentedProvidersSettingsIn: &ConsentedProvidersSettingsIn{ConsentedProvidersString: "ConsentedProvidersString"}, consentedProvidersSettingsInDirty: true}, 368 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"ConsentedProvidersSettings":{"consented_providers":"ConsentedProvidersString"}}`)}}, 369 }, 370 // Not-nil req.User.Ext 371 { 372 description: "Populated req.User.Ext - Not Dirty UserExt - No Change", 373 request: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"ConsentedProvidersSettings":{"consented_providers":"1~X.X.X.X"}}`)}}, 374 requestUserExtWrapper: UserExt{}, 375 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"ConsentedProvidersSettings":{"consented_providers":"1~X.X.X.X"}}`)}}, 376 }, 377 { 378 description: "Populated req.User.Ext - Dirty UserExt with nil consentedProviderSettings - Populated req.User.Ext gets overriden with nil User.Ext", 379 request: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"ConsentedProvidersSettings":{"consented_providers":"1~X.X.X.X"}}`)}}, 380 requestUserExtWrapper: UserExt{consentedProvidersSettingsIn: nil, consentedProvidersSettingsInDirty: true}, 381 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}}, 382 }, 383 { 384 description: "Populated req.User.Ext - Dirty UserExt with empty consentedProviderSettings - Populated req.User.Ext gets overriden with nil User.Ext", 385 request: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"1~X.X.X.X":{"consented_providers":"1~X.X.X.X"}}`)}}, 386 requestUserExtWrapper: UserExt{consentedProvidersSettingsIn: &ConsentedProvidersSettingsIn{}, consentedProvidersSettingsInDirty: true}, 387 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}}, 388 }, 389 { 390 description: "Populated req.User.Ext - Dirty UserExt with populated consentedProviderSettings - ConsentedProvidersSettings are overriden", 391 request: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"ConsentedProvidersSettings":{"consented_providers":"1~X.X.X.X"}}`)}}, 392 requestUserExtWrapper: UserExt{consentedProvidersSettingsIn: &ConsentedProvidersSettingsIn{ConsentedProvidersString: "1~1.35.41.101"}, consentedProvidersSettingsInDirty: true}, 393 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"ConsentedProvidersSettings":{"consented_providers":"1~1.35.41.101"}}`)}}, 394 }, 395 }, 396 }, 397 { 398 groupDesc: "Consented provider settings array", 399 tests: []testCase{ 400 // Nil req.User 401 { 402 description: "Nil req.User - Dirty UserExt with nil consentedProviderSettings - No Change", 403 request: openrtb2.BidRequest{}, 404 requestUserExtWrapper: UserExt{}, 405 expectedRequest: openrtb2.BidRequest{}, 406 }, 407 { 408 description: "Nil req.User - Dirty UserExt with empty consentedProviderSettings - No Change", 409 request: openrtb2.BidRequest{}, 410 requestUserExtWrapper: UserExt{consentedProvidersSettingsOut: &ConsentedProvidersSettingsOut{}, consentedProvidersSettingsOutDirty: true}, 411 expectedRequest: openrtb2.BidRequest{}, 412 }, 413 { 414 description: "Nil req.User - Dirty UserExt with populated consentedProviderSettings - ConsentedProvidersSettings are added", 415 request: openrtb2.BidRequest{}, 416 requestUserExtWrapper: UserExt{consentedProvidersSettingsOut: &ConsentedProvidersSettingsOut{ConsentedProvidersList: []int{1, 35, 41, 101}}, consentedProvidersSettingsOutDirty: true}, 417 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consented_providers_settings":{"consented_providers":[1,35,41,101]}}`)}}, 418 }, 419 // Nil req.User.Ext 420 { 421 description: "Nil req.User.Ext - Dirty UserExt with nil consentedProviderSettings - No Change", 422 request: openrtb2.BidRequest{User: &openrtb2.User{}}, 423 requestUserExtWrapper: UserExt{consentedProvidersSettingsOut: nil, consentedProvidersSettingsOutDirty: true}, 424 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}}, 425 }, 426 { 427 description: "Nil req.User.Ext - Dirty UserExt with empty consentedProviderSettings - No Change", 428 request: openrtb2.BidRequest{User: &openrtb2.User{}}, 429 requestUserExtWrapper: UserExt{consentedProvidersSettingsOut: &ConsentedProvidersSettingsOut{}, consentedProvidersSettingsOutDirty: true}, 430 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}}, 431 }, 432 { 433 description: "Nil req.User.Ext - Dirty UserExt with populated consentedProviderSettings - ConsentedProvidersSettings are added", 434 request: openrtb2.BidRequest{User: &openrtb2.User{}}, 435 requestUserExtWrapper: UserExt{consentedProvidersSettingsOut: &ConsentedProvidersSettingsOut{ConsentedProvidersList: []int{1, 35, 41, 101}}, consentedProvidersSettingsOutDirty: true}, 436 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consented_providers_settings":{"consented_providers":[1,35,41,101]}}`)}}, 437 }, 438 // Not-nil req.User.Ext 439 { 440 description: "Populated req.User.Ext - Not Dirty UserExt - No Change", 441 request: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consented_providers_settings":{"consented_providers":[1,35,41,101]}}`)}}, 442 requestUserExtWrapper: UserExt{}, 443 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consented_providers_settings":{"consented_providers":[1,35,41,101]}}`)}}, 444 }, 445 { 446 description: "Populated req.User.Ext - Dirty UserExt with nil consentedProviderSettingsOut - Populated req.User.Ext gets overriden with nil User.Ext", 447 request: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consented_providers_settings":{"consented_providers":[1,35,41,101]}}`)}}, 448 requestUserExtWrapper: UserExt{consentedProvidersSettingsOut: nil, consentedProvidersSettingsOutDirty: true}, 449 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}}, 450 }, 451 { 452 description: "Populated req.User.Ext - Dirty UserExt with empty consentedProviderSettingsOut - Populated req.User.Ext gets overriden with nil User.Ext", 453 request: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consented_providers_settings":{"consented_providers":[1,35,41,101]}}`)}}, 454 requestUserExtWrapper: UserExt{consentedProvidersSettingsOut: &ConsentedProvidersSettingsOut{}, consentedProvidersSettingsOutDirty: true}, 455 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}}, 456 }, 457 { 458 description: "Populated req.User.Ext - Dirty UserExt with populated consentedProviderSettingsOut - consented_providers list elements are overriden", 459 request: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consented_providers_settings":{"consented_providers":[1,35,41,101]}}`)}}, 460 requestUserExtWrapper: UserExt{consentedProvidersSettingsOut: &ConsentedProvidersSettingsOut{ConsentedProvidersList: []int{35, 36, 240}}, consentedProvidersSettingsOutDirty: true}, 461 expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consented_providers_settings":{"consented_providers":[35,36,240]}}`)}}, 462 }, 463 }, 464 }, 465 } 466 467 for _, group := range testGroups { 468 for _, test := range group.tests { 469 // create required filed in the test loop to keep test declaration easier to read 470 test.requestUserExtWrapper.ext = make(map[string]json.RawMessage) 471 472 w := RequestWrapper{BidRequest: &test.request, userExt: &test.requestUserExtWrapper} 473 w.RebuildRequest() 474 assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description) 475 } 476 } 477 } 478 479 func TestUserExtUnmarshal(t *testing.T) { 480 type testInput struct { 481 userExt *UserExt 482 extJson json.RawMessage 483 } 484 testCases := []struct { 485 desc string 486 in testInput 487 expectError bool 488 }{ 489 { 490 desc: "UserExt.ext is not nil, don't expect error", 491 in: testInput{ 492 userExt: &UserExt{ 493 ext: map[string]json.RawMessage{ 494 "eids": json.RawMessage(`[{"source":"value"}]`), 495 }, 496 }, 497 extJson: json.RawMessage(`{"prebid":{"buyeruids":{"elem1":"value1"}}}`), 498 }, 499 expectError: false, 500 }, 501 { 502 desc: "UserExt.ext is dirty, don't expect error", 503 in: testInput{ 504 userExt: &UserExt{extDirty: true}, 505 extJson: json.RawMessage(`{"prebid":{"buyeruids":{"elem1":"value1"}}}`), 506 }, 507 expectError: false, 508 }, 509 // Eids 510 { 511 desc: "Has eids and it is valid JSON", 512 in: testInput{ 513 userExt: &UserExt{}, 514 extJson: json.RawMessage(`{"eids":[{"source":"value"}]}`), 515 }, 516 expectError: false, 517 }, 518 { 519 desc: "Has malformed eids expect error", 520 in: testInput{ 521 userExt: &UserExt{}, 522 extJson: json.RawMessage(`{"eids":123}`), 523 }, 524 expectError: true, 525 }, 526 // prebid 527 { 528 desc: "Has prebid and it is valid JSON", 529 in: testInput{ 530 userExt: &UserExt{}, 531 extJson: json.RawMessage(`{"prebid":{"buyeruids":{"elem1":"value1"}}}`), 532 }, 533 expectError: false, 534 }, 535 { 536 desc: "Has malformed prebid expect error", 537 in: testInput{ 538 userExt: &UserExt{}, 539 extJson: json.RawMessage(`{"prebid":{"buyeruids":123}}`), 540 }, 541 expectError: true, 542 }, 543 // ConsentedProvidersSettings 544 { 545 desc: "Has ConsentedProvidersSettings and it is valid JSON", 546 in: testInput{ 547 userExt: &UserExt{}, 548 extJson: json.RawMessage(`{"ConsentedProvidersSettings":{"consented_providers":"ConsentedProvidersString"}}`), 549 }, 550 expectError: false, 551 }, 552 { 553 desc: "Has malformed ConsentedProvidersSettings expect error", 554 in: testInput{ 555 userExt: &UserExt{}, 556 extJson: json.RawMessage(`{"ConsentedProvidersSettings":{"consented_providers":123}}`), 557 }, 558 expectError: true, 559 }, 560 // consented_providers_settings 561 { 562 desc: "Has consented_providers_settings and it is valid JSON", 563 in: testInput{ 564 userExt: &UserExt{}, 565 extJson: json.RawMessage(`{"consented_providers_settings":{"consented_providers":[2,25]}}`), 566 }, 567 expectError: false, 568 }, 569 { 570 desc: "Has malformed consented_providers_settings expect error", 571 in: testInput{ 572 userExt: &UserExt{}, 573 extJson: json.RawMessage(`{"consented_providers_settings":{"consented_providers":123}}`), 574 }, 575 expectError: true, 576 }, 577 } 578 for _, tc := range testCases { 579 err := tc.in.userExt.unmarshal(tc.in.extJson) 580 581 if tc.expectError { 582 assert.Error(t, err, tc.desc) 583 } else { 584 assert.NoError(t, err, tc.desc) 585 } 586 } 587 } 588 589 func TestCloneUserExt(t *testing.T) { 590 testCases := []struct { 591 name string 592 userExt *UserExt 593 userExtCopy *UserExt // manual copy of above ext object to verify against 594 mutator func(t *testing.T, userExt *UserExt) // function to modify the Ext object 595 }{ 596 { 597 name: "Nil", // Verify the nil case 598 userExt: nil, 599 userExtCopy: nil, 600 mutator: func(t *testing.T, user *UserExt) {}, 601 }, 602 { 603 name: "NoMutate", 604 userExt: &UserExt{ 605 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 606 consent: ptrutil.ToPtr("Myconsent"), 607 consentDirty: true, 608 prebid: &ExtUserPrebid{ 609 BuyerUIDs: map[string]string{"A": "X", "B": "Y"}, 610 }, 611 prebidDirty: true, 612 eids: &[]openrtb2.EID{}, 613 }, 614 userExtCopy: &UserExt{ 615 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 616 consent: ptrutil.ToPtr("Myconsent"), 617 consentDirty: true, 618 prebid: &ExtUserPrebid{ 619 BuyerUIDs: map[string]string{"A": "X", "B": "Y"}, 620 }, 621 prebidDirty: true, 622 eids: &[]openrtb2.EID{}, 623 }, 624 mutator: func(t *testing.T, user *UserExt) {}, 625 }, 626 { 627 name: "General", 628 userExt: &UserExt{ 629 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 630 consent: ptrutil.ToPtr("Myconsent"), 631 consentDirty: true, 632 prebid: &ExtUserPrebid{ 633 BuyerUIDs: map[string]string{"A": "X", "B": "Y"}, 634 }, 635 prebidDirty: true, 636 eids: &[]openrtb2.EID{}, 637 }, 638 userExtCopy: &UserExt{ 639 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 640 consent: ptrutil.ToPtr("Myconsent"), 641 consentDirty: true, 642 prebid: &ExtUserPrebid{ 643 BuyerUIDs: map[string]string{"A": "X", "B": "Y"}, 644 }, 645 prebidDirty: true, 646 eids: &[]openrtb2.EID{}, 647 }, 648 mutator: func(t *testing.T, user *UserExt) { 649 user.ext["A"] = json.RawMessage(`G`) 650 user.ext["C"] = json.RawMessage(`L`) 651 user.extDirty = true 652 user.consent = nil 653 user.consentDirty = false 654 user.prebid.BuyerUIDs["A"] = "C" 655 user.prebid.BuyerUIDs["C"] = "A" 656 user.prebid = nil 657 }, 658 }, 659 { 660 name: "EIDs", 661 userExt: &UserExt{ 662 eids: &[]openrtb2.EID{ 663 { 664 Source: "Sauce", 665 UIDs: []openrtb2.UID{ 666 {ID: "A", AType: 5, Ext: json.RawMessage(`{}`)}, 667 {ID: "B", AType: 1, Ext: json.RawMessage(`{"extra": "stuff"}`)}, 668 }, 669 }, 670 { 671 Source: "Moon", 672 UIDs: []openrtb2.UID{ 673 {ID: "G", AType: 3, Ext: json.RawMessage(`{}`)}, 674 {ID: "D", AType: 1}, 675 }, 676 }, 677 }, 678 }, 679 userExtCopy: &UserExt{ 680 eids: &[]openrtb2.EID{ 681 { 682 Source: "Sauce", 683 UIDs: []openrtb2.UID{ 684 {ID: "A", AType: 5, Ext: json.RawMessage(`{}`)}, 685 {ID: "B", AType: 1, Ext: json.RawMessage(`{"extra": "stuff"}`)}, 686 }, 687 }, 688 { 689 Source: "Moon", 690 UIDs: []openrtb2.UID{ 691 {ID: "G", AType: 3, Ext: json.RawMessage(`{}`)}, 692 {ID: "D", AType: 1}, 693 }, 694 }, 695 }, 696 }, 697 mutator: func(t *testing.T, userExt *UserExt) { 698 eids := *userExt.eids 699 eids[0].UIDs[1].ID = "G2" 700 eids[1].UIDs[0].AType = 0 701 eids[0].UIDs = append(eids[0].UIDs, openrtb2.UID{ID: "Z", AType: 2}) 702 eids = append(eids, openrtb2.EID{Source: "Blank"}) 703 userExt.eids = nil 704 }, 705 }, 706 { 707 name: "ConsentedProviders", 708 userExt: &UserExt{ 709 consentedProvidersSettingsIn: &ConsentedProvidersSettingsIn{ 710 ConsentedProvidersString: "A,B,C", 711 }, 712 consentedProvidersSettingsOut: &ConsentedProvidersSettingsOut{ 713 ConsentedProvidersList: []int{1, 2, 3, 4}, 714 }, 715 }, 716 userExtCopy: &UserExt{ 717 consentedProvidersSettingsIn: &ConsentedProvidersSettingsIn{ 718 ConsentedProvidersString: "A,B,C", 719 }, 720 consentedProvidersSettingsOut: &ConsentedProvidersSettingsOut{ 721 ConsentedProvidersList: []int{1, 2, 3, 4}, 722 }, 723 }, 724 mutator: func(t *testing.T, userExt *UserExt) { 725 userExt.consentedProvidersSettingsIn.ConsentedProvidersString = "B,C,D" 726 userExt.consentedProvidersSettingsIn = &ConsentedProvidersSettingsIn{ 727 ConsentedProvidersString: "G,H,I", 728 } 729 userExt.consentedProvidersSettingsOut.ConsentedProvidersList[1] = 5 730 userExt.consentedProvidersSettingsOut.ConsentedProvidersList = append(userExt.consentedProvidersSettingsOut.ConsentedProvidersList, 7) 731 userExt.consentedProvidersSettingsOut = nil 732 }, 733 }, 734 } 735 736 for _, test := range testCases { 737 t.Run(test.name, func(t *testing.T) { 738 clone := test.userExt.Clone() 739 test.mutator(t, test.userExt) 740 assert.Equal(t, test.userExtCopy, clone) 741 }) 742 } 743 } 744 745 func TestRebuildDeviceExt(t *testing.T) { 746 prebidContent1 := ExtDevicePrebid{Interstitial: &ExtDeviceInt{MinWidthPerc: 1}} 747 prebidContent2 := ExtDevicePrebid{Interstitial: &ExtDeviceInt{MinWidthPerc: 2}} 748 749 testCases := []struct { 750 description string 751 request openrtb2.BidRequest 752 requestDeviceExtWrapper DeviceExt 753 expectedRequest openrtb2.BidRequest 754 }{ 755 { 756 description: "Nil - Not Dirty", 757 request: openrtb2.BidRequest{}, 758 requestDeviceExtWrapper: DeviceExt{}, 759 expectedRequest: openrtb2.BidRequest{}, 760 }, 761 { 762 description: "Nil - Dirty", 763 request: openrtb2.BidRequest{}, 764 requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true}, 765 expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, 766 }, 767 { 768 description: "Nil - Dirty - No Change", 769 request: openrtb2.BidRequest{}, 770 requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true}, 771 expectedRequest: openrtb2.BidRequest{}, 772 }, 773 { 774 description: "Empty - Not Dirty", 775 request: openrtb2.BidRequest{Device: &openrtb2.Device{}}, 776 requestDeviceExtWrapper: DeviceExt{}, 777 expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{}}, 778 }, 779 { 780 description: "Empty - Dirty", 781 request: openrtb2.BidRequest{Device: &openrtb2.Device{}}, 782 requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true}, 783 expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, 784 }, 785 { 786 description: "Empty - Dirty - No Change", 787 request: openrtb2.BidRequest{Device: &openrtb2.Device{}}, 788 requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true}, 789 expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{}}, 790 }, 791 { 792 description: "Populated - Not Dirty", 793 request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, 794 requestDeviceExtWrapper: DeviceExt{}, 795 expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, 796 }, 797 { 798 description: "Populated - Dirty", 799 request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, 800 requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent2, prebidDirty: true}, 801 expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":2,"minheightperc":0}}}`)}}, 802 }, 803 { 804 description: "Populated - Dirty - No Change", 805 request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, 806 requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true}, 807 expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, 808 }, 809 { 810 description: "Populated - Dirty - Cleared", 811 request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, 812 requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true}, 813 expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{}}, 814 }, 815 } 816 817 for _, test := range testCases { 818 // create required filed in the test loop to keep test declaration easier to read 819 test.requestDeviceExtWrapper.ext = make(map[string]json.RawMessage) 820 821 w := RequestWrapper{BidRequest: &test.request, deviceExt: &test.requestDeviceExtWrapper} 822 w.RebuildRequest() 823 assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description) 824 } 825 } 826 827 func TestRebuildRequestExt(t *testing.T) { 828 prebidContent1 := ExtRequestPrebid{Integration: "1"} 829 prebidContent2 := ExtRequestPrebid{Integration: "2"} 830 831 testCases := []struct { 832 description string 833 request openrtb2.BidRequest 834 requestRequestExtWrapper RequestExt 835 expectedRequest openrtb2.BidRequest 836 }{ 837 { 838 description: "Empty - Not Dirty", 839 request: openrtb2.BidRequest{}, 840 requestRequestExtWrapper: RequestExt{}, 841 expectedRequest: openrtb2.BidRequest{}, 842 }, 843 { 844 description: "Empty - Dirty", 845 request: openrtb2.BidRequest{}, 846 requestRequestExtWrapper: RequestExt{prebid: &prebidContent1, prebidDirty: true}, 847 expectedRequest: openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)}, 848 }, 849 { 850 description: "Empty - Dirty - No Change", 851 request: openrtb2.BidRequest{}, 852 requestRequestExtWrapper: RequestExt{prebid: nil, prebidDirty: true}, 853 expectedRequest: openrtb2.BidRequest{}, 854 }, 855 { 856 description: "Populated - Not Dirty", 857 request: openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)}, 858 requestRequestExtWrapper: RequestExt{}, 859 expectedRequest: openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)}, 860 }, 861 { 862 description: "Populated - Dirty", 863 request: openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)}, 864 requestRequestExtWrapper: RequestExt{prebid: &prebidContent2, prebidDirty: true}, 865 expectedRequest: openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"2"}}`)}, 866 }, 867 { 868 description: "Populated - Dirty - No Change", 869 request: openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)}, 870 requestRequestExtWrapper: RequestExt{prebid: &prebidContent1, prebidDirty: true}, 871 expectedRequest: openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)}, 872 }, 873 { 874 description: "Populated - Dirty - Cleared", 875 request: openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)}, 876 requestRequestExtWrapper: RequestExt{prebid: nil, prebidDirty: true}, 877 expectedRequest: openrtb2.BidRequest{}, 878 }, 879 } 880 881 for _, test := range testCases { 882 // create required filed in the test loop to keep test declaration easier to read 883 test.requestRequestExtWrapper.ext = make(map[string]json.RawMessage) 884 885 w := RequestWrapper{BidRequest: &test.request, requestExt: &test.requestRequestExtWrapper} 886 w.RebuildRequest() 887 assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description) 888 } 889 } 890 891 func TestCloneRequestExt(t *testing.T) { 892 testCases := []struct { 893 name string 894 reqExt *RequestExt 895 reqExtCopy *RequestExt // manual copy of above ext object to verify against 896 mutator func(t *testing.T, reqExt *RequestExt) // function to modify the Ext object 897 }{ 898 { 899 name: "Nil", // Verify the nil case 900 reqExt: nil, 901 reqExtCopy: nil, 902 mutator: func(t *testing.T, reqExt *RequestExt) {}, 903 }, 904 { 905 name: "NoMutate", // Verify the nil case 906 reqExt: &RequestExt{ 907 ext: map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)}, 908 extDirty: true, 909 prebid: &ExtRequestPrebid{ 910 BidderParams: json.RawMessage(`{}`), 911 }, 912 }, 913 reqExtCopy: &RequestExt{ 914 ext: map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)}, 915 extDirty: true, 916 prebid: &ExtRequestPrebid{ 917 BidderParams: json.RawMessage(`{}`), 918 }, 919 }, 920 mutator: func(t *testing.T, reqExt *RequestExt) {}, 921 }, 922 { 923 name: "General", // Verify the nil case 924 reqExt: &RequestExt{ 925 ext: map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)}, 926 extDirty: true, 927 prebid: &ExtRequestPrebid{ 928 BidderParams: json.RawMessage(`{}`), 929 }, 930 }, 931 reqExtCopy: &RequestExt{ 932 ext: map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)}, 933 extDirty: true, 934 prebid: &ExtRequestPrebid{ 935 BidderParams: json.RawMessage(`{}`), 936 }, 937 }, 938 mutator: func(t *testing.T, reqExt *RequestExt) { 939 reqExt.ext["A"] = json.RawMessage(`"string"`) 940 reqExt.ext["C"] = json.RawMessage(`{}`) 941 reqExt.extDirty = false 942 reqExt.prebid.Channel = &ExtRequestPrebidChannel{Name: "Bob"} 943 reqExt.prebid.BidderParams = nil 944 reqExt.prebid = nil 945 }, 946 }, 947 { 948 name: "SChain", // Verify the nil case 949 reqExt: &RequestExt{ 950 schain: &openrtb2.SupplyChain{ 951 Complete: 1, 952 Ver: "1.1", 953 Nodes: []openrtb2.SupplyChainNode{ 954 {ASI: "Is a", RID: "off", HP: ptrutil.ToPtr[int8](1)}, 955 {ASI: "other", RID: "drift", HP: ptrutil.ToPtr[int8](0)}, 956 }, 957 }, 958 }, 959 reqExtCopy: &RequestExt{ 960 schain: &openrtb2.SupplyChain{ 961 Complete: 1, 962 Ver: "1.1", 963 Nodes: []openrtb2.SupplyChainNode{ 964 {ASI: "Is a", RID: "off", HP: ptrutil.ToPtr[int8](1)}, 965 {ASI: "other", RID: "drift", HP: ptrutil.ToPtr[int8](0)}, 966 }, 967 }, 968 }, 969 mutator: func(t *testing.T, reqExt *RequestExt) { 970 reqExt.schain.Complete = 0 971 reqExt.schain.Ver = "1.2" 972 reqExt.schain.Nodes[0].ASI = "some" 973 reqExt.schain.Nodes[1].HP = nil 974 reqExt.schain.Nodes = append(reqExt.schain.Nodes, openrtb2.SupplyChainNode{ASI: "added"}) 975 reqExt.schain = nil 976 }, 977 }, 978 } 979 980 for _, test := range testCases { 981 t.Run(test.name, func(t *testing.T) { 982 clone := test.reqExt.Clone() 983 test.mutator(t, test.reqExt) 984 assert.Equal(t, test.reqExtCopy, clone) 985 }) 986 } 987 988 } 989 990 func TestCloneDeviceExt(t *testing.T) { 991 testCases := []struct { 992 name string 993 devExt *DeviceExt 994 devExtCopy *DeviceExt // manual copy of above ext object to verify against 995 mutator func(t *testing.T, devExt *DeviceExt) // function to modify the Ext object 996 }{ 997 { 998 name: "Nil", // Verify the nil case 999 devExt: nil, 1000 devExtCopy: nil, 1001 mutator: func(t *testing.T, devExt *DeviceExt) {}, 1002 }, 1003 { 1004 name: "NoMutate", 1005 devExt: &DeviceExt{ 1006 ext: map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)}, 1007 extDirty: true, 1008 prebid: &ExtDevicePrebid{ 1009 Interstitial: &ExtDeviceInt{MinWidthPerc: 65.0, MinHeightPerc: 75.0}, 1010 }, 1011 }, 1012 devExtCopy: &DeviceExt{ 1013 ext: map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)}, 1014 extDirty: true, 1015 prebid: &ExtDevicePrebid{ 1016 Interstitial: &ExtDeviceInt{MinWidthPerc: 65.0, MinHeightPerc: 75.0}, 1017 }, 1018 }, 1019 mutator: func(t *testing.T, devExt *DeviceExt) {}, 1020 }, 1021 { 1022 name: "General", 1023 devExt: &DeviceExt{ 1024 ext: map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)}, 1025 extDirty: true, 1026 prebid: &ExtDevicePrebid{ 1027 Interstitial: &ExtDeviceInt{MinWidthPerc: 65.0, MinHeightPerc: 75.0}, 1028 }, 1029 }, 1030 devExtCopy: &DeviceExt{ 1031 ext: map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)}, 1032 extDirty: true, 1033 prebid: &ExtDevicePrebid{ 1034 Interstitial: &ExtDeviceInt{MinWidthPerc: 65, MinHeightPerc: 75}, 1035 }, 1036 }, 1037 mutator: func(t *testing.T, devExt *DeviceExt) { 1038 devExt.ext["A"] = json.RawMessage(`"string"`) 1039 devExt.ext["C"] = json.RawMessage(`{}`) 1040 devExt.extDirty = false 1041 devExt.prebid.Interstitial.MinHeightPerc = 55 1042 devExt.prebid.Interstitial = &ExtDeviceInt{MinWidthPerc: 80} 1043 devExt.prebid = nil 1044 }, 1045 }, 1046 } 1047 1048 for _, test := range testCases { 1049 t.Run(test.name, func(t *testing.T) { 1050 clone := test.devExt.Clone() 1051 test.mutator(t, test.devExt) 1052 assert.Equal(t, test.devExtCopy, clone) 1053 }) 1054 } 1055 } 1056 1057 func TestRebuildAppExt(t *testing.T) { 1058 prebidContent1 := ExtAppPrebid{Source: "1"} 1059 prebidContent2 := ExtAppPrebid{Source: "2"} 1060 1061 testCases := []struct { 1062 description string 1063 request openrtb2.BidRequest 1064 requestAppExtWrapper AppExt 1065 expectedRequest openrtb2.BidRequest 1066 }{ 1067 { 1068 description: "Nil - Not Dirty", 1069 request: openrtb2.BidRequest{}, 1070 requestAppExtWrapper: AppExt{}, 1071 expectedRequest: openrtb2.BidRequest{}, 1072 }, 1073 { 1074 description: "Nil - Dirty", 1075 request: openrtb2.BidRequest{}, 1076 requestAppExtWrapper: AppExt{prebid: &prebidContent1, prebidDirty: true}, 1077 expectedRequest: openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}}, 1078 }, 1079 { 1080 description: "Nil - Dirty - No Change", 1081 request: openrtb2.BidRequest{}, 1082 requestAppExtWrapper: AppExt{prebid: nil, prebidDirty: true}, 1083 expectedRequest: openrtb2.BidRequest{}, 1084 }, 1085 { 1086 description: "Empty - Not Dirty", 1087 request: openrtb2.BidRequest{App: &openrtb2.App{}}, 1088 requestAppExtWrapper: AppExt{}, 1089 expectedRequest: openrtb2.BidRequest{App: &openrtb2.App{}}, 1090 }, 1091 { 1092 description: "Empty - Dirty", 1093 request: openrtb2.BidRequest{App: &openrtb2.App{}}, 1094 requestAppExtWrapper: AppExt{prebid: &prebidContent1, prebidDirty: true}, 1095 expectedRequest: openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}}, 1096 }, 1097 { 1098 description: "Empty - Dirty - No Change", 1099 request: openrtb2.BidRequest{App: &openrtb2.App{}}, 1100 requestAppExtWrapper: AppExt{prebid: nil, prebidDirty: true}, 1101 expectedRequest: openrtb2.BidRequest{App: &openrtb2.App{}}, 1102 }, 1103 { 1104 description: "Populated - Not Dirty", 1105 request: openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}}, 1106 requestAppExtWrapper: AppExt{}, 1107 expectedRequest: openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}}, 1108 }, 1109 { 1110 description: "Populated - Dirty", 1111 request: openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}}, 1112 requestAppExtWrapper: AppExt{prebid: &prebidContent2, prebidDirty: true}, 1113 expectedRequest: openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"2"}}`)}}, 1114 }, 1115 { 1116 description: "Populated - Dirty - No Change", 1117 request: openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}}, 1118 requestAppExtWrapper: AppExt{prebid: &prebidContent1, prebidDirty: true}, 1119 expectedRequest: openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}}, 1120 }, 1121 { 1122 description: "Populated - Dirty - Cleared", 1123 request: openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}}, 1124 requestAppExtWrapper: AppExt{prebid: nil, prebidDirty: true}, 1125 expectedRequest: openrtb2.BidRequest{App: &openrtb2.App{}}, 1126 }, 1127 } 1128 1129 for _, test := range testCases { 1130 // create required filed in the test loop to keep test declaration easier to read 1131 test.requestAppExtWrapper.ext = make(map[string]json.RawMessage) 1132 1133 w := RequestWrapper{BidRequest: &test.request, appExt: &test.requestAppExtWrapper} 1134 w.RebuildRequest() 1135 assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description) 1136 } 1137 } 1138 1139 func TestCloneAppExt(t *testing.T) { 1140 testCases := []struct { 1141 name string 1142 appExt *AppExt 1143 appExtCopy *AppExt // manual copy of above ext object to verify against 1144 mutator func(t *testing.T, appExt *AppExt) // function to modify the Ext object 1145 }{ 1146 { 1147 name: "Nil", // Verify the nil case 1148 appExt: nil, 1149 appExtCopy: nil, 1150 mutator: func(t *testing.T, appExt *AppExt) {}, 1151 }, 1152 { 1153 name: "NoMutate", 1154 appExt: &AppExt{ 1155 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1156 extDirty: true, 1157 prebid: &ExtAppPrebid{ 1158 Source: "Sauce", 1159 Version: "2.2", 1160 }, 1161 }, 1162 appExtCopy: &AppExt{ 1163 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1164 extDirty: true, 1165 prebid: &ExtAppPrebid{ 1166 Source: "Sauce", 1167 Version: "2.2", 1168 }, 1169 }, 1170 mutator: func(t *testing.T, appExt *AppExt) {}, 1171 }, 1172 { 1173 name: "General", 1174 appExt: &AppExt{ 1175 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1176 extDirty: true, 1177 prebid: &ExtAppPrebid{ 1178 Source: "Sauce", 1179 Version: "2.2", 1180 }, 1181 }, 1182 appExtCopy: &AppExt{ 1183 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1184 extDirty: true, 1185 prebid: &ExtAppPrebid{ 1186 Source: "Sauce", 1187 Version: "2.2", 1188 }, 1189 }, 1190 mutator: func(t *testing.T, appExt *AppExt) { 1191 appExt.ext["A"] = json.RawMessage(`"string"`) 1192 appExt.ext["C"] = json.RawMessage(`{}`) 1193 appExt.extDirty = false 1194 appExt.prebid.Source = "foobar" 1195 appExt.prebid = nil 1196 }, 1197 }, 1198 } 1199 1200 for _, test := range testCases { 1201 t.Run(test.name, func(t *testing.T) { 1202 clone := test.appExt.Clone() 1203 test.mutator(t, test.appExt) 1204 assert.Equal(t, test.appExtCopy, clone) 1205 }) 1206 } 1207 } 1208 1209 func TestCloneRegExt(t *testing.T) { 1210 testCases := []struct { 1211 name string 1212 regExt *RegExt 1213 regExtCopy *RegExt // manual copy of above ext object to verify against 1214 mutator func(t *testing.T, regExt *RegExt) // function to modify the Ext object 1215 }{ 1216 { 1217 name: "Nil", // Verify the nil case 1218 regExt: nil, 1219 regExtCopy: nil, 1220 mutator: func(t *testing.T, appExt *RegExt) {}, 1221 }, 1222 { 1223 name: "NoMutate", 1224 regExt: &RegExt{ 1225 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1226 extDirty: true, 1227 gdpr: ptrutil.ToPtr[int8](1), 1228 usPrivacy: "priv", 1229 usPrivacyDirty: true, 1230 }, 1231 regExtCopy: &RegExt{ 1232 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1233 extDirty: true, 1234 gdpr: ptrutil.ToPtr[int8](1), 1235 usPrivacy: "priv", 1236 usPrivacyDirty: true, 1237 }, 1238 mutator: func(t *testing.T, appExt *RegExt) {}, 1239 }, 1240 { 1241 name: "General", 1242 regExt: &RegExt{ 1243 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1244 extDirty: true, 1245 gdpr: ptrutil.ToPtr[int8](1), 1246 usPrivacy: "priv", 1247 usPrivacyDirty: true, 1248 }, 1249 regExtCopy: &RegExt{ 1250 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1251 extDirty: true, 1252 gdpr: ptrutil.ToPtr[int8](1), 1253 usPrivacy: "priv", 1254 usPrivacyDirty: true, 1255 }, 1256 mutator: func(t *testing.T, appExt *RegExt) { 1257 appExt.ext["A"] = json.RawMessage(`"string"`) 1258 appExt.ext["C"] = json.RawMessage(`{}`) 1259 appExt.extDirty = false 1260 appExt.gdpr = nil 1261 appExt.gdprDirty = true 1262 appExt.usPrivacy = "Other" 1263 }, 1264 }, 1265 } 1266 1267 for _, test := range testCases { 1268 t.Run(test.name, func(t *testing.T) { 1269 clone := test.regExt.Clone() 1270 test.mutator(t, test.regExt) 1271 assert.Equal(t, test.regExtCopy, clone) 1272 }) 1273 } 1274 } 1275 1276 func TestRebuildSiteExt(t *testing.T) { 1277 int1 := int8(1) 1278 int2 := int8(2) 1279 1280 testCases := []struct { 1281 description string 1282 request openrtb2.BidRequest 1283 requestSiteExtWrapper SiteExt 1284 expectedRequest openrtb2.BidRequest 1285 }{ 1286 { 1287 description: "Nil - Not Dirty", 1288 request: openrtb2.BidRequest{}, 1289 requestSiteExtWrapper: SiteExt{}, 1290 expectedRequest: openrtb2.BidRequest{}, 1291 }, 1292 { 1293 description: "Nil - Dirty", 1294 request: openrtb2.BidRequest{}, 1295 requestSiteExtWrapper: SiteExt{amp: &int1, ampDirty: true}, 1296 expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}}, 1297 }, 1298 { 1299 description: "Nil - Dirty - No Change", 1300 request: openrtb2.BidRequest{}, 1301 requestSiteExtWrapper: SiteExt{amp: nil, ampDirty: true}, 1302 expectedRequest: openrtb2.BidRequest{}, 1303 }, 1304 { 1305 description: "Empty - Not Dirty", 1306 request: openrtb2.BidRequest{Site: &openrtb2.Site{}}, 1307 requestSiteExtWrapper: SiteExt{}, 1308 expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{}}, 1309 }, 1310 { 1311 description: "Empty - Dirty", 1312 request: openrtb2.BidRequest{Site: &openrtb2.Site{}}, 1313 requestSiteExtWrapper: SiteExt{amp: &int1, ampDirty: true}, 1314 expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}}, 1315 }, 1316 { 1317 description: "Empty - Dirty - No Change", 1318 request: openrtb2.BidRequest{Site: &openrtb2.Site{}}, 1319 requestSiteExtWrapper: SiteExt{amp: nil, ampDirty: true}, 1320 expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{}}, 1321 }, 1322 { 1323 description: "Populated - Not Dirty", 1324 request: openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}}, 1325 requestSiteExtWrapper: SiteExt{}, 1326 expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}}, 1327 }, 1328 { 1329 description: "Populated - Dirty", 1330 request: openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}}, 1331 requestSiteExtWrapper: SiteExt{amp: &int2, ampDirty: true}, 1332 expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":2}`)}}, 1333 }, 1334 { 1335 description: "Populated - Dirty - No Change", 1336 request: openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}}, 1337 requestSiteExtWrapper: SiteExt{amp: &int1, ampDirty: true}, 1338 expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}}, 1339 }, 1340 { 1341 description: "Populated - Dirty - Cleared", 1342 request: openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}}, 1343 requestSiteExtWrapper: SiteExt{amp: nil, ampDirty: true}, 1344 expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{}}, 1345 }, 1346 } 1347 1348 for _, test := range testCases { 1349 // create required filed in the test loop to keep test declaration easier to read 1350 test.requestSiteExtWrapper.ext = make(map[string]json.RawMessage) 1351 1352 w := RequestWrapper{BidRequest: &test.request, siteExt: &test.requestSiteExtWrapper} 1353 w.RebuildRequest() 1354 assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description) 1355 } 1356 } 1357 1358 func TestCloneSiteExt(t *testing.T) { 1359 testCases := []struct { 1360 name string 1361 siteExt *SiteExt 1362 siteExtCopy *SiteExt // manual copy of above ext object to verify against 1363 mutator func(t *testing.T, siteExt *SiteExt) // function to modify the Ext object 1364 }{ 1365 { 1366 name: "Nil", // Verify the nil case 1367 siteExt: nil, 1368 siteExtCopy: nil, 1369 mutator: func(t *testing.T, siteExt *SiteExt) {}, 1370 }, 1371 { 1372 name: "NoMutate", 1373 siteExt: &SiteExt{ 1374 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1375 extDirty: true, 1376 amp: ptrutil.ToPtr[int8](1), 1377 }, 1378 siteExtCopy: &SiteExt{ 1379 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1380 extDirty: true, 1381 amp: ptrutil.ToPtr[int8](1), 1382 }, 1383 mutator: func(t *testing.T, siteExt *SiteExt) {}, 1384 }, 1385 { 1386 name: "General", 1387 siteExt: &SiteExt{ 1388 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1389 extDirty: true, 1390 amp: ptrutil.ToPtr[int8](1), 1391 }, 1392 siteExtCopy: &SiteExt{ 1393 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1394 extDirty: true, 1395 amp: ptrutil.ToPtr[int8](1), 1396 }, 1397 mutator: func(t *testing.T, siteExt *SiteExt) { 1398 siteExt.ext["A"] = json.RawMessage(`"string"`) 1399 siteExt.ext["C"] = json.RawMessage(`{}`) 1400 siteExt.extDirty = false 1401 siteExt.amp = nil 1402 siteExt.ampDirty = true 1403 }, 1404 }, 1405 } 1406 1407 for _, test := range testCases { 1408 t.Run(test.name, func(t *testing.T) { 1409 clone := test.siteExt.Clone() 1410 test.mutator(t, test.siteExt) 1411 assert.Equal(t, test.siteExtCopy, clone) 1412 }) 1413 } 1414 } 1415 1416 func TestRebuildSourceExt(t *testing.T) { 1417 schainContent1 := openrtb2.SupplyChain{Ver: "1"} 1418 schainContent2 := openrtb2.SupplyChain{Ver: "2"} 1419 1420 testCases := []struct { 1421 description string 1422 request openrtb2.BidRequest 1423 requestSourceExtWrapper SourceExt 1424 expectedRequest openrtb2.BidRequest 1425 }{ 1426 { 1427 description: "Nil - Not Dirty", 1428 request: openrtb2.BidRequest{}, 1429 requestSourceExtWrapper: SourceExt{}, 1430 expectedRequest: openrtb2.BidRequest{}, 1431 }, 1432 { 1433 description: "Nil - Dirty", 1434 request: openrtb2.BidRequest{}, 1435 requestSourceExtWrapper: SourceExt{schain: &schainContent1, schainDirty: true}, 1436 expectedRequest: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}}, 1437 }, 1438 { 1439 description: "Nil - Dirty - No Change", 1440 request: openrtb2.BidRequest{}, 1441 requestSourceExtWrapper: SourceExt{schain: nil, schainDirty: true}, 1442 expectedRequest: openrtb2.BidRequest{}, 1443 }, 1444 { 1445 description: "Empty - Not Dirty", 1446 request: openrtb2.BidRequest{Source: &openrtb2.Source{}}, 1447 requestSourceExtWrapper: SourceExt{}, 1448 expectedRequest: openrtb2.BidRequest{Source: &openrtb2.Source{}}, 1449 }, 1450 { 1451 description: "Empty - Dirty", 1452 request: openrtb2.BidRequest{Source: &openrtb2.Source{}}, 1453 requestSourceExtWrapper: SourceExt{schain: &schainContent1, schainDirty: true}, 1454 expectedRequest: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}}, 1455 }, 1456 { 1457 description: "Empty - Dirty - No Change", 1458 request: openrtb2.BidRequest{Source: &openrtb2.Source{}}, 1459 requestSourceExtWrapper: SourceExt{schain: nil, schainDirty: true}, 1460 expectedRequest: openrtb2.BidRequest{Source: &openrtb2.Source{}}, 1461 }, 1462 { 1463 description: "Populated - Not Dirty", 1464 request: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}}, 1465 requestSourceExtWrapper: SourceExt{}, 1466 expectedRequest: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}}, 1467 }, 1468 { 1469 description: "Populated - Dirty", 1470 request: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}}, 1471 requestSourceExtWrapper: SourceExt{schain: &schainContent2, schainDirty: true}, 1472 expectedRequest: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"2"}}`)}}, 1473 }, 1474 { 1475 description: "Populated - Dirty - No Change", 1476 request: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}}, 1477 requestSourceExtWrapper: SourceExt{schain: &schainContent1, schainDirty: true}, 1478 expectedRequest: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}}, 1479 }, 1480 { 1481 description: "Populated - Dirty - Cleared", 1482 request: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}}, 1483 requestSourceExtWrapper: SourceExt{schain: nil, schainDirty: true}, 1484 expectedRequest: openrtb2.BidRequest{Source: &openrtb2.Source{}}, 1485 }, 1486 } 1487 1488 for _, test := range testCases { 1489 // create required filed in the test loop to keep test declaration easier to read 1490 test.requestSourceExtWrapper.ext = make(map[string]json.RawMessage) 1491 1492 w := RequestWrapper{BidRequest: &test.request, sourceExt: &test.requestSourceExtWrapper} 1493 w.RebuildRequest() 1494 assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description) 1495 } 1496 } 1497 1498 func TestCloneSourceExt(t *testing.T) { 1499 testCases := []struct { 1500 name string 1501 sourceExt *SourceExt 1502 sourceExtCopy *SourceExt // manual copy of above ext object to verify against 1503 mutator func(t *testing.T, sourceExt *SourceExt) // function to modify the Ext object 1504 }{ 1505 { 1506 name: "Nil", // Verify the nil case 1507 sourceExt: nil, 1508 sourceExtCopy: nil, 1509 mutator: func(t *testing.T, sourceExt *SourceExt) {}, 1510 }, 1511 { 1512 name: "NoMutate", 1513 sourceExt: &SourceExt{ 1514 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1515 extDirty: true, 1516 schain: &openrtb2.SupplyChain{ 1517 Complete: 1, 1518 Ver: "1.1", 1519 Nodes: []openrtb2.SupplyChainNode{ 1520 {ASI: "Is a", RID: "off", HP: ptrutil.ToPtr[int8](1)}, 1521 {ASI: "other", RID: "drift", HP: ptrutil.ToPtr[int8](0)}, 1522 }, 1523 }, 1524 }, 1525 sourceExtCopy: &SourceExt{ 1526 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1527 extDirty: true, 1528 schain: &openrtb2.SupplyChain{ 1529 Complete: 1, 1530 Ver: "1.1", 1531 Nodes: []openrtb2.SupplyChainNode{ 1532 {ASI: "Is a", RID: "off", HP: ptrutil.ToPtr[int8](1)}, 1533 {ASI: "other", RID: "drift", HP: ptrutil.ToPtr[int8](0)}, 1534 }, 1535 }, 1536 }, 1537 mutator: func(t *testing.T, sourceExt *SourceExt) { 1538 sourceExt.ext["A"] = json.RawMessage(`"string"`) 1539 sourceExt.ext["C"] = json.RawMessage(`{}`) 1540 sourceExt.extDirty = false 1541 sourceExt.schain.Complete = 0 1542 sourceExt.schain.Ver = "1.2" 1543 sourceExt.schain.Nodes[0].ASI = "some" 1544 sourceExt.schain.Nodes[1].HP = nil 1545 sourceExt.schain.Nodes = append(sourceExt.schain.Nodes, openrtb2.SupplyChainNode{ASI: "added"}) 1546 sourceExt.schain = nil 1547 1548 }, 1549 }, 1550 } 1551 1552 for _, test := range testCases { 1553 t.Run(test.name, func(t *testing.T) { 1554 clone := test.sourceExt.Clone() 1555 test.mutator(t, test.sourceExt) 1556 assert.Equal(t, test.sourceExtCopy, clone) 1557 }) 1558 } 1559 } 1560 1561 func TestImpWrapperRebuildImp(t *testing.T) { 1562 var ( 1563 isRewardedInventoryOne int8 = 1 1564 isRewardedInventoryTwo int8 = 2 1565 ) 1566 1567 testCases := []struct { 1568 description string 1569 imp openrtb2.Imp 1570 impExtWrapper ImpExt 1571 expectedImp openrtb2.Imp 1572 }{ 1573 { 1574 description: "Empty - Not Dirty", 1575 imp: openrtb2.Imp{}, 1576 impExtWrapper: ImpExt{}, 1577 expectedImp: openrtb2.Imp{}, 1578 }, 1579 { 1580 description: "Empty - Dirty", 1581 imp: openrtb2.Imp{}, 1582 impExtWrapper: ImpExt{prebid: &ExtImpPrebid{IsRewardedInventory: &isRewardedInventoryOne}, prebidDirty: true}, 1583 expectedImp: openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1}}`)}, 1584 }, 1585 { 1586 description: "Empty - Dirty - No Change", 1587 imp: openrtb2.Imp{}, 1588 impExtWrapper: ImpExt{prebid: nil, prebidDirty: true}, 1589 expectedImp: openrtb2.Imp{}, 1590 }, 1591 { 1592 description: "Populated - Not Dirty", 1593 imp: openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1}}`)}, 1594 impExtWrapper: ImpExt{}, 1595 expectedImp: openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1}}`)}, 1596 }, 1597 { 1598 description: "Populated - Dirty", 1599 imp: openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1}}`)}, 1600 impExtWrapper: ImpExt{prebid: &ExtImpPrebid{IsRewardedInventory: &isRewardedInventoryTwo}, prebidDirty: true}, 1601 expectedImp: openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":2}}`)}, 1602 }, 1603 { 1604 description: "Populated - Dirty - No Change", 1605 imp: openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1}}`)}, 1606 impExtWrapper: ImpExt{prebid: &ExtImpPrebid{IsRewardedInventory: &isRewardedInventoryOne}, prebidDirty: true}, 1607 expectedImp: openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1}}`)}, 1608 }, 1609 { 1610 description: "Populated - Dirty - Cleared", 1611 imp: openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1}}`)}, 1612 impExtWrapper: ImpExt{prebid: nil, prebidDirty: true}, 1613 expectedImp: openrtb2.Imp{}, 1614 }, 1615 { 1616 description: "Populated - Dirty - Empty Prebid Object", 1617 imp: openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1}}`)}, 1618 impExtWrapper: ImpExt{prebid: &ExtImpPrebid{IsRewardedInventory: nil}, prebidDirty: true}, 1619 expectedImp: openrtb2.Imp{}, 1620 }, 1621 { 1622 description: "Populated Tid - Dirty", 1623 imp: openrtb2.Imp{Ext: json.RawMessage(`{"tid": "some-tid"}`)}, 1624 impExtWrapper: ImpExt{tidDirty: true, tid: "12345"}, 1625 expectedImp: openrtb2.Imp{Ext: json.RawMessage(`{"tid":"12345"}`)}, 1626 }, 1627 { 1628 description: "Populated Tid - Dirty - No Change", 1629 imp: openrtb2.Imp{Ext: json.RawMessage(`{"tid": "some-tid"}`)}, 1630 impExtWrapper: ImpExt{tid: "some-tid", tidDirty: true}, 1631 expectedImp: openrtb2.Imp{Ext: json.RawMessage(`{"tid":"some-tid"}`)}, 1632 }, 1633 { 1634 description: "Populated Tid - Dirty - Cleared", 1635 imp: openrtb2.Imp{Ext: json.RawMessage(`{"tid":"some-tid"}`)}, 1636 impExtWrapper: ImpExt{tid: "", tidDirty: true}, 1637 expectedImp: openrtb2.Imp{}, 1638 }, 1639 } 1640 1641 for _, test := range testCases { 1642 // create required filed in the test loop to keep test declaration easier to read 1643 test.impExtWrapper.ext = make(map[string]json.RawMessage) 1644 1645 w := &ImpWrapper{Imp: &test.imp, impExt: &test.impExtWrapper} 1646 w.RebuildImp() 1647 assert.Equal(t, test.expectedImp, *w.Imp, test.description) 1648 } 1649 } 1650 1651 func TestImpWrapperGetImpExt(t *testing.T) { 1652 var isRewardedInventoryOne int8 = 1 1653 1654 testCases := []struct { 1655 description string 1656 givenWrapper ImpWrapper 1657 expectedImpExt ImpExt 1658 expectedError string 1659 }{ 1660 { 1661 description: "Empty", 1662 givenWrapper: ImpWrapper{}, 1663 expectedImpExt: ImpExt{ext: make(map[string]json.RawMessage)}, 1664 }, 1665 { 1666 description: "Populated - Ext", 1667 givenWrapper: ImpWrapper{Imp: &openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1},"other":42,"tid":"test-tid","gpid":"test-gpid","data":{"adserver":{"name":"ads","adslot":"adslot123"},"pbadslot":"pbadslot123"}}`)}}, 1668 expectedImpExt: ImpExt{ 1669 ext: map[string]json.RawMessage{ 1670 "prebid": json.RawMessage(`{"is_rewarded_inventory":1}`), 1671 "other": json.RawMessage(`42`), 1672 "tid": json.RawMessage(`"test-tid"`), 1673 "gpid": json.RawMessage(`"test-gpid"`), 1674 "data": json.RawMessage(`{"adserver":{"name":"ads","adslot":"adslot123"},"pbadslot":"pbadslot123"}`), 1675 }, 1676 tid: "test-tid", 1677 gpId: "test-gpid", 1678 data: &ExtImpData{ 1679 AdServer: &ExtImpDataAdServer{ 1680 Name: "ads", 1681 AdSlot: "adslot123", 1682 }, 1683 PbAdslot: "pbadslot123", 1684 }, 1685 prebid: &ExtImpPrebid{IsRewardedInventory: &isRewardedInventoryOne}, 1686 }, 1687 }, 1688 { 1689 description: "Already Retrieved - Dirty - Not Unmarshalled Again", 1690 givenWrapper: ImpWrapper{ 1691 Imp: &openrtb2.Imp{Ext: json.RawMessage(`{"will":"be ignored"}`)}, 1692 impExt: &ImpExt{ext: map[string]json.RawMessage{"foo": json.RawMessage("bar")}}}, 1693 expectedImpExt: ImpExt{ext: map[string]json.RawMessage{"foo": json.RawMessage("bar")}}, 1694 }, 1695 { 1696 description: "Error - Ext", 1697 givenWrapper: ImpWrapper{Imp: &openrtb2.Imp{Ext: json.RawMessage(`malformed`)}}, 1698 expectedError: "invalid character 'm' looking for beginning of value", 1699 }, 1700 { 1701 description: "Error - Ext - Prebid", 1702 givenWrapper: ImpWrapper{Imp: &openrtb2.Imp{Ext: json.RawMessage(`{"prebid":malformed}`)}}, 1703 expectedError: "invalid character 'm' looking for beginning of value", 1704 }, 1705 } 1706 1707 for _, test := range testCases { 1708 impExt, err := test.givenWrapper.GetImpExt() 1709 if test.expectedError != "" { 1710 assert.EqualError(t, err, test.expectedError, test.description) 1711 } else { 1712 assert.NoError(t, err, test.description) 1713 assert.Equal(t, test.expectedImpExt, *impExt, test.description) 1714 } 1715 } 1716 } 1717 1718 func TestImpExtTid(t *testing.T) { 1719 impExt := &ImpExt{} 1720 1721 impExt.unmarshal(nil) 1722 assert.Equal(t, false, impExt.Dirty(), "New impext should not be dirty.") 1723 assert.Empty(t, impExt.GetTid(), "Empty ImpExt should have empty tid") 1724 1725 newTid := "tid" 1726 impExt.SetTid(newTid) 1727 assert.Equal(t, "tid", impExt.GetTid(), "ImpExt tid is incorrect") 1728 assert.Equal(t, true, impExt.Dirty(), "New impext should be dirty.") 1729 } 1730 1731 func TestCloneImpWrapper(t *testing.T) { 1732 testCases := []struct { 1733 name string 1734 impWrapper *ImpWrapper 1735 impWrapperCopy *ImpWrapper // manual copy of above ext object to verify against 1736 mutator func(t *testing.T, impWrapper *ImpWrapper) // function to modify the Ext object 1737 }{ 1738 { 1739 name: "Nil", // Verify the nil case 1740 impWrapper: nil, 1741 impWrapperCopy: nil, 1742 mutator: func(t *testing.T, impWrapper *ImpWrapper) {}, 1743 }, 1744 { 1745 name: "NoMutate", 1746 impWrapper: &ImpWrapper{ 1747 impExt: &ImpExt{ 1748 tid: "occupied", 1749 }, 1750 }, 1751 impWrapperCopy: &ImpWrapper{ 1752 impExt: &ImpExt{ 1753 tid: "occupied", 1754 }, 1755 }, 1756 mutator: func(t *testing.T, impWrapper *ImpWrapper) {}, 1757 }, 1758 { 1759 name: "General", 1760 impWrapper: &ImpWrapper{ 1761 impExt: &ImpExt{ 1762 tid: "occupied", 1763 }, 1764 }, 1765 impWrapperCopy: &ImpWrapper{ 1766 impExt: &ImpExt{ 1767 tid: "occupied", 1768 }, 1769 }, 1770 mutator: func(t *testing.T, impWrapper *ImpWrapper) { 1771 impWrapper.impExt.extDirty = true 1772 impWrapper.impExt.tid = "Something" 1773 impWrapper.impExt = nil 1774 }, 1775 }, 1776 } 1777 1778 for _, test := range testCases { 1779 t.Run(test.name, func(t *testing.T) { 1780 clone := test.impWrapper.Clone() 1781 test.mutator(t, test.impWrapper) 1782 assert.Equal(t, test.impWrapperCopy, clone) 1783 }) 1784 } 1785 } 1786 1787 func TestCloneImpExt(t *testing.T) { 1788 testCases := []struct { 1789 name string 1790 impExt *ImpExt 1791 impExtCopy *ImpExt // manual copy of above ext object to verify against 1792 mutator func(t *testing.T, impExt *ImpExt) // function to modify the Ext object 1793 }{ 1794 { 1795 name: "Nil", // Verify the nil case 1796 impExt: nil, 1797 impExtCopy: nil, 1798 mutator: func(t *testing.T, impExt *ImpExt) {}, 1799 }, 1800 { 1801 name: "NoMutate", 1802 impExt: &ImpExt{ 1803 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1804 extDirty: true, 1805 tid: "TID", 1806 }, 1807 impExtCopy: &ImpExt{ 1808 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1809 extDirty: true, 1810 tid: "TID", 1811 }, 1812 mutator: func(t *testing.T, impExt *ImpExt) {}, 1813 }, 1814 { 1815 name: "General", 1816 impExt: &ImpExt{ 1817 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1818 extDirty: true, 1819 tid: "TID", 1820 }, 1821 impExtCopy: &ImpExt{ 1822 ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, 1823 extDirty: true, 1824 tid: "TID", 1825 }, 1826 mutator: func(t *testing.T, impExt *ImpExt) { 1827 impExt.ext["A"] = json.RawMessage(`"string"`) 1828 impExt.ext["C"] = json.RawMessage(`{}`) 1829 impExt.extDirty = false 1830 impExt.tid = "other" 1831 impExt.tidDirty = true 1832 }, 1833 }, 1834 { 1835 name: "Prebid", 1836 impExt: &ImpExt{ 1837 prebid: &ExtImpPrebid{ 1838 StoredRequest: &ExtStoredRequest{ID: "abc123"}, 1839 StoredAuctionResponse: &ExtStoredAuctionResponse{ID: "123abc"}, 1840 StoredBidResponse: []ExtStoredBidResponse{ 1841 {ID: "foo", Bidder: "bar", ReplaceImpId: ptrutil.ToPtr(true)}, 1842 {ID: "def", Bidder: "xyz", ReplaceImpId: ptrutil.ToPtr(false)}, 1843 }, 1844 IsRewardedInventory: ptrutil.ToPtr[int8](1), 1845 Bidder: map[string]json.RawMessage{ 1846 "abc": json.RawMessage(`{}`), 1847 "def": json.RawMessage(`{"alpha":"beta"}`), 1848 }, 1849 Options: &Options{EchoVideoAttrs: true}, 1850 Passthrough: json.RawMessage(`{"foo":"bar"}`), 1851 Floors: &ExtImpPrebidFloors{ 1852 FloorRule: "Rule 16", 1853 FloorRuleValue: 16.17, 1854 FloorValue: 6.7, 1855 }, 1856 }, 1857 }, 1858 impExtCopy: &ImpExt{ 1859 prebid: &ExtImpPrebid{ 1860 StoredRequest: &ExtStoredRequest{ID: "abc123"}, 1861 StoredAuctionResponse: &ExtStoredAuctionResponse{ID: "123abc"}, 1862 StoredBidResponse: []ExtStoredBidResponse{ 1863 {ID: "foo", Bidder: "bar", ReplaceImpId: ptrutil.ToPtr(true)}, 1864 {ID: "def", Bidder: "xyz", ReplaceImpId: ptrutil.ToPtr(false)}, 1865 }, 1866 IsRewardedInventory: ptrutil.ToPtr[int8](1), 1867 Bidder: map[string]json.RawMessage{ 1868 "abc": json.RawMessage(`{}`), 1869 "def": json.RawMessage(`{"alpha":"beta"}`), 1870 }, 1871 Options: &Options{EchoVideoAttrs: true}, 1872 Passthrough: json.RawMessage(`{"foo":"bar"}`), 1873 Floors: &ExtImpPrebidFloors{ 1874 FloorRule: "Rule 16", 1875 FloorRuleValue: 16.17, 1876 FloorValue: 6.7, 1877 }, 1878 }, 1879 }, 1880 mutator: func(t *testing.T, impExt *ImpExt) { 1881 impExt.prebid.StoredRequest.ID = "seventy" 1882 impExt.prebid.StoredRequest = nil 1883 impExt.prebid.StoredAuctionResponse.ID = "xyz" 1884 impExt.prebid.StoredAuctionResponse = nil 1885 impExt.prebid.StoredBidResponse[0].ID = "alpha" 1886 impExt.prebid.StoredBidResponse[1].ReplaceImpId = nil 1887 impExt.prebid.StoredBidResponse[0] = ExtStoredBidResponse{ID: "o", Bidder: "k", ReplaceImpId: ptrutil.ToPtr(false)} 1888 impExt.prebid.StoredBidResponse = append(impExt.prebid.StoredBidResponse, ExtStoredBidResponse{ID: "jay", Bidder: "walk"}) 1889 impExt.prebid.IsRewardedInventory = nil 1890 impExt.prebid.Bidder["def"] = json.RawMessage(``) 1891 delete(impExt.prebid.Bidder, "abc") 1892 impExt.prebid.Bidder["xyz"] = json.RawMessage(`{"jar":5}`) 1893 impExt.prebid.Options.EchoVideoAttrs = false 1894 impExt.prebid.Options = nil 1895 impExt.prebid.Passthrough = json.RawMessage(`{}`) 1896 impExt.prebid.Floors.FloorRule = "Friday" 1897 impExt.prebid.Floors.FloorMinCur = "EUR" 1898 impExt.prebid.Floors = nil 1899 }, 1900 }, 1901 } 1902 1903 for _, test := range testCases { 1904 t.Run(test.name, func(t *testing.T) { 1905 clone := test.impExt.Clone() 1906 test.mutator(t, test.impExt) 1907 assert.Equal(t, test.impExtCopy, clone) 1908 }) 1909 } 1910 }