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