github.com/prebid/prebid-server/v2@v2.18.0/privacy/ccpa/policy_test.go (about)

     1  package ccpa
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"testing"
     7  
     8  	gpplib "github.com/prebid/go-gpp"
     9  	gppConstants "github.com/prebid/go-gpp/constants"
    10  	"github.com/prebid/openrtb/v20/openrtb2"
    11  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  func TestReadFromRequestWrapper(t *testing.T) {
    16  	testCases := []struct {
    17  		description    string
    18  		request        *openrtb2.BidRequest
    19  		giveGPP        gpplib.GppContainer
    20  		expectedPolicy Policy
    21  		expectedError  bool
    22  	}{
    23  		{
    24  			description: "Success",
    25  			request: &openrtb2.BidRequest{
    26  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)},
    27  				Ext:  json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
    28  			},
    29  			expectedPolicy: Policy{
    30  				Consent:       "ABC",
    31  				NoSaleBidders: []string{"a", "b"},
    32  			},
    33  		},
    34  		{
    35  			description: "Nil Request",
    36  			request:     nil,
    37  			expectedPolicy: Policy{
    38  				Consent:       "",
    39  				NoSaleBidders: nil,
    40  			},
    41  		},
    42  		{
    43  			description: "Nil Regs",
    44  			request: &openrtb2.BidRequest{
    45  				Regs: nil,
    46  				Ext:  json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
    47  			},
    48  			expectedPolicy: Policy{
    49  				Consent:       "",
    50  				NoSaleBidders: []string{"a", "b"},
    51  			},
    52  		},
    53  		{
    54  			description: "Nil Regs.Ext",
    55  			request: &openrtb2.BidRequest{
    56  				Regs: &openrtb2.Regs{},
    57  				Ext:  json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
    58  			},
    59  			expectedPolicy: Policy{
    60  				Consent:       "",
    61  				NoSaleBidders: []string{"a", "b"},
    62  			},
    63  		},
    64  		{
    65  			description: "Empty Regs.Ext",
    66  			request: &openrtb2.BidRequest{
    67  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`{}`)},
    68  				Ext:  json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
    69  			},
    70  			expectedPolicy: Policy{
    71  				Consent:       "",
    72  				NoSaleBidders: []string{"a", "b"},
    73  			},
    74  		},
    75  		{
    76  			description: "Missing Regs.Ext USPrivacy Value",
    77  			request: &openrtb2.BidRequest{
    78  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"anythingElse":"42"}`)},
    79  				Ext:  json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
    80  			},
    81  			expectedPolicy: Policy{
    82  				Consent:       "",
    83  				NoSaleBidders: []string{"a", "b"},
    84  			},
    85  		},
    86  		{
    87  			description: "Malformed Regs.Ext",
    88  			request: &openrtb2.BidRequest{
    89  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`malformed`)},
    90  				Ext:  json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
    91  			},
    92  			expectedError: true,
    93  		},
    94  		{
    95  			description: "Invalid Regs.Ext Type",
    96  			request: &openrtb2.BidRequest{
    97  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":123`)},
    98  				Ext:  json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
    99  			},
   100  			expectedError: true,
   101  		},
   102  		{
   103  			description: "Nil Ext",
   104  			request: &openrtb2.BidRequest{
   105  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)},
   106  				Ext:  nil,
   107  			},
   108  			expectedPolicy: Policy{
   109  				Consent:       "ABC",
   110  				NoSaleBidders: nil,
   111  			},
   112  		},
   113  		{
   114  			description: "Empty Ext",
   115  			request: &openrtb2.BidRequest{
   116  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)},
   117  				Ext:  json.RawMessage(`{}`),
   118  			},
   119  			expectedPolicy: Policy{
   120  				Consent:       "ABC",
   121  				NoSaleBidders: nil,
   122  			},
   123  		},
   124  		{
   125  			description: "Missing Ext.Prebid No Sale Value",
   126  			request: &openrtb2.BidRequest{
   127  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)},
   128  				Ext:  json.RawMessage(`{"anythingElse":"42"}`),
   129  			},
   130  			expectedPolicy: Policy{
   131  				Consent:       "ABC",
   132  				NoSaleBidders: nil,
   133  			},
   134  		},
   135  		{
   136  			description: "Malformed Ext",
   137  			request: &openrtb2.BidRequest{
   138  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)},
   139  				Ext:  json.RawMessage(`malformed`),
   140  			},
   141  			expectedError: true,
   142  		},
   143  		{
   144  			description: "Invalid Ext.Prebid.NoSale Type",
   145  			request: &openrtb2.BidRequest{
   146  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)},
   147  				Ext:  json.RawMessage(`{"prebid":{"nosale":"wrongtype"}}`),
   148  			},
   149  			expectedError: true,
   150  		},
   151  		{
   152  			description: "Injection Attack",
   153  			request: &openrtb2.BidRequest{
   154  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`)},
   155  			},
   156  			expectedPolicy: Policy{
   157  				Consent: "1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\"",
   158  			},
   159  		},
   160  		{
   161  			description: "GPP Success",
   162  			request: &openrtb2.BidRequest{
   163  				Regs: &openrtb2.Regs{GPP: "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~present",
   164  					GPPSID: []int8{6}},
   165  				Ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
   166  			},
   167  			giveGPP: gpplib.GppContainer{Version: 1, SectionTypes: []gppConstants.SectionID{6}, Sections: []gpplib.Section{&upsv1Section}},
   168  			expectedPolicy: Policy{
   169  				Consent:       "gppContainerConsent",
   170  				NoSaleBidders: []string{"a", "b"},
   171  			},
   172  		},
   173  		{
   174  			description: "GPP Success, has Regs.ext",
   175  			request: &openrtb2.BidRequest{
   176  				Regs: &openrtb2.Regs{GPP: "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~present",
   177  					GPPSID: []int8{6},
   178  					Ext:    json.RawMessage(`{"us_privacy":"ABC"}`)},
   179  				Ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
   180  			},
   181  			giveGPP: gpplib.GppContainer{Version: 1, SectionTypes: []gppConstants.SectionID{6}, Sections: []gpplib.Section{&upsv1Section}},
   182  			expectedPolicy: Policy{
   183  				Consent:       "gppContainerConsent",
   184  				NoSaleBidders: []string{"a", "b"},
   185  			},
   186  		},
   187  		{
   188  			description: "GPP Success, has regs.us_privacy",
   189  			request: &openrtb2.BidRequest{
   190  				Regs: &openrtb2.Regs{GPP: "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~present",
   191  					GPPSID:    []int8{6},
   192  					USPrivacy: "conflicting"},
   193  				Ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
   194  			},
   195  			giveGPP: gpplib.GppContainer{Version: 1, SectionTypes: []gppConstants.SectionID{6}, Sections: []gpplib.Section{&upsv1Section}},
   196  			expectedPolicy: Policy{
   197  				Consent:       "gppContainerConsent",
   198  				NoSaleBidders: []string{"a", "b"},
   199  			},
   200  			expectedError: true,
   201  		},
   202  		{
   203  			description: "Has regs.us_privacy",
   204  			request: &openrtb2.BidRequest{
   205  				Regs: &openrtb2.Regs{USPrivacy: "present"},
   206  				Ext:  json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
   207  			},
   208  			expectedPolicy: Policy{
   209  				Consent:       "present",
   210  				NoSaleBidders: []string{"a", "b"},
   211  			},
   212  			expectedError: false,
   213  		},
   214  		{
   215  			description: "GPP Success, no USPV1",
   216  			request: &openrtb2.BidRequest{
   217  				Regs: &openrtb2.Regs{GPP: "DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   218  					GPPSID: []int8{6}},
   219  			},
   220  			giveGPP: gpplib.GppContainer{Version: 1, SectionTypes: []gppConstants.SectionID{2}, Sections: []gpplib.Section{&tcf1Section}},
   221  			expectedPolicy: Policy{
   222  				Consent: "",
   223  			},
   224  		},
   225  	}
   226  
   227  	for _, test := range testCases {
   228  		t.Run(test.description, func(t *testing.T) {
   229  			reqWrapper := &openrtb_ext.RequestWrapper{BidRequest: test.request}
   230  			result, err := ReadFromRequestWrapper(reqWrapper, test.giveGPP)
   231  			assertError(t, test.expectedError, err, test.description)
   232  			assert.Equal(t, test.expectedPolicy, result)
   233  		})
   234  	}
   235  }
   236  
   237  func TestReadFromRequest(t *testing.T) {
   238  	testCases := []struct {
   239  		description    string
   240  		request        *openrtb2.BidRequest
   241  		expectedPolicy Policy
   242  		expectedError  bool
   243  	}{
   244  		{
   245  			description: "Success",
   246  			request: &openrtb2.BidRequest{
   247  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)},
   248  				Ext:  json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
   249  			},
   250  			expectedPolicy: Policy{
   251  				Consent:       "ABC",
   252  				NoSaleBidders: []string{"a", "b"},
   253  			},
   254  		},
   255  		{
   256  			description: "Nil Request",
   257  			request:     nil,
   258  			expectedPolicy: Policy{
   259  				Consent:       "",
   260  				NoSaleBidders: nil,
   261  			},
   262  		},
   263  		{
   264  			description: "Nil Regs",
   265  			request: &openrtb2.BidRequest{
   266  				Regs: nil,
   267  				Ext:  json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
   268  			},
   269  			expectedPolicy: Policy{
   270  				Consent:       "",
   271  				NoSaleBidders: []string{"a", "b"},
   272  			},
   273  		},
   274  		{
   275  			description: "GPP Success",
   276  			request: &openrtb2.BidRequest{
   277  				Regs: &openrtb2.Regs{GPP: "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
   278  					GPPSID: []int8{6}},
   279  				Ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
   280  			},
   281  			expectedPolicy: Policy{
   282  				Consent:       "1YNN",
   283  				NoSaleBidders: []string{"a", "b"},
   284  			},
   285  		},
   286  		{
   287  			description: "GPP Success, has Regs.ext",
   288  			request: &openrtb2.BidRequest{
   289  				Regs: &openrtb2.Regs{GPP: "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
   290  					GPPSID: []int8{6},
   291  					Ext:    json.RawMessage(`{"us_privacy":"ABC"}`)},
   292  				Ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
   293  			},
   294  			expectedPolicy: Policy{
   295  				Consent:       "1YNN",
   296  				NoSaleBidders: []string{"a", "b"},
   297  			},
   298  		},
   299  		{
   300  			description: "GPP Success, no USPV1",
   301  			request: &openrtb2.BidRequest{
   302  				Regs: &openrtb2.Regs{GPP: "DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   303  					GPPSID: []int8{6}}},
   304  			expectedPolicy: Policy{
   305  				Consent: "",
   306  			},
   307  		},
   308  		{
   309  			description: "GPP Success, no signal",
   310  			request: &openrtb2.BidRequest{
   311  				Regs: &openrtb2.Regs{GPP: "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
   312  					GPPSID: []int8{}}},
   313  			expectedPolicy: Policy{
   314  				Consent: "",
   315  			},
   316  		},
   317  		{
   318  			description: "GPP Success, wrong signal",
   319  			request: &openrtb2.BidRequest{
   320  				Regs: &openrtb2.Regs{GPP: "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
   321  					GPPSID: []int8{2}}},
   322  			expectedPolicy: Policy{
   323  				Consent: "",
   324  			},
   325  		},
   326  	}
   327  
   328  	for _, test := range testCases {
   329  		t.Run(test.description, func(t *testing.T) {
   330  			result, err := ReadFromRequest(test.request)
   331  			assertError(t, test.expectedError, err, test.description)
   332  			assert.Equal(t, test.expectedPolicy, result)
   333  		})
   334  	}
   335  }
   336  
   337  func TestWrite(t *testing.T) {
   338  	testCases := []struct {
   339  		description   string
   340  		policy        Policy
   341  		request       *openrtb2.BidRequest
   342  		expected      *openrtb2.BidRequest
   343  		expectedError bool
   344  	}{
   345  		{
   346  			description: "Nil Request",
   347  			policy:      Policy{Consent: "anyConsent", NoSaleBidders: []string{"a", "b"}},
   348  			request:     nil,
   349  			expected:    nil,
   350  		},
   351  		{
   352  			description: "Success",
   353  			policy:      Policy{Consent: "anyConsent", NoSaleBidders: []string{"a", "b"}},
   354  			request:     &openrtb2.BidRequest{},
   355  			expected: &openrtb2.BidRequest{
   356  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`)},
   357  				Ext:  json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`),
   358  			},
   359  		},
   360  		{
   361  			description: "Error Regs.Ext - No Partial Update To Request",
   362  			policy:      Policy{Consent: "anyConsent", NoSaleBidders: []string{"a", "b"}},
   363  			request: &openrtb2.BidRequest{
   364  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`malformed}`)},
   365  			},
   366  			expectedError: true,
   367  			expected: &openrtb2.BidRequest{
   368  				Regs: &openrtb2.Regs{Ext: json.RawMessage(`malformed}`)},
   369  			},
   370  		},
   371  		{
   372  			description: "Error Ext - No Partial Update To Request",
   373  			policy:      Policy{Consent: "anyConsent", NoSaleBidders: []string{"a", "b"}},
   374  			request: &openrtb2.BidRequest{
   375  				Ext: json.RawMessage(`malformed}`),
   376  			},
   377  			expectedError: true,
   378  			expected: &openrtb2.BidRequest{
   379  				Ext: json.RawMessage(`malformed}`),
   380  			},
   381  		},
   382  	}
   383  
   384  	for _, test := range testCases {
   385  		reqWrapper := &openrtb_ext.RequestWrapper{BidRequest: test.request}
   386  		var err error
   387  		_, err = reqWrapper.GetRegExt()
   388  		if err == nil {
   389  			_, err = reqWrapper.GetRequestExt()
   390  			if err == nil {
   391  				err = test.policy.Write(reqWrapper)
   392  				if err == nil && reqWrapper.BidRequest != nil {
   393  					err = reqWrapper.RebuildRequest()
   394  				}
   395  			}
   396  		}
   397  		assertError(t, test.expectedError, err, test.description)
   398  		assert.Equal(t, test.expected, reqWrapper.BidRequest, test.description)
   399  	}
   400  }
   401  
   402  func TestBuildRegs(t *testing.T) {
   403  	testCases := []struct {
   404  		description   string
   405  		consent       string
   406  		regs          *openrtb2.Regs
   407  		expected      *openrtb2.Regs
   408  		expectedError bool
   409  	}{
   410  		{
   411  			description: "Clear",
   412  			consent:     "",
   413  			regs: &openrtb2.Regs{
   414  				Ext: json.RawMessage(`{"us_privacy":"ABC"}`),
   415  			},
   416  			expected: &openrtb2.Regs{},
   417  		},
   418  		{
   419  			description: "Clear - Error",
   420  			consent:     "",
   421  			regs: &openrtb2.Regs{
   422  				Ext: json.RawMessage(`malformed`),
   423  			},
   424  			expected: &openrtb2.Regs{
   425  				Ext: json.RawMessage(`malformed`),
   426  			},
   427  			expectedError: true,
   428  		},
   429  		{
   430  			description: "Write",
   431  			consent:     "anyConsent",
   432  			regs:        nil,
   433  			expected: &openrtb2.Regs{
   434  				Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`),
   435  			},
   436  		},
   437  		{
   438  			description: "Write - Error",
   439  			consent:     "anyConsent",
   440  			regs: &openrtb2.Regs{
   441  				Ext: json.RawMessage(`malformed`),
   442  			},
   443  			expected: &openrtb2.Regs{
   444  				Ext: json.RawMessage(`malformed`),
   445  			},
   446  			expectedError: true,
   447  		},
   448  	}
   449  
   450  	for _, test := range testCases {
   451  		request := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Regs: test.regs}}
   452  		regsExt, err := request.GetRegExt()
   453  		if err == nil {
   454  			regsExt.SetUSPrivacy(test.consent)
   455  			request.RebuildRequest()
   456  		}
   457  		assertError(t, test.expectedError, err, test.description)
   458  		assert.Equal(t, test.expected, request.Regs, test.description)
   459  	}
   460  }
   461  
   462  func TestBuildRegsClear(t *testing.T) {
   463  	testCases := []struct {
   464  		description   string
   465  		regs          *openrtb2.Regs
   466  		expected      *openrtb2.Regs
   467  		expectedError bool
   468  	}{
   469  		{
   470  			description: "Nil Regs",
   471  			regs:        nil,
   472  			expected:    nil,
   473  		},
   474  		{
   475  			description: "Nil Regs.Ext",
   476  			regs:        &openrtb2.Regs{Ext: nil},
   477  			expected:    &openrtb2.Regs{Ext: nil},
   478  		},
   479  		{
   480  			description: "Empty Regs.Ext",
   481  			regs:        &openrtb2.Regs{Ext: json.RawMessage(`{}`)},
   482  			expected:    &openrtb2.Regs{},
   483  		},
   484  		{
   485  			description: "Removes Regs.Ext Entirely",
   486  			regs:        &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)},
   487  			expected:    &openrtb2.Regs{},
   488  		},
   489  		{
   490  			description: "Leaves Other Regs.Ext Values",
   491  			regs:        &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC", "other":"any"}`)},
   492  			expected:    &openrtb2.Regs{Ext: json.RawMessage(`{"other":"any"}`)},
   493  		},
   494  		{
   495  			description:   "Invalid Regs.Ext Type - Returns Error, doesn't clear",
   496  			regs:          &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":123}`)},
   497  			expected:      &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":123}`)},
   498  			expectedError: true,
   499  		},
   500  		{
   501  			description:   "Malformed Regs.Ext",
   502  			regs:          &openrtb2.Regs{Ext: json.RawMessage(`malformed`)},
   503  			expected:      &openrtb2.Regs{Ext: json.RawMessage(`malformed`)},
   504  			expectedError: true,
   505  		},
   506  	}
   507  
   508  	for _, test := range testCases {
   509  		request := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Regs: test.regs}}
   510  		regsExt, err := request.GetRegExt()
   511  		if err == nil {
   512  			regsExt.SetUSPrivacy("")
   513  			request.RebuildRequest()
   514  		}
   515  		assertError(t, test.expectedError, err, test.description)
   516  		assert.Equal(t, test.expected, request.Regs, test.description)
   517  	}
   518  }
   519  
   520  func TestBuildRegsWrite(t *testing.T) {
   521  	testCases := []struct {
   522  		description   string
   523  		consent       string
   524  		regs          *openrtb2.Regs
   525  		expected      *openrtb2.Regs
   526  		expectedError bool
   527  	}{
   528  		{
   529  			description: "Nil Regs",
   530  			consent:     "anyConsent",
   531  			regs:        nil,
   532  			expected:    &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`)},
   533  		},
   534  		{
   535  			description: "Nil Regs.Ext",
   536  			consent:     "anyConsent",
   537  			regs:        &openrtb2.Regs{Ext: nil},
   538  			expected:    &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`)},
   539  		},
   540  		{
   541  			description: "Empty Regs.Ext",
   542  			consent:     "anyConsent",
   543  			regs:        &openrtb2.Regs{Ext: json.RawMessage(`{}`)},
   544  			expected:    &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`)},
   545  		},
   546  		{
   547  			description: "Overwrites Existing",
   548  			consent:     "anyConsent",
   549  			regs:        &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)},
   550  			expected:    &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`)},
   551  		},
   552  		{
   553  			description: "Leaves Other Ext Values",
   554  			consent:     "anyConsent",
   555  			regs:        &openrtb2.Regs{Ext: json.RawMessage(`{"other":"any"}`)},
   556  			expected:    &openrtb2.Regs{Ext: json.RawMessage(`{"other":"any","us_privacy":"anyConsent"}`)},
   557  		},
   558  		{
   559  			description:   "Invalid Regs.Ext Type - Doesn't Overwrite",
   560  			consent:       "anyConsent",
   561  			regs:          &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":123}`)},
   562  			expected:      &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":123}`)},
   563  			expectedError: true,
   564  		},
   565  		{
   566  			description:   "Malformed Regs.Ext",
   567  			consent:       "anyConsent",
   568  			regs:          &openrtb2.Regs{Ext: json.RawMessage(`malformed`)},
   569  			expected:      &openrtb2.Regs{Ext: json.RawMessage(`malformed`)},
   570  			expectedError: true,
   571  		},
   572  	}
   573  
   574  	for _, test := range testCases {
   575  		request := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Regs: test.regs}}
   576  		regsExt, err := request.GetRegExt()
   577  		if err == nil {
   578  			regsExt.SetUSPrivacy(test.consent)
   579  			request.RebuildRequest()
   580  		}
   581  		assertError(t, test.expectedError, err, test.description)
   582  		assert.Equal(t, test.expected, request.Regs, test.description)
   583  	}
   584  }
   585  
   586  func TestBuildExt(t *testing.T) {
   587  	testCases := []struct {
   588  		description   string
   589  		noSaleBidders []string
   590  		ext           json.RawMessage
   591  		expected      json.RawMessage
   592  		expectedError bool
   593  	}{
   594  		{
   595  			description:   "Clear - Nil",
   596  			noSaleBidders: nil,
   597  			ext:           json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
   598  			expected:      nil,
   599  		},
   600  		{
   601  			description:   "Clear - Empty",
   602  			noSaleBidders: []string{},
   603  			ext:           json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`),
   604  			expected:      nil,
   605  		},
   606  		{
   607  			description:   "Clear - Error",
   608  			noSaleBidders: []string{},
   609  			ext:           json.RawMessage(`malformed`),
   610  			expectedError: true,
   611  		},
   612  		{
   613  			description:   "Write",
   614  			noSaleBidders: []string{"a", "b"},
   615  			ext:           nil,
   616  			expected:      json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`),
   617  		},
   618  		{
   619  			description:   "Write - Error",
   620  			noSaleBidders: []string{"a", "b"},
   621  			ext:           json.RawMessage(`malformed`),
   622  			expectedError: true,
   623  		},
   624  	}
   625  
   626  	for _, test := range testCases {
   627  		request := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Ext: test.ext}}
   628  		reqExt, err := request.GetRequestExt()
   629  		var result json.RawMessage
   630  		if err == nil {
   631  			setPrebidNoSale(test.noSaleBidders, reqExt)
   632  			err = request.RebuildRequest()
   633  			result = request.Ext
   634  		}
   635  		assertError(t, test.expectedError, err, test.description)
   636  		assert.Equal(t, test.expected, result, test.description)
   637  	}
   638  }
   639  
   640  func TestBuildExtClear(t *testing.T) {
   641  	testCases := []struct {
   642  		description   string
   643  		ext           json.RawMessage
   644  		expected      json.RawMessage
   645  		expectedError bool
   646  	}{
   647  		{
   648  			description: "Nil Ext",
   649  			ext:         nil,
   650  			expected:    nil,
   651  		},
   652  		{
   653  			description: "Empty Ext",
   654  			ext:         json.RawMessage(``),
   655  			expected:    json.RawMessage(``),
   656  		},
   657  		{
   658  			description: "Empty Ext Object",
   659  			ext:         json.RawMessage(`{}`),
   660  			expected:    json.RawMessage(`{}`),
   661  		},
   662  		{
   663  			description: "Empty Ext.Prebid",
   664  			ext:         json.RawMessage(`{"prebid":{}}`),
   665  			expected:    nil,
   666  		},
   667  		{
   668  			description: "Removes Ext Entirely",
   669  			ext:         json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`),
   670  			expected:    nil,
   671  		},
   672  		{
   673  			description: "Leaves Other Ext Values",
   674  			ext:         json.RawMessage(`{"other":"any","prebid":{"nosale":["a","b"]}}`),
   675  			expected:    json.RawMessage(`{"other":"any"}`),
   676  		},
   677  		{
   678  			description: "Leaves Other Ext.Prebid Values",
   679  			ext:         json.RawMessage(`{"prebid":{"nosale":["a","b"],"aliases":{"a":"b"}}}`),
   680  			expected:    json.RawMessage(`{"prebid":{"aliases":{"a":"b"}}}`),
   681  		},
   682  		{
   683  			description: "Leaves All Other Values",
   684  			ext:         json.RawMessage(`{"other":"ABC","prebid":{"nosale":["a","b"],"supportdeals":true}}`),
   685  			expected:    json.RawMessage(`{"other":"ABC","prebid":{"supportdeals":true}}`),
   686  		},
   687  		{
   688  			description:   "Malformed Ext",
   689  			ext:           json.RawMessage(`malformed`),
   690  			expectedError: true,
   691  		},
   692  		{
   693  			description:   "Malformed Ext.Prebid",
   694  			ext:           json.RawMessage(`{"prebid":malformed}`),
   695  			expectedError: true,
   696  		},
   697  		{
   698  			description:   "Invalid Ext.Prebid Type",
   699  			ext:           json.RawMessage(`{"prebid":123}`),
   700  			expectedError: true,
   701  		},
   702  	}
   703  
   704  	for _, test := range testCases {
   705  		request := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Ext: test.ext}}
   706  		reqExt, err := request.GetRequestExt()
   707  		var result json.RawMessage
   708  		if err == nil {
   709  			setPrebidNoSaleClear(reqExt)
   710  			err = request.RebuildRequest()
   711  			result = request.Ext
   712  		}
   713  		assertError(t, test.expectedError, err, test.description)
   714  		assert.Equal(t, test.expected, result, test.description)
   715  	}
   716  }
   717  
   718  func TestBuildExtWrite(t *testing.T) {
   719  	testCases := []struct {
   720  		description   string
   721  		noSaleBidders []string
   722  		ext           json.RawMessage
   723  		expected      json.RawMessage
   724  		expectedError bool
   725  	}{
   726  		{
   727  			description:   "Nil Ext",
   728  			noSaleBidders: []string{"a", "b"},
   729  			ext:           nil,
   730  			expected:      json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`),
   731  		},
   732  		{
   733  			description:   "Empty Ext",
   734  			noSaleBidders: []string{"a", "b"},
   735  			ext:           json.RawMessage(``),
   736  			expected:      json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`),
   737  		},
   738  		{
   739  			description:   "Empty Ext Object",
   740  			noSaleBidders: []string{"a", "b"},
   741  			ext:           json.RawMessage(`{}`),
   742  			expected:      json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`),
   743  		},
   744  		{
   745  			description:   "Empty Ext.Prebid",
   746  			noSaleBidders: []string{"a", "b"},
   747  			ext:           json.RawMessage(`{"prebid":{}}`),
   748  			expected:      json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`),
   749  		},
   750  		{
   751  			description:   "Overwrites Existing",
   752  			noSaleBidders: []string{"a", "b"},
   753  			ext:           json.RawMessage(`{"prebid":{"nosale":["x","y"]}}`),
   754  			expected:      json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`),
   755  		},
   756  		{
   757  			description:   "Leaves Other Ext Values",
   758  			noSaleBidders: []string{"a", "b"},
   759  			ext:           json.RawMessage(`{"other":"any"}`),
   760  			expected:      json.RawMessage(`{"other":"any","prebid":{"nosale":["a","b"]}}`),
   761  		},
   762  		{
   763  			description:   "Leaves Other Ext.Prebid Values",
   764  			noSaleBidders: []string{"a", "b"},
   765  			ext:           json.RawMessage(`{"prebid":{"supportdeals":true}}`),
   766  			expected:      json.RawMessage(`{"prebid":{"supportdeals":true,"nosale":["a","b"]}}`),
   767  		},
   768  		{
   769  			description:   "Leaves All Other Values",
   770  			noSaleBidders: []string{"a", "b"},
   771  			ext:           json.RawMessage(`{"other":"ABC","prebid":{"aliases":{"a":"b"}}}`),
   772  			expected:      json.RawMessage(`{"other":"ABC","prebid":{"aliases":{"a":"b"},"nosale":["a","b"]}}`),
   773  		},
   774  		{
   775  			description:   "Invalid Ext.Prebid No Sale Type - Still Overrides",
   776  			noSaleBidders: []string{"a", "b"},
   777  			ext:           json.RawMessage(`{"prebid":{"nosale":123}}`),
   778  			expected:      json.RawMessage(`{"prebid":{"nosale":123}}`),
   779  			expectedError: true,
   780  		},
   781  		{
   782  			description:   "Invalid Ext.Prebid Type ",
   783  			noSaleBidders: []string{"a", "b"},
   784  			ext:           json.RawMessage(`{"prebid":"wrongtype"}`),
   785  			expected:      json.RawMessage(`{"prebid":"wrongtype"}`),
   786  			expectedError: true,
   787  		},
   788  		{
   789  			description:   "Malformed Ext",
   790  			noSaleBidders: []string{"a", "b"},
   791  			ext:           json.RawMessage(`{malformed`),
   792  			expected:      json.RawMessage(`{malformed`),
   793  			expectedError: true,
   794  		},
   795  		{
   796  			description:   "Malformed Ext.Prebid",
   797  			noSaleBidders: []string{"a", "b"},
   798  			ext:           json.RawMessage(`{"prebid":malformed}`),
   799  			expected:      json.RawMessage(`{"prebid":malformed}`),
   800  			expectedError: true,
   801  		},
   802  	}
   803  
   804  	for _, test := range testCases {
   805  		request := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Ext: test.ext}}
   806  		reqExt, err := request.GetRequestExt()
   807  		var result json.RawMessage
   808  		if err == nil {
   809  			setPrebidNoSaleWrite(test.noSaleBidders, reqExt)
   810  			err = request.RebuildRequest()
   811  			result = request.Ext
   812  		} else {
   813  			result = test.ext
   814  		}
   815  		assertError(t, test.expectedError, err, test.description)
   816  		assert.Equal(t, test.expected, result, test.description)
   817  	}
   818  }
   819  
   820  func TestSelectCCPAConsent(t *testing.T) {
   821  	type testInput struct {
   822  		requestUSPrivacy string
   823  		gpp              gpplib.GppContainer
   824  		gppSIDs          []int8
   825  	}
   826  	testCases := []struct {
   827  		desc         string
   828  		in           testInput
   829  		expectedCCPA string
   830  		expectedErr  error
   831  	}{
   832  		{
   833  			desc: "SectionUSPV1 in both GPP_SID and GPP container. Consent equal to request US_Privacy. Expect valid string and nil error",
   834  			in: testInput{
   835  				requestUSPrivacy: "gppContainerConsent",
   836  				gpp:              gpplib.GppContainer{Version: 1, SectionTypes: []gppConstants.SectionID{gppConstants.SectionUSPV1}, Sections: []gpplib.Section{upsv1Section}},
   837  				gppSIDs:          []int8{int8(6)},
   838  			},
   839  			expectedCCPA: "gppContainerConsent",
   840  			expectedErr:  nil,
   841  		},
   842  		{
   843  			desc: "No SectionUSPV1 in GPP_SID array expect request US_Privacy",
   844  			in: testInput{
   845  				requestUSPrivacy: "requestConsent",
   846  				gpp:              gpplib.GppContainer{Version: 1, SectionTypes: []gppConstants.SectionID{gppConstants.SectionUSPV1}, Sections: []gpplib.Section{upsv1Section}},
   847  				gppSIDs:          []int8{int8(2), int8(4)},
   848  			},
   849  			expectedCCPA: "requestConsent",
   850  			expectedErr:  nil,
   851  		},
   852  		{
   853  			desc: "No SectionUSPV1 in gpp.SectionTypes array expect request US_Privacy",
   854  			in: testInput{
   855  				requestUSPrivacy: "requestConsent",
   856  				gpp:              gpplib.GppContainer{Version: 1, SectionTypes: []gppConstants.SectionID{}, Sections: []gpplib.Section{upsv1Section}},
   857  				gppSIDs:          []int8{int8(6)},
   858  			},
   859  			expectedCCPA: "requestConsent",
   860  			expectedErr:  nil,
   861  		},
   862  		{
   863  			desc: "No SectionUSPV1 in GPP_SID array, blank request US_Privacy, expect blank consent",
   864  			in: testInput{
   865  				requestUSPrivacy: "",
   866  				gpp:              gpplib.GppContainer{Version: 1, SectionTypes: []gppConstants.SectionID{gppConstants.SectionUSPV1}, Sections: []gpplib.Section{upsv1Section}},
   867  				gppSIDs:          []int8{int8(2), int8(4)},
   868  			},
   869  			expectedCCPA: "",
   870  			expectedErr:  nil,
   871  		},
   872  		{
   873  			desc: "No SectionUSPV1 in gpp.SectionTypes array, blank request US_Privacy, expect blank consent",
   874  			in: testInput{
   875  				requestUSPrivacy: "",
   876  				gpp:              gpplib.GppContainer{Version: 1, SectionTypes: []gppConstants.SectionID{}, Sections: []gpplib.Section{upsv1Section}},
   877  				gppSIDs:          []int8{int8(6)},
   878  			},
   879  			expectedCCPA: "",
   880  			expectedErr:  nil,
   881  		},
   882  		{
   883  			desc: "SectionUSPV1 in both GPP_SID and GPP container. Consent equal to request US_Privacy. Expect valid string and nil error",
   884  			in: testInput{
   885  				requestUSPrivacy: "requestConsent",
   886  				gpp:              gpplib.GppContainer{Version: 1, SectionTypes: []gppConstants.SectionID{gppConstants.SectionUSPV1}, Sections: []gpplib.Section{upsv1Section}},
   887  				gppSIDs:          []int8{int8(6)},
   888  			},
   889  			expectedCCPA: "gppContainerConsent",
   890  			expectedErr:  errors.New("request.us_privacy consent does not match uspv1"),
   891  		},
   892  	}
   893  	for _, tc := range testCases {
   894  		out, outErr := SelectCCPAConsent(tc.in.requestUSPrivacy, tc.in.gpp, tc.in.gppSIDs)
   895  
   896  		assert.Equal(t, tc.expectedCCPA, out, tc.desc)
   897  		assert.Equal(t, tc.expectedErr, outErr, tc.desc)
   898  	}
   899  }
   900  
   901  func assertError(t *testing.T, expectError bool, err error, description string) {
   902  	t.Helper()
   903  	if expectError {
   904  		assert.Error(t, err, description)
   905  	} else {
   906  		assert.NoError(t, err, description)
   907  	}
   908  }
   909  
   910  var upsv1Section mockGPPSection = mockGPPSection{sectionID: 6, value: "gppContainerConsent"}
   911  var tcf1Section mockGPPSection = mockGPPSection{sectionID: 2, value: "BOS2bx5OS2bx5ABABBAAABoAAAAAFA"}
   912  
   913  type mockGPPSection struct {
   914  	sectionID gppConstants.SectionID
   915  	value     string
   916  }
   917  
   918  func (ms mockGPPSection) GetID() gppConstants.SectionID {
   919  	return ms.sectionID
   920  }
   921  
   922  func (ms mockGPPSection) GetValue() string {
   923  	return ms.value
   924  }
   925  
   926  func (ms mockGPPSection) Encode(bool) []byte {
   927  	return nil
   928  }