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

     1  package endpoints
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"net/url"
    10  	"regexp"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/prebid/prebid-server/analytics"
    16  	"github.com/prebid/prebid-server/config"
    17  	"github.com/prebid/prebid-server/errortypes"
    18  	"github.com/prebid/prebid-server/gdpr"
    19  	"github.com/prebid/prebid-server/macros"
    20  	"github.com/prebid/prebid-server/metrics"
    21  	"github.com/prebid/prebid-server/openrtb_ext"
    22  	"github.com/prebid/prebid-server/usersync"
    23  	"github.com/stretchr/testify/assert"
    24  
    25  	analyticsConf "github.com/prebid/prebid-server/analytics/config"
    26  	metricsConf "github.com/prebid/prebid-server/metrics/config"
    27  )
    28  
    29  func TestSetUIDEndpoint(t *testing.T) {
    30  	testCases := []struct {
    31  		uri                    string
    32  		syncersBidderNameToKey map[string]string
    33  		existingSyncs          map[string]string
    34  		gdprAllowsHostCookies  bool
    35  		gdprReturnsError       bool
    36  		gdprMalformed          bool
    37  		expectedSyncs          map[string]string
    38  		expectedBody           string
    39  		expectedStatusCode     int
    40  		expectedHeaders        map[string]string
    41  		description            string
    42  	}{
    43  		{
    44  			uri:                    "/setuid?bidder=pubmatic&uid=123",
    45  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
    46  			existingSyncs:          nil,
    47  			gdprAllowsHostCookies:  true,
    48  			expectedSyncs:          map[string]string{"pubmatic": "123"},
    49  			expectedStatusCode:     http.StatusOK,
    50  			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
    51  			description:            "Set uid for valid bidder",
    52  		},
    53  		{
    54  			uri:                    "/setuid?bidder=appnexus&uid=123",
    55  			syncersBidderNameToKey: map[string]string{"appnexus": "adnxs"},
    56  			existingSyncs:          nil,
    57  			gdprAllowsHostCookies:  true,
    58  			expectedSyncs:          map[string]string{"adnxs": "123"},
    59  			expectedStatusCode:     http.StatusOK,
    60  			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
    61  			description:            "Set uid for valid bidder with different key",
    62  		},
    63  		{
    64  			uri:                    "/setuid?bidder=unsupported-bidder&uid=123",
    65  			syncersBidderNameToKey: map[string]string{},
    66  			existingSyncs:          nil,
    67  			gdprAllowsHostCookies:  true,
    68  			expectedSyncs:          nil,
    69  			expectedStatusCode:     http.StatusBadRequest,
    70  			expectedBody:           "The bidder name provided is not supported by Prebid Server",
    71  			description:            "Don't set uid for an unsupported bidder",
    72  		},
    73  		{
    74  			uri:                    "/setuid?bidder=&uid=123",
    75  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
    76  			existingSyncs:          nil,
    77  			gdprAllowsHostCookies:  true,
    78  			expectedSyncs:          nil,
    79  			expectedStatusCode:     http.StatusBadRequest,
    80  			expectedBody:           `"bidder" query param is required`,
    81  			description:            "Don't set uid for an empty bidder",
    82  		},
    83  		{
    84  			uri:                    "/setuid?bidder=unsupported-bidder&uid=123",
    85  			syncersBidderNameToKey: map[string]string{},
    86  			existingSyncs:          map[string]string{"pubmatic": "1234"},
    87  			gdprAllowsHostCookies:  true,
    88  			expectedSyncs:          nil,
    89  			expectedStatusCode:     http.StatusBadRequest,
    90  			expectedBody:           "The bidder name provided is not supported by Prebid Server",
    91  			description: "No need to set existing syncs back in response for a request " +
    92  				"to set uid for an unsupported bidder",
    93  		},
    94  		{
    95  			uri:                    "/setuid?bidder=&uid=123",
    96  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
    97  			existingSyncs:          map[string]string{"pubmatic": "1234"},
    98  			gdprAllowsHostCookies:  true,
    99  			expectedSyncs:          nil,
   100  			expectedStatusCode:     http.StatusBadRequest,
   101  			expectedBody:           `"bidder" query param is required`,
   102  			description: "No need to set existing syncs back in response for a request " +
   103  				"to set uid for an empty bidder",
   104  		},
   105  		{
   106  			uri:                    "/setuid?bidder=pubmatic",
   107  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   108  			existingSyncs:          map[string]string{"pubmatic": "1234"},
   109  			gdprAllowsHostCookies:  true,
   110  			expectedSyncs:          map[string]string{},
   111  			expectedStatusCode:     http.StatusOK,
   112  			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
   113  			description:            "Unset uid for a bidder if the request contains an empty uid for that bidder",
   114  		},
   115  		{
   116  			uri:                    "/setuid?bidder=pubmatic&uid=123",
   117  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   118  			existingSyncs:          map[string]string{"rubicon": "def"},
   119  			gdprAllowsHostCookies:  true,
   120  			expectedSyncs:          map[string]string{"pubmatic": "123", "rubicon": "def"},
   121  			expectedStatusCode:     http.StatusOK,
   122  			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
   123  			description:            "Add the uid for the requested bidder to the list of existing syncs",
   124  		},
   125  		{
   126  			uri:                    "/setuid?bidder=pubmatic&uid=123&gdpr=0",
   127  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   128  			existingSyncs:          nil,
   129  			gdprAllowsHostCookies:  true,
   130  			expectedSyncs:          map[string]string{"pubmatic": "123"},
   131  			expectedStatusCode:     http.StatusOK,
   132  			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
   133  			description:            "Don't care about GDPR consent if GDPR is set to 0",
   134  		},
   135  		{
   136  			uri:                    "/setuid?uid=123",
   137  			syncersBidderNameToKey: map[string]string{"appnexus": "appnexus"},
   138  			existingSyncs:          nil,
   139  			expectedSyncs:          nil,
   140  			gdprAllowsHostCookies:  true,
   141  			expectedStatusCode:     http.StatusBadRequest,
   142  			expectedBody:           `"bidder" query param is required`,
   143  			description:            "Return an error if the bidder param is missing from the request",
   144  		},
   145  		{
   146  			uri:                    "/setuid?bidder=appnexus&uid=123&gdpr=2",
   147  			syncersBidderNameToKey: map[string]string{"appnexus": "appnexus"},
   148  			existingSyncs:          nil,
   149  			expectedSyncs:          nil,
   150  			gdprAllowsHostCookies:  true,
   151  			expectedStatusCode:     http.StatusBadRequest,
   152  			expectedBody:           "the gdpr query param must be either 0 or 1. You gave 2",
   153  			description:            "Return an error if GDPR is set to anything else other that 0 or 1",
   154  		},
   155  		{
   156  			uri:                    "/setuid?bidder=appnexus&uid=123&gdpr=1",
   157  			syncersBidderNameToKey: map[string]string{"appnexus": "appnexus"},
   158  			existingSyncs:          nil,
   159  			expectedSyncs:          nil,
   160  			gdprAllowsHostCookies:  true,
   161  			expectedStatusCode:     http.StatusBadRequest,
   162  			expectedBody:           "GDPR consent is required when gdpr signal equals 1",
   163  			description:            "Return an error if GDPR is set to 1 but GDPR consent string is missing",
   164  		},
   165  		{
   166  			uri: "/setuid?bidder=pubmatic&uid=123&gdpr_consent=" +
   167  				"BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw",
   168  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   169  			existingSyncs:          nil,
   170  			expectedSyncs:          nil,
   171  			gdprReturnsError:       true,
   172  			expectedStatusCode:     http.StatusBadRequest,
   173  			expectedBody: "No global vendor list was available to interpret this consent string. " +
   174  				"If this is a new, valid version, it should become available soon.",
   175  			description: "Return an error if the GDPR string is either malformed or using a newer version that isn't yet supported",
   176  		},
   177  		{
   178  			uri: "/setuid?bidder=pubmatic&uid=123&gdpr=1&gdpr_consent=" +
   179  				"BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw",
   180  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   181  			existingSyncs:          nil,
   182  			expectedSyncs:          nil,
   183  			expectedStatusCode:     http.StatusUnavailableForLegalReasons,
   184  			expectedBody:           "The gdpr_consent string prevents cookies from being saved",
   185  			description:            "Shouldn't set uid for a bidder if it is not allowed by the GDPR consent string",
   186  		},
   187  		{
   188  			uri: "/setuid?bidder=pubmatic&uid=123&gdpr=1&gdpr_consent=" +
   189  				"BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw",
   190  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   191  			gdprAllowsHostCookies:  true,
   192  			existingSyncs:          nil,
   193  			expectedSyncs:          map[string]string{"pubmatic": "123"},
   194  			expectedStatusCode:     http.StatusOK,
   195  			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
   196  			description:            "Should set uid for a bidder that is allowed by the GDPR consent string",
   197  		},
   198  		{
   199  			uri:                    "/setuid?bidder=pubmatic&uid=123&gpp_sid=2,4&gpp=DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   200  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   201  			gdprAllowsHostCookies:  true,
   202  			existingSyncs:          nil,
   203  			expectedSyncs:          map[string]string{"pubmatic": "123"},
   204  			expectedStatusCode:     http.StatusOK,
   205  			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
   206  			description:            "Sets uid for a bidder allowed by GDPR consent string in the GPP query field",
   207  		},
   208  		{
   209  			uri: "/setuid?bidder=pubmatic&uid=123&gpp_sid=2,4&gpp=DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA" +
   210  				"gdpr=1&gdpr_consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw",
   211  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   212  			gdprAllowsHostCookies:  true,
   213  			existingSyncs:          nil,
   214  			expectedSyncs:          map[string]string{"pubmatic": "123"},
   215  			expectedStatusCode:     http.StatusOK,
   216  			expectedBody:           "Warning: 'gpp' value will be used over the one found in the deprecated 'gdpr_consent' field.",
   217  			expectedHeaders:        map[string]string{"Content-Type": "text/plain; charset=utf-8"},
   218  			description:            "Sets uid for a bidder allowed by GDPR in GPP, throws warning because GDPR legacy values weren't used",
   219  		},
   220  		{
   221  			uri:                    "/setuid?bidder=pubmatic&uid=123&gdpr=1&gdpr_consent=malformed",
   222  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   223  			gdprAllowsHostCookies:  true,
   224  			gdprMalformed:          true,
   225  			existingSyncs:          nil,
   226  			expectedStatusCode:     http.StatusBadRequest,
   227  			expectedBody:           "gdpr_consent was invalid. malformed consent string malformed: some error",
   228  			description:            "Should return an error if GDPR consent string is malformed",
   229  		},
   230  		{
   231  			uri:                    "/setuid?bidder=pubmatic&uid=123&f=b",
   232  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   233  			existingSyncs:          nil,
   234  			gdprAllowsHostCookies:  true,
   235  			expectedSyncs:          map[string]string{"pubmatic": "123"},
   236  			expectedStatusCode:     http.StatusOK,
   237  			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
   238  			description:            "Set uid for valid bidder with iframe format",
   239  		},
   240  		{
   241  			uri:                    "/setuid?bidder=pubmatic&uid=123&f=i",
   242  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   243  			existingSyncs:          nil,
   244  			gdprAllowsHostCookies:  true,
   245  			expectedSyncs:          map[string]string{"pubmatic": "123"},
   246  			expectedStatusCode:     http.StatusOK,
   247  			expectedHeaders:        map[string]string{"Content-Type": "image/png", "Content-Length": "86"},
   248  			description:            "Set uid for valid bidder with redirect format",
   249  		},
   250  		{
   251  			uri:                    "/setuid?bidder=pubmatic&uid=123&f=x",
   252  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   253  			existingSyncs:          nil,
   254  			gdprAllowsHostCookies:  true,
   255  			expectedSyncs:          nil,
   256  			expectedStatusCode:     http.StatusBadRequest,
   257  			expectedBody:           `"f" query param is invalid. must be "b" or "i"`,
   258  			description:            "Set uid for valid bidder with invalid format",
   259  		},
   260  		{
   261  			uri:                    "/setuid?bidder=pubmatic&uid=123&account=valid_acct",
   262  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   263  			existingSyncs:          nil,
   264  			gdprAllowsHostCookies:  true,
   265  			expectedSyncs:          map[string]string{"pubmatic": "123"},
   266  			expectedStatusCode:     http.StatusOK,
   267  			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
   268  			description:            "Set uid for valid bidder with valid account provided",
   269  		},
   270  		{
   271  			uri:                    "/setuid?bidder=pubmatic&uid=123&account=disabled_acct",
   272  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   273  			existingSyncs:          nil,
   274  			gdprAllowsHostCookies:  true,
   275  			expectedSyncs:          nil,
   276  			expectedStatusCode:     http.StatusBadRequest,
   277  			expectedBody:           "account is disabled, please reach out to the prebid server host",
   278  			description:            "Set uid for valid bidder with valid disabled account provided",
   279  		},
   280  		{
   281  			uri:                    "/setuid?bidder=pubmatic&uid=123&account=valid_acct_with_valid_activities_usersync_enabled",
   282  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   283  			existingSyncs:          nil,
   284  			gdprAllowsHostCookies:  true,
   285  			expectedSyncs:          map[string]string{"pubmatic": "123"},
   286  			expectedStatusCode:     http.StatusOK,
   287  			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
   288  			description:            "Set uid for valid bidder with valid account provided with user sync allowed activity",
   289  		},
   290  		{
   291  			uri:                    "/setuid?bidder=pubmatic&uid=123&account=valid_acct_with_valid_activities_usersync_disabled",
   292  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   293  			existingSyncs:          nil,
   294  			gdprAllowsHostCookies:  true,
   295  			expectedSyncs:          nil,
   296  			expectedStatusCode:     http.StatusUnavailableForLegalReasons,
   297  			description:            "Set uid for valid bidder with valid account provided with user sync disallowed activity",
   298  		},
   299  		{
   300  			uri:                    "/setuid?bidder=pubmatic&uid=123&account=valid_acct_with_invalid_activities",
   301  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
   302  			existingSyncs:          nil,
   303  			gdprAllowsHostCookies:  true,
   304  			expectedSyncs:          map[string]string{"pubmatic": "123"},
   305  			expectedStatusCode:     http.StatusOK,
   306  			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
   307  			description:            "Set uid for valid bidder with valid account provided with invalid user sync activity",
   308  		},
   309  		{
   310  			description:            "gppsid-valid",
   311  			uri:                    "/setuid?bidder=appnexus&uid=123&gpp_sid=100,101", // fake sids to avoid GDPR logic in this test
   312  			syncersBidderNameToKey: map[string]string{"appnexus": "appnexus"},
   313  			existingSyncs:          nil,
   314  			gdprAllowsHostCookies:  true,
   315  			expectedSyncs:          map[string]string{"appnexus": "123"},
   316  			expectedStatusCode:     http.StatusOK,
   317  			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
   318  		},
   319  		{
   320  			description:            "gppsid-malformed",
   321  			uri:                    "/setuid?bidder=appnexus&uid=123&gpp_sid=malformed",
   322  			syncersBidderNameToKey: map[string]string{"appnexus": "appnexus"},
   323  			existingSyncs:          nil,
   324  			gdprAllowsHostCookies:  true,
   325  			expectedSyncs:          nil,
   326  			expectedStatusCode:     http.StatusBadRequest,
   327  			expectedBody:           "invalid gpp_sid encoding, must be a csv list of integers",
   328  		},
   329  	}
   330  
   331  	analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{})
   332  	metrics := &metricsConf.NilMetricsEngine{}
   333  
   334  	for _, test := range testCases {
   335  		response := doRequest(makeRequest(test.uri, test.existingSyncs), analytics, metrics,
   336  			test.syncersBidderNameToKey, test.gdprAllowsHostCookies, test.gdprReturnsError, test.gdprMalformed, false, 0, nil)
   337  		assert.Equal(t, test.expectedStatusCode, response.Code, "Test Case: %s. /setuid returned unexpected error code", test.description)
   338  
   339  		if test.expectedSyncs != nil {
   340  			assertHasSyncs(t, test.description, response, test.expectedSyncs)
   341  		} else {
   342  			assert.Equal(t, "", response.Header().Get("Set-Cookie"), "Test Case: %s. /setuid returned unexpected cookie", test.description)
   343  		}
   344  
   345  		if test.expectedBody != "" {
   346  			assert.Equal(t, test.expectedBody, response.Body.String(), "Test Case: %s. /setuid returned unexpected message", test.description)
   347  		}
   348  
   349  		// compare header values, except for the cookies
   350  		responseHeaders := map[string]string{}
   351  		for k, v := range response.Result().Header {
   352  			if k != "Set-Cookie" {
   353  				responseHeaders[k] = v[0]
   354  			}
   355  		}
   356  		if test.expectedHeaders == nil {
   357  			test.expectedHeaders = map[string]string{}
   358  		}
   359  		assert.Equal(t, test.expectedHeaders, responseHeaders, test.description+":headers")
   360  	}
   361  }
   362  
   363  func TestSetUIDPriorityEjection(t *testing.T) {
   364  	decoder := usersync.Base64Decoder{}
   365  	analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{})
   366  	syncersByBidder := map[string]string{
   367  		"pubmatic":             "pubmatic",
   368  		"syncer1":              "syncer1",
   369  		"syncer2":              "syncer2",
   370  		"syncer3":              "syncer3",
   371  		"syncer4":              "syncer4",
   372  		"mismatchedBidderName": "syncer5",
   373  		"syncerToEject":        "syncerToEject",
   374  	}
   375  
   376  	testCases := []struct {
   377  		description           string
   378  		uri                   string
   379  		givenExistingSyncs    []string
   380  		givenPriorityGroups   [][]string
   381  		givenMaxCookieSize    int
   382  		expectedStatusCode    int
   383  		expectedSyncer        string
   384  		expectedUID           string
   385  		expectedNumOfElements int
   386  		expectedWarning       string
   387  	}{
   388  		{
   389  			description:           "Cookie empty, expect bidder to be synced, no ejection",
   390  			uri:                   "/setuid?bidder=pubmatic&uid=123",
   391  			givenPriorityGroups:   [][]string{},
   392  			givenMaxCookieSize:    500,
   393  			expectedSyncer:        "pubmatic",
   394  			expectedUID:           "123",
   395  			expectedNumOfElements: 1,
   396  			expectedStatusCode:    http.StatusOK,
   397  		},
   398  		{
   399  			description:           "Cookie full, no priority groups, one ejection",
   400  			uri:                   "/setuid?bidder=pubmatic&uid=123",
   401  			givenExistingSyncs:    []string{"syncer1", "syncer2", "syncer3", "syncer4"},
   402  			givenPriorityGroups:   [][]string{},
   403  			givenMaxCookieSize:    500,
   404  			expectedUID:           "123",
   405  			expectedSyncer:        "pubmatic",
   406  			expectedNumOfElements: 4,
   407  			expectedStatusCode:    http.StatusOK,
   408  		},
   409  		{
   410  			description:           "Cookie full, eject lowest priority element",
   411  			uri:                   "/setuid?bidder=pubmatic&uid=123",
   412  			givenExistingSyncs:    []string{"syncer2", "syncer3", "syncer4", "syncerToEject"},
   413  			givenPriorityGroups:   [][]string{{"pubmatic", "syncer2", "syncer3", "syncer4"}, {"syncerToEject"}},
   414  			givenMaxCookieSize:    500,
   415  			expectedUID:           "123",
   416  			expectedSyncer:        "pubmatic",
   417  			expectedNumOfElements: 4,
   418  			expectedStatusCode:    http.StatusOK,
   419  		},
   420  		{
   421  			description:           "Cookie full, all elements same priority, one ejection",
   422  			uri:                   "/setuid?bidder=pubmatic&uid=123",
   423  			givenExistingSyncs:    []string{"syncer1", "syncer2", "syncer3", "syncer5"},
   424  			givenPriorityGroups:   [][]string{{"pubmatic", "syncer1", "syncer2", "syncer3", "mismatchedBidderName"}},
   425  			givenMaxCookieSize:    500,
   426  			expectedUID:           "123",
   427  			expectedSyncer:        "pubmatic",
   428  			expectedNumOfElements: 4,
   429  			expectedStatusCode:    http.StatusOK,
   430  		},
   431  		{
   432  			description:         "There are only priority elements left, but the bidder being synced isn't one",
   433  			uri:                 "/setuid?bidder=pubmatic&uid=123",
   434  			givenExistingSyncs:  []string{"syncer1", "syncer2", "syncer3", "syncer4"},
   435  			givenPriorityGroups: [][]string{{"syncer1", "syncer2", "syncer3", "syncer4"}},
   436  			givenMaxCookieSize:  500,
   437  			expectedStatusCode:  http.StatusOK,
   438  			expectedWarning:     "Warning: syncer key is not a priority, and there are only priority elements left, cookie not updated",
   439  		},
   440  		{
   441  			description:        "Uid that's trying to be synced is bigger than MaxCookieSize",
   442  			uri:                "/setuid?bidder=pubmatic&uid=123",
   443  			givenMaxCookieSize: 1,
   444  			expectedStatusCode: http.StatusBadRequest,
   445  		},
   446  	}
   447  	for _, test := range testCases {
   448  		request := httptest.NewRequest("GET", test.uri, nil)
   449  
   450  		// Cookie Set Up
   451  		cookie := usersync.NewCookie()
   452  		for _, key := range test.givenExistingSyncs {
   453  			cookie.Sync(key, "111")
   454  		}
   455  		httpCookie, err := ToHTTPCookie(cookie)
   456  		assert.NoError(t, err)
   457  		request.AddCookie(httpCookie)
   458  
   459  		// Make Request to /setuid
   460  		response := doRequest(request, analytics, &metricsConf.NilMetricsEngine{}, syncersByBidder, true, false, false, false, test.givenMaxCookieSize, test.givenPriorityGroups)
   461  
   462  		if test.expectedWarning != "" {
   463  			assert.Equal(t, test.expectedWarning, response.Body.String(), test.description)
   464  		} else if test.expectedSyncer != "" {
   465  			// Get Cookie From Header
   466  			var cookieHeader string
   467  			for k, v := range response.Result().Header {
   468  				if k == "Set-Cookie" {
   469  					cookieHeader = v[0]
   470  				}
   471  			}
   472  			encodedCookieValue := getUIDFromHeader(cookieHeader)
   473  
   474  			// Check That Bidder On Request was Synced, it's UID matches, and that the right number of elements are present after ejection
   475  			decodedCookie := decoder.Decode(encodedCookieValue)
   476  			decodedCookieUIDs := decodedCookie.GetUIDs()
   477  
   478  			assert.Equal(t, test.expectedUID, decodedCookieUIDs[test.expectedSyncer], test.description)
   479  			assert.Equal(t, test.expectedNumOfElements, len(decodedCookieUIDs), test.description)
   480  
   481  			// Specific test case handling where we eject the lowest priority element
   482  			if len(test.givenPriorityGroups) == 2 {
   483  				syncer := test.givenPriorityGroups[len(test.givenPriorityGroups)-1][0]
   484  				_, syncerExists := decodedCookieUIDs[syncer]
   485  				assert.False(t, syncerExists, test.description)
   486  			}
   487  		}
   488  		assert.Equal(t, test.expectedStatusCode, response.Result().StatusCode, test.description)
   489  	}
   490  }
   491  
   492  func TestParseSignalFromGPPSID(t *testing.T) {
   493  	type testOutput struct {
   494  		signal gdpr.Signal
   495  		err    error
   496  	}
   497  	testCases := []struct {
   498  		desc     string
   499  		strSID   string
   500  		expected testOutput
   501  	}{
   502  		{
   503  			desc:   "Empty gpp_sid, expect gdpr.SignalAmbiguous",
   504  			strSID: "",
   505  			expected: testOutput{
   506  				signal: gdpr.SignalAmbiguous,
   507  				err:    nil,
   508  			},
   509  		},
   510  		{
   511  			desc:   "Malformed gpp_sid, expect gdpr.SignalAmbiguous",
   512  			strSID: "malformed",
   513  			expected: testOutput{
   514  				signal: gdpr.SignalAmbiguous,
   515  				err:    errors.New(`Error parsing gpp_sid strconv.ParseInt: parsing "malformed": invalid syntax`),
   516  			},
   517  		},
   518  		{
   519  			desc:   "Valid gpp_sid doesn't come with TCF2, expect gdpr.SignalNo",
   520  			strSID: "6",
   521  			expected: testOutput{
   522  				signal: gdpr.SignalNo,
   523  				err:    nil,
   524  			},
   525  		},
   526  		{
   527  			desc:   "Valid gpp_sid comes with TCF2, expect gdpr.SignalYes",
   528  			strSID: "2",
   529  			expected: testOutput{
   530  				signal: gdpr.SignalYes,
   531  				err:    nil,
   532  			},
   533  		},
   534  	}
   535  	for _, tc := range testCases {
   536  		outSignal, outErr := parseSignalFromGppSidStr(tc.strSID)
   537  
   538  		assert.Equal(t, tc.expected.signal, outSignal, tc.desc)
   539  		assert.Equal(t, tc.expected.err, outErr, tc.desc)
   540  	}
   541  }
   542  
   543  func TestParseConsentFromGppStr(t *testing.T) {
   544  	type testOutput struct {
   545  		gdprConsent string
   546  		err         error
   547  	}
   548  	testCases := []struct {
   549  		desc       string
   550  		inGppQuery string
   551  		expected   testOutput
   552  	}{
   553  		{
   554  			desc:       "Empty gpp field, expect empty GDPR consent",
   555  			inGppQuery: "",
   556  			expected: testOutput{
   557  				gdprConsent: "",
   558  				err:         nil,
   559  			},
   560  		},
   561  		{
   562  			desc:       "Malformed gpp field value, expect empty GDPR consent and error",
   563  			inGppQuery: "malformed",
   564  			expected: testOutput{
   565  				gdprConsent: "",
   566  				err:         errors.New(`error parsing GPP header, base64 decoding: illegal base64 data at input byte 8`),
   567  			},
   568  		},
   569  		{
   570  			desc:       "Valid gpp string comes with TCF2 in its gppConstants.SectionID's, expect non-empty GDPR consent",
   571  			inGppQuery: "DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   572  			expected: testOutput{
   573  				gdprConsent: "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   574  				err:         nil,
   575  			},
   576  		},
   577  		{
   578  			desc:       "Valid gpp string doesn't come with TCF2 in its gppConstants.SectionID's, expect blank GDPR consent",
   579  			inGppQuery: "DBABjw~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
   580  			expected: testOutput{
   581  				gdprConsent: "",
   582  				err:         nil,
   583  			},
   584  		},
   585  	}
   586  	for _, tc := range testCases {
   587  		outConsent, outErr := parseConsentFromGppStr(tc.inGppQuery)
   588  
   589  		assert.Equal(t, tc.expected.gdprConsent, outConsent, tc.desc)
   590  		assert.Equal(t, tc.expected.err, outErr, tc.desc)
   591  	}
   592  }
   593  
   594  func TestParseGDPRFromGPP(t *testing.T) {
   595  	type testOutput struct {
   596  		reqInfo gdpr.RequestInfo
   597  		err     error
   598  	}
   599  	type aTest struct {
   600  		desc     string
   601  		inUri    string
   602  		expected testOutput
   603  	}
   604  	testGroups := []struct {
   605  		groupDesc string
   606  		testCases []aTest
   607  	}{
   608  		{
   609  			groupDesc: "No gpp_sid nor gpp",
   610  			testCases: []aTest{
   611  				{
   612  					desc:  "Input URL is mising gpp_sid and gpp, expect signal ambiguous and no error",
   613  					inUri: "/setuid?bidder=pubmatic&uid=123",
   614  					expected: testOutput{
   615  						reqInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous},
   616  						err:     nil,
   617  					},
   618  				},
   619  			},
   620  		},
   621  		{
   622  			groupDesc: "gpp only",
   623  			testCases: []aTest{
   624  				{
   625  					desc:  "gpp is malformed, expect error",
   626  					inUri: "/setuid?gpp=malformed",
   627  					expected: testOutput{
   628  						reqInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous},
   629  						err:     errors.New("error parsing GPP header, base64 decoding: illegal base64 data at input byte 8"),
   630  					},
   631  				},
   632  				{
   633  					desc:  "gpp with a valid TCF2 value. Expect valid consent string and no error",
   634  					inUri: "/setuid?gpp=DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   635  					expected: testOutput{
   636  						reqInfo: gdpr.RequestInfo{
   637  							GDPRSignal: gdpr.SignalAmbiguous,
   638  							Consent:    "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   639  						},
   640  						err: nil,
   641  					},
   642  				},
   643  				{
   644  					desc:  "gpp does not include TCF2 string. Expect empty consent string and no error",
   645  					inUri: "/setuid?gpp=DBABjw~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
   646  					expected: testOutput{
   647  						reqInfo: gdpr.RequestInfo{
   648  							GDPRSignal: gdpr.SignalAmbiguous,
   649  							Consent:    "",
   650  						},
   651  						err: nil,
   652  					},
   653  				},
   654  			},
   655  		},
   656  		{
   657  			groupDesc: "gpp_sid only",
   658  			testCases: []aTest{
   659  				{
   660  					desc:  "gpp_sid is malformed, expect error",
   661  					inUri: "/setuid?gpp_sid=malformed",
   662  					expected: testOutput{
   663  						reqInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous},
   664  						err:     errors.New("Error parsing gpp_sid strconv.ParseInt: parsing \"malformed\": invalid syntax"),
   665  					},
   666  				},
   667  				{
   668  					desc:  "TCF2 found in gpp_sid list. Given that the consent string will be empty, expect an error",
   669  					inUri: "/setuid?gpp_sid=2,6",
   670  					expected: testOutput{
   671  						reqInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalYes},
   672  						err:     nil,
   673  					},
   674  				},
   675  				{
   676  					desc:  "TCF2 not found in gpp_sid list. Expect SignalNo and no error",
   677  					inUri: "/setuid?gpp_sid=6,8",
   678  					expected: testOutput{
   679  						reqInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalNo},
   680  						err:     nil,
   681  					},
   682  				},
   683  			},
   684  		},
   685  		{
   686  			groupDesc: "both gpp_sid and gpp",
   687  			testCases: []aTest{
   688  				{
   689  					desc:  "TCF2 found in gpp_sid list and gpp has a valid GDPR string. Expect no error",
   690  					inUri: "/setuid?gpp_sid=2,6&gpp=DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   691  					expected: testOutput{
   692  						reqInfo: gdpr.RequestInfo{
   693  							GDPRSignal: gdpr.SignalYes,
   694  							Consent:    "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   695  						},
   696  						err: nil,
   697  					},
   698  				},
   699  			},
   700  		},
   701  	}
   702  	for _, tgroup := range testGroups {
   703  		for _, tc := range tgroup.testCases {
   704  			// set test
   705  			testURL, err := url.Parse(tc.inUri)
   706  			assert.NoError(t, err, "%s - %s", tgroup.groupDesc, tc.desc)
   707  
   708  			query := testURL.Query()
   709  
   710  			// run
   711  			outReqInfo, outErr := parseGDPRFromGPP(query)
   712  
   713  			// assertions
   714  			assert.Equal(t, tc.expected.reqInfo, outReqInfo, "%s - %s", tgroup.groupDesc, tc.desc)
   715  			assert.Equal(t, tc.expected.err, outErr, "%s - %s", tgroup.groupDesc, tc.desc)
   716  		}
   717  	}
   718  }
   719  
   720  func TestParseLegacyGDPRFields(t *testing.T) {
   721  	type testInput struct {
   722  		uri            string
   723  		gppGDPRSignal  gdpr.Signal
   724  		gppGDPRConsent string
   725  	}
   726  	type testOutput struct {
   727  		signal  gdpr.Signal
   728  		consent string
   729  		err     error
   730  	}
   731  	testCases := []struct {
   732  		desc     string
   733  		in       testInput
   734  		expected testOutput
   735  	}{
   736  		{
   737  			desc: `both "gdpr" and "gdpr_consent" missing from URI, expect SignalAmbiguous, blank consent and no error`,
   738  			in: testInput{
   739  				uri: "/setuid?bidder=pubmatic&uid=123",
   740  			},
   741  			expected: testOutput{
   742  				signal:  gdpr.SignalAmbiguous,
   743  				consent: "",
   744  				err:     nil,
   745  			},
   746  		},
   747  		{
   748  			desc: `invalid "gdpr" value, expect SignalAmbiguous, blank consent and error`,
   749  			in: testInput{
   750  				uri:           "/setuid?gdpr=2",
   751  				gppGDPRSignal: gdpr.SignalAmbiguous,
   752  			},
   753  			expected: testOutput{
   754  				signal:  gdpr.SignalAmbiguous,
   755  				consent: "",
   756  				err:     errors.New("the gdpr query param must be either 0 or 1. You gave 2"),
   757  			},
   758  		},
   759  		{
   760  			desc: `valid "gdpr" value but valid GDPRSignal was previously parsed before, expect SignalAmbiguous, blank consent and a warning`,
   761  			in: testInput{
   762  				uri:           "/setuid?gdpr=1",
   763  				gppGDPRSignal: gdpr.SignalYes,
   764  			},
   765  			expected: testOutput{
   766  				signal:  gdpr.SignalAmbiguous,
   767  				consent: "",
   768  				err: &errortypes.Warning{
   769  					Message:     "'gpp_sid' signal value will be used over the one found in the deprecated 'gdpr' field.",
   770  					WarningCode: errortypes.UnknownWarningCode,
   771  				},
   772  			},
   773  		},
   774  		{
   775  			desc: `valid "gdpr_consent" value but valid GDPRSignal was previously parsed before, expect SignalAmbiguous, blank consent and a warning`,
   776  			in: testInput{
   777  				uri:            "/setuid?gdpr_consent=someConsent",
   778  				gppGDPRConsent: "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   779  			},
   780  			expected: testOutput{
   781  				signal:  gdpr.SignalAmbiguous,
   782  				consent: "",
   783  				err: &errortypes.Warning{
   784  					Message:     "'gpp' value will be used over the one found in the deprecated 'gdpr_consent' field.",
   785  					WarningCode: errortypes.UnknownWarningCode,
   786  				},
   787  			},
   788  		},
   789  	}
   790  	for _, tc := range testCases {
   791  		// set test
   792  		testURL, err := url.Parse(tc.in.uri)
   793  		assert.NoError(t, err, tc.desc)
   794  
   795  		query := testURL.Query()
   796  
   797  		// run
   798  		outSignal, outConsent, outErr := parseLegacyGDPRFields(query, tc.in.gppGDPRSignal, tc.in.gppGDPRConsent)
   799  
   800  		// assertions
   801  		assert.Equal(t, tc.expected.signal, outSignal, tc.desc)
   802  		assert.Equal(t, tc.expected.consent, outConsent, tc.desc)
   803  		assert.Equal(t, tc.expected.err, outErr, tc.desc)
   804  	}
   805  }
   806  
   807  func TestExtractGDPRInfo(t *testing.T) {
   808  	type testOutput struct {
   809  		requestInfo gdpr.RequestInfo
   810  		err         error
   811  	}
   812  	type testCase struct {
   813  		desc     string
   814  		inUri    string
   815  		expected testOutput
   816  	}
   817  	testSuite := []struct {
   818  		sDesc string
   819  		tests []testCase
   820  	}{
   821  		{
   822  			sDesc: "no gdpr nor gpp values in query",
   823  			tests: []testCase{
   824  				{
   825  					desc:  "expect blank consent, signalNo and nil error",
   826  					inUri: "/setuid?bidder=pubmatic&uid=123",
   827  					expected: testOutput{
   828  						requestInfo: gdpr.RequestInfo{
   829  							Consent:    "",
   830  							GDPRSignal: gdpr.SignalAmbiguous,
   831  						},
   832  						err: nil,
   833  					},
   834  				},
   835  			},
   836  		},
   837  		{
   838  			sDesc: "missing gpp, gdpr only",
   839  			tests: []testCase{
   840  				{
   841  					desc:  "Invalid gdpr signal value in query, expect blank request info and error",
   842  					inUri: "/setuid?gdpr=2",
   843  					expected: testOutput{
   844  						requestInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous},
   845  						err:         errors.New("the gdpr query param must be either 0 or 1. You gave 2"),
   846  					},
   847  				},
   848  				{
   849  					desc:  "GDPR equals 0, blank consent, expect blank consent, signalNo and nil error",
   850  					inUri: "/setuid?gdpr=0",
   851  					expected: testOutput{
   852  						requestInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalNo},
   853  						err:         nil,
   854  					},
   855  				},
   856  				{
   857  					desc:  "GDPR equals 1, blank consent, expect blank request info and error",
   858  					inUri: "/setuid?gdpr=1",
   859  					expected: testOutput{
   860  						requestInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous},
   861  						err:         errors.New("GDPR consent is required when gdpr signal equals 1"),
   862  					},
   863  				},
   864  				{
   865  					desc:  "GDPR equals 0, non-blank consent, expect non-blank request info and nil error",
   866  					inUri: "/setuid?gdpr=0&gdpr_consent=someConsent",
   867  					expected: testOutput{
   868  						requestInfo: gdpr.RequestInfo{
   869  							Consent:    "someConsent",
   870  							GDPRSignal: gdpr.SignalNo,
   871  						},
   872  						err: nil,
   873  					},
   874  				},
   875  				{
   876  					desc:  "GDPR equals 1, non-blank consent, expect non-blank request info and nil error",
   877  					inUri: "/setuid?gdpr=1&gdpr_consent=someConsent",
   878  					expected: testOutput{
   879  						requestInfo: gdpr.RequestInfo{
   880  							Consent:    "someConsent",
   881  							GDPRSignal: gdpr.SignalYes,
   882  						},
   883  						err: nil,
   884  					},
   885  				},
   886  			},
   887  		},
   888  		{
   889  			sDesc: "missing gdpr, gpp only",
   890  			tests: []testCase{
   891  				{
   892  					desc:  "Malformed GPP_SID string, expect blank request info and error",
   893  					inUri: "/setuid?gpp_sid=malformed",
   894  					expected: testOutput{
   895  						requestInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous},
   896  						err:         errors.New("Error parsing gpp_sid strconv.ParseInt: parsing \"malformed\": invalid syntax"),
   897  					},
   898  				},
   899  				{
   900  					desc:  "Valid GPP_SID string but invalid GPP string in query, expect blank request info and error",
   901  					inUri: "/setuid?gpp=malformed&gpp_sid=2",
   902  					expected: testOutput{
   903  						requestInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous},
   904  						err:         errors.New("error parsing GPP header, base64 decoding: illegal base64 data at input byte 8"),
   905  					},
   906  				},
   907  				{
   908  					desc:  "SectionTCFEU2 not found in GPP string, expect blank consent and signalAmbiguous",
   909  					inUri: "/setuid?gpp=DBABBgA~xlgWEYCZAA",
   910  					expected: testOutput{
   911  						requestInfo: gdpr.RequestInfo{
   912  							Consent:    "",
   913  							GDPRSignal: gdpr.SignalAmbiguous,
   914  						},
   915  						err: nil,
   916  					},
   917  				},
   918  				{
   919  					desc:  "No GPP string, nor SectionTCFEU2 found in SID list in query, expect blank consent and signalAmbiguous",
   920  					inUri: "/setuid?gpp_sid=3,6",
   921  					expected: testOutput{
   922  						requestInfo: gdpr.RequestInfo{
   923  							Consent:    "",
   924  							GDPRSignal: gdpr.SignalNo,
   925  						},
   926  						err: nil,
   927  					},
   928  				},
   929  				{
   930  					desc:  "No GPP string, SectionTCFEU2 found in SID list in query, expect blank request info and error",
   931  					inUri: "/setuid?gpp_sid=2",
   932  					expected: testOutput{
   933  						requestInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous},
   934  						err:         errors.New("GDPR consent is required when gdpr signal equals 1"),
   935  					},
   936  				},
   937  				{
   938  					desc:  "SectionTCFEU2 only found in SID list, expect blank request info and error",
   939  					inUri: "/setuid?gpp=DBABBgA~xlgWEYCZAA&gpp_sid=2",
   940  					expected: testOutput{
   941  						requestInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous},
   942  						err:         errors.New("GDPR consent is required when gdpr signal equals 1"),
   943  					},
   944  				},
   945  				{
   946  					desc:  "SectionTCFEU2 found in GPP string but SID list is nil, expect valid consent and SignalAmbiguous",
   947  					inUri: "/setuid?gpp=DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   948  					expected: testOutput{
   949  						requestInfo: gdpr.RequestInfo{
   950  							Consent:    "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   951  							GDPRSignal: gdpr.SignalAmbiguous,
   952  						},
   953  						err: nil,
   954  					},
   955  				},
   956  				{
   957  					desc:  "SectionTCFEU2 found in GPP string but not in the non-nil SID list, expect valid consent and signalNo",
   958  					inUri: "/setuid?gpp=DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA&gpp_sid=6",
   959  					expected: testOutput{
   960  						requestInfo: gdpr.RequestInfo{
   961  							Consent:    "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   962  							GDPRSignal: gdpr.SignalNo,
   963  						},
   964  						err: nil,
   965  					},
   966  				},
   967  				{
   968  					desc:  "SectionTCFEU2 found both in GPP string and SID list, expect valid consent and signalYes",
   969  					inUri: "/setuid?gpp=DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA&gpp_sid=2,4",
   970  					expected: testOutput{
   971  						requestInfo: gdpr.RequestInfo{
   972  							Consent:    "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   973  							GDPRSignal: gdpr.SignalYes,
   974  						},
   975  						err: nil,
   976  					},
   977  				},
   978  			},
   979  		},
   980  		{
   981  			sDesc: "GPP values take priority over GDPR",
   982  			tests: []testCase{
   983  				{
   984  					desc:  "SignalNo in gdpr field but SignalYes in SID list, CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA consent in gpp but legacyConsent in gdpr_consent, expect GPP values to prevail",
   985  					inUri: "/setuid?gpp=DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA&gpp_sid=2,4&gdpr=0&gdpr_consent=legacyConsent",
   986  					expected: testOutput{
   987  						requestInfo: gdpr.RequestInfo{
   988  							Consent:    "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
   989  							GDPRSignal: gdpr.SignalYes,
   990  						},
   991  						err: &errortypes.Warning{
   992  							Message:     "'gpp' value will be used over the one found in the deprecated 'gdpr_consent' field.",
   993  							WarningCode: errortypes.UnknownWarningCode,
   994  						},
   995  					},
   996  				},
   997  				{
   998  					desc:  "SignalNo in gdpr field but SignalYes in SID list because SectionTCFEU2 is listed, expect GPP to prevail",
   999  					inUri: "/setuid?gpp=DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA&gpp_sid=2,4&gdpr=0",
  1000  					expected: testOutput{
  1001  						requestInfo: gdpr.RequestInfo{
  1002  							Consent:    "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
  1003  							GDPRSignal: gdpr.SignalYes,
  1004  						},
  1005  						err: &errortypes.Warning{
  1006  							Message:     "'gpp_sid' signal value will be used over the one found in the deprecated 'gdpr' field.",
  1007  							WarningCode: errortypes.UnknownWarningCode,
  1008  						},
  1009  					},
  1010  				},
  1011  				{
  1012  					desc:  "No gpp string in URL query, use gdpr_consent and SignalYes found in SID list because SectionTCFEU2 is listed",
  1013  					inUri: "/setuid?gpp_sid=2,4&gdpr_consent=legacyConsent",
  1014  					expected: testOutput{
  1015  						requestInfo: gdpr.RequestInfo{
  1016  							Consent:    "",
  1017  							GDPRSignal: gdpr.SignalAmbiguous,
  1018  						},
  1019  						err: errors.New("GDPR consent is required when gdpr signal equals 1"),
  1020  					},
  1021  				},
  1022  				{
  1023  					desc:  "SectionTCFEU2 not found in GPP string but found in SID list, choose the GDPR_CONSENT and GPP_SID signal",
  1024  					inUri: "/setuid?gpp=DBABBgA~xlgWEYCZAA&gpp_sid=2&gdpr=0&gdpr_consent=legacyConsent",
  1025  					expected: testOutput{
  1026  						requestInfo: gdpr.RequestInfo{
  1027  							Consent:    "",
  1028  							GDPRSignal: gdpr.SignalAmbiguous,
  1029  						},
  1030  						err: errors.New("GDPR consent is required when gdpr signal equals 1"),
  1031  					},
  1032  				},
  1033  				{
  1034  					desc:  "SectionTCFEU2 found in GPP string but not in SID list, choose GDPR signal GPP consent value",
  1035  					inUri: "/setuid?gpp=DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA&gpp_sid=6&gdpr=1&gdpr_consent=legacyConsent",
  1036  					expected: testOutput{
  1037  						requestInfo: gdpr.RequestInfo{
  1038  							Consent:    "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
  1039  							GDPRSignal: gdpr.SignalNo,
  1040  						},
  1041  						err: &errortypes.Warning{
  1042  							Message:     "'gpp' value will be used over the one found in the deprecated 'gdpr_consent' field.",
  1043  							WarningCode: errortypes.UnknownWarningCode,
  1044  						},
  1045  					},
  1046  				},
  1047  				{
  1048  					desc:  "SectionTCFEU2 not found in GPP, use GDPR_CONSENT value. SignalYes found in gdpr field, but not in the valid SID list, expect SignalNo",
  1049  					inUri: "/setuid?gpp=DBABBgA~xlgWEYCZAA&gpp_sid=6&gdpr=1&gdpr_consent=legacyConsent",
  1050  					expected: testOutput{
  1051  						requestInfo: gdpr.RequestInfo{
  1052  							Consent:    "",
  1053  							GDPRSignal: gdpr.SignalNo,
  1054  						},
  1055  						err: &errortypes.Warning{
  1056  							Message:     "'gpp_sid' signal value will be used over the one found in the deprecated 'gdpr' field.",
  1057  							WarningCode: errortypes.UnknownWarningCode,
  1058  						},
  1059  					},
  1060  				},
  1061  			},
  1062  		},
  1063  	}
  1064  
  1065  	for _, ts := range testSuite {
  1066  		for _, tc := range ts.tests {
  1067  			// set test
  1068  			testURL, err := url.Parse(tc.inUri)
  1069  			assert.NoError(t, err, tc.desc)
  1070  
  1071  			query := testURL.Query()
  1072  
  1073  			// run
  1074  			outReqInfo, outErr := extractGDPRInfo(query)
  1075  
  1076  			// assertions
  1077  			assert.Equal(t, tc.expected.requestInfo, outReqInfo, tc.desc)
  1078  			assert.Equal(t, tc.expected.err, outErr, tc.desc)
  1079  		}
  1080  	}
  1081  }
  1082  
  1083  func TestSetUIDEndpointMetrics(t *testing.T) {
  1084  	cookieWithOptOut := usersync.NewCookie()
  1085  	cookieWithOptOut.SetOptOut(true)
  1086  
  1087  	testCases := []struct {
  1088  		description            string
  1089  		uri                    string
  1090  		cookies                []*usersync.Cookie
  1091  		syncersBidderNameToKey map[string]string
  1092  		gdprAllowsHostCookies  bool
  1093  		cfgAccountRequired     bool
  1094  		expectedResponseCode   int
  1095  		expectedMetrics        func(*metrics.MetricsEngineMock)
  1096  		expectedAnalytics      func(*MockAnalytics)
  1097  	}{
  1098  		{
  1099  			description:            "Success - Sync",
  1100  			uri:                    "/setuid?bidder=pubmatic&uid=123",
  1101  			cookies:                []*usersync.Cookie{},
  1102  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
  1103  			gdprAllowsHostCookies:  true,
  1104  			expectedResponseCode:   200,
  1105  			expectedMetrics: func(m *metrics.MetricsEngineMock) {
  1106  				m.On("RecordSetUid", metrics.SetUidOK).Once()
  1107  				m.On("RecordSyncerSet", "pubmatic", metrics.SyncerSetUidOK).Once()
  1108  			},
  1109  			expectedAnalytics: func(a *MockAnalytics) {
  1110  				expected := analytics.SetUIDObject{
  1111  					Status:  200,
  1112  					Bidder:  "pubmatic",
  1113  					UID:     "123",
  1114  					Errors:  []error{},
  1115  					Success: true,
  1116  				}
  1117  				a.On("LogSetUIDObject", &expected).Once()
  1118  			},
  1119  		},
  1120  		{
  1121  			description:            "Success - Unsync",
  1122  			uri:                    "/setuid?bidder=pubmatic&uid=",
  1123  			cookies:                []*usersync.Cookie{},
  1124  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
  1125  			gdprAllowsHostCookies:  true,
  1126  			expectedResponseCode:   200,
  1127  			expectedMetrics: func(m *metrics.MetricsEngineMock) {
  1128  				m.On("RecordSetUid", metrics.SetUidOK).Once()
  1129  				m.On("RecordSyncerSet", "pubmatic", metrics.SyncerSetUidCleared).Once()
  1130  			},
  1131  			expectedAnalytics: func(a *MockAnalytics) {
  1132  				expected := analytics.SetUIDObject{
  1133  					Status:  200,
  1134  					Bidder:  "pubmatic",
  1135  					UID:     "",
  1136  					Errors:  []error{},
  1137  					Success: true,
  1138  				}
  1139  				a.On("LogSetUIDObject", &expected).Once()
  1140  			},
  1141  		},
  1142  		{
  1143  			description:            "Cookie Opted Out",
  1144  			uri:                    "/setuid?bidder=pubmatic&uid=123",
  1145  			cookies:                []*usersync.Cookie{cookieWithOptOut},
  1146  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
  1147  			gdprAllowsHostCookies:  true,
  1148  			expectedResponseCode:   401,
  1149  			expectedMetrics: func(m *metrics.MetricsEngineMock) {
  1150  				m.On("RecordSetUid", metrics.SetUidOptOut).Once()
  1151  			},
  1152  			expectedAnalytics: func(a *MockAnalytics) {
  1153  				expected := analytics.SetUIDObject{
  1154  					Status:  401,
  1155  					Bidder:  "",
  1156  					UID:     "",
  1157  					Errors:  []error{},
  1158  					Success: false,
  1159  				}
  1160  				a.On("LogSetUIDObject", &expected).Once()
  1161  			},
  1162  		},
  1163  		{
  1164  			description:            "Unknown Syncer Key",
  1165  			uri:                    "/setuid?bidder=pubmatic&uid=123",
  1166  			cookies:                []*usersync.Cookie{},
  1167  			syncersBidderNameToKey: map[string]string{},
  1168  			gdprAllowsHostCookies:  true,
  1169  			expectedResponseCode:   400,
  1170  			expectedMetrics: func(m *metrics.MetricsEngineMock) {
  1171  				m.On("RecordSetUid", metrics.SetUidSyncerUnknown).Once()
  1172  			},
  1173  			expectedAnalytics: func(a *MockAnalytics) {
  1174  				expected := analytics.SetUIDObject{
  1175  					Status:  400,
  1176  					Bidder:  "",
  1177  					UID:     "",
  1178  					Errors:  []error{errors.New("The bidder name provided is not supported by Prebid Server")},
  1179  					Success: false,
  1180  				}
  1181  				a.On("LogSetUIDObject", &expected).Once()
  1182  			},
  1183  		},
  1184  		{
  1185  			description:            "Unknown Format",
  1186  			uri:                    "/setuid?bidder=pubmatic&uid=123&f=z",
  1187  			cookies:                []*usersync.Cookie{},
  1188  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
  1189  			gdprAllowsHostCookies:  true,
  1190  			expectedResponseCode:   400,
  1191  			expectedMetrics: func(m *metrics.MetricsEngineMock) {
  1192  				m.On("RecordSetUid", metrics.SetUidBadRequest).Once()
  1193  			},
  1194  			expectedAnalytics: func(a *MockAnalytics) {
  1195  				expected := analytics.SetUIDObject{
  1196  					Status:  400,
  1197  					Bidder:  "pubmatic",
  1198  					UID:     "",
  1199  					Errors:  []error{errors.New(`"f" query param is invalid. must be "b" or "i"`)},
  1200  					Success: false,
  1201  				}
  1202  				a.On("LogSetUIDObject", &expected).Once()
  1203  			},
  1204  		},
  1205  		{
  1206  			description:            "Prevented By GDPR - Invalid Consent String",
  1207  			uri:                    "/setuid?bidder=pubmatic&uid=123&gdpr=1",
  1208  			cookies:                []*usersync.Cookie{},
  1209  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
  1210  			gdprAllowsHostCookies:  true,
  1211  			expectedResponseCode:   400,
  1212  			expectedMetrics: func(m *metrics.MetricsEngineMock) {
  1213  				m.On("RecordSetUid", metrics.SetUidBadRequest).Once()
  1214  			},
  1215  			expectedAnalytics: func(a *MockAnalytics) {
  1216  				expected := analytics.SetUIDObject{
  1217  					Status:  400,
  1218  					Bidder:  "pubmatic",
  1219  					UID:     "",
  1220  					Errors:  []error{errors.New("GDPR consent is required when gdpr signal equals 1")},
  1221  					Success: false,
  1222  				}
  1223  				a.On("LogSetUIDObject", &expected).Once()
  1224  			},
  1225  		},
  1226  		{
  1227  			description:            "Prevented By GDPR - Permission Denied By Consent String",
  1228  			uri:                    "/setuid?bidder=pubmatic&uid=123&gdpr=1&gdpr_consent=any",
  1229  			cookies:                []*usersync.Cookie{},
  1230  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
  1231  			gdprAllowsHostCookies:  false,
  1232  			expectedResponseCode:   451,
  1233  			expectedMetrics: func(m *metrics.MetricsEngineMock) {
  1234  				m.On("RecordSetUid", metrics.SetUidGDPRHostCookieBlocked).Once()
  1235  			},
  1236  			expectedAnalytics: func(a *MockAnalytics) {
  1237  				expected := analytics.SetUIDObject{
  1238  					Status:  451,
  1239  					Bidder:  "pubmatic",
  1240  					UID:     "",
  1241  					Errors:  []error{errors.New("The gdpr_consent string prevents cookies from being saved")},
  1242  					Success: false,
  1243  				}
  1244  				a.On("LogSetUIDObject", &expected).Once()
  1245  			},
  1246  		},
  1247  		{
  1248  			description:            "Blocked account",
  1249  			uri:                    "/setuid?bidder=pubmatic&uid=123&account=blocked_acct",
  1250  			cookies:                []*usersync.Cookie{},
  1251  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
  1252  			gdprAllowsHostCookies:  true,
  1253  			expectedResponseCode:   400,
  1254  			expectedMetrics: func(m *metrics.MetricsEngineMock) {
  1255  				m.On("RecordSetUid", metrics.SetUidAccountBlocked).Once()
  1256  			},
  1257  			expectedAnalytics: func(a *MockAnalytics) {
  1258  				expected := analytics.SetUIDObject{
  1259  					Status:  400,
  1260  					Bidder:  "pubmatic",
  1261  					UID:     "",
  1262  					Errors:  []error{errCookieSyncAccountBlocked},
  1263  					Success: false,
  1264  				}
  1265  				a.On("LogSetUIDObject", &expected).Once()
  1266  			},
  1267  		},
  1268  		{
  1269  			description:            "Invalid account",
  1270  			uri:                    "/setuid?bidder=pubmatic&uid=123&account=unknown",
  1271  			cookies:                []*usersync.Cookie{},
  1272  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
  1273  			gdprAllowsHostCookies:  true,
  1274  			cfgAccountRequired:     true,
  1275  			expectedResponseCode:   400,
  1276  			expectedMetrics: func(m *metrics.MetricsEngineMock) {
  1277  				m.On("RecordSetUid", metrics.SetUidAccountInvalid).Once()
  1278  			},
  1279  			expectedAnalytics: func(a *MockAnalytics) {
  1280  				expected := analytics.SetUIDObject{
  1281  					Status:  400,
  1282  					Bidder:  "pubmatic",
  1283  					UID:     "",
  1284  					Errors:  []error{errCookieSyncAccountInvalid},
  1285  					Success: false,
  1286  				}
  1287  				a.On("LogSetUIDObject", &expected).Once()
  1288  			},
  1289  		},
  1290  		{
  1291  			description:            "Malformed account",
  1292  			uri:                    "/setuid?bidder=pubmatic&uid=123&account=malformed_acct",
  1293  			cookies:                []*usersync.Cookie{},
  1294  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
  1295  			gdprAllowsHostCookies:  true,
  1296  			cfgAccountRequired:     true,
  1297  			expectedResponseCode:   400,
  1298  			expectedMetrics: func(m *metrics.MetricsEngineMock) {
  1299  				m.On("RecordSetUid", metrics.SetUidAccountConfigMalformed).Once()
  1300  			},
  1301  			expectedAnalytics: func(a *MockAnalytics) {
  1302  				expected := analytics.SetUIDObject{
  1303  					Status:  400,
  1304  					Bidder:  "pubmatic",
  1305  					UID:     "",
  1306  					Errors:  []error{errCookieSyncAccountConfigMalformed},
  1307  					Success: false,
  1308  				}
  1309  				a.On("LogSetUIDObject", &expected).Once()
  1310  			},
  1311  		},
  1312  		{
  1313  			description:            "Invalid JSON account",
  1314  			uri:                    "/setuid?bidder=pubmatic&uid=123&account=invalid_json_acct",
  1315  			cookies:                []*usersync.Cookie{},
  1316  			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
  1317  			gdprAllowsHostCookies:  true,
  1318  			cfgAccountRequired:     true,
  1319  			expectedResponseCode:   400,
  1320  			expectedMetrics: func(m *metrics.MetricsEngineMock) {
  1321  				m.On("RecordSetUid", metrics.SetUidBadRequest).Once()
  1322  			},
  1323  			expectedAnalytics: func(a *MockAnalytics) {
  1324  				expected := analytics.SetUIDObject{
  1325  					Status:  400,
  1326  					Bidder:  "pubmatic",
  1327  					UID:     "",
  1328  					Errors:  []error{errors.New("unexpected end of JSON input")},
  1329  					Success: false,
  1330  				}
  1331  				a.On("LogSetUIDObject", &expected).Once()
  1332  			},
  1333  		},
  1334  	}
  1335  
  1336  	for _, test := range testCases {
  1337  		analyticsEngine := &MockAnalytics{}
  1338  		test.expectedAnalytics(analyticsEngine)
  1339  
  1340  		metricsEngine := &metrics.MetricsEngineMock{}
  1341  		test.expectedMetrics(metricsEngine)
  1342  
  1343  		req := httptest.NewRequest("GET", test.uri, nil)
  1344  		for _, v := range test.cookies {
  1345  			addCookie(req, v)
  1346  		}
  1347  		response := doRequest(req, analyticsEngine, metricsEngine, test.syncersBidderNameToKey, test.gdprAllowsHostCookies, false, false, test.cfgAccountRequired, 0, nil)
  1348  
  1349  		assert.Equal(t, test.expectedResponseCode, response.Code, test.description)
  1350  		analyticsEngine.AssertExpectations(t)
  1351  		metricsEngine.AssertExpectations(t)
  1352  	}
  1353  }
  1354  
  1355  func TestOptedOut(t *testing.T) {
  1356  	request := httptest.NewRequest("GET", "/setuid?bidder=pubmatic&uid=123", nil)
  1357  	cookie := usersync.NewCookie()
  1358  	cookie.SetOptOut(true)
  1359  	addCookie(request, cookie)
  1360  	syncersBidderNameToKey := map[string]string{"pubmatic": "pubmatic"}
  1361  	analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{})
  1362  	metrics := &metricsConf.NilMetricsEngine{}
  1363  	response := doRequest(request, analytics, metrics, syncersBidderNameToKey, true, false, false, false, 0, nil)
  1364  
  1365  	assert.Equal(t, http.StatusUnauthorized, response.Code)
  1366  }
  1367  
  1368  func TestSiteCookieCheck(t *testing.T) {
  1369  	testCases := []struct {
  1370  		ua             string
  1371  		expectedResult bool
  1372  		description    string
  1373  	}{
  1374  		{
  1375  			ua:             "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
  1376  			expectedResult: true,
  1377  			description:    "Should return true for a valid chrome version",
  1378  		},
  1379  		{
  1380  			ua:             "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3770.142 Safari/537.36",
  1381  			expectedResult: false,
  1382  			description:    "Should return false for chrome version below than the supported min version",
  1383  		},
  1384  	}
  1385  
  1386  	for _, test := range testCases {
  1387  		assert.Equal(t, test.expectedResult, siteCookieCheck(test.ua), test.description)
  1388  	}
  1389  }
  1390  
  1391  func TestGetResponseFormat(t *testing.T) {
  1392  	testCases := []struct {
  1393  		urlValues      url.Values
  1394  		syncer         usersync.Syncer
  1395  		expectedFormat string
  1396  		expectedError  string
  1397  		description    string
  1398  	}{
  1399  		{
  1400  			urlValues:      url.Values{},
  1401  			syncer:         fakeSyncer{key: "a", defaultSyncType: usersync.SyncTypeIFrame},
  1402  			expectedFormat: "b",
  1403  			description:    "parameter not provided, use default sync type iframe",
  1404  		},
  1405  		{
  1406  			urlValues:      url.Values{},
  1407  			syncer:         fakeSyncer{key: "a", defaultSyncType: usersync.SyncTypeRedirect},
  1408  			expectedFormat: "i",
  1409  			description:    "parameter not provided, use default sync type redirect",
  1410  		},
  1411  		{
  1412  			urlValues:      url.Values{},
  1413  			syncer:         fakeSyncer{key: "a", defaultSyncType: usersync.SyncType("invalid")},
  1414  			expectedFormat: "",
  1415  			description:    "parameter not provided,  default sync type is invalid",
  1416  		},
  1417  		{
  1418  			urlValues:      url.Values{"f": []string{"b"}},
  1419  			syncer:         fakeSyncer{key: "a", defaultSyncType: usersync.SyncTypeRedirect},
  1420  			expectedFormat: "b",
  1421  			description:    "parameter given as `b`, default sync type is opposite",
  1422  		},
  1423  		{
  1424  			urlValues:      url.Values{"f": []string{"B"}},
  1425  			syncer:         fakeSyncer{key: "a", defaultSyncType: usersync.SyncTypeRedirect},
  1426  			expectedFormat: "b",
  1427  			description:    "parameter given as `b`, default sync type is opposite - case insensitive",
  1428  		},
  1429  		{
  1430  			urlValues:      url.Values{"f": []string{"i"}},
  1431  			syncer:         fakeSyncer{key: "a", defaultSyncType: usersync.SyncTypeIFrame},
  1432  			expectedFormat: "i",
  1433  			description:    "parameter given as `b`, default sync type is opposite",
  1434  		},
  1435  		{
  1436  			urlValues:      url.Values{"f": []string{"I"}},
  1437  			syncer:         fakeSyncer{key: "a", defaultSyncType: usersync.SyncTypeIFrame},
  1438  			expectedFormat: "i",
  1439  			description:    "parameter given as `b`, default sync type is opposite - case insensitive",
  1440  		},
  1441  		{
  1442  			urlValues:     url.Values{"f": []string{"x"}},
  1443  			syncer:        fakeSyncer{key: "a", defaultSyncType: usersync.SyncTypeIFrame},
  1444  			expectedError: `"f" query param is invalid. must be "b" or "i"`,
  1445  			description:   "parameter given invalid",
  1446  		},
  1447  		{
  1448  			urlValues:      url.Values{"f": []string{}},
  1449  			syncer:         fakeSyncer{key: "a", defaultSyncType: usersync.SyncTypeRedirect},
  1450  			expectedFormat: "i",
  1451  			description:    "parameter given is empty (by slice), use default sync type redirect",
  1452  		},
  1453  		{
  1454  			urlValues:      url.Values{"f": []string{""}},
  1455  			syncer:         fakeSyncer{key: "a", defaultSyncType: usersync.SyncTypeRedirect},
  1456  			expectedFormat: "i",
  1457  			description:    "parameter given is empty (by empty item), use default sync type redirect",
  1458  		},
  1459  	}
  1460  
  1461  	for _, test := range testCases {
  1462  		result, err := getResponseFormat(test.urlValues, test.syncer)
  1463  
  1464  		if test.expectedError == "" {
  1465  			assert.NoError(t, err, test.description+":err")
  1466  			assert.Equal(t, test.expectedFormat, result, test.description+":result")
  1467  		} else {
  1468  			assert.EqualError(t, err, test.expectedError, test.description+":err")
  1469  			assert.Empty(t, result, test.description+":result")
  1470  		}
  1471  	}
  1472  }
  1473  
  1474  func TestIsSyncerPriority(t *testing.T) {
  1475  	testCases := []struct {
  1476  		name                           string
  1477  		givenBidderNameFromSyncerQuery string
  1478  		givenPriorityGroups            [][]string
  1479  		expected                       bool
  1480  	}{
  1481  		{
  1482  			name:                           "bidder-name-is-priority",
  1483  			givenBidderNameFromSyncerQuery: "priorityBidder",
  1484  			givenPriorityGroups: [][]string{
  1485  				{"priorityBidder"},
  1486  				{"2", "3"},
  1487  			},
  1488  			expected: true,
  1489  		},
  1490  		{
  1491  			name:                           "bidder-name-is-not-priority",
  1492  			givenBidderNameFromSyncerQuery: "notPriorityBidderName",
  1493  			givenPriorityGroups: [][]string{
  1494  				{"1"},
  1495  				{"2", "3"},
  1496  			},
  1497  			expected: false,
  1498  		},
  1499  		{
  1500  			name:                           "no-bidder-name-given",
  1501  			givenBidderNameFromSyncerQuery: "",
  1502  			givenPriorityGroups: [][]string{
  1503  				{"1"},
  1504  				{"2", "3"},
  1505  			},
  1506  			expected: false,
  1507  		},
  1508  		{
  1509  			name:                           "no-priority-groups-given",
  1510  			givenBidderNameFromSyncerQuery: "bidderName",
  1511  			givenPriorityGroups:            [][]string{},
  1512  			expected:                       false,
  1513  		},
  1514  	}
  1515  
  1516  	for _, test := range testCases {
  1517  		t.Run(test.name, func(t *testing.T) {
  1518  			isPriority := isSyncerPriority(test.givenBidderNameFromSyncerQuery, test.givenPriorityGroups)
  1519  			assert.Equal(t, test.expected, isPriority)
  1520  		})
  1521  	}
  1522  }
  1523  
  1524  func assertHasSyncs(t *testing.T, testCase string, resp *httptest.ResponseRecorder, syncs map[string]string) {
  1525  	t.Helper()
  1526  	cookie := parseCookieString(t, resp)
  1527  
  1528  	assert.Equal(t, len(syncs), len(cookie.GetUIDs()), "Test Case: %s. /setuid response doesn't contain expected number of syncs", testCase)
  1529  
  1530  	for bidder, uid := range syncs {
  1531  		assert.True(t, cookie.HasLiveSync(bidder), "Test Case: %s. /setuid response cookie doesn't contain uid for bidder: %s", testCase, bidder)
  1532  		actualUID, _, _ := cookie.GetUID(bidder)
  1533  		assert.Equal(t, uid, actualUID, "Test Case: %s. /setuid response cookie doesn't contain correct uid for bidder: %s", testCase, bidder)
  1534  	}
  1535  }
  1536  
  1537  func makeRequest(uri string, existingSyncs map[string]string) *http.Request {
  1538  	request := httptest.NewRequest("GET", uri, nil)
  1539  	if len(existingSyncs) > 0 {
  1540  		pbsCookie := usersync.NewCookie()
  1541  		for key, value := range existingSyncs {
  1542  			pbsCookie.Sync(key, value)
  1543  		}
  1544  		addCookie(request, pbsCookie)
  1545  	}
  1546  	return request
  1547  }
  1548  
  1549  func doRequest(req *http.Request, analytics analytics.PBSAnalyticsModule, metrics metrics.MetricsEngine, syncersBidderNameToKey map[string]string, gdprAllowsHostCookies, gdprReturnsError, gdprReturnsMalformedError, cfgAccountRequired bool, maxCookieSize int, priorityGroups [][]string) *httptest.ResponseRecorder {
  1550  	cfg := config.Configuration{
  1551  		AccountRequired: cfgAccountRequired,
  1552  		BlacklistedAcctMap: map[string]bool{
  1553  			"blocked_acct": true,
  1554  		},
  1555  		AccountDefaults: config.Account{},
  1556  		UserSync: config.UserSync{
  1557  			PriorityGroups: priorityGroups,
  1558  		},
  1559  		HostCookie: config.HostCookie{
  1560  			MaxCookieSizeBytes: maxCookieSize,
  1561  		},
  1562  	}
  1563  	cfg.MarshalAccountDefaults()
  1564  
  1565  	query := req.URL.Query()
  1566  
  1567  	perms := &fakePermsSetUID{
  1568  		allowHost:           gdprAllowsHostCookies,
  1569  		consent:             query.Get("gdpr_consent"),
  1570  		errorHost:           gdprReturnsError,
  1571  		errorMalformed:      gdprReturnsMalformedError,
  1572  		personalInfoAllowed: true,
  1573  	}
  1574  	gdprPermsBuilder := fakePermissionsBuilder{
  1575  		permissions: perms,
  1576  	}.Builder
  1577  	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
  1578  		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
  1579  	}.Builder
  1580  
  1581  	syncersByBidder := make(map[string]usersync.Syncer)
  1582  	for bidderName, syncerKey := range syncersBidderNameToKey {
  1583  		syncersByBidder[bidderName] = fakeSyncer{key: syncerKey, defaultSyncType: usersync.SyncTypeIFrame}
  1584  		if priorityGroups == nil {
  1585  			cfg.UserSync.PriorityGroups = [][]string{{}}
  1586  			cfg.UserSync.PriorityGroups[0] = append(cfg.UserSync.PriorityGroups[0], bidderName)
  1587  		}
  1588  	}
  1589  
  1590  	fakeAccountsFetcher := FakeAccountsFetcher{AccountData: map[string]json.RawMessage{
  1591  		"valid_acct":        json.RawMessage(`{"disabled":false}`),
  1592  		"disabled_acct":     json.RawMessage(`{"disabled":true}`),
  1593  		"malformed_acct":    json.RawMessage(`{"disabled":"malformed"}`),
  1594  		"invalid_json_acct": json.RawMessage(`{"}`),
  1595  
  1596  		"valid_acct_with_valid_activities_usersync_enabled":  json.RawMessage(`{"privacy":{"allowactivities":{"syncUser":{"default": true}}}}`),
  1597  		"valid_acct_with_valid_activities_usersync_disabled": json.RawMessage(`{"privacy":{"allowactivities":{"syncUser":{"default": false}}}}`),
  1598  		"valid_acct_with_invalid_activities":                 json.RawMessage(`{"privacy":{"allowactivities":{"syncUser":{"rules":[{"condition":{"componentName": ["bidderA.bidderB.bidderC"]}}]}}}}`),
  1599  	}}
  1600  
  1601  	endpoint := NewSetUIDEndpoint(&cfg, syncersByBidder, gdprPermsBuilder, tcf2ConfigBuilder, analytics, fakeAccountsFetcher, metrics)
  1602  	response := httptest.NewRecorder()
  1603  	endpoint(response, req, nil)
  1604  	return response
  1605  }
  1606  
  1607  func addCookie(req *http.Request, cookie *usersync.Cookie) {
  1608  	httpCookie, _ := ToHTTPCookie(cookie)
  1609  	req.AddCookie(httpCookie)
  1610  }
  1611  
  1612  func parseCookieString(t *testing.T, response *httptest.ResponseRecorder) *usersync.Cookie {
  1613  	decoder := usersync.Base64Decoder{}
  1614  	cookieString := response.Header().Get("Set-Cookie")
  1615  	parser := regexp.MustCompile("uids=(.*?);")
  1616  	res := parser.FindStringSubmatch(cookieString)
  1617  	assert.Equal(t, 2, len(res))
  1618  	httpCookie := http.Cookie{
  1619  		Name:  "uids",
  1620  		Value: res[1],
  1621  	}
  1622  	return decoder.Decode(httpCookie.Value)
  1623  }
  1624  
  1625  type fakePermissionsBuilder struct {
  1626  	permissions gdpr.Permissions
  1627  }
  1628  
  1629  func (fpb fakePermissionsBuilder) Builder(gdpr.TCF2ConfigReader, gdpr.RequestInfo) gdpr.Permissions {
  1630  	return fpb.permissions
  1631  }
  1632  
  1633  type fakeTCF2ConfigBuilder struct {
  1634  	cfg gdpr.TCF2ConfigReader
  1635  }
  1636  
  1637  func (fcr fakeTCF2ConfigBuilder) Builder(hostConfig config.TCF2, accountConfig config.AccountGDPR) gdpr.TCF2ConfigReader {
  1638  	return fcr.cfg
  1639  }
  1640  
  1641  type fakePermsSetUID struct {
  1642  	allowHost           bool
  1643  	consent             string
  1644  	errorHost           bool
  1645  	errorMalformed      bool
  1646  	personalInfoAllowed bool
  1647  }
  1648  
  1649  func (g *fakePermsSetUID) HostCookiesAllowed(ctx context.Context) (bool, error) {
  1650  	if g.errorMalformed {
  1651  		return g.allowHost, &gdpr.ErrorMalformedConsent{Consent: g.consent, Cause: errors.New("some error")}
  1652  	}
  1653  	if g.errorHost {
  1654  		return g.allowHost, errors.New("something went wrong")
  1655  	}
  1656  	return g.allowHost, nil
  1657  }
  1658  
  1659  func (g *fakePermsSetUID) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName) (bool, error) {
  1660  	return false, nil
  1661  }
  1662  
  1663  func (g *fakePermsSetUID) AuctionActivitiesAllowed(ctx context.Context, bidderCoreName openrtb_ext.BidderName, bidder openrtb_ext.BidderName) (permissions gdpr.AuctionPermissions, err error) {
  1664  	return gdpr.AuctionPermissions{
  1665  		AllowBidRequest: g.personalInfoAllowed,
  1666  		PassGeo:         g.personalInfoAllowed,
  1667  		PassID:          g.personalInfoAllowed,
  1668  	}, nil
  1669  }
  1670  
  1671  type fakeSyncer struct {
  1672  	key             string
  1673  	defaultSyncType usersync.SyncType
  1674  }
  1675  
  1676  func (s fakeSyncer) Key() string {
  1677  	return s.key
  1678  }
  1679  
  1680  func (s fakeSyncer) DefaultSyncType() usersync.SyncType {
  1681  	return s.defaultSyncType
  1682  }
  1683  
  1684  func (s fakeSyncer) SupportsType(syncTypes []usersync.SyncType) bool {
  1685  	return true
  1686  }
  1687  
  1688  func (s fakeSyncer) GetSync(syncTypes []usersync.SyncType, privacyMacros macros.UserSyncPrivacy) (usersync.Sync, error) {
  1689  	return usersync.Sync{}, nil
  1690  }
  1691  
  1692  func ToHTTPCookie(cookie *usersync.Cookie) (*http.Cookie, error) {
  1693  	encoder := usersync.Base64Encoder{}
  1694  	encodedCookie, err := encoder.Encode(cookie)
  1695  	if err != nil {
  1696  		return nil, nil
  1697  	}
  1698  
  1699  	return &http.Cookie{
  1700  		Name:    uidCookieName,
  1701  		Value:   encodedCookie,
  1702  		Expires: time.Now().Add((90 * 24 * time.Hour)),
  1703  		Path:    "/",
  1704  	}, nil
  1705  }
  1706  
  1707  func getUIDFromHeader(setCookieHeader string) string {
  1708  	cookies := strings.Split(setCookieHeader, ";")
  1709  	for _, cookie := range cookies {
  1710  		trimmedCookie := strings.TrimSpace(cookie)
  1711  		if strings.HasPrefix(trimmedCookie, "uids=") {
  1712  			parts := strings.SplitN(trimmedCookie, "=", 2)
  1713  			if len(parts) == 2 {
  1714  				return parts[1]
  1715  			}
  1716  		}
  1717  	}
  1718  	return ""
  1719  }