Skip to content
This repository has been archived by the owner on Dec 22, 2022. It is now read-only.

Commit

Permalink
Adding /setuid tests (prebid#507)
Browse files Browse the repository at this point in the history
* Moved the usersyncers into their own package. Pulled the cookie parsing code into the usersync package.

* Fixed the tests, moving them to the appropriate area.

* Updated markdown docs.

* Moved the usersync endpoint alongside the new code, and added tests.

* Removed the unused getRawQueryMap method.

* Made test code use helper function.
  • Loading branch information
dbemiller authored and cirla committed May 17, 2018
1 parent d380980 commit 6f536d4
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 77 deletions.
16 changes: 4 additions & 12 deletions endpoints/cookie_sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ import (

func TestCookieSyncNoCookies(t *testing.T) {
rr := doPost(`{"bidders":["appnexus", "audienceNetwork", "random"]}`, nil)
if rr.Code != http.StatusOK {
t.Fatalf("Wrong status: %d (%s)", rr.Code, rr.Body)
}
assertIntsMatch(t, http.StatusOK, rr.Code)
assertSyncsExist(t, rr.Body.Bytes(), "appnexus", "audienceNetwork")
assertStatus(t, rr.Body.Bytes(), "no_cookie")
}
Expand All @@ -32,29 +30,23 @@ func TestCookieSyncHasCookies(t *testing.T) {
"adnxs": "1234",
"audienceNetwork": "2345",
})
if rr.Code != http.StatusOK {
t.Fatalf("Wrong status: %d", rr.Code)
}
assertIntsMatch(t, http.StatusOK, rr.Code)
assertSyncsExist(t, rr.Body.Bytes())
assertStatus(t, rr.Body.Bytes(), "ok")
}

// Make sure that an empty bidders array returns no syncs
func TestCookieSyncEmptyBidders(t *testing.T) {
rr := doPost(`{"bidders": []}`, nil)
if rr.Code != http.StatusOK {
t.Fatalf("Wrong status: %d (%s)", rr.Code, rr.Body)
}
assertIntsMatch(t, http.StatusOK, rr.Code)
assertSyncsExist(t, rr.Body.Bytes())
assertStatus(t, rr.Body.Bytes(), "no_cookie")
}

// Make sure that all syncs are returned if "bidders" isn't a key
func TestCookieSyncNoBidders(t *testing.T) {
rr := doPost("{}", nil)
if rr.Code != http.StatusOK {
t.Fatalf("Wrong status: %d (%s)", rr.Code, rr.Body)
}
assertIntsMatch(t, http.StatusOK, rr.Code)
assertSyncsExist(t, rr.Body.Bytes(), "appnexus", "audienceNetwork", "lifestreet", "pubmatic")
assertStatus(t, rr.Body.Bytes(), "no_cookie")
}
Expand Down
79 changes: 79 additions & 0 deletions endpoints/setuid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package endpoints

import (
"net/http"
"strings"
"time"

"github.com/julienschmidt/httprouter"
"github.com/prebid/prebid-server/analytics"
"github.com/prebid/prebid-server/config"
"github.com/prebid/prebid-server/openrtb_ext"
"github.com/prebid/prebid-server/pbsmetrics"
"github.com/prebid/prebid-server/usersync"
)

func NewSetUIDEndpoint(cfg config.HostCookie, pbsanalytics analytics.PBSAnalyticsModule, metrics pbsmetrics.MetricsEngine) httprouter.Handle {
cookieTTL := time.Duration(cfg.TTL) * 24 * time.Hour
return httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
so := analytics.SetUIDObject{
Status: http.StatusOK,
Errors: make([]error, 0),
}

defer pbsanalytics.LogSetUIDObject(&so)

pc := usersync.ParsePBSCookieFromRequest(r, &cfg.OptOutCookie)
if !pc.AllowSyncs() {
w.WriteHeader(http.StatusUnauthorized)
metrics.RecordUserIDSet(pbsmetrics.UserLabels{Action: pbsmetrics.RequestActionOptOut})
so.Status = http.StatusUnauthorized
return
}

query := getRawQueryMap(r.URL.RawQuery)
bidder := query["bidder"]
if bidder == "" {
w.WriteHeader(http.StatusBadRequest)
metrics.RecordUserIDSet(pbsmetrics.UserLabels{Action: pbsmetrics.RequestActionErr})
so.Status = http.StatusBadRequest
return
}
so.Bidder = bidder

uid := query["uid"]
so.UID = uid

var err error = nil
if uid == "" {
pc.Unsync(bidder)
} else {
err = pc.TrySync(bidder, uid)
}

if err == nil {
labels := pbsmetrics.UserLabels{
Action: pbsmetrics.RequestActionSet,
Bidder: openrtb_ext.BidderName(bidder),
}
metrics.RecordUserIDSet(labels)
so.Success = true
}

pc.SetCookieOnResponse(w, cfg.Domain, cookieTTL)
})
}

func getRawQueryMap(query string) map[string]string {
m := make(map[string]string)
for _, kv := range strings.SplitN(query, "&", -1) {
if len(kv) == 0 {
continue
}
pair := strings.SplitN(kv, "=", 2)
if len(pair) == 2 {
m[pair[0]] = pair[1]
}
}
return m
}
125 changes: 125 additions & 0 deletions endpoints/setuid_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package endpoints

import (
"net/http"
"net/http/httptest"
"regexp"
"testing"
"time"

"github.com/prebid/prebid-server/usersync"

"github.com/prebid/prebid-server/openrtb_ext"

"github.com/prebid/prebid-server/pbsmetrics"

analyticsConf "github.com/prebid/prebid-server/analytics/config"
"github.com/prebid/prebid-server/config"
)

func TestNormalSet(t *testing.T) {
response := doRequest(makeRequest("/setuid?bidder=pubmatic&uid=123", nil))
assertIntsMatch(t, http.StatusOK, response.Code)

cookie := parseCookieString(t, response)
assertIntsMatch(t, 1, cookie.LiveSyncCount())
assertBoolsMatch(t, true, cookie.HasLiveSync("pubmatic"))
assertSyncValue(t, cookie, "pubmatic", "123")
}

func TestUnset(t *testing.T) {
response := doRequest(makeRequest("/setuid?bidder=pubmatic", map[string]string{"pubmatic": "1234"}))
assertIntsMatch(t, http.StatusOK, response.Code)

cookie := parseCookieString(t, response)
assertIntsMatch(t, 0, cookie.LiveSyncCount())
}

func TestMergeSet(t *testing.T) {
response := doRequest(makeRequest("/setuid?bidder=pubmatic&uid=123", map[string]string{"rubicon": "def"}))
assertIntsMatch(t, http.StatusOK, response.Code)

cookie := parseCookieString(t, response)
assertIntsMatch(t, 2, cookie.LiveSyncCount())
assertBoolsMatch(t, true, cookie.HasLiveSync("pubmatic"))
assertBoolsMatch(t, true, cookie.HasLiveSync("rubicon"))
assertSyncValue(t, cookie, "pubmatic", "123")
assertSyncValue(t, cookie, "rubicon", "def")
}

func TestNoBidder(t *testing.T) {
response := doRequest(makeRequest("/setuid?uid=123", nil))
assertIntsMatch(t, http.StatusBadRequest, response.Code)
}

func TestOptedOut(t *testing.T) {
request := httptest.NewRequest("GET", "/setuid?bidder=pubmatic&uid=123", nil)
cookie := usersync.NewPBSCookie()
cookie.SetPreference(false)
addCookie(request, cookie)
response := doRequest(request)

assertIntsMatch(t, http.StatusUnauthorized, response.Code)
}

func makeRequest(uri string, existingSyncs map[string]string) *http.Request {
request := httptest.NewRequest("GET", uri, nil)
if len(existingSyncs) > 0 {
pbsCookie := usersync.NewPBSCookie()
for family, value := range existingSyncs {
pbsCookie.TrySync(family, value)
}
addCookie(request, pbsCookie)
}
return request
}

func doRequest(req *http.Request) *httptest.ResponseRecorder {
cfg := config.Configuration{}
endpoint := NewSetUIDEndpoint(cfg.HostCookie, analyticsConf.NewPBSAnalytics(&cfg.Analytics), pbsmetrics.NewMetricsEngine(&cfg, openrtb_ext.BidderList()))
response := httptest.NewRecorder()
endpoint(response, req, nil)
return response
}

func addCookie(req *http.Request, cookie *usersync.PBSCookie) {
req.AddCookie(cookie.ToHTTPCookie(time.Duration(1) * time.Hour))
}

func parseCookieString(t *testing.T, response *httptest.ResponseRecorder) *usersync.PBSCookie {
cookieString := response.Header().Get("Set-Cookie")
parser := regexp.MustCompile("uids=(.*?);")
res := parser.FindStringSubmatch(cookieString)
assertIntsMatch(t, 2, len(res))
httpCookie := http.Cookie{
Name: "uids",
Value: res[1],
}
return usersync.ParsePBSCookie(&httpCookie)
}

func assertIntsMatch(t *testing.T, expected int, actual int) {
t.Helper()
if expected != actual {
t.Errorf("Expected %d, got %d", expected, actual)
}
}

func assertBoolsMatch(t *testing.T, expected bool, actual bool) {
t.Helper()
if expected != actual {
t.Errorf("Expected %t, got %t", expected, actual)
}
}

func assertStringsMatch(t *testing.T, expected string, actual string) {
t.Helper()
if expected != actual {
t.Errorf("Expected %s, got %s", expected, actual)
}
}

func assertSyncValue(t *testing.T, cookie *usersync.PBSCookie, family string, expectedValue string) {
got, _, _ := cookie.GetUID(family)
assertStringsMatch(t, expectedValue, got)
}
64 changes: 0 additions & 64 deletions pbs/usersync.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/julienschmidt/httprouter"
"github.com/prebid/prebid-server/analytics"
"github.com/prebid/prebid-server/config"
"github.com/prebid/prebid-server/openrtb_ext"
"github.com/prebid/prebid-server/pbsmetrics"
"github.com/prebid/prebid-server/ssl"
"github.com/prebid/prebid-server/usersync"
Expand Down Expand Up @@ -73,69 +72,6 @@ func (deps *UserSyncDeps) GetUIDs(w http.ResponseWriter, r *http.Request, _ http
return
}

func getRawQueryMap(query string) map[string]string {
m := make(map[string]string)
for _, kv := range strings.SplitN(query, "&", -1) {
if len(kv) == 0 {
continue
}
pair := strings.SplitN(kv, "=", 2)
if len(pair) == 2 {
m[pair[0]] = pair[1]
}
}
return m
}

func (deps *UserSyncDeps) SetUID(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {

so := analytics.SetUIDObject{
Status: http.StatusOK,
Errors: make([]error, 0),
}

defer deps.PBSAnalytics.LogSetUIDObject(&so)

pc := usersync.ParsePBSCookieFromRequest(r, &deps.HostCookieSettings.OptOutCookie)
if !pc.AllowSyncs() {
w.WriteHeader(http.StatusUnauthorized)
deps.MetricsEngine.RecordUserIDSet(pbsmetrics.UserLabels{Action: pbsmetrics.RequestActionOptOut})
so.Status = http.StatusUnauthorized
return
}

query := getRawQueryMap(r.URL.RawQuery)
bidder := query["bidder"]
if bidder == "" {
w.WriteHeader(http.StatusBadRequest)
deps.MetricsEngine.RecordUserIDSet(pbsmetrics.UserLabels{Action: pbsmetrics.RequestActionErr})
so.Status = http.StatusBadRequest
return
}
so.Bidder = bidder

uid := query["uid"]
so.UID = uid

var err error = nil
if uid == "" {
pc.Unsync(bidder)
} else {
err = pc.TrySync(bidder, uid)
}

if err == nil {
labels := pbsmetrics.UserLabels{
Action: pbsmetrics.RequestActionSet,
Bidder: openrtb_ext.BidderName(bidder),
}
deps.MetricsEngine.RecordUserIDSet(labels)
so.Success = true
}

pc.SetCookieOnResponse(w, deps.HostCookieSettings.Domain, deps.HostCookieSettings.TTL)
}

// Struct for parsing json in google's response
type googleResponse struct {
Success bool
Expand Down
2 changes: 1 addition & 1 deletion pbs_light.go
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,7 @@ func serve(cfg *config.Configuration) error {
}

router.GET("/getuids", userSyncDeps.GetUIDs)
router.GET("/setuid", userSyncDeps.SetUID)
router.GET("/setuid", endpoints.NewSetUIDEndpoint(cfg.HostCookie, pbsAnalytics, metricsEngine))
router.POST("/optout", userSyncDeps.OptOut)
router.GET("/optout", userSyncDeps.OptOut)

Expand Down

0 comments on commit 6f536d4

Please sign in to comment.