Skip to content

Commit

Permalink
Pass syncerByBidder, add new test
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexBVolcy committed Aug 29, 2023
1 parent 8440f54 commit 2e84e37
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 30 deletions.
2 changes: 1 addition & 1 deletion endpoints/setuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use
setSiteCookie := siteCookieCheck(r.UserAgent())

// Priority Ejector Set Up
priorityEjector := &usersync.PriorityBidderEjector{PriorityGroups: cfg.UserSync.PriorityGroups, TieEjector: &usersync.OldestEjector{}}
priorityEjector := &usersync.PriorityBidderEjector{PriorityGroups: cfg.UserSync.PriorityGroups, TieEjector: &usersync.OldestEjector{}, SyncersByBidder: syncersByBidder}
priorityEjector.IsSyncerPriority = isSyncerPriority(bidderName, cfg.UserSync.PriorityGroups)

// Write Cookie
Expand Down
164 changes: 145 additions & 19 deletions endpoints/setuid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/http/httptest"
"net/url"
"regexp"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -306,23 +307,14 @@ func TestSetUIDEndpoint(t *testing.T) {
expectedHeaders: map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
description: "Set uid for valid bidder with valid account provided with invalid user sync activity",
},
{
uri: "/setuid?bidder=nonPriorityBidder&uid=123",
syncersBidderNameToKey: map[string]string{"nonPriorityBidder": "nonPrioritySyncer"},
existingSyncs: nil,
gdprAllowsHostCookies: true,
expectedSyncs: nil,
expectedStatusCode: http.StatusBadRequest,
description: "Syncer is not a priority",
},
}

analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{})
metrics := &metricsConf.NilMetricsEngine{}

for _, test := range testCases {
response := doRequest(makeRequest(test.uri, test.existingSyncs), analytics, metrics,
test.syncersBidderNameToKey, test.gdprAllowsHostCookies, test.gdprReturnsError, test.gdprMalformed, false)
test.syncersBidderNameToKey, test.gdprAllowsHostCookies, test.gdprReturnsError, test.gdprMalformed, false, 0, nil)
assert.Equal(t, test.expectedStatusCode, response.Code, "Test Case: %s. /setuid returned unexpected error code", test.description)

if test.expectedSyncs != nil {
Expand All @@ -349,6 +341,126 @@ func TestSetUIDEndpoint(t *testing.T) {
}
}

func TestSetUIDPriorityEjection(t *testing.T) {
decoder := usersync.Base64Decoder{}
analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{})
syncersByBidder := map[string]string{
"pubmatic": "pubmatic",
"syncer1": "syncer1",
"syncer2": "syncer2",
"syncer3": "syncer3",
"syncer4": "syncer4",
"mismatchedBidderName": "syncer5",
"syncerToEject": "syncerToEject",
}

testCases := []struct {
description string
uri string
givenExistingSyncs []string
givenPriorityGroups [][]string
givenMaxCookieSize int
expectedStatusCode int
expectedSyncer string
expectedUID string
expectedNumOfRemainingElements int
}{
{
description: "Cookie empty, expect bidder to be synced, no ejection",
uri: "/setuid?bidder=pubmatic&uid=123",
givenPriorityGroups: [][]string{},
givenMaxCookieSize: 500,
expectedSyncer: "pubmatic",
expectedUID: "123",
expectedNumOfRemainingElements: 1,
expectedStatusCode: http.StatusOK,
},
{
description: "Cookie full, no priority groups, one ejection",
uri: "/setuid?bidder=pubmatic&uid=123",
givenExistingSyncs: []string{"syncer1", "syncer2", "syncer3", "syncer4"},
givenPriorityGroups: [][]string{},
givenMaxCookieSize: 500,
expectedUID: "123",
expectedSyncer: "pubmatic",
expectedNumOfRemainingElements: 4,
expectedStatusCode: http.StatusOK,
},
{
description: "Cookie full, eject lowest priority element",
uri: "/setuid?bidder=pubmatic&uid=123",
givenExistingSyncs: []string{"syncer2", "syncer3", "syncer4", "syncerToEject"},
givenPriorityGroups: [][]string{{"pubmatic", "syncer2", "syncer3", "syncer4"}, {"syncerToEject"}},
givenMaxCookieSize: 500,
expectedUID: "123",
expectedSyncer: "pubmatic",
expectedNumOfRemainingElements: 4,
expectedStatusCode: http.StatusOK,
},
{
description: "Cookie full, all elements same priority, one ejection",
uri: "/setuid?bidder=pubmatic&uid=123",
givenExistingSyncs: []string{"syncer1", "syncer2", "syncer3", "syncer5"},
givenPriorityGroups: [][]string{{"pubmatic", "syncer1", "syncer2", "syncer3", "mismatchedBidderName"}},
givenMaxCookieSize: 500,
expectedUID: "123",
expectedSyncer: "pubmatic",
expectedNumOfRemainingElements: 4,
expectedStatusCode: http.StatusOK,
},
{
description: "There are only priority elements left, but the bidder being synced isn't one",
uri: "/setuid?bidder=pubmatic&uid=123",
givenExistingSyncs: []string{"syncer1", "syncer2", "syncer3", "syncer4"},
givenPriorityGroups: [][]string{{"syncer1", "syncer2", "syncer3", "syncer4"}},
givenMaxCookieSize: 500,
expectedStatusCode: http.StatusBadRequest,
},
}
for _, test := range testCases {
request := httptest.NewRequest("GET", test.uri, nil)

// Cookie Set Up
cookie := usersync.NewCookie()
for _, key := range test.givenExistingSyncs {
cookie.Sync(key, "111")
}
httpCookie, err := ToHTTPCookie(cookie)
assert.NoError(t, err)
request.AddCookie(httpCookie)

// Make Request to /setuid
response := doRequest(request, analytics, &metricsConf.NilMetricsEngine{}, syncersByBidder, true, false, false, false, test.givenMaxCookieSize, test.givenPriorityGroups)

// Get Cookie From Header
var cookieHeader string
for k, v := range response.Result().Header {
if k == "Set-Cookie" {
cookieHeader = v[0]
}
}
encodedCookieValue := getUIDFromHeader(cookieHeader)

// Check That Bidder On Request was Synced, it's UID matches, and that the right number of elements are present after ejection
decodedCookie := decoder.Decode(encodedCookieValue)
decodedCookieUIDs := decodedCookie.GetUIDs()
if test.expectedStatusCode == http.StatusOK {
uid, ok := decodedCookieUIDs[test.expectedSyncer]
assert.Equal(t, true, ok, test.description)
assert.Equal(t, test.expectedUID, uid, test.description)
assert.Equal(t, test.expectedNumOfRemainingElements, len(decodedCookieUIDs), test.description)

// Specific test case handling where we eject the lowest priority element
if len(test.givenPriorityGroups) == 2 {
syncer := test.givenPriorityGroups[len(test.givenPriorityGroups)-1][0]
_, ok := decodedCookieUIDs[syncer]
assert.Equal(t, false, ok, test.description)
}
}
assert.Equal(t, test.expectedStatusCode, response.Result().StatusCode, test.description)
}
}

func TestParseSignalFromGPPSID(t *testing.T) {
type testOutput struct {
signal gdpr.Signal
Expand Down Expand Up @@ -1204,7 +1316,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
for _, v := range test.cookies {
addCookie(req, v)
}
response := doRequest(req, analyticsEngine, metricsEngine, test.syncersBidderNameToKey, test.gdprAllowsHostCookies, false, false, test.cfgAccountRequired)
response := doRequest(req, analyticsEngine, metricsEngine, test.syncersBidderNameToKey, test.gdprAllowsHostCookies, false, false, test.cfgAccountRequired, 0, nil)

assert.Equal(t, test.expectedResponseCode, response.Code, test.description)
analyticsEngine.AssertExpectations(t)
Expand All @@ -1220,7 +1332,7 @@ func TestOptedOut(t *testing.T) {
syncersBidderNameToKey := map[string]string{"pubmatic": "pubmatic"}
analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{})
metrics := &metricsConf.NilMetricsEngine{}
response := doRequest(request, analytics, metrics, syncersBidderNameToKey, true, false, false, false)
response := doRequest(request, analytics, metrics, syncersBidderNameToKey, true, false, false, false, 0, nil)

assert.Equal(t, http.StatusUnauthorized, response.Code)
}
Expand Down Expand Up @@ -1406,17 +1518,18 @@ func makeRequest(uri string, existingSyncs map[string]string) *http.Request {
return request
}

func doRequest(req *http.Request, analytics analytics.PBSAnalyticsModule, metrics metrics.MetricsEngine, syncersBidderNameToKey map[string]string, gdprAllowsHostCookies, gdprReturnsError, gdprReturnsMalformedError, cfgAccountRequired bool) *httptest.ResponseRecorder {
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 {
cfg := config.Configuration{
AccountRequired: cfgAccountRequired,
BlacklistedAcctMap: map[string]bool{
"blocked_acct": true,
},
AccountDefaults: config.Account{},
UserSync: config.UserSync{
PriorityGroups: [][]string{
{},
},
PriorityGroups: priorityGroups,
},
HostCookie: config.HostCookie{
MaxCookieSizeBytes: maxCookieSize,
},
}
cfg.MarshalAccountDefaults()
Expand All @@ -1440,10 +1553,9 @@ func doRequest(req *http.Request, analytics analytics.PBSAnalyticsModule, metric
syncersByBidder := make(map[string]usersync.Syncer)
for bidderName, syncerKey := range syncersBidderNameToKey {
syncersByBidder[bidderName] = fakeSyncer{key: syncerKey, defaultSyncType: usersync.SyncTypeIFrame}
if bidderName != "nonPriorityBidder" {
if priorityGroups == nil {
cfg.UserSync.PriorityGroups = [][]string{{}}
cfg.UserSync.PriorityGroups[0] = append(cfg.UserSync.PriorityGroups[0], bidderName)
} else {
cfg.HostCookie.MaxCookieSizeBytes = 100
}
}

Expand Down Expand Up @@ -1563,3 +1675,17 @@ func ToHTTPCookie(cookie *usersync.Cookie) (*http.Cookie, error) {
Path: "/",
}, nil
}

func getUIDFromHeader(setCookieHeader string) string {
cookies := strings.Split(setCookieHeader, ";")
for _, cookie := range cookies {
trimmedCookie := strings.TrimSpace(cookie)
if strings.HasPrefix(trimmedCookie, "uids=") {
parts := strings.SplitN(trimmedCookie, "=", 2)
if len(parts) == 2 {
return parts[1]
}
}
}
return ""
}
2 changes: 1 addition & 1 deletion usersync/cookie_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ func TestPrepareCookieForWrite(t *testing.T) {
{"4", "5", "6"},
{"7"},
},
syncersByBidder: map[string]Syncer{
SyncersByBidder: map[string]Syncer{
"mainUID": fakeSyncer{
key: "mainUID",
},
Expand Down
6 changes: 3 additions & 3 deletions usersync/ejector.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type OldestEjector struct{}

type PriorityBidderEjector struct {
PriorityGroups [][]string
syncersByBidder map[string]Syncer
SyncersByBidder map[string]Syncer
IsSyncerPriority bool
TieEjector Ejector
}
Expand All @@ -34,7 +34,7 @@ func (o *OldestEjector) Choose(uids map[string]UIDEntry) (string, error) {

// Choose method for priority ejector will return the oldest lowest priority element
func (p *PriorityBidderEjector) Choose(uids map[string]UIDEntry) (string, error) {
nonPriorityUids := getNonPriorityUids(uids, p.PriorityGroups, p.syncersByBidder)
nonPriorityUids := getNonPriorityUids(uids, p.PriorityGroups, p.SyncersByBidder)
if err := p.checkSyncerPriority(nonPriorityUids); err != nil {
return "", err
}
Expand All @@ -50,7 +50,7 @@ func (p *PriorityBidderEjector) Choose(uids map[string]UIDEntry) (string, error)
return uidToDelete, nil
}

lowestPriorityUids := getPriorityUids(lowestPriorityGroup, uids, p.syncersByBidder)
lowestPriorityUids := getPriorityUids(lowestPriorityGroup, uids, p.SyncersByBidder)
uidToDelete, err := p.TieEjector.Choose(lowestPriorityUids)
if err != nil {
return "", err
Expand Down
12 changes: 6 additions & 6 deletions usersync/ejector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestPriorityEjector(t *testing.T) {
{"highestPriorityBidder"},
{"lowestPriority"},
},
syncersByBidder: map[string]Syncer{
SyncersByBidder: map[string]Syncer{
"highestPriorityBidder": fakeSyncer{
key: "highestPrioritySyncer",
},
Expand Down Expand Up @@ -62,7 +62,7 @@ func TestPriorityEjector(t *testing.T) {
PriorityGroups: [][]string{
{"newerButSamePriority", "olderButSamePriority"},
},
syncersByBidder: map[string]Syncer{
SyncersByBidder: map[string]Syncer{
"newerButSamePriority": fakeSyncer{
key: "newerButSamePriority",
},
Expand Down Expand Up @@ -100,7 +100,7 @@ func TestPriorityEjector(t *testing.T) {
{"higherPriority"},
{"lowestPriority"},
},
syncersByBidder: map[string]Syncer{
SyncersByBidder: map[string]Syncer{
"higherPriority": fakeSyncer{
key: "higherPriority",
},
Expand Down Expand Up @@ -132,7 +132,7 @@ func TestPriorityEjector(t *testing.T) {
},
},
givenEjector: &PriorityBidderEjector{
syncersByBidder: map[string]Syncer{
SyncersByBidder: map[string]Syncer{
"oldestNonPriority": fakeSyncer{
key: "oldestNonPriority",
},
Expand All @@ -157,7 +157,7 @@ func TestPriorityEjector(t *testing.T) {
PriorityGroups: [][]string{
{"onlyPriorityElement"},
},
syncersByBidder: map[string]Syncer{
SyncersByBidder: map[string]Syncer{
"onlyPriorityElement": fakeSyncer{
key: "onlyPriorityElement",
},
Expand All @@ -182,7 +182,7 @@ func TestPriorityEjector(t *testing.T) {
PriorityGroups: [][]string{
{"onlyPriorityElement"},
},
syncersByBidder: map[string]Syncer{
SyncersByBidder: map[string]Syncer{
"onlyPriorityElement": fakeSyncer{
key: "onlyPriorityElement",
},
Expand Down

0 comments on commit 2e84e37

Please sign in to comment.