diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index a3d0f243548..1eae515403a 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -2,15 +2,21 @@ name: Validate on: push: - branches: [master] + branches: + - master + - main + - ci pull_request: - branches: [master] + branches: + - master + - main + - ci jobs: validate: strategy: matrix: - go-version: [1.19.x, 1.20.x] + go-version: [1.20.x] os: [ubuntu-20.04] runs-on: ${{ matrix.os }} diff --git a/analytics/pubmatic/helper.go b/analytics/pubmatic/helper.go index 01a94bea9e4..c025b4436c0 100644 --- a/analytics/pubmatic/helper.go +++ b/analytics/pubmatic/helper.go @@ -2,12 +2,15 @@ package pubmatic import ( "encoding/json" + "errors" "net/http" "net/url" "strconv" "time" "github.com/golang/glog" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/analytics" "github.com/prebid/prebid-server/v2/analytics/pubmatic/mhttp" "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) @@ -69,6 +72,34 @@ func send(rCtx *models.RequestCtx, url string, headers http.Header, mhc mhttp.Mu rCtx.MetricsEngine.RecordPublisherWrapperLoggerFailure(rCtx.PubIDStr, rCtx.ProfileIDStr, "") return } - rCtx.MetricsEngine.RecordSendLoggerDataTime(rCtx.Endpoint, rCtx.ProfileIDStr, time.Since(startTime)) + rCtx.MetricsEngine.RecordSendLoggerDataTime(time.Since(startTime)) // TODO: this will increment HB specific metric (ow_pbs_sshb_*), verify labels } + +// RestoreBidResponse restores the original bid response for AppLovinMax from the signal data +func RestoreBidResponse(rctx *models.RequestCtx, ao analytics.AuctionObject) error { + if rctx.Endpoint != models.EndpointAppLovinMax { + return nil + } + + if ao.Response.NBR != nil { + return nil + } + + signalData := map[string]string{} + if err := json.Unmarshal(ao.Response.SeatBid[0].Bid[0].Ext, &signalData); err != nil { + return err + } + + if val, ok := signalData[models.SignalData]; !ok || val == "" { + return errors.New("signal data not found in the response") + } + + orignalResponse := &openrtb2.BidResponse{} + if err := json.Unmarshal([]byte(signalData[models.SignalData]), orignalResponse); err != nil { + return err + } + + *ao.Response = *orignalResponse + return nil +} diff --git a/analytics/pubmatic/helper_test.go b/analytics/pubmatic/helper_test.go index 6d5e69a45ce..0e43b15fa03 100644 --- a/analytics/pubmatic/helper_test.go +++ b/analytics/pubmatic/helper_test.go @@ -1,15 +1,20 @@ package pubmatic import ( + "encoding/json" "net/http" "net/url" "testing" "github.com/golang/mock/gomock" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/analytics" "github.com/prebid/prebid-server/v2/analytics/pubmatic/mhttp" mock_mhttp "github.com/prebid/prebid-server/v2/analytics/pubmatic/mhttp/mock" mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -176,7 +181,7 @@ func TestSendMethod(t *testing.T) { }, getMetricsEngine: func() *mock_metrics.MockMetricsEngine { mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) - mockEngine.EXPECT().RecordSendLoggerDataTime(models.EndpointV25, "1", gomock.Any()) + mockEngine.EXPECT().RecordSendLoggerDataTime(gomock.Any()) return mockEngine }, getMockMultiHttpContext: func() *mock_mhttp.MockMultiHttpContextInterface { @@ -220,3 +225,223 @@ func TestSendMethod(t *testing.T) { }) } } + +func TestRestoreBidResponse(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rctx *models.RequestCtx + } + tests := []struct { + name string + args args + want *openrtb2.BidResponse + wantErr string + }{ + { + name: "Endpoint is not AppLovinMax", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + ID: "test-case-1", + }, + }, + rctx: &models.RequestCtx{ + Endpoint: models.EndpointV25, + }, + }, + want: &openrtb2.BidResponse{ + ID: "test-case-1", + }, + }, + { + name: "NBR is not nil", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + ID: "test-case-1", + NBR: ptrutil.ToPtr(nbr.InvalidProfileConfiguration), + }, + }, + rctx: &models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + }, + }, + want: &openrtb2.BidResponse{ + ID: "test-case-1", + NBR: ptrutil.ToPtr(nbr.InvalidProfileConfiguration), + }, + }, + { + name: "failed to unmarshal BidResponse.SeatBid[0].Bid[0].Ext", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + ID: "test-case-1", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "123", + Ext: json.RawMessage(`{`), + }, + }, + }, + }, + }, + }, + rctx: &models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + }, + }, + want: &openrtb2.BidResponse{ + ID: "test-case-1", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "123", + Ext: json.RawMessage(`{`), + }, + }, + }, + }, + }, + wantErr: "unexpected end of JSON input", + }, + { + name: "signaldata not present in ext", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + ID: "test-case-1", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "123", + Ext: json.RawMessage(`{"signalData1": "{\"matchedimpression\":{\"appnexus\":50,\"pubmatic\":50}}\r\n"}`), + }, + }, + }, + }, + }, + }, + rctx: &models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + }, + }, + want: &openrtb2.BidResponse{ + ID: "test-case-1", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "123", + Ext: json.RawMessage(`{"signalData1": "{\"matchedimpression\":{\"appnexus\":50,\"pubmatic\":50}}\r\n"}`), + }, + }, + }, + }, + }, + wantErr: "signal data not found in the response", + }, + { + name: "failed to unmarshal signaldata", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + Ext: json.RawMessage(`{"signaldata": "{"id":123}"}"`), + }, + }, + }, + }, + }, + }, + rctx: &models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + }, + }, + want: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + Ext: json.RawMessage(`{"signaldata": "{"id":123}"}"`), + }, + }, + }, + }, + }, + wantErr: `invalid character 'i' after object key:value pair`, + }, + { + name: "valid AppLovinMax Response", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + Ext: json.RawMessage(`{"signaldata":"{\"id\":\"123\",\"seatbid\":[{\"bid\":[{\"id\":\"bid-id-1\",\"impid\":\"imp_1\",\"price\":0}],\"seat\":\"pubmatic\"}],\"bidid\":\"bid-id-1\",\"cur\":\"USD\",\"ext\":{\"matchedimpression\":{\"appnexus\":50,\"pubmatic\":50}}}\r\n"}`), + }, + }, + }, + }, + }, + }, + rctx: &models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + }, + }, + want: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := RestoreBidResponse(tt.args.rctx, tt.args.ao) + if err != nil { + assert.Equal(t, tt.wantErr, err.Error(), tt.name) + } + assert.Equal(t, tt.want, tt.args.ao.Response, tt.name) + }) + } +} diff --git a/analytics/pubmatic/logger.go b/analytics/pubmatic/logger.go index 6a15f5be3a5..67fb1562265 100644 --- a/analytics/pubmatic/logger.go +++ b/analytics/pubmatic/logger.go @@ -25,8 +25,8 @@ type bidWrapper struct { Nbr *openrtb3.NoBidReason } -// getUUID is a function variable which will return uuid -var getUUID = func() string { +// GetUUID is a function variable which will return uuid +var GetUUID = func() string { return uuid.NewV4().String() } @@ -103,7 +103,7 @@ func GetLogAuctionObjectAsURL(ao analytics.AuctionObject, rCtx *models.RequestCt } slots = append(slots, SlotRecord{ - SlotId: getUUID(), + SlotId: GetUUID(), SlotName: impCtx.SlotName, SlotSize: impCtx.IncomingSlots, Adunit: impCtx.AdUnitName, diff --git a/analytics/pubmatic/logger_test.go b/analytics/pubmatic/logger_test.go index 21bcae7d2d0..82328929f36 100644 --- a/analytics/pubmatic/logger_test.go +++ b/analytics/pubmatic/logger_test.go @@ -4011,13 +4011,13 @@ func TestGetLogAuctionObjectAsURLForFloorType(t *testing.T) { } func TestGetLogAuctionObjectAsURLForFloorDetailsAndCDS(t *testing.T) { cfg := ow.cfg - uuidFunc := getUUID + uuidFunc := GetUUID defer func() { ow.cfg = cfg - getUUID = uuidFunc + GetUUID = uuidFunc }() - getUUID = func() string { return "uuid" } + GetUUID = func() string { return "uuid" } ow.cfg.Endpoint = "http://10.172.141.11/wl" ow.cfg.PublicEndpoint = "http://t.pubmatic.com/wl" @@ -4204,13 +4204,13 @@ func TestGetLogAuctionObjectAsURLForFloorDetailsAndCDS(t *testing.T) { } func TestSlotRecordsInGetLogAuctionObjectAsURL(t *testing.T) { cfg := ow.cfg - uuidFunc := getUUID + uuidFunc := GetUUID defer func() { ow.cfg = cfg - getUUID = uuidFunc + GetUUID = uuidFunc }() - getUUID = func() string { + GetUUID = func() string { return "sid" } diff --git a/analytics/pubmatic/pubmatic.go b/analytics/pubmatic/pubmatic.go index 293003d1d2d..ef9cac22f9e 100644 --- a/analytics/pubmatic/pubmatic.go +++ b/analytics/pubmatic/pubmatic.go @@ -59,6 +59,11 @@ func (ow HTTPLogger) LogAuctionObject(ao *analytics.AuctionObject) { return } + err := RestoreBidResponse(rCtx, *ao) + if err != nil { + glog.Error("Failed to restore bid response for pub:[%d], profile:[%d], version:[%d], err:[%s].", rCtx.PubID, rCtx.ProfileID, rCtx.VersionID, err.Error()) + } + url, headers := GetLogAuctionObjectAsURL(*ao, rCtx, false, false) if url == "" { glog.Errorf("Failed to prepare the owlogger for pub:[%d], profile:[%d], version:[%d].", diff --git a/analytics/pubmatic/pubmatic_test.go b/analytics/pubmatic/pubmatic_test.go index 6bef274d2e4..186c684e8e6 100644 --- a/analytics/pubmatic/pubmatic_test.go +++ b/analytics/pubmatic/pubmatic_test.go @@ -1,8 +1,10 @@ package pubmatic import ( + "encoding/json" "testing" + "github.com/prebid/openrtb/v20/openrtb2" "github.com/prebid/prebid-server/v2/analytics" "github.com/prebid/prebid-server/v2/config" "github.com/prebid/prebid-server/v2/hooks/hookanalytics" @@ -35,8 +37,9 @@ func TestNewHTTPLogger(t *testing.T) { // TestLogAuctionObject just increases code coverage, it does not validate anything func TestLogAuctionObject(t *testing.T) { tests := []struct { - name string - ao *analytics.AuctionObject + name string + ao *analytics.AuctionObject + RestoredResponse *openrtb2.BidResponse }{ { name: "rctx is nil", @@ -72,6 +75,74 @@ func TestLogAuctionObject(t *testing.T) { }, }, }, + { + name: "AppLovinMax request . RestoreBidResponse for logger", + ao: &analytics.AuctionObject{ + HookExecutionOutcome: []hookexecution.StageOutcome{ + { + Groups: []hookexecution.GroupOutcome{ + { + InvocationResults: []hookexecution.HookOutcome{ + { + AnalyticsTags: hookanalytics.Analytics{ + Activities: []hookanalytics.Activity{ + { + Results: []hookanalytics.Result{ + { + Values: map[string]interface{}{ + "request-ctx": &models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + Debug: false, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + + Response: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + Ext: json.RawMessage(`{"signaldata":"{\"id\":\"123\",\"seatbid\":[{\"bid\":[{\"id\":\"bid-id-1\",\"impid\":\"imp_1\",\"price\":0}],\"seat\":\"pubmatic\"}],\"bidid\":\"bid-id-1\",\"cur\":\"USD\",\"ext\":{\"matchedimpression\":{\"appnexus\":50,\"pubmatic\":50}}}\r\n"}`), + }, + }, + }, + }, + }, + }, + RestoredResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + }, { name: "logger_disabled", ao: &analytics.AuctionObject{ @@ -107,5 +178,6 @@ func TestLogAuctionObject(t *testing.T) { } for _, tt := range tests { HTTPLogger{}.LogAuctionObject(tt.ao) + assert.Equal(t, tt.RestoredResponse, tt.ao.Response, tt.name) } } diff --git a/analytics/pubmatic/record.go b/analytics/pubmatic/record.go index a408e6a6775..4b98ed36d29 100644 --- a/analytics/pubmatic/record.go +++ b/analytics/pubmatic/record.go @@ -178,7 +178,7 @@ func (wlog *WloggerRecord) logIntegrationType(endpoint string) { switch endpoint { case models.EndpointAMP: wlog.IntegrationType = models.TypeAmp - case models.EndpointV25: + case models.EndpointV25, models.EndpointAppLovinMax: wlog.IntegrationType = models.TypeSDK case models.EndpointVAST: wlog.IntegrationType = models.TypeTag diff --git a/analytics/pubmatic/record_test.go b/analytics/pubmatic/record_test.go index 86d25d6e119..57f8d20f66c 100644 --- a/analytics/pubmatic/record_test.go +++ b/analytics/pubmatic/record_test.go @@ -21,6 +21,11 @@ func TestLogIntegrationType(t *testing.T) { endpoint: models.EndpointV25, integrationType: models.TypeSDK, }, + { + name: "applovinmax", + endpoint: models.EndpointAppLovinMax, + integrationType: models.TypeSDK, + }, { name: "amp", endpoint: models.EndpointAMP, diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index c94f3e50575..4e0b096dd81 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -344,7 +344,7 @@ func rejectAuctionRequest( // TODO merge this with success case stageOutcomes := hookExecutor.GetOutcomes() ao.HookExecutionOutcome = stageOutcomes - UpdateResponseExtOW(response, ao) + UpdateResponseExtOW(w, response, ao) ao.Response = response ao.Errors = append(ao.Errors, rejectErr) @@ -366,7 +366,7 @@ func sendAuctionResponse( if response != nil { stageOutcomes := hookExecutor.GetOutcomes() ao.HookExecutionOutcome = stageOutcomes - UpdateResponseExtOW(response, ao) + UpdateResponseExtOW(w, response, ao) ext, warns, err := hookexecution.EnrichExtBidResponse(response.Ext, stageOutcomes, request, account) if err != nil { diff --git a/endpoints/openrtb2/auction_ow.go b/endpoints/openrtb2/auction_ow.go index ce91e1ec5d3..48fe957387c 100644 --- a/endpoints/openrtb2/auction_ow.go +++ b/endpoints/openrtb2/auction_ow.go @@ -2,15 +2,18 @@ package openrtb2 import ( "encoding/json" + "net/http" "runtime/debug" "strconv" + "github.com/buger/jsonparser" "github.com/golang/glog" "github.com/prebid/openrtb/v20/openrtb2" "github.com/prebid/openrtb/v20/openrtb3" "github.com/prebid/prebid-server/v2/analytics" "github.com/prebid/prebid-server/v2/analytics/pubmatic" "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/v2/openrtb_ext" ) @@ -32,7 +35,7 @@ func recordRejectedBids(pubID string, seatNonBids []openrtb_ext.SeatNonBid, metr } } -func UpdateResponseExtOW(bidResponse *openrtb2.BidResponse, ao analytics.AuctionObject) { +func UpdateResponseExtOW(w http.ResponseWriter, bidResponse *openrtb2.BidResponse, ao analytics.AuctionObject) { defer func() { if r := recover(); r != nil { response, err := json.Marshal(bidResponse) @@ -53,89 +56,31 @@ func UpdateResponseExtOW(bidResponse *openrtb2.BidResponse, ao analytics.Auction return } - extBidResponse := openrtb_ext.ExtBidResponse{} - if len(bidResponse.Ext) != 0 { - if err := json.Unmarshal(bidResponse.Ext, &extBidResponse); err != nil { - return + //Send owlogger in response only in case of debug mode + if rCtx.Debug { + var orignalMaxBidResponse *openrtb2.BidResponse + if rCtx.Endpoint == models.EndpointAppLovinMax { + orignalMaxBidResponse = new(openrtb2.BidResponse) + *orignalMaxBidResponse = *bidResponse + pubmatic.RestoreBidResponse(rCtx, ao) } - } - - if rCtx.LogInfoFlag == 1 && !rCtx.LoggerDisabled { - extBidResponse.OwLogInfo.Logger, _ = pubmatic.GetLogAuctionObjectAsURL(ao, rCtx, true, true) - } - // TODO: uncomment after seatnonbid PR is merged https://github.com/prebid/prebid-server/v2/pull/2505 - // if seatNonBids := updateSeatNoBid(rCtx, ao); len(seatNonBids) != 0 { - // if extBidResponse.Prebid == nil { - // extBidResponse.Prebid = &openrtb_ext.ExtResponsePrebid{} - // } - // extBidResponse.Prebid.SeatNonBid = seatNonBids - // } - - if rCtx.Debug && !rCtx.LoggerDisabled { - extBidResponse.OwLogger, _ = pubmatic.GetLogAuctionObjectAsURL(ao, rCtx, false, true) + if !rCtx.LoggerDisabled { + owlogger, _ := pubmatic.GetLogAuctionObjectAsURL(ao, rCtx, false, true) + if rCtx.Endpoint == models.EndpointAppLovinMax { + *bidResponse = *orignalMaxBidResponse + } + if len(bidResponse.Ext) == 0 { + bidResponse.Ext = []byte("{}") + } + if updatedExt, err := jsonparser.Set([]byte(bidResponse.Ext), []byte(strconv.Quote(owlogger)), "owlogger"); err == nil { + bidResponse.Ext = updatedExt + } + } + } else if rCtx.Endpoint == models.EndpointAppLovinMax { + bidResponse.Ext = nil + if rCtx.AppLovinMax.Reject { + w.WriteHeader(http.StatusNoContent) + } } - - bidResponse.Ext, _ = json.Marshal(extBidResponse) } - -// TODO: uncomment after seatnonbid PR is merged https://github.com/prebid/prebid-server/v2/pull/2505 -// TODO: Move this to module once it gets []analytics.RejectedBid as param (submit it in vanilla) -// func updateSeatNoBid(rCtx *models.RequestCtx, ao analytics.AuctionObject) []openrtb_ext.SeatNonBid { -// seatNonBids := make([]openrtb_ext.SeatNonBid, 0, len(ao.RejectedBids)) - -// seatNoBids := make(map[string][]analytics.RejectedBid) -// for _, rejectedBid := range ao.RejectedBids { -// seatNoBids[rejectedBid.Seat] = append(seatNoBids[rejectedBid.Seat], rejectedBid) -// } - -// for seat, rejectedBids := range seatNoBids { -// extSeatNoBid := openrtb_ext.SeatNonBid{ -// Seat: seat, -// NonBids: make([]openrtb_ext.NonBid, 0, len(rejectedBids)), -// } - -// for _, rejectedBid := range rejectedBids { -// bid := *rejectedBid.Bid.Bid -// addClientConfig(rCtx, seat, &bid) -// extSeatNoBid.NonBids = append(extSeatNoBid.NonBids, openrtb_ext.NonBid{ -// ImpId: rejectedBid.Bid.Bid.ImpID, -// StatusCode: rejectedBid.RejectionReason, -// Ext: openrtb_ext.NonBidExt{ -// Prebid: openrtb_ext.ExtResponseNonBidPrebid{ -// Bid: openrtb_ext.Bid{ -// Bid: bid, -// }, -// }, -// }, -// }) -// } - -// seatNonBids = append(seatNonBids, extSeatNoBid) -// } - -// return seatNonBids -// } - -// func addClientConfig(rCtx *models.RequestCtx, seat string, bid *openrtb2.Bid) { -// if seatNoBidBySeat, ok := rCtx.NoSeatBids[bid.ImpID]; ok { -// if seatNoBids, ok := seatNoBidBySeat[seat]; ok { -// for _, seatNoBid := range seatNoBids { -// bidExt := models.BidExt{} -// if err := json.Unmarshal(seatNoBid.Ext, &bidExt); err != nil { -// continue -// } - -// inBidExt := models.BidExt{} -// if err := json.Unmarshal(bid.Ext, &inBidExt); err != nil { -// continue -// } - -// inBidExt.Banner = bidExt.Banner -// inBidExt.Video = bidExt.Video - -// bid.Ext, _ = json.Marshal(inBidExt) -// } -// } -// } -// } diff --git a/endpoints/openrtb2/auction_ow_test.go b/endpoints/openrtb2/auction_ow_test.go index 936c16969f1..228fb2f709c 100644 --- a/endpoints/openrtb2/auction_ow_test.go +++ b/endpoints/openrtb2/auction_ow_test.go @@ -3,19 +3,27 @@ package openrtb2 import ( "encoding/json" "fmt" - + "net/http" + "net/http/httptest" "testing" + "github.com/prebid/prebid-server/v2/analytics/pubmatic" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/analytics" analyticsBuild "github.com/prebid/prebid-server/v2/analytics/build" "github.com/prebid/prebid-server/v2/config" "github.com/prebid/prebid-server/v2/errortypes" "github.com/prebid/prebid-server/v2/exchange" "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" "github.com/prebid/prebid-server/v2/metrics" metricsConfig "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/prebid/prebid-server/v2/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -177,3 +185,662 @@ func TestRecordRejectedBids(t *testing.T) { me.AssertNumberOfCalls(t, "RecordRejectedBids", test.want.expectedCalls) } } + +func TestUpdateResponseExtOW(t *testing.T) { + uuidFunc := pubmatic.GetUUID + defer func() { + pubmatic.GetUUID = uuidFunc + }() + + pubmatic.GetUUID = func() string { return "uuid" } + type args struct { + w http.ResponseWriter + bidResponse *openrtb2.BidResponse + ao analytics.AuctionObject + } + tests := []struct { + name string + args args + want *openrtb2.BidResponse + RestoredResponse *openrtb2.BidResponse + rejectResponse bool + }{ + { + name: "empty bid response", + args: args{ + bidResponse: nil, + ao: analytics.AuctionObject{ + Response: nil, + }, + }, + want: nil, + RestoredResponse: nil, + }, + { + name: "rctx is nil", + args: args{ + bidResponse: &openrtb2.BidResponse{}, + ao: analytics.AuctionObject{ + HookExecutionOutcome: []hookexecution.StageOutcome{ + { + Groups: []hookexecution.GroupOutcome{ + { + InvocationResults: []hookexecution.HookOutcome{ + { + AnalyticsTags: hookanalytics.Analytics{ + Activities: []hookanalytics.Activity{ + { + Results: []hookanalytics.Result{ + { + Values: map[string]interface{}{ + "request-ctx": nil, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Response: &openrtb2.BidResponse{}, + }, + }, + want: &openrtb2.BidResponse{}, + RestoredResponse: &openrtb2.BidResponse{}, + }, + { + name: "debug is enabled and endpoint is other than applovinmax", + args: args{ + bidResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + ao: analytics.AuctionObject{ + HookExecutionOutcome: []hookexecution.StageOutcome{ + { + Groups: []hookexecution.GroupOutcome{ + { + InvocationResults: []hookexecution.HookOutcome{ + { + AnalyticsTags: hookanalytics.Analytics{ + Activities: []hookanalytics.Activity{ + { + Results: []hookanalytics.Result{ + { + Values: map[string]interface{}{ + "request-ctx": &models.RequestCtx{ + PubID: 5890, + Debug: true, + Endpoint: models.EndpointV25, + ImpBidCtx: map[string]models.ImpCtx{ + "imp_1": { + IncomingSlots: []string{"0x0v", "100x200"}, + IsRewardInventory: ptrutil.ToPtr(int8(1)), + SlotName: "imp_1_tagid_1", + AdUnitName: "tagid_1", + }, + "imp_2": { + AdUnitName: "tagid_2", + SlotName: "imp_2_tagid_2", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + { + ID: "imp_1", + TagID: "tagid_1", + }, + { + ID: "imp_2", + TagID: "tagid_2", + }, + }, + }, + }, + Response: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + }, + }, + want: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50},"owlogger":"?json=%7B%22pubid%22%3A5890%2C%22pid%22%3A%220%22%2C%22pdvid%22%3A%220%22%2C%22sl%22%3A1%2C%22s%22%3A%5B%7B%22sid%22%3A%22uuid%22%2C%22sn%22%3A%22imp_1_tagid_1%22%2C%22sz%22%3A%5B%220x0v%22%2C%22100x200%22%5D%2C%22au%22%3A%22tagid_1%22%2C%22ps%22%3A%5B%7B%22pn%22%3A%22pubmatic%22%2C%22bc%22%3A%22pubmatic%22%2C%22kgpv%22%3A%22%22%2C%22kgpsv%22%3A%22%22%2C%22psz%22%3A%220x0%22%2C%22af%22%3A%22%22%2C%22eg%22%3A0%2C%22en%22%3A0%2C%22l1%22%3A0%2C%22l2%22%3A0%2C%22t%22%3A0%2C%22wb%22%3A0%2C%22bidid%22%3A%22bid-id-1%22%2C%22origbidid%22%3A%22bid-id-1%22%2C%22di%22%3A%22-1%22%2C%22dc%22%3A%22%22%2C%22db%22%3A0%2C%22ss%22%3A1%2C%22mi%22%3A0%2C%22ocpm%22%3A0%2C%22ocry%22%3A%22USD%22%7D%5D%2C%22rwrd%22%3A1%7D%2C%7B%22sid%22%3A%22uuid%22%2C%22sn%22%3A%22imp_2_tagid_2%22%2C%22au%22%3A%22tagid_2%22%2C%22ps%22%3A%5B%5D%7D%5D%2C%22dvc%22%3A%7B%7D%2C%22ft%22%3A0%2C%22it%22%3A%22sdk%22%7D&pubid=5890"}`), + }, + RestoredResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + }, + { + name: "debug is enabled and endpoint is AppLovinMax", + args: args{ + bidResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + Ext: json.RawMessage(`{"signaldata":"{\"id\":\"123\",\"seatbid\":[{\"bid\":[{\"id\":\"bid-id-1\",\"impid\":\"imp_1\",\"price\":0}],\"seat\":\"pubmatic\"}],\"bidid\":\"bid-id-1\",\"cur\":\"USD\",\"ext\":{\"matchedimpression\":{\"appnexus\":50,\"pubmatic\":50}}}\r\n"}`), + }, + }, + }, + }, + }, + ao: analytics.AuctionObject{ + HookExecutionOutcome: []hookexecution.StageOutcome{ + { + Groups: []hookexecution.GroupOutcome{ + { + InvocationResults: []hookexecution.HookOutcome{ + { + AnalyticsTags: hookanalytics.Analytics{ + Activities: []hookanalytics.Activity{ + { + Results: []hookanalytics.Result{ + { + Values: map[string]interface{}{ + "request-ctx": &models.RequestCtx{ + PubID: 5890, + Debug: true, + Endpoint: models.EndpointAppLovinMax, + ImpBidCtx: map[string]models.ImpCtx{ + "imp_1": { + IncomingSlots: []string{"0x0v", "100x200"}, + IsRewardInventory: ptrutil.ToPtr(int8(1)), + SlotName: "imp_1_tagid_1", + AdUnitName: "tagid_1", + }, + "imp_2": { + AdUnitName: "tagid_2", + SlotName: "imp_2_tagid_2", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + { + ID: "imp_1", + TagID: "tagid_1", + }, + { + ID: "imp_2", + TagID: "tagid_2", + }, + }, + }, + }, + Response: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + Ext: json.RawMessage(`{"signaldata":"{\"id\":\"123\",\"seatbid\":[{\"bid\":[{\"id\":\"bid-id-1\",\"impid\":\"imp_1\",\"price\":0}],\"seat\":\"pubmatic\"}],\"bidid\":\"bid-id-1\",\"cur\":\"USD\",\"ext\":{\"matchedimpression\":{\"appnexus\":50,\"pubmatic\":50}}}\r\n"}`), + }, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + Ext: json.RawMessage(`{"signaldata":"{\"id\":\"123\",\"seatbid\":[{\"bid\":[{\"id\":\"bid-id-1\",\"impid\":\"imp_1\",\"price\":0}],\"seat\":\"pubmatic\"}],\"bidid\":\"bid-id-1\",\"cur\":\"USD\",\"ext\":{\"matchedimpression\":{\"appnexus\":50,\"pubmatic\":50}}}\r\n"}`), + }, + }, + }, + }, + Ext: json.RawMessage(`{"owlogger":"?json=%7B%22pubid%22%3A5890%2C%22pid%22%3A%220%22%2C%22pdvid%22%3A%220%22%2C%22sl%22%3A1%2C%22s%22%3A%5B%7B%22sid%22%3A%22uuid%22%2C%22sn%22%3A%22imp_1_tagid_1%22%2C%22sz%22%3A%5B%220x0v%22%2C%22100x200%22%5D%2C%22au%22%3A%22tagid_1%22%2C%22ps%22%3A%5B%7B%22pn%22%3A%22pubmatic%22%2C%22bc%22%3A%22pubmatic%22%2C%22kgpv%22%3A%22%22%2C%22kgpsv%22%3A%22%22%2C%22psz%22%3A%220x0%22%2C%22af%22%3A%22%22%2C%22eg%22%3A0%2C%22en%22%3A0%2C%22l1%22%3A0%2C%22l2%22%3A0%2C%22t%22%3A0%2C%22wb%22%3A0%2C%22bidid%22%3A%22bid-id-1%22%2C%22origbidid%22%3A%22bid-id-1%22%2C%22di%22%3A%22-1%22%2C%22dc%22%3A%22%22%2C%22db%22%3A0%2C%22ss%22%3A1%2C%22mi%22%3A0%2C%22ocpm%22%3A0%2C%22ocry%22%3A%22USD%22%7D%5D%2C%22rwrd%22%3A1%7D%2C%7B%22sid%22%3A%22uuid%22%2C%22sn%22%3A%22imp_2_tagid_2%22%2C%22au%22%3A%22tagid_2%22%2C%22ps%22%3A%5B%5D%7D%5D%2C%22dvc%22%3A%7B%7D%2C%22ft%22%3A0%2C%22it%22%3A%22sdk%22%7D&pubid=5890"}`), + }, + RestoredResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + }, + { + name: "debug is not enabled and request is other than AppLovinMax", + args: args{ + bidResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + ao: analytics.AuctionObject{ + HookExecutionOutcome: []hookexecution.StageOutcome{ + { + Groups: []hookexecution.GroupOutcome{ + { + InvocationResults: []hookexecution.HookOutcome{ + { + AnalyticsTags: hookanalytics.Analytics{ + Activities: []hookanalytics.Activity{ + { + Results: []hookanalytics.Result{ + { + Values: map[string]interface{}{ + "request-ctx": &models.RequestCtx{ + PubID: 5890, + Debug: false, + Endpoint: models.EndpintInappVideo, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Response: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + }, + }, + want: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + RestoredResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + }, + { + name: "debug is not enabled and endpoint is AppLovinMax but AppLovinMax reject is false", + args: args{ + bidResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + ao: analytics.AuctionObject{ + HookExecutionOutcome: []hookexecution.StageOutcome{ + { + Groups: []hookexecution.GroupOutcome{ + { + InvocationResults: []hookexecution.HookOutcome{ + { + AnalyticsTags: hookanalytics.Analytics{ + Activities: []hookanalytics.Activity{ + { + Results: []hookanalytics.Result{ + { + Values: map[string]interface{}{ + "request-ctx": &models.RequestCtx{ + PubID: 5890, + Debug: false, + Endpoint: models.EndpointAppLovinMax, + AppLovinMax: models.AppLovinMax{ + Reject: false, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Response: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + }, + }, + want: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: nil, + }, + RestoredResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + }, + { + name: "debug is not enabled and endpoint is AppLovinMax but AppLovinMax reject is true", + args: args{ + w: httptest.NewRecorder(), + bidResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + ao: analytics.AuctionObject{ + HookExecutionOutcome: []hookexecution.StageOutcome{ + { + Groups: []hookexecution.GroupOutcome{ + { + InvocationResults: []hookexecution.HookOutcome{ + { + AnalyticsTags: hookanalytics.Analytics{ + Activities: []hookanalytics.Activity{ + { + Results: []hookanalytics.Result{ + { + Values: map[string]interface{}{ + "request-ctx": &models.RequestCtx{ + PubID: 5890, + Debug: false, + Endpoint: models.EndpointAppLovinMax, + AppLovinMax: models.AppLovinMax{ + Reject: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Response: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + }, + }, + rejectResponse: true, + want: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + }, + RestoredResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "bid-id-1", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + Ext: json.RawMessage(`{"matchedimpression":{"appnexus":50,"pubmatic":50}}`), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + UpdateResponseExtOW(tt.args.w, tt.args.bidResponse, tt.args.ao) + assert.Equal(t, tt.want, tt.args.bidResponse, tt.name) + assert.Equal(t, tt.RestoredResponse, tt.args.ao.Response) + if tt.rejectResponse { + assert.Equal(t, http.StatusNoContent, tt.args.w.(*httptest.ResponseRecorder).Code, tt.name) + } + }) + } +} diff --git a/modules/pubmatic/openwrap/applovinmax.go b/modules/pubmatic/openwrap/applovinmax.go new file mode 100644 index 00000000000..4d90d385c45 --- /dev/null +++ b/modules/pubmatic/openwrap/applovinmax.go @@ -0,0 +1,287 @@ +package openwrap + +import ( + "encoding/json" + "strconv" + + "github.com/buger/jsonparser" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" +) + +func getSignalData(requestBody []byte) *openrtb2.BidRequest { + signal, err := jsonparser.GetString(requestBody, "user", "data", "[0]", "segment", "[0]", "signal") + if err != nil { + return nil + } + + signalData := &openrtb2.BidRequest{} + if err := json.Unmarshal([]byte(signal), signalData); err != nil { + return nil + } + return signalData +} + +func addSignalDataInRequest(signalData *openrtb2.BidRequest, maxRequest *openrtb2.BidRequest) { + updateRequestWrapper(signalData.Ext, maxRequest) + updateImpression(signalData.Imp, maxRequest.Imp) + updateDevice(signalData.Device, maxRequest) + updateApp(signalData.App, maxRequest) + updateRegs(signalData.Regs, maxRequest) + updateSource(signalData.Source, maxRequest) + updateUser(signalData.User, maxRequest) +} + +func updateImpression(signalImps []openrtb2.Imp, maxImps []openrtb2.Imp) { + if len(maxImps) == 0 || len(signalImps) == 0 { + return + } + + signalImp := signalImps[0] + if signalImp.DisplayManager != "" { + maxImps[0].DisplayManager = signalImp.DisplayManager + } + + if signalImp.DisplayManagerVer != "" { + maxImps[0].DisplayManagerVer = signalImp.DisplayManagerVer + } + + if signalImp.ClickBrowser != nil { + maxImps[0].ClickBrowser = signalImp.ClickBrowser + } + + if signalImp.Video != nil { + maxImps[0].Video = signalImp.Video + } + + if maxImps[0].Banner != nil { + if signalImp.Banner != nil && len(signalImp.Banner.API) > 0 { + maxImps[0].Banner.API = signalImp.Banner.API + } + + bannertype, err := jsonparser.GetString(maxImps[0].Banner.Ext, "bannertype") + if err == nil && bannertype == models.TypeRewarded { + maxImps[0].Banner = nil + } + } + + maxImps[0].Ext = setIfKeysExists(signalImp.Ext, maxImps[0].Ext, "reward", "skadn") +} + +func updateDevice(signalDevice *openrtb2.Device, maxRequest *openrtb2.BidRequest) { + if signalDevice == nil { + return + } + + if maxRequest.Device == nil { + maxRequest.Device = &openrtb2.Device{} + } + + if signalDevice.MCCMNC != "" { + maxRequest.Device.MCCMNC = signalDevice.MCCMNC + } + + if signalDevice.ConnectionType != nil { + maxRequest.Device.ConnectionType = signalDevice.ConnectionType + } + + maxRequest.Device.Ext = setIfKeysExists(signalDevice.Ext, maxRequest.Device.Ext, "atts") + + if signalDevice.Geo == nil { + return + } + + if maxRequest.Device.Geo == nil { + maxRequest.Device.Geo = &openrtb2.Geo{} + } + + if signalDevice.Geo.City != "" { + maxRequest.Device.Geo.City = signalDevice.Geo.City + } + + if signalDevice.Geo.UTCOffset != 0 { + maxRequest.Device.Geo.UTCOffset = signalDevice.Geo.UTCOffset + } +} + +func updateApp(signalApp *openrtb2.App, maxRequest *openrtb2.BidRequest) { + if signalApp == nil { + return + } + + if maxRequest.App == nil { + maxRequest.App = &openrtb2.App{} + } + + if signalApp.Paid != nil { + maxRequest.App.Paid = signalApp.Paid + } + + if signalApp.Keywords != "" { + maxRequest.App.Keywords = signalApp.Keywords + } + + if signalApp.Domain != "" { + maxRequest.App.Domain = signalApp.Domain + } +} + +func updateRegs(signalRegs *openrtb2.Regs, maxRequest *openrtb2.BidRequest) { + if signalRegs == nil { + return + } + + if maxRequest.Regs == nil { + maxRequest.Regs = &openrtb2.Regs{} + } + + if signalRegs.COPPA != 0 { + maxRequest.Regs.COPPA = signalRegs.COPPA + } + maxRequest.Regs.Ext = setIfKeysExists(signalRegs.Ext, maxRequest.Regs.Ext, "gdpr", "gpp", "gpp_sid", "us_privacy") +} + +func updateSource(signalSource *openrtb2.Source, maxRequest *openrtb2.BidRequest) { + if signalSource == nil || len(signalSource.Ext) == 0 { + return + } + + if maxRequest.Source == nil { + maxRequest.Source = &openrtb2.Source{} + } + + maxRequest.Source.Ext = setIfKeysExists(signalSource.Ext, maxRequest.Source.Ext, "omidpn", "omidpv") +} + +func updateUser(signalUser *openrtb2.User, maxRequest *openrtb2.BidRequest) { + if signalUser == nil { + return + } + + if maxRequest.User == nil { + maxRequest.User = &openrtb2.User{} + } + + if signalUser.Yob != 0 { + maxRequest.User.Yob = signalUser.Yob + } + + if signalUser.Gender != "" { + maxRequest.User.Gender = signalUser.Gender + } + + if signalUser.Keywords != "" { + maxRequest.User.Keywords = signalUser.Keywords + } + + maxRequest.User.Data = signalUser.Data + maxRequest.User.Ext = setIfKeysExists(signalUser.Ext, maxRequest.User.Ext, "consent", "eids") +} + +func setIfKeysExists(source []byte, target []byte, keys ...string) []byte { + newTarget := target + if len(keys) > 0 && len(newTarget) == 0 { + newTarget = []byte(`{}`) + } + + for _, key := range keys { + field, dataType, _, err := jsonparser.Get(source, key) + if err != nil { + continue + } + + if dataType == jsonparser.String { + quotedStr := strconv.Quote(string(field)) + field = []byte(quotedStr) + } + + newTarget, err = jsonparser.Set(newTarget, field, key) + if err != nil { + return target + } + } + + if len(newTarget) == 2 { + return target + } + return newTarget +} + +func updateRequestWrapper(signalExt json.RawMessage, maxRequest *openrtb2.BidRequest) { + clientConfigFlag, err := jsonparser.GetInt(signalExt, "wrapper", "clientconfig") + if err != nil || clientConfigFlag != 1 { + return + } + + if maxReqExt, err := jsonparser.Set(maxRequest.Ext, []byte(`1`), "prebid", "bidderparams", "pubmatic", "wrapper", "clientconfig"); err == nil { + maxRequest.Ext = maxReqExt + } +} + +func updateAppLovinMaxRequest(requestBody []byte) []byte { + signalData := getSignalData(requestBody) + if signalData == nil { + return requestBody + } + + maxRequest := &openrtb2.BidRequest{} + if err := json.Unmarshal(requestBody, maxRequest); err != nil { + return requestBody + } + + addSignalDataInRequest(signalData, maxRequest) + if maxRequestbytes, err := json.Marshal(maxRequest); err == nil { + return maxRequestbytes + } + return requestBody +} + +func updateAppLovinMaxResponse(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) models.AppLovinMax { + maxAppLovin := models.AppLovinMax{Reject: false} + + if bidResponse.NBR != nil { + if !rctx.Debug { + maxAppLovin.Reject = true + } + } else if len(bidResponse.SeatBid) == 0 || len(bidResponse.SeatBid[0].Bid) == 0 { + maxAppLovin.Reject = true + } + return maxAppLovin +} + +func applyAppLovinMaxResponse(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) *openrtb2.BidResponse { + if rctx.AppLovinMax.Reject { + return bidResponse + } + + //This condition is applied only in case if debug=1 refer func updateMaxAppLovinResponse + if bidResponse.NBR != nil { + return bidResponse + } + + resp, err := json.Marshal(bidResponse) + if err != nil { + return bidResponse + } + + signaldata := `{"` + models.SignalData + `":` + strconv.Quote(string(resp)) + `}` + *bidResponse = openrtb2.BidResponse{ + ID: bidResponse.ID, + BidID: bidResponse.SeatBid[0].Bid[0].ID, + Cur: bidResponse.Cur, + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: bidResponse.SeatBid[0].Bid[0].ID, + ImpID: bidResponse.SeatBid[0].Bid[0].ImpID, + Price: bidResponse.SeatBid[0].Bid[0].Price, + BURL: bidResponse.SeatBid[0].Bid[0].BURL, + Ext: json.RawMessage(signaldata), + }, + }, + }, + }, + } + return bidResponse +} diff --git a/modules/pubmatic/openwrap/applovinmax_test.go b/modules/pubmatic/openwrap/applovinmax_test.go new file mode 100644 index 00000000000..f33a55a6a52 --- /dev/null +++ b/modules/pubmatic/openwrap/applovinmax_test.go @@ -0,0 +1,963 @@ +package openwrap + +import ( + "encoding/json" + "testing" + + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/stretchr/testify/assert" +) + +func TestUpdateImpression(t *testing.T) { + type args struct { + signalImps []openrtb2.Imp + maxImps []openrtb2.Imp + } + tests := []struct { + name string + args args + want []openrtb2.Imp + }{ + { + name: "maxImps nil", + args: args{ + maxImps: nil, + }, + want: nil, + }, + { + name: "signalImps nil", + args: args{ + maxImps: nil, + signalImps: nil, + }, + want: nil, + }, + { + name: "signalImps with no impressions", + args: args{ + maxImps: nil, + signalImps: []openrtb2.Imp{}, + }, + want: nil, + }, + { + name: "maxImps with no impressions", + args: args{ + maxImps: []openrtb2.Imp{}, + signalImps: []openrtb2.Imp{}, + }, + want: []openrtb2.Imp{}, + }, + { + name: "maxImps and signalImps with empty impressions", + args: args{ + maxImps: []openrtb2.Imp{{}}, + signalImps: []openrtb2.Imp{{}}, + }, + want: []openrtb2.Imp{{}}, + }, + { + name: "maxImp video not present", + args: args{ + signalImps: []openrtb2.Imp{{ClickBrowser: openrtb2.Int8Ptr(0), DisplayManager: "PubMatic_SDK", DisplayManagerVer: "1.2"}}, + maxImps: []openrtb2.Imp{{ClickBrowser: openrtb2.Int8Ptr(1), DisplayManager: "Applovin_SDK"}}, + }, + want: []openrtb2.Imp{{ClickBrowser: openrtb2.Int8Ptr(0), DisplayManager: "PubMatic_SDK", DisplayManagerVer: "1.2"}}, + }, + { + name: "only maxImp has video", + args: args{ + signalImps: []openrtb2.Imp{{ClickBrowser: openrtb2.Int8Ptr(0), DisplayManager: "PubMatic_SDK", DisplayManagerVer: "1.2"}}, + maxImps: []openrtb2.Imp{{ClickBrowser: openrtb2.Int8Ptr(1), DisplayManager: "Applovin_SDK", Video: &openrtb2.Video{W: openrtb2.Int64Ptr(300), H: openrtb2.Int64Ptr(250)}}}, + }, + want: []openrtb2.Imp{{ClickBrowser: openrtb2.Int8Ptr(0), DisplayManager: "PubMatic_SDK", DisplayManagerVer: "1.2", Video: &openrtb2.Video{W: openrtb2.Int64Ptr(300), H: openrtb2.Int64Ptr(250)}}}, + }, + { + name: "maxImp and sdkImp has video", + args: args{ + signalImps: []openrtb2.Imp{{ClickBrowser: openrtb2.Int8Ptr(0), DisplayManager: "PubMatic_SDK", DisplayManagerVer: "1.2", Video: &openrtb2.Video{W: openrtb2.Int64Ptr(300), H: openrtb2.Int64Ptr(250), BAttr: []adcom1.CreativeAttribute{1, 2}}}}, + maxImps: []openrtb2.Imp{{ClickBrowser: openrtb2.Int8Ptr(1), DisplayManager: "Applovin_SDK", Video: &openrtb2.Video{W: openrtb2.Int64Ptr(750), H: openrtb2.Int64Ptr(500), BAttr: []adcom1.CreativeAttribute{6, 1, 8, 4}}}}, + }, + want: []openrtb2.Imp{{ClickBrowser: openrtb2.Int8Ptr(0), DisplayManager: "PubMatic_SDK", DisplayManagerVer: "1.2", Video: &openrtb2.Video{W: openrtb2.Int64Ptr(300), H: openrtb2.Int64Ptr(250), BAttr: []adcom1.CreativeAttribute{1, 2}}}}, + }, + { + name: "maxImp has and sdkImp has banner with api", + args: args{ + signalImps: []openrtb2.Imp{{Banner: &openrtb2.Banner{ID: "sdk_banner", API: []adcom1.APIFramework{1, 2, 3, 4}}}}, + maxImps: []openrtb2.Imp{{Banner: &openrtb2.Banner{ID: "max_banner"}}}, + }, + want: []openrtb2.Imp{{Banner: &openrtb2.Banner{ID: "max_banner", API: []adcom1.APIFramework{1, 2, 3, 4}}}}, + }, + { + name: "maxImp has bannertype rewarded", + args: args{ + signalImps: []openrtb2.Imp{{Banner: &openrtb2.Banner{ID: "sdk_banner", API: []adcom1.APIFramework{1, 2, 3, 4}}}}, + maxImps: []openrtb2.Imp{{Banner: &openrtb2.Banner{ID: "max_banner", Ext: json.RawMessage(`{"bannertype":"rewarded"}`)}}}, + }, + want: []openrtb2.Imp{{}}, + }, + { + name: "Banner API not present in signalImp", + args: args{ + signalImps: []openrtb2.Imp{{Banner: &openrtb2.Banner{ID: "sdk_banner"}}}, + maxImps: []openrtb2.Imp{{Banner: &openrtb2.Banner{ID: "max_banner", API: []adcom1.APIFramework{1, 2}, Ext: json.RawMessage(`{"bannertype":"not-rewarded"}`)}}}, + }, + want: []openrtb2.Imp{{Banner: &openrtb2.Banner{ID: "max_banner", API: []adcom1.APIFramework{1, 2}, Ext: json.RawMessage(`{"bannertype":"not-rewarded"}`)}}}, + }, + { + name: "maxImp has no ext, signalImp has reward in ext", + args: args{ + signalImps: []openrtb2.Imp{{Ext: json.RawMessage(`{"reward":1}`)}}, + maxImps: []openrtb2.Imp{{}}, + }, + want: []openrtb2.Imp{{Ext: json.RawMessage(`{"reward":1}`)}}, + }, + { + name: "maxImp has no ext, signalImp has reward and skadn in ext", + args: args{ + signalImps: []openrtb2.Imp{{Ext: json.RawMessage(`{"reward":1,"skadn":{"versions":["2.0","2.1"],"sourceapp":"11111","skadnetids":["424m5254lk.skadnetwork","4fzdc2evr5.skadnetwork"]}}`)}}, + maxImps: []openrtb2.Imp{{}}, + }, + want: []openrtb2.Imp{{Ext: json.RawMessage(`{"reward":1,"skadn":{"versions":["2.0","2.1"],"sourceapp":"11111","skadnetids":["424m5254lk.skadnetwork","4fzdc2evr5.skadnetwork"]}}`)}}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + updateImpression(tt.args.signalImps, tt.args.maxImps) + assert.Equal(t, tt.want, tt.args.maxImps, tt.name) + }) + } +} + +func TestUpdateDevice(t *testing.T) { + type args struct { + sdkDevice *openrtb2.Device + maxRequest *openrtb2.BidRequest + } + tests := []struct { + name string + args args + want *openrtb2.Device + }{ + { + name: "sdkDevice nil", + args: args{ + sdkDevice: nil, + maxRequest: &openrtb2.BidRequest{Device: &openrtb2.Device{DeviceType: 5}}, + }, + want: &openrtb2.Device{DeviceType: 5}, + }, + { + name: "sdkDevice has mccmnc,connectiontype", + args: args{ + sdkDevice: &openrtb2.Device{MCCMNC: "mccmnc", ConnectionType: adcom1.Connection2G.Ptr()}, + maxRequest: &openrtb2.BidRequest{Device: &openrtb2.Device{DeviceType: 5}}, + }, + want: &openrtb2.Device{DeviceType: 5, MCCMNC: "mccmnc", ConnectionType: adcom1.Connection2G.Ptr()}, + }, + { + name: "sdkDeviceExt has atts", + args: args{ + sdkDevice: &openrtb2.Device{Ext: json.RawMessage(`{"atts":3}`)}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: &openrtb2.Device{Ext: json.RawMessage(`{"atts":3}`)}, + }, + { + name: "sdkDevice has geo city and utcoffset", + args: args{ + sdkDevice: &openrtb2.Device{Geo: &openrtb2.Geo{City: "Delhi", UTCOffset: 3}, Ext: json.RawMessage(`{"atts":3}`)}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: &openrtb2.Device{Geo: &openrtb2.Geo{City: "Delhi", UTCOffset: 3}, Ext: json.RawMessage(`{"atts":3}`)}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + updateDevice(tt.args.sdkDevice, tt.args.maxRequest) + assert.Equal(t, tt.want, tt.args.maxRequest.Device, tt.name) + }) + } +} + +func TestUpdateApp(t *testing.T) { + type args struct { + signalApp *openrtb2.App + maxRequest *openrtb2.BidRequest + } + tests := []struct { + name string + args args + want *openrtb2.App + }{ + { + name: "signalApp is nil", + args: args{ + signalApp: nil, + maxRequest: &openrtb2.BidRequest{App: &openrtb2.App{ID: "signalApp"}}, + }, + want: &openrtb2.App{ID: "signalApp"}, + }, + { + name: "maxDevice is nil", + args: args{ + signalApp: &openrtb2.App{}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: &openrtb2.App{}, + }, + { + name: "signalApp has Paid,Keywords and Domain", + args: args{ + signalApp: &openrtb2.App{Paid: openrtb2.Int8Ptr(1), Keywords: "k1=v1", Domain: "abc.com"}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: &openrtb2.App{Paid: openrtb2.Int8Ptr(1), Keywords: "k1=v1", Domain: "abc.com"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + updateApp(tt.args.signalApp, tt.args.maxRequest) + assert.Equal(t, tt.want, tt.args.maxRequest.App, tt.name) + }) + } +} + +func TestUpdateRegs(t *testing.T) { + type args struct { + signalRegs *openrtb2.Regs + maxRequest *openrtb2.BidRequest + } + tests := []struct { + name string + args args + want *openrtb2.Regs + }{ + { + name: "signalRegs is nil", + args: args{ + signalRegs: nil, + maxRequest: &openrtb2.BidRequest{}, + }, + want: nil, + }, + { + name: "signalRegsExt is nil", + args: args{ + signalRegs: &openrtb2.Regs{}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: &openrtb2.Regs{}, + }, + { + name: "maxRegs is nil", + args: args{ + signalRegs: &openrtb2.Regs{Ext: json.RawMessage(`{}`)}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: &openrtb2.Regs{}, + }, + { + name: "signalRegs has coppa, signalRegsExt has gdpr, gpp", + args: args{ + signalRegs: &openrtb2.Regs{COPPA: 1, Ext: json.RawMessage(`{"gdpr":1,"gpp":"sdfewe3cer"}`)}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: &openrtb2.Regs{COPPA: 1, Ext: json.RawMessage(`{"gdpr":1,"gpp":"sdfewe3cer"}`)}, + }, + { + name: "signalRegs has coppa as 0, signalRegsExt has gdpr, gpp", + args: args{ + signalRegs: &openrtb2.Regs{COPPA: 0, Ext: json.RawMessage(`{"gdpr":1,"gpp":"sdfewe3cer"}`)}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: &openrtb2.Regs{Ext: json.RawMessage(`{"gdpr":1,"gpp":"sdfewe3cer"}`)}, + }, + { + name: "signalRegsExt has gdpr, gpp, gpp_sid, us_privacy and maxRegsExt has gpp", + args: args{ + signalRegs: &openrtb2.Regs{Ext: json.RawMessage(`{"gdpr":1,"gpp":"sdfewe3cer","gpp_sid":[6],"us_privacy":"uspConsentString"}`)}, + maxRequest: &openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gpp":"gpp_string"}`)}}, + }, + want: &openrtb2.Regs{Ext: json.RawMessage(`{"gpp":"sdfewe3cer","gdpr":1,"gpp_sid":[6],"us_privacy":"uspConsentString"}`)}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + updateRegs(tt.args.signalRegs, tt.args.maxRequest) + assert.Equal(t, tt.want, tt.args.maxRequest.Regs, tt.name) + }) + } +} + +func TestUpdateSource(t *testing.T) { + type args struct { + signalSource *openrtb2.Source + maxRequest *openrtb2.BidRequest + } + tests := []struct { + name string + args args + want *openrtb2.Source + }{ + { + name: "signalSource is nil", + args: args{ + signalSource: nil, + maxRequest: &openrtb2.BidRequest{}, + }, + want: nil, + }, + { + name: "signalSourceExt is nil", + args: args{ + signalSource: &openrtb2.Source{}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: nil, + }, + { + name: "maxSource is nil", + args: args{ + signalSource: &openrtb2.Source{Ext: json.RawMessage(`{}`)}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: &openrtb2.Source{}, + }, + { + name: "signalSourceExt has omidpn, omidpv", + args: args{ + signalSource: &openrtb2.Source{Ext: json.RawMessage(`{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}`)}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: &openrtb2.Source{Ext: json.RawMessage(`{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}`)}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + updateSource(tt.args.signalSource, tt.args.maxRequest) + assert.Equal(t, tt.want, tt.args.maxRequest.Source, tt.name) + }) + } +} + +func TestUpdateUser(t *testing.T) { + type args struct { + signalUser *openrtb2.User + maxRequest *openrtb2.BidRequest + } + tests := []struct { + name string + args args + want *openrtb2.User + }{ + { + name: "signalUser is nil", + args: args{ + signalUser: nil, + maxRequest: &openrtb2.BidRequest{}, + }, + want: nil, + }, + { + name: "maxUser is nil", + args: args{ + signalUser: &openrtb2.User{Ext: json.RawMessage(``)}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: &openrtb2.User{}, + }, + { + name: "signalUser has yob, gender, keywords", + args: args{ + signalUser: &openrtb2.User{Yob: 1999, Gender: "M", Keywords: "k1=v2;k2=v2", Ext: json.RawMessage(``)}, + maxRequest: &openrtb2.BidRequest{}, + }, + want: &openrtb2.User{Yob: 1999, Gender: "M", Keywords: "k1=v2;k2=v2"}, + }, + { + name: "signalUser and maxUser has yob, gender, keywords and data", + args: args{ + signalUser: &openrtb2.User{Data: []openrtb2.Data{{ID: "123", Name: "PubMatic_SDK", Segment: []openrtb2.Segment{{ID: "seg_id", Name: "PubMatic_Seg", Value: "segment_value", Ext: json.RawMessage(`{"segtax":4}`)}}}}, Yob: 1999, Gender: "M", Keywords: "k1=v2;k2=v2", Ext: json.RawMessage(``)}, + maxRequest: &openrtb2.BidRequest{User: &openrtb2.User{Data: []openrtb2.Data{{ID: "max_id", Name: "Publisher Passed", Segment: []openrtb2.Segment{{ID: "max_seg_id", Name: "max_Seg", Value: "max_segment_value"}}}}, Yob: 2000, Gender: "F", Keywords: "k52=v43"}}, + }, + want: &openrtb2.User{Data: []openrtb2.Data{{ID: "123", Name: "PubMatic_SDK", Segment: []openrtb2.Segment{{ID: "seg_id", Name: "PubMatic_Seg", Value: "segment_value", Ext: json.RawMessage(`{"segtax":4}`)}}}}, Yob: 1999, Gender: "M", Keywords: "k1=v2;k2=v2"}, + }, + { + name: "signalUserExt has consent", + args: args{ + signalUser: &openrtb2.User{ID: "sdkID", Yob: 1999, Gender: "M", Keywords: "k1=v2;k2=v2", Ext: json.RawMessage(`{"consent":"consent_string"}`)}, + maxRequest: &openrtb2.BidRequest{User: &openrtb2.User{ID: "maxID", Yob: 2000, Gender: "F", Keywords: "k52=v43"}}, + }, + want: &openrtb2.User{ID: "maxID", Yob: 1999, Gender: "M", Keywords: "k1=v2;k2=v2", Ext: json.RawMessage(`{"consent":"consent_string"}`)}, + }, + { + name: "signalUserExt has consent and eids", + args: args{ + signalUser: &openrtb2.User{ID: "sdkID", Yob: 1999, Gender: "M", Keywords: "k1=v2;k2=v2", Ext: json.RawMessage(`{"consent":"consent_string","eids":[{"source":"amxid","uids":[{"atype":1,"id":"88de601e-3d98-48e7-81d7-00000000"}]},{"source":"adserver.org","uids":[{"id":"1234567","ext":{"rtiPartner":"TDID"}}]}]}`)}, + maxRequest: &openrtb2.BidRequest{User: &openrtb2.User{ID: "maxID", Yob: 2000, Gender: "F", Keywords: "k52=v43"}}, + }, + want: &openrtb2.User{ID: "maxID", Yob: 1999, Gender: "M", Keywords: "k1=v2;k2=v2", Ext: json.RawMessage(`{"consent":"consent_string","eids":[{"source":"amxid","uids":[{"atype":1,"id":"88de601e-3d98-48e7-81d7-00000000"}]},{"source":"adserver.org","uids":[{"id":"1234567","ext":{"rtiPartner":"TDID"}}]}]}`)}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + updateUser(tt.args.signalUser, tt.args.maxRequest) + assert.Equal(t, tt.want, tt.args.maxRequest.User, tt.name) + }) + } +} + +func TestSetIfKeysExists(t *testing.T) { + type args struct { + source []byte + target []byte + keys []string + } + tests := []struct { + name string + args args + want []byte + }{ + { + name: "keys not found in source", + args: args{ + source: nil, + target: nil, + keys: []string{"key1", "key2"}, + }, + want: nil, + }, + { + name: "int value key found out of all keys", + args: args{ + source: []byte(`{"key1":23,"key40":"v40"}`), + target: nil, + keys: []string{"key1", "key2"}, + }, + want: []byte(`{"key1":23}`), + }, + { + name: "string value key found out of all keys", + args: args{ + source: []byte(`{"key1":23,"key40":"v40"}`), + target: nil, + keys: []string{"key40", "key2"}, + }, + want: []byte(`{"key40":"v40"}`), + }, + { + name: "overwrite string value key in target", + args: args{ + source: []byte(`{"key1":55555,"key40":"v40"}`), + target: []byte(`{"key1":23,"key40":"will_overwrite"}`), + keys: []string{"key40", "key2"}, + }, + want: []byte(`{"key1":23,"key40":"v40"}`), + }, + { + name: "error while setting key, return oldTarget", + args: args{ + source: []byte(`{"key1":555555,"key40":"v40"}`), + target: []byte(`"key1":23,"key40":"value40"}`), + keys: []string{"key40", "key2"}, + }, + want: []byte(`"key1":23,"key40":"value40"}`), + }, + { + name: "overwrite key in target with object", + args: args{ + source: []byte(`{"key1":55555,"key40":{"user":{"id":"1kjh3429kjh295jkl","ext":{"consent":"CONSENT_STRING"}},"regs":{"ext":{"gdpr":1}}}}`), + target: []byte(`{"key1":23,"key40":[]}`), + keys: []string{"key40", "key2"}, + }, + want: []byte(`{"key1":23,"key40":{"user":{"id":"1kjh3429kjh295jkl","ext":{"consent":"CONSENT_STRING"}},"regs":{"ext":{"gdpr":1}}}}`), + }, + { + name: "set slice in key", + args: args{ + source: []byte(`{"key1":555555,"key40":[1,2,3,4,5]}`), + target: []byte(`{"key1":23,"key40":"value40"}`), + keys: []string{"key40", "key2"}, + }, + want: []byte(`{"key1":23,"key40":[1,2,3,4,5]}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := setIfKeysExists(tt.args.source, tt.args.target, tt.args.keys...) + assert.Equal(t, tt.want, got, tt.name) + }) + } +} + +func TestAddSignalDataInRequest(t *testing.T) { + type args struct { + signal string + maxRequest json.RawMessage + } + tests := []struct { + name string + args args + wantMaxRequest json.RawMessage + }{ + { + name: "replace or add from signal", + args: args{ + signal: `{"device":{"devicetype":4,"w":393,"h":852,"ifa":"F5BA1637-7156-4369-BA7E-3C45033D9F61","mccmnc":"311-480","js":1,"osv":"17.3.1","connectiontype":5,"os":"iOS","pxratio":3,"geo":{"lastfix":8,"lat":37.48773508935608,"utcoffset":-480,"lon":-122.22855027909678,"type":1},"language":"en","make":"Apple","ext":{"atts":3},"ua":"Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148","model":"iPhone15,2","carrier":"Verizon"},"source":{"ext":{"omidpn":"Pubmatic","omidpv":"3.1.0"}},"id":"CE204A0E-31C3-4D7F-A1A0-D34AF5ED1A7F","app":{"id":"406719683","paid":1,"keywords":"k1=v1","domain":"abc.com","bundle":"406719683","storeurl":"https://apps.apple.com/us/app/gasbuddy-find-pay-for-gas/id406719683","name":"GasBuddy","publisher":{"id":"160361"},"ver":"700.89.22927"},"ext":{"wrapper":{"sumry_disable":1,"profileid":3422}},"imp":[{"secure":1,"tagid":"Mobile_iPhone_List_Screen_Bottom","banner":{"pos":0,"format":[{"w":300,"h":250}],"api":[5,6,7]},"id":"98D9318E-5276-402F-BAA4-CDBD8A364957","ext":{"skadn":{"sourceapp":"406719683","versions":["2.0","2.1","2.2","3.0","4.0"],"skadnetids":["cstr6suwn9.skadnetwork","7ug5zh24hu.skadnetwork","uw77j35x4d.skadnetwork","c6k4g5qg8m.skadnetwork","hs6bdukanm.skadnetwork","yclnxrl5pm.skadnetwork","3sh42y64q3.skadnetwork","cj5566h2ga.skadnetwork","klf5c3l5u5.skadnetwork","8s468mfl3y.skadnetwork","2u9pt9hc89.skadnetwork","7rz58n8ntl.skadnetwork","ppxm28t8ap.skadnetwork","mtkv5xtk9e.skadnetwork","cg4yq2srnc.skadnetwork","wzmmz9fp6w.skadnetwork","k674qkevps.skadnetwork","v72qych5uu.skadnetwork","578prtvx9j.skadnetwork","3rd42ekr43.skadnetwork","g28c52eehv.skadnetwork","2fnua5tdw4.skadnetwork","9nlqeag3gk.skadnetwork","5lm9lj6jb7.skadnetwork","97r2b46745.skadnetwork","e5fvkxwrpn.skadnetwork","4pfyvq9l8r.skadnetwork","tl55sbb4fm.skadnetwork","t38b2kh725.skadnetwork","prcb7njmu6.skadnetwork","mlmmfzh3r3.skadnetwork","9t245vhmpl.skadnetwork","9rd848q2bz.skadnetwork","4fzdc2evr5.skadnetwork","4468km3ulz.skadnetwork","m8dbw4sv7c.skadnetwork","ejvt5qm6ak.skadnetwork","5lm9lj6jb7.skadnetwork","44jx6755aq.skadnetwork","6g9af3uyq4.skadnetwork","u679fj5vs4.skadnetwork","rx5hdcabgc.skadnetwork","275upjj5gd.skadnetwork","p78axxw29g.skadnetwork"],"productpage":1,"version":"2.0"}},"displaymanagerver":"3.1.0","clickbrowser":1,"video":{"companionad":[{"pos":0,"format":[{"w":300,"h":250}],"vcm":1}],"protocols":[2,3,5,6,7,8,11,12,13,14],"h":250,"w":300,"linearity":1,"pos":0,"boxingallowed":1,"placement":2,"mimes":["video/3gpp2","video/quicktime","video/mp4","video/x-m4v","video/3gpp"],"companiontype":[1,2,3],"delivery":[2],"startdelay":0,"playbackend":1,"api":[7]},"displaymanager":"PubMatic_OpenWrap_SDK","instl":0}],"at":1,"cur":["USD"],"regs":{"coppa":1,"ext":{"ccpa":0,"gdpr":1,"gpp":"gpp_string","gpp_sid":[7],"us_privacy":"uspConsentString","consent":"0"}}}`, + maxRequest: json.RawMessage(`{"id":"{BID_ID}","at":1,"bcat":["IAB26-4","IAB26-2","IAB25-6","IAB25-5","IAB25-4","IAB25-3","IAB25-1","IAB25-7","IAB8-18","IAB26-3","IAB26-1","IAB8-5","IAB25-2","IAB11-4"],"tmax":3000,"app":{"name":"DrawHappyAngel","ver":"0.5.4","bundle":"com.newstory.DrawHappyAngel","cat":["IAB9-30"],"id":"{NETWORK_APP_ID}","publisher":{"name":"New Story Inc.","ext":{"installed_sdk":{"id":"MOLOCO_BIDDING","sdk_version":{"major":1,"minor":0,"micro":0},"adapter_version":{"major":1,"minor":0,"micro":0}}}},"ext":{"orientation":1}},"device":{"ifa":"497a10d6-c4dd-4e04-a986-c32b7180d462","ip":"38.158.207.171","carrier":"MYTEL","language":"en_US","hwv":"ruby","ppi":440,"pxratio":2.75,"devicetype":4,"connectiontype":2,"js":1,"h":2400,"w":1080,"geo":{"type":2,"ipservice":3,"lat":40.7429,"lon":-73.9392,"long":-73.9392,"city":"Queens","country":"USA","region":"ny","dma":"501","metro":"501","zip":"11101","ext":{"org":"Myanmar Broadband Telecom Co.","isp":"Myanmar Broadband Telecom Co."}},"ext":{},"osv":"13.0.0","ua":"Mozilla/5.0 (Linux; Android 13; 22101316C Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.230 Mobile Safari/537.36","make":"xiaomi","model":"22101316c","os":"android"},"imp":[{"id":"1","displaymanager":"applovin_mediation","displaymanagerver":"11.8.2","instl":0,"secure":0,"tagid":"{NETWORK_PLACEMENT_ID}","bidfloor":0.01,"bidfloorcur":"USD","exp":14400,"banner":{"id":"1","w":320,"h":50,"btype":[],"battr":[1,2,5,8,9,14,17],"pos":1,"format":[{"w":320,"h":50}]},"rwdd":0}],"user":{"data":[{"id":"1","name":"Publisher Passed","segment":[{"signal":"{BIDDING_SIGNAL}"}]}],"ext":{"gdpr":0}},"regs":{"ext":{"ccpa":0,"gdpr":1,"consent":"0","tcf_consent_string":"{TCF_STRING}"}},"source":{"ext":{"schain":{"ver":"1.0","complete":1,"nodes":[{"asi":"applovin.com","sid":"53bf468f18c5a0e2b7d4e3f748c677c1","rid":"494dbe15a3ce08c54f4e456363f35a022247f997","hp":1}]}}},"ext":{"prebid":{"bidderparams":{"pubmatic":{"wrapper":{"profileid":1234}}}}}}`), + }, + wantMaxRequest: json.RawMessage(`{"id":"{BID_ID}","at":1,"bcat":["IAB26-4","IAB26-2","IAB25-6","IAB25-5","IAB25-4","IAB25-3","IAB25-1","IAB25-7","IAB8-18","IAB26-3","IAB26-1","IAB8-5","IAB25-2","IAB11-4"],"tmax":3000,"app":{"paid":1,"keywords":"k1=v1","domain":"abc.com","name":"DrawHappyAngel","ver":"0.5.4","bundle":"com.newstory.DrawHappyAngel","cat":["IAB9-30"],"id":"{NETWORK_APP_ID}","publisher":{"name":"New Story Inc.","ext":{"installed_sdk":{"id":"MOLOCO_BIDDING","sdk_version":{"major":1,"minor":0,"micro":0},"adapter_version":{"major":1,"minor":0,"micro":0}}}},"ext":{"orientation":1}},"device":{"ifa":"497a10d6-c4dd-4e04-a986-c32b7180d462","ip":"38.158.207.171","carrier":"MYTEL","language":"en_US","hwv":"ruby","ppi":440,"pxratio":2.75,"devicetype":4,"mccmnc":"311-480","connectiontype":5,"js":1,"h":2400,"w":1080,"geo":{"city":"Queens","type":2,"ipservice":3,"lat":40.7429,"lon":-73.9392,"long":-73.9392,"country":"USA","region":"ny","dma":"501","metro":"501","zip":"11101","utcoffset":-480,"ext":{"org":"Myanmar Broadband Telecom Co.","isp":"Myanmar Broadband Telecom Co."}},"ext":{"atts":3},"osv":"13.0.0","ua":"Mozilla/5.0 (Linux; Android 13; 22101316C Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.230 Mobile Safari/537.36","make":"xiaomi","model":"22101316c","os":"android"},"imp":[{"id":"1","displaymanagerver":"3.1.0","clickbrowser":1,"displaymanager":"PubMatic_OpenWrap_SDK","instl":0,"secure":0,"tagid":"{NETWORK_PLACEMENT_ID}","bidfloor":0.01,"bidfloorcur":"USD","exp":14400,"banner":{"id":"1","w":320,"h":50,"btype":[],"api":[5,6,7],"battr":[1,2,5,8,9,14,17],"pos":1,"format":[{"w":320,"h":50}]},"video":{"companionad":[{"pos":0,"format":[{"w":300,"h":250}],"vcm":1}],"protocols":[2,3,5,6,7,8,11,12,13,14],"h":250,"w":300,"linearity":1,"pos":0,"boxingallowed":1,"placement":2,"mimes":["video/3gpp2","video/quicktime","video/mp4","video/x-m4v","video/3gpp"],"companiontype":[1,2,3],"delivery":[2],"startdelay":0,"playbackend":1,"api":[7]},"rwdd":0,"ext":{"skadn":{"sourceapp":"406719683","versions":["2.0","2.1","2.2","3.0","4.0"],"skadnetids":["cstr6suwn9.skadnetwork","7ug5zh24hu.skadnetwork","uw77j35x4d.skadnetwork","c6k4g5qg8m.skadnetwork","hs6bdukanm.skadnetwork","yclnxrl5pm.skadnetwork","3sh42y64q3.skadnetwork","cj5566h2ga.skadnetwork","klf5c3l5u5.skadnetwork","8s468mfl3y.skadnetwork","2u9pt9hc89.skadnetwork","7rz58n8ntl.skadnetwork","ppxm28t8ap.skadnetwork","mtkv5xtk9e.skadnetwork","cg4yq2srnc.skadnetwork","wzmmz9fp6w.skadnetwork","k674qkevps.skadnetwork","v72qych5uu.skadnetwork","578prtvx9j.skadnetwork","3rd42ekr43.skadnetwork","g28c52eehv.skadnetwork","2fnua5tdw4.skadnetwork","9nlqeag3gk.skadnetwork","5lm9lj6jb7.skadnetwork","97r2b46745.skadnetwork","e5fvkxwrpn.skadnetwork","4pfyvq9l8r.skadnetwork","tl55sbb4fm.skadnetwork","t38b2kh725.skadnetwork","prcb7njmu6.skadnetwork","mlmmfzh3r3.skadnetwork","9t245vhmpl.skadnetwork","9rd848q2bz.skadnetwork","4fzdc2evr5.skadnetwork","4468km3ulz.skadnetwork","m8dbw4sv7c.skadnetwork","ejvt5qm6ak.skadnetwork","5lm9lj6jb7.skadnetwork","44jx6755aq.skadnetwork","6g9af3uyq4.skadnetwork","u679fj5vs4.skadnetwork","rx5hdcabgc.skadnetwork","275upjj5gd.skadnetwork","p78axxw29g.skadnetwork"],"productpage":1,"version":"2.0"}}}],"user":{"data":[{"id":"1","name":"Publisher Passed","segment":[{"signal":"{BIDDING_SIGNAL}"}]}],"ext":{"gdpr":0}},"regs":{"coppa":1,"ext":{"ccpa":0,"gdpr":1,"consent":"0","tcf_consent_string":"{TCF_STRING}","gpp":"gpp_string","gpp_sid":[7],"us_privacy":"uspConsentString"}},"source":{"ext":{"schain":{"ver":"1.0","complete":1,"nodes":[{"asi":"applovin.com","sid":"53bf468f18c5a0e2b7d4e3f748c677c1","rid":"494dbe15a3ce08c54f4e456363f35a022247f997","hp":1}]},"omidpn":"Pubmatic","omidpv":"3.1.0"}},"ext":{"prebid":{"bidderparams":{"pubmatic":{"wrapper":{"profileid":1234}}}}}}`), + }, + { + name: "replace or add from signal,and remove banner as bannertype rewarded", + args: args{ + signal: `{"device":{"devicetype":4,"w":393,"h":852,"ifa":"F5BA1637-7156-4369-BA7E-3C45033D9F61","mccmnc":"311-480","js":1,"osv":"17.3.1","connectiontype":5,"os":"iOS","pxratio":3,"geo":{"lastfix":8,"lat":37.48773508935608,"utcoffset":-480,"lon":-122.22855027909678,"type":1},"language":"en","make":"Apple","ext":{"atts":3},"ua":"Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148","model":"iPhone15,2","carrier":"Verizon"},"source":{"ext":{"omidpn":"Pubmatic","omidpv":"3.1.0"}},"id":"CE204A0E-31C3-4D7F-A1A0-D34AF5ED1A7F","app":{"id":"406719683","paid":1,"keywords":"k1=v1","domain":"abc.com","bundle":"406719683","storeurl":"https://apps.apple.com/us/app/gasbuddy-find-pay-for-gas/id406719683","name":"GasBuddy","publisher":{"id":"160361"},"ver":"700.89.22927"},"ext":{"wrapper":{"sumry_disable":1,"clientconfig":1,"profileid":3422}},"imp":[{"secure":1,"tagid":"Mobile_iPhone_List_Screen_Bottom","banner":{"pos":0,"format":[{"w":300,"h":250}],"api":[5,6,7]},"id":"98D9318E-5276-402F-BAA4-CDBD8A364957","ext":{"skadn":{"sourceapp":"406719683","versions":["2.0","2.1","2.2","3.0","4.0"],"skadnetids":["cstr6suwn9.skadnetwork","7ug5zh24hu.skadnetwork","uw77j35x4d.skadnetwork","c6k4g5qg8m.skadnetwork","hs6bdukanm.skadnetwork","yclnxrl5pm.skadnetwork","3sh42y64q3.skadnetwork","cj5566h2ga.skadnetwork","klf5c3l5u5.skadnetwork","8s468mfl3y.skadnetwork","2u9pt9hc89.skadnetwork","7rz58n8ntl.skadnetwork","ppxm28t8ap.skadnetwork","mtkv5xtk9e.skadnetwork","cg4yq2srnc.skadnetwork","wzmmz9fp6w.skadnetwork","k674qkevps.skadnetwork","v72qych5uu.skadnetwork","578prtvx9j.skadnetwork","3rd42ekr43.skadnetwork","g28c52eehv.skadnetwork","2fnua5tdw4.skadnetwork","9nlqeag3gk.skadnetwork","5lm9lj6jb7.skadnetwork","97r2b46745.skadnetwork","e5fvkxwrpn.skadnetwork","4pfyvq9l8r.skadnetwork","tl55sbb4fm.skadnetwork","t38b2kh725.skadnetwork","prcb7njmu6.skadnetwork","mlmmfzh3r3.skadnetwork","9t245vhmpl.skadnetwork","9rd848q2bz.skadnetwork","4fzdc2evr5.skadnetwork","4468km3ulz.skadnetwork","m8dbw4sv7c.skadnetwork","ejvt5qm6ak.skadnetwork","5lm9lj6jb7.skadnetwork","44jx6755aq.skadnetwork","6g9af3uyq4.skadnetwork","u679fj5vs4.skadnetwork","rx5hdcabgc.skadnetwork","275upjj5gd.skadnetwork","p78axxw29g.skadnetwork"],"productpage":1,"version":"2.0"}},"displaymanagerver":"3.1.0","clickbrowser":1,"video":{"companionad":[{"pos":0,"format":[{"w":300,"h":250}],"vcm":1}],"protocols":[2,3,5,6,7,8,11,12,13,14],"h":250,"w":300,"linearity":1,"pos":0,"boxingallowed":1,"placement":2,"mimes":["video/3gpp2","video/quicktime","video/mp4","video/x-m4v","video/3gpp"],"companiontype":[1,2,3],"delivery":[2],"startdelay":0,"playbackend":1,"api":[7]},"displaymanager":"PubMatic_OpenWrap_SDK","instl":0}],"at":1,"cur":["USD"],"regs":{"coppa":1,"ext":{"ccpa":0,"gdpr":1,"gpp":"gpp_string","gpp_sid":[7],"us_privacy":"uspConsentString","consent":"0"}}}`, + maxRequest: json.RawMessage(`{"id":"{BID_ID}","at":1,"bcat":["IAB26-4","IAB26-2","IAB25-6","IAB25-5","IAB25-4","IAB25-3","IAB25-1","IAB25-7","IAB8-18","IAB26-3","IAB26-1","IAB8-5","IAB25-2","IAB11-4"],"tmax":3000,"app":{"name":"DrawHappyAngel","ver":"0.5.4","bundle":"com.newstory.DrawHappyAngel","cat":["IAB9-30"],"id":"{NETWORK_APP_ID}","publisher":{"name":"New Story Inc.","ext":{"installed_sdk":{"id":"MOLOCO_BIDDING","sdk_version":{"major":1,"minor":0,"micro":0},"adapter_version":{"major":1,"minor":0,"micro":0}}}},"ext":{"orientation":1}},"device":{"ifa":"497a10d6-c4dd-4e04-a986-c32b7180d462","ip":"38.158.207.171","carrier":"MYTEL","language":"en_US","hwv":"ruby","ppi":440,"pxratio":2.75,"devicetype":4,"connectiontype":2,"js":1,"h":2400,"w":1080,"geo":{"type":2,"ipservice":3,"lat":40.7429,"lon":-73.9392,"long":-73.9392,"city":"Queens","country":"USA","region":"ny","dma":"501","metro":"501","zip":"11101","ext":{"org":"Myanmar Broadband Telecom Co.","isp":"Myanmar Broadband Telecom Co."}},"ext":{},"osv":"13.0.0","ua":"Mozilla/5.0 (Linux; Android 13; 22101316C Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.230 Mobile Safari/537.36","make":"xiaomi","model":"22101316c","os":"android"},"imp":[{"id":"1","displaymanager":"applovin_mediation","displaymanagerver":"11.8.2","instl":0,"secure":0,"tagid":"{NETWORK_PLACEMENT_ID}","bidfloor":0.01,"bidfloorcur":"USD","exp":14400,"banner":{"id":"1","w":320,"h":50,"btype":[],"battr":[1,2,5,8,9,14,17],"pos":1,"format":[{"w":320,"h":50}],"ext":{"bannertype":"rewarded"}},"rwdd":0}],"user":{"data":[{"id":"1","name":"Publisher Passed","segment":[{"signal":"{BIDDING_SIGNAL}"}]}],"ext":{"gdpr":0}},"regs":{"ext":{"ccpa":0,"gdpr":1,"consent":"0","tcf_consent_string":"{TCF_STRING}"}},"source":{"ext":{"schain":{"ver":"1.0","complete":1,"nodes":[{"asi":"applovin.com","sid":"53bf468f18c5a0e2b7d4e3f748c677c1","rid":"494dbe15a3ce08c54f4e456363f35a022247f997","hp":1}]}}}}`), + }, + wantMaxRequest: json.RawMessage(`{"id":"{BID_ID}","at":1,"bcat":["IAB26-4","IAB26-2","IAB25-6","IAB25-5","IAB25-4","IAB25-3","IAB25-1","IAB25-7","IAB8-18","IAB26-3","IAB26-1","IAB8-5","IAB25-2","IAB11-4"],"tmax":3000,"app":{"paid":1,"keywords":"k1=v1","domain":"abc.com","name":"DrawHappyAngel","ver":"0.5.4","bundle":"com.newstory.DrawHappyAngel","cat":["IAB9-30"],"id":"{NETWORK_APP_ID}","publisher":{"name":"New Story Inc.","ext":{"installed_sdk":{"id":"MOLOCO_BIDDING","sdk_version":{"major":1,"minor":0,"micro":0},"adapter_version":{"major":1,"minor":0,"micro":0}}}},"ext":{"orientation":1}},"device":{"ifa":"497a10d6-c4dd-4e04-a986-c32b7180d462","ip":"38.158.207.171","carrier":"MYTEL","language":"en_US","hwv":"ruby","ppi":440,"pxratio":2.75,"devicetype":4,"mccmnc":"311-480","connectiontype":5,"js":1,"h":2400,"w":1080,"geo":{"city":"Queens","type":2,"ipservice":3,"lat":40.7429,"lon":-73.9392,"long":-73.9392,"country":"USA","region":"ny","dma":"501","metro":"501","zip":"11101","utcoffset":-480,"ext":{"org":"Myanmar Broadband Telecom Co.","isp":"Myanmar Broadband Telecom Co."}},"ext":{"atts":3},"osv":"13.0.0","ua":"Mozilla/5.0 (Linux; Android 13; 22101316C Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.230 Mobile Safari/537.36","make":"xiaomi","model":"22101316c","os":"android"},"imp":[{"id":"1","displaymanagerver":"3.1.0","clickbrowser":1,"displaymanager":"PubMatic_OpenWrap_SDK","instl":0,"secure":0,"tagid":"{NETWORK_PLACEMENT_ID}","bidfloor":0.01,"bidfloorcur":"USD","exp":14400,"rwdd":0,"video":{"companionad":[{"pos":0,"format":[{"w":300,"h":250}],"vcm":1}],"protocols":[2,3,5,6,7,8,11,12,13,14],"h":250,"w":300,"linearity":1,"pos":0,"boxingallowed":1,"placement":2,"mimes":["video/3gpp2","video/quicktime","video/mp4","video/x-m4v","video/3gpp"],"companiontype":[1,2,3],"delivery":[2],"startdelay":0,"playbackend":1,"api":[7]},"ext":{"skadn":{"sourceapp":"406719683","versions":["2.0","2.1","2.2","3.0","4.0"],"skadnetids":["cstr6suwn9.skadnetwork","7ug5zh24hu.skadnetwork","uw77j35x4d.skadnetwork","c6k4g5qg8m.skadnetwork","hs6bdukanm.skadnetwork","yclnxrl5pm.skadnetwork","3sh42y64q3.skadnetwork","cj5566h2ga.skadnetwork","klf5c3l5u5.skadnetwork","8s468mfl3y.skadnetwork","2u9pt9hc89.skadnetwork","7rz58n8ntl.skadnetwork","ppxm28t8ap.skadnetwork","mtkv5xtk9e.skadnetwork","cg4yq2srnc.skadnetwork","wzmmz9fp6w.skadnetwork","k674qkevps.skadnetwork","v72qych5uu.skadnetwork","578prtvx9j.skadnetwork","3rd42ekr43.skadnetwork","g28c52eehv.skadnetwork","2fnua5tdw4.skadnetwork","9nlqeag3gk.skadnetwork","5lm9lj6jb7.skadnetwork","97r2b46745.skadnetwork","e5fvkxwrpn.skadnetwork","4pfyvq9l8r.skadnetwork","tl55sbb4fm.skadnetwork","t38b2kh725.skadnetwork","prcb7njmu6.skadnetwork","mlmmfzh3r3.skadnetwork","9t245vhmpl.skadnetwork","9rd848q2bz.skadnetwork","4fzdc2evr5.skadnetwork","4468km3ulz.skadnetwork","m8dbw4sv7c.skadnetwork","ejvt5qm6ak.skadnetwork","5lm9lj6jb7.skadnetwork","44jx6755aq.skadnetwork","6g9af3uyq4.skadnetwork","u679fj5vs4.skadnetwork","rx5hdcabgc.skadnetwork","275upjj5gd.skadnetwork","p78axxw29g.skadnetwork"],"productpage":1,"version":"2.0"}}}],"user":{"data":[{"id":"1","name":"Publisher Passed","segment":[{"signal":"{BIDDING_SIGNAL}"}]}],"ext":{"gdpr":0}},"regs":{"coppa":1,"ext":{"ccpa":0,"gdpr":1,"consent":"0","tcf_consent_string":"{TCF_STRING}","gpp":"gpp_string","gpp_sid":[7],"us_privacy":"uspConsentString"}},"source":{"ext":{"schain":{"ver":"1.0","complete":1,"nodes":[{"asi":"applovin.com","sid":"53bf468f18c5a0e2b7d4e3f748c677c1","rid":"494dbe15a3ce08c54f4e456363f35a022247f997","hp":1}]},"omidpn":"Pubmatic","omidpv":"3.1.0"}}}`), + }, + { + name: "replace imp.video from signal", + args: args{ + signal: `{"device":{"devicetype":4,"w":393,"h":852,"ifa":"F5BA1637-7156-4369-BA7E-3C45033D9F61","mccmnc":"311-480","js":1,"osv":"17.3.1","connectiontype":5,"os":"iOS","pxratio":3,"geo":{"lastfix":8,"lat":37.48773508935608,"utcoffset":-480,"lon":-122.22855027909678,"type":1},"language":"en","make":"Apple","ext":{"atts":3},"ua":"Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148","model":"iPhone15,2","carrier":"Verizon"},"source":{"ext":{"omidpn":"Pubmatic","omidpv":"3.1.0"}},"id":"CE204A0E-31C3-4D7F-A1A0-D34AF5ED1A7F","app":{"id":"406719683","paid":1,"keywords":"k1=v1","domain":"abc.com","bundle":"406719683","storeurl":"https://apps.apple.com/us/app/gasbuddy-find-pay-for-gas/id406719683","name":"GasBuddy","publisher":{"id":"160361"},"ver":"700.89.22927"},"ext":{"wrapper":{"sumry_disable":1,"clientconfig":1,"profileid":3422}},"imp":[{"secure":1,"tagid":"Mobile_iPhone_List_Screen_Bottom","banner":{"pos":0,"format":[{"w":300,"h":250}],"api":[5,6,7]},"id":"98D9318E-5276-402F-BAA4-CDBD8A364957","ext":{"skadn":{"sourceapp":"406719683","versions":["2.0","2.1","2.2","3.0","4.0"],"skadnetids":["cstr6suwn9.skadnetwork","7ug5zh24hu.skadnetwork","uw77j35x4d.skadnetwork","c6k4g5qg8m.skadnetwork","hs6bdukanm.skadnetwork","yclnxrl5pm.skadnetwork","3sh42y64q3.skadnetwork","cj5566h2ga.skadnetwork","klf5c3l5u5.skadnetwork","8s468mfl3y.skadnetwork","2u9pt9hc89.skadnetwork","7rz58n8ntl.skadnetwork","ppxm28t8ap.skadnetwork","mtkv5xtk9e.skadnetwork","cg4yq2srnc.skadnetwork","wzmmz9fp6w.skadnetwork","k674qkevps.skadnetwork","v72qych5uu.skadnetwork","578prtvx9j.skadnetwork","3rd42ekr43.skadnetwork","g28c52eehv.skadnetwork","2fnua5tdw4.skadnetwork","9nlqeag3gk.skadnetwork","5lm9lj6jb7.skadnetwork","97r2b46745.skadnetwork","e5fvkxwrpn.skadnetwork","4pfyvq9l8r.skadnetwork","tl55sbb4fm.skadnetwork","t38b2kh725.skadnetwork","prcb7njmu6.skadnetwork","mlmmfzh3r3.skadnetwork","9t245vhmpl.skadnetwork","9rd848q2bz.skadnetwork","4fzdc2evr5.skadnetwork","4468km3ulz.skadnetwork","m8dbw4sv7c.skadnetwork","ejvt5qm6ak.skadnetwork","5lm9lj6jb7.skadnetwork","44jx6755aq.skadnetwork","6g9af3uyq4.skadnetwork","u679fj5vs4.skadnetwork","rx5hdcabgc.skadnetwork","275upjj5gd.skadnetwork","p78axxw29g.skadnetwork"],"productpage":1,"version":"2.0"}},"displaymanagerver":"3.1.0","clickbrowser":1,"video":{"companionad":[{"pos":0,"format":[{"w":300,"h":250}],"vcm":1}],"protocols":[2,3,5,6,7,8,11,12,13,14],"h":250,"w":300,"linearity":1,"pos":0,"boxingallowed":1,"placement":2,"mimes":["video/3gpp2","video/quicktime","video/mp4","video/x-m4v","video/3gpp"],"companiontype":[1,2,3],"delivery":[2],"startdelay":0,"playbackend":1,"api":[7]},"displaymanager":"PubMatic_OpenWrap_SDK","instl":0}],"at":1,"cur":["USD"],"regs":{"coppa":1,"ext":{"ccpa":0,"gdpr":1,"gpp":"gpp_string","gpp_sid":[7],"us_privacy":"uspConsentString","consent":"0"}}}`, + maxRequest: json.RawMessage(`{"id":"{BID_ID}","at":1,"bcat":["IAB26-4","IAB26-2","IAB25-6","IAB25-5","IAB25-4","IAB25-3","IAB25-1","IAB25-7","IAB8-18","IAB26-3","IAB26-1","IAB8-5","IAB25-2","IAB11-4"],"tmax":3000,"app":{"name":"DrawHappyAngel","ver":"0.5.4","bundle":"com.newstory.DrawHappyAngel","cat":["IAB9-30"],"id":"{NETWORK_APP_ID}","publisher":{"name":"New Story Inc.","ext":{"installed_sdk":{"id":"MOLOCO_BIDDING","sdk_version":{"major":1,"minor":0,"micro":0},"adapter_version":{"major":1,"minor":0,"micro":0}}}},"ext":{"orientation":1}},"device":{"ifa":"497a10d6-c4dd-4e04-a986-c32b7180d462","ip":"38.158.207.171","carrier":"MYTEL","language":"en_US","hwv":"ruby","ppi":440,"pxratio":2.75,"devicetype":4,"connectiontype":2,"js":1,"h":2400,"w":1080,"geo":{"type":2,"ipservice":3,"lat":40.7429,"lon":-73.9392,"long":-73.9392,"city":"Queens","country":"USA","region":"ny","dma":"501","metro":"501","zip":"11101","ext":{"org":"Myanmar Broadband Telecom Co.","isp":"Myanmar Broadband Telecom Co."}},"ext":{},"osv":"13.0.0","ua":"Mozilla/5.0 (Linux; Android 13; 22101316C Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.230 Mobile Safari/537.36","make":"xiaomi","model":"22101316c","os":"android"},"imp":[{"id":"1","displaymanager":"applovin_mediation","displaymanagerver":"11.8.2","instl":1,"secure":0,"tagid":"{NETWORK_PLACEMENT_ID}","exp":14400,"banner":{"id":"1","w":320,"h":480,"btype":[],"battr":[1,2,5,8,9,14,17],"pos":7,"format":[{"w":320,"h":480}]},"video":{"w":320,"h":480,"battr":[1,2,5,8,9,14,17],"mimes":["video/mp4","video/3gpp","video/3gpp2","video/x-m4v"],"placement":5,"pos":7,"minduration":5,"maxduration":60,"skipafter":5,"skipmin":0,"startdelay":0,"playbackmethod":[1],"linearity":1},"rwdd":0}],"user":{"data":[{"id":"1","name":"Publisher Passed","segment":[{"signal":"{BIDDING_SIGNAL}"}]}],"ext":{"gdpr":0}},"regs":{"ext":{"ccpa":0,"gdpr":1,"consent":"0","tcf_consent_string":"{TCF_STRING}"}},"source":{"ext":{"schain":{"ver":"1.0","complete":1,"nodes":[{"asi":"applovin.com","sid":"53bf468f18c5a0e2b7d4e3f748c677c1","rid":"494dbe15a3ce08c54f4e456363f35a022247f997","hp":1}]}}}}`), + }, + wantMaxRequest: json.RawMessage(`{"id":"{BID_ID}","at":1,"bcat":["IAB26-4","IAB26-2","IAB25-6","IAB25-5","IAB25-4","IAB25-3","IAB25-1","IAB25-7","IAB8-18","IAB26-3","IAB26-1","IAB8-5","IAB25-2","IAB11-4"],"tmax":3000,"app":{"paid":1,"keywords":"k1=v1","domain":"abc.com","name":"DrawHappyAngel","ver":"0.5.4","bundle":"com.newstory.DrawHappyAngel","cat":["IAB9-30"],"id":"{NETWORK_APP_ID}","publisher":{"name":"New Story Inc.","ext":{"installed_sdk":{"id":"MOLOCO_BIDDING","sdk_version":{"major":1,"minor":0,"micro":0},"adapter_version":{"major":1,"minor":0,"micro":0}}}},"ext":{"orientation":1}},"device":{"ifa":"497a10d6-c4dd-4e04-a986-c32b7180d462","ip":"38.158.207.171","carrier":"MYTEL","language":"en_US","hwv":"ruby","ppi":440,"pxratio":2.75,"devicetype":4,"mccmnc":"311-480","connectiontype":5,"js":1,"h":2400,"w":1080,"geo":{"city":"Queens","type":2,"ipservice":3,"lat":40.7429,"lon":-73.9392,"long":-73.9392,"country":"USA","region":"ny","dma":"501","metro":"501","zip":"11101","utcoffset":-480,"ext":{"org":"Myanmar Broadband Telecom Co.","isp":"Myanmar Broadband Telecom Co."}},"ext":{"atts":3},"osv":"13.0.0","ua":"Mozilla/5.0 (Linux; Android 13; 22101316C Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.230 Mobile Safari/537.36","make":"xiaomi","model":"22101316c","os":"android"},"imp":[{"id":"1","displaymanagerver":"3.1.0","clickbrowser":1,"displaymanager":"PubMatic_OpenWrap_SDK","instl":1,"secure":0,"tagid":"{NETWORK_PLACEMENT_ID}","exp":14400,"banner":{"id":"1","w":320,"h":480,"btype":[],"api":[5,6,7],"battr":[1,2,5,8,9,14,17],"pos":7,"format":[{"w":320,"h":480}]},"video":{"companionad":[{"pos":0,"format":[{"w":300,"h":250}],"vcm":1}],"protocols":[2,3,5,6,7,8,11,12,13,14],"h":250,"w":300,"linearity":1,"pos":0,"boxingallowed":1,"placement":2,"mimes":["video/3gpp2","video/quicktime","video/mp4","video/x-m4v","video/3gpp"],"companiontype":[1,2,3],"delivery":[2],"startdelay":0,"playbackend":1,"api":[7]},"rwdd":0,"ext":{"skadn":{"sourceapp":"406719683","versions":["2.0","2.1","2.2","3.0","4.0"],"skadnetids":["cstr6suwn9.skadnetwork","7ug5zh24hu.skadnetwork","uw77j35x4d.skadnetwork","c6k4g5qg8m.skadnetwork","hs6bdukanm.skadnetwork","yclnxrl5pm.skadnetwork","3sh42y64q3.skadnetwork","cj5566h2ga.skadnetwork","klf5c3l5u5.skadnetwork","8s468mfl3y.skadnetwork","2u9pt9hc89.skadnetwork","7rz58n8ntl.skadnetwork","ppxm28t8ap.skadnetwork","mtkv5xtk9e.skadnetwork","cg4yq2srnc.skadnetwork","wzmmz9fp6w.skadnetwork","k674qkevps.skadnetwork","v72qych5uu.skadnetwork","578prtvx9j.skadnetwork","3rd42ekr43.skadnetwork","g28c52eehv.skadnetwork","2fnua5tdw4.skadnetwork","9nlqeag3gk.skadnetwork","5lm9lj6jb7.skadnetwork","97r2b46745.skadnetwork","e5fvkxwrpn.skadnetwork","4pfyvq9l8r.skadnetwork","tl55sbb4fm.skadnetwork","t38b2kh725.skadnetwork","prcb7njmu6.skadnetwork","mlmmfzh3r3.skadnetwork","9t245vhmpl.skadnetwork","9rd848q2bz.skadnetwork","4fzdc2evr5.skadnetwork","4468km3ulz.skadnetwork","m8dbw4sv7c.skadnetwork","ejvt5qm6ak.skadnetwork","5lm9lj6jb7.skadnetwork","44jx6755aq.skadnetwork","6g9af3uyq4.skadnetwork","u679fj5vs4.skadnetwork","rx5hdcabgc.skadnetwork","275upjj5gd.skadnetwork","p78axxw29g.skadnetwork"],"productpage":1,"version":"2.0"}}}],"user":{"data":[{"id":"1","name":"Publisher Passed","segment":[{"signal":"{BIDDING_SIGNAL}"}]}],"ext":{"gdpr":0}},"regs":{"coppa":1,"ext":{"ccpa":0,"gdpr":1,"consent":"0","tcf_consent_string":"{TCF_STRING}","gpp":"gpp_string","gpp_sid":[7],"us_privacy":"uspConsentString"}},"source":{"ext":{"schain":{"ver":"1.0","complete":1,"nodes":[{"asi":"applovin.com","sid":"53bf468f18c5a0e2b7d4e3f748c677c1","rid":"494dbe15a3ce08c54f4e456363f35a022247f997","hp":1}]},"omidpn":"Pubmatic","omidpv":"3.1.0"}}}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var maxRequest openrtb2.BidRequest + if err := json.Unmarshal(tt.args.maxRequest, &maxRequest); err != nil { + t.Errorf("Unmarshal Faild for Incoming MaxRequest, Error: %s", err) + } + + signalData := &openrtb2.BidRequest{} + if err := json.Unmarshal([]byte(tt.args.signal), &signalData); err != nil { + t.Errorf("Unmarshal Faild for Incoming MaxRequest, Error: %s", err) + } + + var expectedMaxRequest openrtb2.BidRequest + addSignalDataInRequest(signalData, &maxRequest) + if err := json.Unmarshal(tt.wantMaxRequest, &expectedMaxRequest); err != nil { + t.Errorf("Unmarshal Faild for Expected MaxRequest, Error: %s", err) + } + assert.Equal(t, expectedMaxRequest, maxRequest, tt.name) + }) + } +} + +func TestGetSignalData(t *testing.T) { + type args struct { + requestBody []byte + } + tests := []struct { + name string + args args + want *openrtb2.BidRequest + }{ + { + name: "incorrect body", + args: args{ + requestBody: []byte(`{"id":"123","user":Passed","segment":[{"signal":{BIDDING_SIGNA}]}],"ext":{"gdpr":0}}}`), + }, + want: nil, + }, + { + name: "signal parsing fail", + args: args{ + requestBody: []byte(`{"id":"123","user":{"data":[{"id":"1","name":"Publisher Passed","segment":[{"signal":"{BIDDING_SIGNA}"]}],"ext":{"gdpr":0}}}`), + }, + want: nil, + }, + { + name: "single user.data with signal with incorrect signal", + args: args{ + requestBody: []byte(`{"id":"123","user":{"data":[{"id":"1","name":"Publisher Passed","segment":[{"signal":{BIDDING_SIGNA}]}],"ext":{"gdpr":0}}}`), + }, + want: nil, + }, + { + name: "single user.data with signal", + args: args{ + requestBody: []byte(`{"id":"123","user":{"data":[{"id":"1","name":"Publisher Passed","segment":[{"signal":"{\"device\":{\"devicetype\":4,\"w\":393,\"h\":852}}"}]}],"ext":{"gdpr":0}}}`), + }, + want: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + DeviceType: 4, + W: 393, + H: 852, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getSignalData(tt.args.requestBody) + assert.Equal(t, tt.want, got, tt.name) + }) + } +} + +func TestUpdateMaxAppLovinRequest(t *testing.T) { + type args struct { + requestBody []byte + } + tests := []struct { + name string + args args + want []byte + }{ + { + name: "signal not present", + args: args{ + requestBody: []byte(``), + }, + want: []byte(``), + }, + { + name: "invalid request body", + args: args{ + requestBody: []byte(`{"id","user":{"data":[{"segment":[{"signal":"{}"}]}]}}`), + }, + want: []byte(`{"id","user":{"data":[{"segment":[{"signal":"{}"}]}]}}`), + }, + { + name: "update maxrequest body from signal", + args: args{ + requestBody: []byte(`{"id":"test-case-1","at":1,"bcat":["IAB26-4","IAB26-2","IAB25-6","IAB25-5","IAB25-4","IAB25-3","IAB25-1","IAB25-7","IAB8-18","IAB26-3","IAB26-1","IAB8-5","IAB25-2","IAB11-4"],"tmax":1000,"app":{"publisher":{"name":"New Story Inc.","id":"5890","ext":{"installed_sdk":{"id":"MOLOCO_BIDDING","sdk_version":{"major":1,"minor":0,"micro":0},"adapter_version":{"major":1,"minor":0,"micro":0}}}},"paid":0,"name":"DrawHappyAngel","ver":"0.5.4","bundle":"com.newstory.DrawHappyAngel","cat":["IAB9-30"],"id":"1234567","ext":{"orientation":1}},"device":{"ifa":"497a10d6-c4dd-4e04-a986-c32b7180d462","ip":"38.158.207.171","carrier":"MYTEL","language":"en_US","hwv":"ruby","ppi":440,"pxratio":2.75,"devicetype":4,"connectiontype":2,"js":1,"h":2400,"w":1080,"geo":{"type":2,"ipservice":3,"lat":40.7429,"lon":-73.9392,"long":-73.9392,"city":"Queens","country":"USA","region":"ny","dma":"501","metro":"501","zip":"11101","ext":{"org":"Myanmar Broadband Telecom Co.","isp":"Myanmar Broadband Telecom Co."}},"ext":{},"osv":"13.0.0","ua":"Mozilla/5.0 (Linux; Android 13; 22101316C Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.230 Mobile Safari/537.36","make":"xiaomi","model":"22101316c","os":"android"},"imp":[{"id":"1","displaymanager":"applovin_mediation","displaymanagerver":"11.8.2","instl":0,"secure":0,"tagid":"/43743431/DMDemo","bidfloor":0.01,"bidfloorcur":"USD","exp":14400,"banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"rwdd":0}],"user":{"data":[{"id":"1","name":"Publisher Passed","segment":[{"signal":"{\"id\":\"95d6643c-3da6-40a2-b9ca-12279393ffbf\",\"at\":1,\"tmax\":500,\"cur\":[\"USD\"],\"imp\":[{\"id\":\"imp176227948\",\"clickbrowser\":0,\"displaymanager\":\"PubMatic_OpenBid_SDK\",\"displaymanagerver\":\"1.4.0\",\"tagid\":\"\/43743431\/DMDemo\",\"secure\":0,\"banner\":{\"pos\":7,\"format\":[{\"w\":300,\"h\":250}],\"api\":[5,6,7]},\"instl\":1}],\"app\":{\"paid\":4,\"name\":\"OpenWrapperSample\",\"bundle\":\"com.pubmatic.openbid.app\",\"storeurl\":\"https:\/\/itunes.apple.com\/us\/app\/pubmatic-sdk-app\/id1175273098?appnexus_banner_fixedbid=1&fixedbid=1\",\"ver\":\"1.0\",\"publisher\":{\"id\":\"5890\"}},\"device\":{\"geo\":{\"type\":1,\"lat\":37.421998333333335,\"lon\":-122.08400000000002},\"pxratio\":2.625,\"mccmnc\":\"310-260\",\"lmt\":0,\"ifa\":\"07c387f2-e030-428f-8336-42f682150759\",\"connectiontype\":5,\"carrier\":\"Android\",\"js\":1,\"ua\":\"Mozilla\/5.0(Linux;Android9;AndroidSDKbuiltforx86Build\/PSR1.180720.075;wv)AppleWebKit\/537.36(KHTML,likeGecko)Version\/4.0Chrome\/69.0.3497.100MobileSafari\/537.36\",\"make\":\"Google\",\"model\":\"AndroidSDKbuiltforx86\",\"os\":\"Android\",\"osv\":\"9\",\"h\":1794,\"w\":1080,\"language\":\"en-US\",\"devicetype\":4,\"ext\":{\"atts\":3}},\"source\":{\"ext\":{\"omidpn\":\"PubMatic\",\"omidpv\":\"1.2.11-Pubmatic\"}},\"user\":{\"data\":[{\"id\":\"1234\"}]},\"ext\":{\"wrapper\":{\"ssauction\":1,\"sumry_disable\":0,\"profileid\":58135,\"versionid\":1,\"clientconfig\":1}}}"}]}],"ext":{"gdpr":0}},"regs":{"coppa":0,"ext":{"gdpr":0}},"source":{"ext":{"schain":{"ver":"1.0","complete":1,"nodes":[{"asi":"applovin.com","sid":"53bf468f18c5a0e2b7d4e3f748c677c1","rid":"494dbe15a3ce08c54f4e456363f35a022247f997","hp":1}]}}},"ext":{"prebid":{"bidderparams":{"pubmatic":{"wrapper":{"profileid":12929,"versionid":1,"clientconfig":1}}}}}}`), + }, + want: []byte(`{"id":"test-case-1","imp":[{"id":"1","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900,"api":[5,6,7]},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"displaymanager":"PubMatic_OpenBid_SDK","displaymanagerver":"1.4.0","tagid":"/43743431/DMDemo","bidfloor":0.01,"bidfloorcur":"USD","clickbrowser":0,"secure":0,"exp":14400}],"app":{"id":"1234567","name":"DrawHappyAngel","bundle":"com.newstory.DrawHappyAngel","cat":["IAB9-30"],"ver":"0.5.4","paid":4,"publisher":{"id":"5890","name":"New Story Inc.","ext":{"installed_sdk":{"id":"MOLOCO_BIDDING","sdk_version":{"major":1,"minor":0,"micro":0},"adapter_version":{"major":1,"minor":0,"micro":0}}}},"ext":{"orientation":1}},"device":{"geo":{"lat":40.7429,"lon":-73.9392,"type":2,"ipservice":3,"country":"USA","region":"ny","metro":"501","city":"Queens","zip":"11101","ext":{"org":"Myanmar Broadband Telecom Co.","isp":"Myanmar Broadband Telecom Co."}},"ua":"Mozilla/5.0 (Linux; Android 13; 22101316C Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.230 Mobile Safari/537.36","ip":"38.158.207.171","devicetype":4,"make":"xiaomi","model":"22101316c","os":"android","osv":"13.0.0","hwv":"ruby","h":2400,"w":1080,"ppi":440,"pxratio":2.75,"js":1,"language":"en_US","carrier":"MYTEL","mccmnc":"310-260","connectiontype":5,"ifa":"497a10d6-c4dd-4e04-a986-c32b7180d462","ext":{"atts":3}},"user":{"data":[{"id":"1234"}],"ext":{"gdpr":0}},"at":1,"tmax":1000,"bcat":["IAB26-4","IAB26-2","IAB25-6","IAB25-5","IAB25-4","IAB25-3","IAB25-1","IAB25-7","IAB8-18","IAB26-3","IAB26-1","IAB8-5","IAB25-2","IAB11-4"],"source":{"ext":{"schain":{"ver":"1.0","complete":1,"nodes":[{"asi":"applovin.com","sid":"53bf468f18c5a0e2b7d4e3f748c677c1","rid":"494dbe15a3ce08c54f4e456363f35a022247f997","hp":1}]},"omidpn":"PubMatic","omidpv":"1.2.11-Pubmatic"}},"regs":{"ext":{"gdpr":0}},"ext":{"prebid":{"bidderparams":{"pubmatic":{"wrapper":{"profileid":12929,"versionid":1,"clientconfig":1}}}}}}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := updateAppLovinMaxRequest(tt.args.requestBody) + assert.Equal(t, tt.want, got, tt.name) + }) + } +} + +func TestUpdateRequestWrapper(t *testing.T) { + type args struct { + signalExt json.RawMessage + maxRequest *openrtb2.BidRequest + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "clientconfig not present", + args: args{ + signalExt: json.RawMessage(`{"ssauction":1}`), + maxRequest: &openrtb2.BidRequest{Ext: json.RawMessage(``)}, + }, + want: json.RawMessage(``), + }, + { + name: "clientconfig is 0", + args: args{ + signalExt: json.RawMessage(`{"wrapper":{"ssauction":1,"clientconfig":0}}`), + maxRequest: &openrtb2.BidRequest{Ext: json.RawMessage(``)}, + }, + want: json.RawMessage(``), + }, + { + name: "clientconfig is 1", + args: args{ + signalExt: json.RawMessage(`{"wrapper":{"ssauction":1,"clientconfig":1}}`), + maxRequest: &openrtb2.BidRequest{Ext: json.RawMessage(`{}`)}, + }, + want: json.RawMessage(`{"prebid":{"bidderparams":{"pubmatic":{"wrapper":{"clientconfig":1}}}}}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + updateRequestWrapper(tt.args.signalExt, tt.args.maxRequest) + assert.Equal(t, tt.want, tt.args.maxRequest.Ext) + }) + } +} + +func TestUpdateMaxApplovinResponse(t *testing.T) { + type args struct { + rctx models.RequestCtx + bidResponse *openrtb2.BidResponse + } + tests := []struct { + name string + args args + want models.AppLovinMax + }{ + { + name: "bidresponse contains NBR and debug is disabled", + args: args{ + rctx: models.RequestCtx{ + Debug: false, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "123", + NBR: ptrutil.ToPtr(nbr.InvalidPlatform), + }, + }, + want: models.AppLovinMax{ + Reject: true, + }, + }, + { + name: "bidresponse contains NBR and debug is enabled", + args: args{ + rctx: models.RequestCtx{ + Debug: true, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "123", + NBR: ptrutil.ToPtr(nbr.InvalidPlatform), + }, + }, + want: models.AppLovinMax{ + Reject: false, + }, + }, + { + name: "bidresponse seatbid is empty", + args: args{ + rctx: models.RequestCtx{ + Debug: false, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "123", + SeatBid: []openrtb2.SeatBid{}, + }, + }, + want: models.AppLovinMax{ + Reject: true, + }, + }, + { + name: "bidresponse seatbid.bid is empty", + args: args{ + rctx: models.RequestCtx{ + Debug: false, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "123", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{}, + }, + }, + }, + }, + want: models.AppLovinMax{ + Reject: true, + }, + }, + { + name: "No NBR and valid bidresponse", + args: args{ + rctx: models.RequestCtx{ + Debug: false, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "123", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "456", + ImpID: "789", + Price: 1.0, + AdM: "", + BURL: "http://example.com", + Ext: json.RawMessage(`{"key":"value"}`), + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + want: models.AppLovinMax{ + Reject: false, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := updateAppLovinMaxResponse(tt.args.rctx, tt.args.bidResponse) + assert.Equal(t, tt.want, got, tt.name) + }) + } +} + +func TestApplyMaxAppLovinResponse(t *testing.T) { + type args struct { + rctx models.RequestCtx + bidResponse *openrtb2.BidResponse + } + tests := []struct { + name string + args args + want *openrtb2.BidResponse + }{ + { + name: "AppLovinMax.Reject is true", + args: args{ + rctx: models.RequestCtx{ + Debug: true, + AppLovinMax: models.AppLovinMax{ + Reject: true, + }, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "123", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "456", + ImpID: "789", + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + ID: "123", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "456", + ImpID: "789", + }, + }, + }, + }, + }, + }, + { + name: "bidresponse contains NBR and AppLovinMax.Reject is false", + args: args{ + rctx: models.RequestCtx{ + Debug: false, + AppLovinMax: models.AppLovinMax{ + Reject: false, + }, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "123", + NBR: ptrutil.ToPtr(nbr.InvalidPlatform), + }, + }, + want: &openrtb2.BidResponse{ + ID: "123", + NBR: ptrutil.ToPtr(nbr.InvalidPlatform), + }, + }, + { + name: "failed to marshal bidresponse", + args: args{ + rctx: models.RequestCtx{ + Debug: true, + AppLovinMax: models.AppLovinMax{ + Reject: false, + }, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "123", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "123", + ImpID: "789", + }, + }, + }, + }, + Ext: json.RawMessage(`{`), + }, + }, + want: &openrtb2.BidResponse{ + ID: "123", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "123", + ImpID: "789", + }, + }, + }, + }, + Ext: json.RawMessage(`{`), + }, + }, + { + name: "valid bidresponse", + args: args{ + rctx: models.RequestCtx{ + AppLovinMax: models.AppLovinMax{ + Reject: false, + }, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "456", + ImpID: "789", + Price: 1.0, + AdM: "", + BURL: "http://example.com", + Ext: json.RawMessage(`{"key":"value"}`), + }, + }, + Seat: "pubmatic", + }, + }, + Ext: json.RawMessage(`{"key":"value"}`), + }, + }, + want: &openrtb2.BidResponse{ + ID: "123", + BidID: "456", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "456", + ImpID: "789", + Price: 1.0, + BURL: "http://example.com", + Ext: json.RawMessage(`{"signaldata":"{\"id\":\"123\",\"seatbid\":[{\"bid\":[{\"id\":\"456\",\"impid\":\"789\",\"price\":1,\"burl\":\"http://example.com\",\"adm\":\"\\u003cimg src=\\\"http://example.com\\\"\\u003e\\u003c/img\\u003e\",\"ext\":{\"key\":\"value\"}}],\"seat\":\"pubmatic\"}],\"cur\":\"USD\",\"ext\":{\"key\":\"value\"}}"}`), + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := applyAppLovinMaxResponse(tt.args.rctx, tt.args.bidResponse) + assert.Equal(t, tt.want, got, tt.name) + }) + } +} diff --git a/modules/pubmatic/openwrap/auctionresponsehook.go b/modules/pubmatic/openwrap/auctionresponsehook.go index 9a5f97e112f..7c546a11b9b 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook.go +++ b/modules/pubmatic/openwrap/auctionresponsehook.go @@ -280,6 +280,8 @@ func (m OpenWrap) handleAuctionResponseHook( result.DebugMessages = append(result.DebugMessages, string(rCtxBytes)) } + rctx.AppLovinMax = updateAppLovinMaxResponse(rctx, payload.BidResponse) + if rctx.Endpoint == models.EndpointWebS2S { result.ChangeSet.AddMutation(func(ap hookstage.AuctionResponsePayload) (hookstage.AuctionResponsePayload, error) { rctx := moduleCtx.ModuleContext["rctx"].(models.RequestCtx) @@ -315,6 +317,10 @@ func (m OpenWrap) handleAuctionResponseHook( ap.BidResponse.Ext = responseExtjson resetBidIdtoOriginal(ap.BidResponse) + + if rctx.Endpoint == models.EndpointAppLovinMax { + ap.BidResponse = applyAppLovinMaxResponse(rctx, ap.BidResponse) + } return ap, err }, hookstage.MutationUpdate, "response-body-with-sshb-format") diff --git a/modules/pubmatic/openwrap/auctionresponsehook_test.go b/modules/pubmatic/openwrap/auctionresponsehook_test.go index ac61e5d9b48..5f75bf056b8 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook_test.go +++ b/modules/pubmatic/openwrap/auctionresponsehook_test.go @@ -1935,3 +1935,126 @@ func TestOpenWrap_handleAuctionResponseHook(t *testing.T) { }) } } + +func TestAuctionResponseHookForApplovinMax(t *testing.T) { + ctrl := gomock.NewController(t) + mockCache := mock_cache.NewMockCache(ctrl) + mockFeature := mock_feature.NewMockFeature(ctrl) + defer ctrl.Finish() + + type args struct { + ctx context.Context + moduleCtx hookstage.ModuleInvocationContext + payload hookstage.AuctionResponsePayload + } + + type want struct { + bidResponse *openrtb2.BidResponse + err error + } + + tests := []struct { + name string + args args + want want + getMetricsEngine func() *mock_metrics.MockMetricsEngine + }{ + { + name: "update_the_bid_response_in_applovin_max_format", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + Platform: models.PLATFORM_VIDEO, + Endpoint: models.EndpointAppLovinMax, + ImpBidCtx: map[string]models.ImpCtx{ + "789": { + ImpID: "789", + }, + }, + BidderResponseTimeMillis: map[string]int{}, + Trackers: map[string]models.OWTracker{ + "456": { + TrackerURL: `Tracker URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "456", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "456", + ImpID: "789", + Price: 1.0, + AdM: `Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1https://dsptracker.com/{PSPM}00:00:04https://www.sample.com`, + BURL: "http://example.com", + Ext: json.RawMessage(`{"key":"value"}`), + }, + }, + Seat: "pubmatic", + }, + }, + Ext: json.RawMessage(`{"key":"value"}`), + }, + }, + }, + want: want{ + bidResponse: &openrtb2.BidResponse{ + ID: "123", + BidID: "456", + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "456", + ImpID: "789", + Price: 1.0, + BURL: `https:?adv=&af=video&aps=0&au=&bc=pubmatic&bidid=456&di=-1&eg=1&en=1&ft=0&iid=&kgpv=&orig=&origbidid=456&pdvid=0&pid=0&plt=0&pn=pubmatic&psz=0x0v&pubid=0&purl=&sl=1&slot=&ss=1&tgid=0&tst=0&owsspburl=http://example.com`, + Ext: json.RawMessage(`{"signaldata":"{\"id\":\"123\",\"seatbid\":[{\"bid\":[{\"id\":\"456\",\"impid\":\"789\",\"price\":1,\"burl\":\"https:?adv=\\u0026af=video\\u0026aps=0\\u0026au=\\u0026bc=pubmatic\\u0026bidid=456\\u0026di=-1\\u0026eg=1\\u0026en=1\\u0026ft=0\\u0026iid=\\u0026kgpv=\\u0026orig=\\u0026origbidid=456\\u0026pdvid=0\\u0026pid=0\\u0026plt=0\\u0026pn=pubmatic\\u0026psz=0x0v\\u0026pubid=0\\u0026purl=\\u0026sl=1\\u0026slot=\\u0026ss=1\\u0026tgid=0\\u0026tst=0\\u0026owsspburl=http://example.com\",\"adm\":\"\\u003cVAST version=\\\"3.0\\\"\\u003e\\u003cAd id=\\\"601364\\\"\\u003e\\u003cInLine\\u003e\\u003cAdSystem\\u003e\\u003c![CDATA[Acudeo Compatible]]\\u003e\\u003c/AdSystem\\u003e\\u003cAdTitle\\u003e\\u003c![CDATA[VAST 2.0 Instream Test 1]]\\u003e\\u003c/AdTitle\\u003e\\u003cDescription\\u003e\\u003c![CDATA[VAST 2.0 Instream Test 1]]\\u003e\\u003c/Description\\u003e\\u003cImpression\\u003e\\u003c![CDATA[http://172.16.4.213/AdServer/AdDisplayTrackerServlet?operId=1\\u0026pubId=5890\\u0026siteId=47163\\u0026adId=1405268\\u0026adType=13\\u0026adServerId=243\\u0026kefact=70.000000\\u0026kaxefact=70.000000\\u0026kadNetFrequecy=0\\u0026kadwidth=0\\u0026kadheight=0\\u0026kadsizeid=97\\u0026kltstamp=1529929473\\u0026indirectAdId=0\\u0026adServerOptimizerId=2\\u0026ranreq=0.1\\u0026kpbmtpfact=100.000000\\u0026dcId=1\\u0026tldId=0\\u0026passback=0\\u0026svr=MADS1107\\u0026ekefact=Ad8wW91TCwCmdG0jlfjXn7Tyzh20hnTVx-m5DoNSep-RXGDr\\u0026ekaxefact=Ad8wWwRUCwAGir4Zzl1eF0bKiC-qrCV0D0yp_eE7YizB_BQk\\u0026ekpbmtpfact=Ad8wWxRUCwD7qgzwwPE2LnS5-Ou19uO5amJl1YT6-XVFvQ41\\u0026imprId=48F73E1A-7F23-443D-A53C-30EE6BBF5F7F\\u0026oid=48F73E1A-7F23-443D-A53C-30EE6BBF5F7F\\u0026crID=creative-1_1_2\\u0026ucrid=160175026529250297\\u0026campaignId=17050\\u0026creativeId=0\\u0026pctr=0.000000\\u0026wDSPByrId=511\\u0026wDspId=6\\u0026wbId=0\\u0026wrId=0\\u0026wAdvID=3170\\u0026isRTB=1\\u0026rtbId=EBCA079F-8D7C-45B8-B733-92951F670AA1\\u0026pmZoneId=zone1\\u0026pageURL=www.yahoo.com\\u0026lpu=ae.com]]\\u003e\\u003c/Impression\\u003e\\u003cImpression\\u003e\\u003c![CDATA[https://dsptracker.com/{PSPM}]]\\u003e\\u003c/Impression\\u003e\\u003cError\\u003e\\u003c![CDATA[http://172.16.4.213/track?operId=7\\u0026p=5890\\u0026s=47163\\u0026a=1405268\\u0026wa=243\\u0026ts=1529929473\\u0026wc=17050\\u0026crId=creative-1_1_2\\u0026ucrid=160175026529250297\\u0026impid=48F73E1A-7F23-443D-A53C-30EE6BBF5F7F\\u0026advertiser_id=3170\\u0026ecpm=70.000000\\u0026er=[ERRORCODE]]]\\u003e\\u003c/Error\\u003e\\u003cError\\u003e\\u003c![CDATA[https://Errortrack.com?p=1234\\u0026er=[ERRORCODE]]]\\u003e\\u003c/Error\\u003e\\u003cCreatives\\u003e\\u003cCreative AdID=\\\"601364\\\"\\u003e\\u003cLinear skipoffset=\\\"20%\\\"\\u003e\\u003cDuration\\u003e\\u003c![CDATA[00:00:04]]\\u003e\\u003c/Duration\\u003e\\u003cVideoClicks\\u003e\\u003cClickTracking\\u003e\\u003c![CDATA[http://172.16.4.213/track?operId=7\\u0026p=5890\\u0026s=47163\\u0026a=1405268\\u0026wa=243\\u0026ts=1529929473\\u0026wc=17050\\u0026crId=creative-1_1_2\\u0026ucrid=160175026529250297\\u0026impid=48F73E1A-7F23-443D-A53C-30EE6BBF5F7F\\u0026advertiser_id=3170\\u0026ecpm=70.000000\\u0026e=99]]\\u003e\\u003c/ClickTracking\\u003e\\u003cClickThrough\\u003e\\u003c![CDATA[https://www.sample.com]]\\u003e\\u003c/ClickThrough\\u003e\\u003c/VideoClicks\\u003e\\u003cMediaFiles\\u003e\\u003cMediaFile delivery=\\\"progressive\\\" type=\\\"video/mp4\\\" bitrate=\\\"500\\\" width=\\\"400\\\" height=\\\"300\\\" scalable=\\\"true\\\" maintainAspectRatio=\\\"true\\\"\\u003e\\u003c![CDATA[https://stagingnyc.pubmatic.com:8443/video/Shashank/mediaFileHost/media/mp4-sample-1.mp4]]\\u003e\\u003c/MediaFile\\u003e\\u003cMediaFile delivery=\\\"progressive\\\" type=\\\"video/mp4\\\" bitrate=\\\"500\\\" width=\\\"400\\\" height=\\\"300\\\" scalable=\\\"true\\\" maintainAspectRatio=\\\"true\\\"\\u003e\\u003c![CDATA[https://stagingnyc.pubmatic.com:8443/video/Shashank/mediaFileHost/media/mp4-sample-2.mp4]]\\u003e\\u003c/MediaFile\\u003e\\u003c/MediaFiles\\u003e\\u003c/Linear\\u003e\\u003c/Creative\\u003e\\u003c/Creatives\\u003e\\u003cPricing model=\\\"CPM\\\" currency=\\\"USD\\\"\\u003e\\u003c![CDATA[1]]\\u003e\\u003c/Pricing\\u003e\\u003c/InLine\\u003e\\u003c/Ad\\u003e\\u003c/VAST\\u003e\",\"ext\":{\"prebid\":{},\"crtype\":\"video\",\"netecpm\":1}}],\"seat\":\"pubmatic\"}],\"bidid\":\"456\",\"cur\":\"USD\",\"ext\":{\"matchedimpression\":{}}}"}`), + }, + }, + }, + }, + }, + err: nil, + }, + getMetricsEngine: func() *mock_metrics.MockMetricsEngine { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats(gomock.Any(), gomock.Any(), gomock.Any()) + mockEngine.EXPECT().RecordPublisherResponseTimeStats(gomock.Any(), gomock.Any()) + mockFeature.EXPECT().IsFscApplicable(gomock.Any(), gomock.Any(), gomock.Any()).Return(false) + mockEngine.EXPECT().RecordPartnerResponseTimeStats(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + return mockEngine + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := OpenWrap{ + metricEngine: tt.getMetricsEngine(), + cache: mockCache, + pubFeatures: mockFeature, + } + hookResult, err := o.handleAuctionResponseHook(tt.args.ctx, tt.args.moduleCtx, tt.args.payload) + assert.Equal(t, tt.want.err, err, tt.name) + mutations := hookResult.ChangeSet.Mutations() + assert.NotEmpty(t, mutations, tt.name) + for _, mut := range mutations { + result, err := mut.Apply(tt.args.payload) + assert.Nil(t, err, tt.name) + assert.Equal(t, tt.want.bidResponse, result.BidResponse, tt.name) + } + }) + } +} diff --git a/modules/pubmatic/openwrap/beforevalidationhook_test.go b/modules/pubmatic/openwrap/beforevalidationhook_test.go index d5ac47da6a1..e9506cac09b 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook_test.go +++ b/modules/pubmatic/openwrap/beforevalidationhook_test.go @@ -2291,7 +2291,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -2337,7 +2337,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{}, nil) + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{}, nil) //prometheus metrics mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") mockEngine.EXPECT().RecordBadRequests(rctx.Endpoint, getPubmaticErrorCode(nbr.InvalidProfileConfiguration)) @@ -2370,7 +2370,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -2415,7 +2415,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -2461,7 +2461,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -2511,7 +2511,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -2557,7 +2557,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -2631,7 +2631,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -2721,7 +2721,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -2830,7 +2830,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PREBID_PARTNER_NAME: "appnexus", models.BidderCode: "appnexus", @@ -2918,7 +2918,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { "adunit@700x900": "1232433543534543", }, }).Times(3) - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 1: { models.PARTNER_ID: "1", models.PREBID_PARTNER_NAME: "pubmatic2", @@ -3033,7 +3033,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { "adunit@700x900": "1232433543534543", }, }) - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -3176,7 +3176,7 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { "adunit@700x900": "1232433543534543", }, }) - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -3452,7 +3452,7 @@ func TestVASTUnwrap_handleBeforeValidationHook(t *testing.T) { "adunit@700x900": "1232433543534543", }, }) - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -3513,7 +3513,7 @@ func TestVASTUnwrap_handleBeforeValidationHook(t *testing.T) { "adunit@700x900": "1232433543534543", }, }) - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -3579,7 +3579,7 @@ func TestVASTUnwrap_handleBeforeValidationHook(t *testing.T) { "adunit@700x900": "1232433543534543", }, }) - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -3645,7 +3645,7 @@ func TestVASTUnwrap_handleBeforeValidationHook(t *testing.T) { "adunit@700x900": "1232433543534543", }, }) - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -3707,7 +3707,7 @@ func TestVASTUnwrap_handleBeforeValidationHook(t *testing.T) { "adunit@700x900": "1232433543534543", }, }) - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -3810,7 +3810,7 @@ func TestImpBidCtx_handleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -3863,7 +3863,7 @@ func TestImpBidCtx_handleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -3916,7 +3916,7 @@ func TestImpBidCtx_handleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -3970,7 +3970,7 @@ func TestImpBidCtx_handleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", @@ -4016,7 +4016,7 @@ func TestImpBidCtx_handleBeforeValidationHook(t *testing.T) { metricEngine: mockEngine, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ 2: { models.PARTNER_ID: "2", models.PREBID_PARTNER_NAME: "appnexus", diff --git a/modules/pubmatic/openwrap/cache/cache.go b/modules/pubmatic/openwrap/cache/cache.go index 1890ea483c0..3f328b14635 100644 --- a/modules/pubmatic/openwrap/cache/cache.go +++ b/modules/pubmatic/openwrap/cache/cache.go @@ -7,7 +7,7 @@ import ( ) type Cache interface { - GetPartnerConfigMap(pubid, profileid, displayversion int, endpoint string) (map[int]map[string]string, error) + GetPartnerConfigMap(pubid, profileid, displayversion int) (map[int]map[string]string, error) GetAdunitConfigFromCache(request *openrtb2.BidRequest, pubID int, profileID, displayVersion int) *adunitconfig.AdUnitConfig GetMappingsFromCacheV25(rctx models.RequestCtx, partnerID int) map[string]models.SlotMapping GetSlotToHashValueMapFromCacheV25(rctx models.RequestCtx, partnerID int) models.SlotMappingInfo diff --git a/modules/pubmatic/openwrap/cache/gocache/partner_config.go b/modules/pubmatic/openwrap/cache/gocache/partner_config.go index 51579f20958..64cf28b65e9 100644 --- a/modules/pubmatic/openwrap/cache/gocache/partner_config.go +++ b/modules/pubmatic/openwrap/cache/gocache/partner_config.go @@ -11,7 +11,7 @@ import ( ) // GetPartnerConfigMap returns partnerConfigMap using given parameters -func (c *cache) GetPartnerConfigMap(pubID, profileID, displayVersion int, endpoint string) (map[int]map[string]string, error) { +func (c *cache) GetPartnerConfigMap(pubID, profileID, displayVersion int) (map[int]map[string]string, error) { dbAccessed := false var err error startTime := time.Now() @@ -58,7 +58,7 @@ func (c *cache) GetPartnerConfigMap(pubID, profileID, displayVersion int, endpoi } if dbAccessed { - c.metricEngine.RecordGetProfileDataTime(endpoint, strconv.Itoa(profileID), time.Since(startTime)) + c.metricEngine.RecordGetProfileDataTime(time.Since(startTime)) } return partnerConfigMap, err } @@ -75,25 +75,29 @@ func (c *cache) getActivePartnerConfigAndPopulateWrapperMappings(pubID, profileI return fmt.Errorf("there are no active partners for pubId:%d, profileId:%d, displayVersion:%d", pubID, profileID, displayVersion) } - c.cache.Set(cacheKey, partnerConfigMap, getSeconds(c.cfg.CacheDefaultExpiry)) - if errWrapperSlotMapping := c.populateCacheWithWrapperSlotMappings(pubID, partnerConfigMap, profileID, displayVersion); errWrapperSlotMapping != nil { - err = models.ErrorWrap(err, errWrapperSlotMapping) + err = c.populateCacheWithWrapperSlotMappings(pubID, partnerConfigMap, profileID, displayVersion) + if err != nil { queryType := models.WrapperSlotMappingsQuery if displayVersion == 0 { queryType = models.WrapperLiveVersionSlotMappings } c.metricEngine.RecordDBQueryFailure(queryType, strconv.Itoa(pubID), strconv.Itoa(profileID)) + return err } - if errAdunitConfig := c.populateCacheWithAdunitConfig(pubID, profileID, displayVersion); errAdunitConfig != nil { + + err = c.populateCacheWithAdunitConfig(pubID, profileID, displayVersion) + if err != nil { queryType := models.AdunitConfigQuery if displayVersion == 0 { queryType = models.AdunitConfigForLiveVersion } - if errors.Is(errAdunitConfig, adunitconfig.ErrAdUnitUnmarshal) { + if errors.Is(err, adunitconfig.ErrAdUnitUnmarshal) { queryType = models.AdUnitFailUnmarshal } c.metricEngine.RecordDBQueryFailure(queryType, strconv.Itoa(pubID), strconv.Itoa(profileID)) - err = models.ErrorWrap(err, errAdunitConfig) + return err } + + c.cache.Set(cacheKey, partnerConfigMap, getSeconds(c.cfg.CacheDefaultExpiry)) return } diff --git a/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go b/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go index 464d1ce5ad5..31783709235 100644 --- a/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go @@ -1,6 +1,7 @@ package gocache import ( + "errors" "fmt" "sync" "testing" @@ -26,7 +27,6 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { pubID int profileID int displayVersion int - endpoint string } tests := []struct { name string @@ -49,7 +49,6 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { pubID: testPubID, profileID: testProfileID, displayVersion: testVersionID, - endpoint: models.EndpointV25, }, setup: func(ctrl *gomock.Controller) (*mock_database.MockDatabase, *mock_metrics.MockMetricsEngine) { mockDatabase := mock_database.NewMockDatabase(ctrl) @@ -69,7 +68,7 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { }, }, }, nil) - mockEngine.EXPECT().RecordGetProfileDataTime(models.EndpointV25, "123", gomock.Any()).Return().Times(1) + mockEngine.EXPECT().RecordGetProfileDataTime(gomock.Any()).Return().Times(1) return mockDatabase, mockEngine }, wantErr: false, @@ -98,7 +97,6 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { pubID: testPubID, profileID: testProfileID, displayVersion: testVersionID, - endpoint: models.EndpointV25, }, setup: func(ctrl *gomock.Controller) (*mock_database.MockDatabase, *mock_metrics.MockMetricsEngine) { mockDatabase := mock_database.NewMockDatabase(ctrl) @@ -106,7 +104,7 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { mockDatabase.EXPECT().GetActivePartnerConfigurations(testPubID, testProfileID, testVersionID).Return(nil, fmt.Errorf("Error from the DB")) mockDatabase.EXPECT().GetPublisherSlotNameHash(testPubID).Return(nil, fmt.Errorf("Error from the DB")) mockDatabase.EXPECT().GetPublisherVASTTags(testPubID).Return(nil, fmt.Errorf("Error from the DB")) - mockEngine.EXPECT().RecordGetProfileDataTime(models.EndpointV25, "123", gomock.Any()).Return() + mockEngine.EXPECT().RecordGetProfileDataTime(gomock.Any()).Return() mockEngine.EXPECT().RecordDBQueryFailure(models.SlotNameHash, "5890", "123").Return() mockEngine.EXPECT().RecordDBQueryFailure(models.PartnerConfigQuery, "5890", "123").Return() mockEngine.EXPECT().RecordDBQueryFailure(models.PublisherVASTTagsQuery, "5890", "123").Return() @@ -116,7 +114,7 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { want: nil, }, { - name: "db_queries_failed_getting_adunitconfig_and_wrapper_slotmappings", + name: "error_in_adunitconfig_unmarshal", fields: fields{ cache: gocache.New(100, 100), cfg: config.Cache{ @@ -128,7 +126,6 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { pubID: testPubID, profileID: testProfileID, displayVersion: 0, - endpoint: models.EndpointAMP, }, setup: func(ctrl *gomock.Controller) (*mock_database.MockDatabase, *mock_metrics.MockMetricsEngine) { mockDatabase := mock_database.NewMockDatabase(ctrl) @@ -137,24 +134,69 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { mockDatabase.EXPECT().GetPublisherSlotNameHash(testPubID).Return(map[string]string{"adunit@728x90": "2aa34b52a9e941c1594af7565e599c8d"}, nil) mockDatabase.EXPECT().GetPublisherVASTTags(testPubID).Return(nil, nil) mockDatabase.EXPECT().GetAdunitConfig(testProfileID, 0).Return(nil, adunitconfig.ErrAdUnitUnmarshal) - mockDatabase.EXPECT().GetWrapperSlotMappings(formTestPartnerConfig(), testProfileID, 0).Return(nil, fmt.Errorf("Error from the DB")) - mockEngine.EXPECT().RecordGetProfileDataTime(models.EndpointAMP, "123", gomock.Any()).Return().Times(1) + mockDatabase.EXPECT().GetWrapperSlotMappings(formTestPartnerConfig(), testProfileID, 0).Return(nil, nil) + mockEngine.EXPECT().RecordGetProfileDataTime(gomock.Any()).Return().Times(1) mockEngine.EXPECT().RecordDBQueryFailure(models.AdUnitFailUnmarshal, "5890", "123").Return().Times(1) - mockEngine.EXPECT().RecordDBQueryFailure(models.WrapperLiveVersionSlotMappings, "5890", "123").Return().Times(1) return mockDatabase, mockEngine }, wantErr: true, - want: map[int]map[string]string{ - 1: { - "partnerId": "1", - "prebidPartnerName": "pubmatic", - "serverSideEnabled": "1", - "level": "multi", - "kgp": "_AU_@_W_x_H", - "timeout": "220", - "bidderCode": "pubmatic", + want: nil, + }, + { + name: "db_queries_failed_getting_adunitconfig", + fields: fields{ + cache: gocache.New(100, 100), + cfg: config.Cache{ + CacheDefaultExpiry: 1000, + VASTTagCacheExpiry: 1000, + }, + }, + args: args{ + pubID: testPubID, + profileID: testProfileID, + displayVersion: 0, + }, + setup: func(ctrl *gomock.Controller) (*mock_database.MockDatabase, *mock_metrics.MockMetricsEngine) { + mockDatabase := mock_database.NewMockDatabase(ctrl) + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockDatabase.EXPECT().GetActivePartnerConfigurations(testPubID, testProfileID, 0).Return(formTestPartnerConfig(), nil) + mockDatabase.EXPECT().GetPublisherSlotNameHash(testPubID).Return(map[string]string{"adunit@728x90": "2aa34b52a9e941c1594af7565e599c8d"}, nil) + mockDatabase.EXPECT().GetPublisherVASTTags(testPubID).Return(nil, nil) + mockDatabase.EXPECT().GetAdunitConfig(testProfileID, 0).Return(nil, errors.New("Failed to connect DB")) + mockDatabase.EXPECT().GetWrapperSlotMappings(formTestPartnerConfig(), testProfileID, 0).Return(nil, nil) + mockEngine.EXPECT().RecordGetProfileDataTime(gomock.Any()).Return().Times(1) + mockEngine.EXPECT().RecordDBQueryFailure(models.AdunitConfigForLiveVersion, "5890", "123").Return().Times(1) + return mockDatabase, mockEngine + }, + wantErr: true, + want: nil}, + { + name: "db_queries_failed_getting_wrapper_slotmappings", + fields: fields{ + cache: gocache.New(100, 100), + cfg: config.Cache{ + CacheDefaultExpiry: 1000, + VASTTagCacheExpiry: 1000, }, }, + args: args{ + pubID: testPubID, + profileID: testProfileID, + displayVersion: 0, + }, + setup: func(ctrl *gomock.Controller) (*mock_database.MockDatabase, *mock_metrics.MockMetricsEngine) { + mockDatabase := mock_database.NewMockDatabase(ctrl) + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockDatabase.EXPECT().GetActivePartnerConfigurations(testPubID, testProfileID, 0).Return(formTestPartnerConfig(), nil) + mockDatabase.EXPECT().GetPublisherSlotNameHash(testPubID).Return(map[string]string{"adunit@728x90": "2aa34b52a9e941c1594af7565e599c8d"}, nil) + mockDatabase.EXPECT().GetPublisherVASTTags(testPubID).Return(nil, nil) + mockDatabase.EXPECT().GetWrapperSlotMappings(formTestPartnerConfig(), testProfileID, 0).Return(nil, fmt.Errorf("Error from the DB")) + mockEngine.EXPECT().RecordGetProfileDataTime(gomock.Any()).Return().Times(1) + mockEngine.EXPECT().RecordDBQueryFailure(models.WrapperLiveVersionSlotMappings, "5890", "123").Return().Times(1) + return mockDatabase, mockEngine + }, + wantErr: true, + want: nil, }, } for ind := range tests { @@ -169,7 +211,7 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { } c.db, c.metricEngine = tt.setup(ctrl) - got, err := c.GetPartnerConfigMap(tt.args.pubID, tt.args.profileID, tt.args.displayVersion, tt.args.endpoint) + got, err := c.GetPartnerConfigMap(tt.args.pubID, tt.args.profileID, tt.args.displayVersion) if (err != nil) != tt.wantErr { t.Errorf("cache.GetPartnerConfigMap() error = %v, wantErr %v", err, tt.wantErr) return @@ -189,7 +231,6 @@ func Test_cache_GetPartnerConfigMap_LockandLoad(t *testing.T) { pubID int profileID int displayVersion int - endpoint string } tests := []struct { name string @@ -212,7 +253,6 @@ func Test_cache_GetPartnerConfigMap_LockandLoad(t *testing.T) { pubID: testPubID, profileID: testProfileID, displayVersion: testVersionID, - endpoint: models.EndpointV25, }, setup: func(ctrl *gomock.Controller) (*mock_database.MockDatabase, *mock_metrics.MockMetricsEngine) { mockDatabase := mock_database.NewMockDatabase(ctrl) @@ -232,7 +272,7 @@ func Test_cache_GetPartnerConfigMap_LockandLoad(t *testing.T) { }, }, }, nil) - mockEngine.EXPECT().RecordGetProfileDataTime(models.EndpointV25, "123", gomock.Any()).Return().Times(1) + mockEngine.EXPECT().RecordGetProfileDataTime(gomock.Any()).Return().Times(1) return mockDatabase, mockEngine }, }, @@ -253,7 +293,7 @@ func Test_cache_GetPartnerConfigMap_LockandLoad(t *testing.T) { for i := 0; i < 10; i++ { wg.Add(1) go func() { - c.GetPartnerConfigMap(tt.args.pubID, tt.args.profileID, tt.args.displayVersion, tt.args.endpoint) + c.GetPartnerConfigMap(tt.args.pubID, tt.args.profileID, tt.args.displayVersion) wg.Done() }() } @@ -302,6 +342,73 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { want want setup func() }{ + { + name: "non_nil_partnerConfigMap_from_DB", + fields: fields{ + cache: gocache.New(100, 100), + cfg: config.Cache{ + CacheDefaultExpiry: 100, + }, + db: mockDatabase, + }, + args: args{ + pubID: testPubID, + profileID: testProfileID, + displayVersion: testVersionID, + }, + want: want{ + cacheEntry: true, + err: nil, + partnerConfigMap: map[int]map[string]string{ + 1: { + "bidderCode": "pubmatic", + "kgp": "_AU_@_W_x_H", + "level": "multi", + "partnerId": "1", + "prebidPartnerName": "pubmatic", + "serverSideEnabled": "1", + "timeout": "220", + }, + }, + }, + setup: func() { + mockDatabase.EXPECT().GetActivePartnerConfigurations(testPubID, testProfileID, testVersionID).Return(formTestPartnerConfig(), nil) + mockDatabase.EXPECT().GetAdunitConfig(testProfileID, testVersionID).Return(nil, nil) + mockDatabase.EXPECT().GetWrapperSlotMappings(formTestPartnerConfig(), testProfileID, testVersionID).Return(map[int][]models.SlotMapping{ + 1: { + { + PartnerId: testPartnerID, + AdapterId: testAdapterID, + VersionId: testVersionID, + SlotName: testSlotName, + MappingJson: "{\"adtag\":\"1405192\",\"site\":\"47124\",\"video\":{\"skippable\":\"TRUE\"}}", + }, + }, + }, nil) + }, + }, + { + name: "empty_partnerConfigMap_from_DB", + fields: fields{ + cache: gocache.New(100, 100), + cfg: config.Cache{ + CacheDefaultExpiry: 100, + }, + db: mockDatabase, + }, + args: args{ + pubID: testPubID, + profileID: testProfileID, + displayVersion: testVersionID, + }, + want: want{ + cacheEntry: false, + err: fmt.Errorf("there are no active partners for pubId:%d, profileId:%d, displayVersion:%d", testPubID, testProfileID, testVersionID), + }, + setup: func() { + mockDatabase.EXPECT().GetActivePartnerConfigurations(testPubID, testProfileID, testVersionID).Return(nil, nil) + }, + }, { name: "error_returning_Active_partner_configuration_from_DB", fields: fields{ @@ -317,9 +424,8 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { displayVersion: testVersionID, }, want: want{ - cacheEntry: false, - err: fmt.Errorf("Error from the DB"), - partnerConfigMap: nil, + cacheEntry: false, + err: fmt.Errorf("Error from the DB"), }, setup: func() { mockDatabase.EXPECT().GetActivePartnerConfigurations(testPubID, testProfileID, testVersionID).Return(nil, fmt.Errorf("Error from the DB")) @@ -327,7 +433,67 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { }, }, { - name: "non_nil_partnerConfigMap_from_DB", + name: "No partner config in case of error in GetWrapperSlotMappings", + fields: fields{ + cache: gocache.New(100, 100), + cfg: config.Cache{ + CacheDefaultExpiry: 100, + }, + db: mockDatabase, + }, + args: args{ + pubID: testPubID, + profileID: testProfileID, + displayVersion: testVersionID, + }, + want: want{ + cacheEntry: false, + err: fmt.Errorf("Error from the DB"), + }, + setup: func() { + mockDatabase.EXPECT().GetActivePartnerConfigurations(testPubID, testProfileID, testVersionID).Return(formTestPartnerConfig(), nil) + + mockDatabase.EXPECT().GetWrapperSlotMappings(formTestPartnerConfig(), testProfileID, testVersionID).Return(nil, errors.New("Error from the DB")) + mockEngine.EXPECT().RecordDBQueryFailure(models.WrapperSlotMappingsQuery, "5890", "123").Return() + }, + }, + { + name: "No partner config in case of error in GetAdunitConfig", + fields: fields{ + cache: gocache.New(100, 100), + cfg: config.Cache{ + CacheDefaultExpiry: 100, + }, + db: mockDatabase, + }, + args: args{ + pubID: testPubID, + profileID: testProfileID, + displayVersion: testVersionID, + }, + want: want{ + cacheEntry: false, + err: fmt.Errorf("Error from the DB"), + }, + setup: func() { + mockDatabase.EXPECT().GetActivePartnerConfigurations(testPubID, testProfileID, testVersionID).Return(formTestPartnerConfig(), nil) + mockDatabase.EXPECT().GetAdunitConfig(testProfileID, testVersionID).Return(nil, errors.New("Error from the DB")) + mockEngine.EXPECT().RecordDBQueryFailure(models.AdunitConfigQuery, "5890", "123").Return() + mockDatabase.EXPECT().GetWrapperSlotMappings(formTestPartnerConfig(), testProfileID, testVersionID).Return(map[int][]models.SlotMapping{ + 1: { + { + PartnerId: testPartnerID, + AdapterId: testAdapterID, + VersionId: testVersionID, + SlotName: testSlotName, + MappingJson: "{\"adtag\":\"1405192\",\"site\":\"47124\",\"video\":{\"skippable\":\"TRUE\"}}", + }, + }, + }, nil) + }, + }, + { + name: "Partner config in case of empty wrapperSlotMappings", fields: fields{ cache: gocache.New(100, 100), cfg: config.Cache{ @@ -358,21 +524,47 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { setup: func() { mockDatabase.EXPECT().GetActivePartnerConfigurations(testPubID, testProfileID, testVersionID).Return(formTestPartnerConfig(), nil) mockDatabase.EXPECT().GetAdunitConfig(testProfileID, testVersionID).Return(nil, nil) - mockDatabase.EXPECT().GetWrapperSlotMappings(formTestPartnerConfig(), testProfileID, testVersionID).Return(map[int][]models.SlotMapping{ + mockDatabase.EXPECT().GetWrapperSlotMappings(formTestPartnerConfig(), testProfileID, testVersionID).Return(nil, nil) + }, + }, + { + name: "Partner config in case of empty adunitConfig", + fields: fields{ + cache: gocache.New(100, 100), + cfg: config.Cache{ + CacheDefaultExpiry: 100, + }, + db: mockDatabase, + }, + args: args{ + pubID: testPubID, + profileID: testProfileID, + displayVersion: testVersionID, + }, + want: want{ + cacheEntry: true, + err: nil, + partnerConfigMap: map[int]map[string]string{ 1: { - { - PartnerId: testPartnerID, - AdapterId: testAdapterID, - VersionId: testVersionID, - SlotName: testSlotName, - MappingJson: "{\"adtag\":\"1405192\",\"site\":\"47124\",\"video\":{\"skippable\":\"TRUE\"}}", - }, + "bidderCode": "pubmatic", + "kgp": "_AU_@_W_x_H", + "level": "multi", + "partnerId": "1", + "prebidPartnerName": "pubmatic", + "serverSideEnabled": "1", + "timeout": "220", }, - }, nil) + }, + }, + setup: func() { + mockDatabase.EXPECT().GetActivePartnerConfigurations(testPubID, testProfileID, testVersionID).Return(formTestPartnerConfig(), nil) + mockDatabase.EXPECT().GetAdunitConfig(testProfileID, testVersionID).Return(nil, nil) + mockDatabase.EXPECT().GetWrapperSlotMappings(formTestPartnerConfig(), testProfileID, testVersionID).Return(nil, nil) }, }, + { - name: "empty_partnerConfigMap_from_DB", + name: "Partner config in case of empty adunitConfig and wrapperSlotMappings", fields: fields{ cache: gocache.New(100, 100), cfg: config.Cache{ @@ -386,12 +578,24 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { displayVersion: testVersionID, }, want: want{ - cacheEntry: false, - err: fmt.Errorf("there are no active partners for pubId:%d, profileId:%d, displayVersion:%d", testPubID, testProfileID, testVersionID), - partnerConfigMap: nil, + cacheEntry: true, + err: nil, + partnerConfigMap: map[int]map[string]string{ + 1: { + "bidderCode": "pubmatic", + "kgp": "_AU_@_W_x_H", + "level": "multi", + "partnerId": "1", + "prebidPartnerName": "pubmatic", + "serverSideEnabled": "1", + "timeout": "220", + }, + }, }, setup: func() { - mockDatabase.EXPECT().GetActivePartnerConfigurations(testPubID, testProfileID, testVersionID).Return(nil, nil) + mockDatabase.EXPECT().GetActivePartnerConfigurations(testPubID, testProfileID, testVersionID).Return(formTestPartnerConfig(), nil) + mockDatabase.EXPECT().GetAdunitConfig(testProfileID, testVersionID).Return(nil, nil) + mockDatabase.EXPECT().GetWrapperSlotMappings(formTestPartnerConfig(), testProfileID, testVersionID).Return(nil, nil) }, }, } @@ -408,13 +612,13 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { metricEngine: mockEngine, } err := c.getActivePartnerConfigAndPopulateWrapperMappings(tt.args.pubID, tt.args.profileID, tt.args.displayVersion) - assert.Equal(t, tt.want.err, err) cacheKey := key(PUB_HB_PARTNER, tt.args.pubID, tt.args.profileID, tt.args.displayVersion) partnerConfigMap, found := c.Get(cacheKey) if tt.want.cacheEntry { assert.True(t, found) assert.Equal(t, tt.want.partnerConfigMap, partnerConfigMap) } else { + assert.Equal(t, tt.want.err.Error(), err.Error()) assert.False(t, found) assert.Nil(t, partnerConfigMap) } diff --git a/modules/pubmatic/openwrap/cache/mock/mock.go b/modules/pubmatic/openwrap/cache/mock/mock.go index fd2c88b0506..e7f1cf2bd05 100644 --- a/modules/pubmatic/openwrap/cache/mock/mock.go +++ b/modules/pubmatic/openwrap/cache/mock/mock.go @@ -95,18 +95,18 @@ func (mr *MockCacheMockRecorder) GetMappingsFromCacheV25(arg0, arg1 interface{}) } // GetPartnerConfigMap mocks base method. -func (m *MockCache) GetPartnerConfigMap(arg0, arg1, arg2 int, arg3 string) (map[int]map[string]string, error) { +func (m *MockCache) GetPartnerConfigMap(arg0, arg1, arg2 int) (map[int]map[string]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPartnerConfigMap", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "GetPartnerConfigMap", arg0, arg1, arg2) ret0, _ := ret[0].(map[int]map[string]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPartnerConfigMap indicates an expected call of GetPartnerConfigMap. -func (mr *MockCacheMockRecorder) GetPartnerConfigMap(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockCacheMockRecorder) GetPartnerConfigMap(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPartnerConfigMap", reflect.TypeOf((*MockCache)(nil).GetPartnerConfigMap), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPartnerConfigMap", reflect.TypeOf((*MockCache)(nil).GetPartnerConfigMap), arg0, arg1, arg2) } // GetPublisherFeatureMap mocks base method. diff --git a/modules/pubmatic/openwrap/entrypointhook.go b/modules/pubmatic/openwrap/entrypointhook.go index 7e619b8f65f..215289f698d 100644 --- a/modules/pubmatic/openwrap/entrypointhook.go +++ b/modules/pubmatic/openwrap/entrypointhook.go @@ -55,12 +55,21 @@ func (m OpenWrap) handleEntrypointHook( rCtx.VastUnwrapEnabled = getVastUnwrapperEnable(payload.Request.Context(), models.VastUnwrapperEnableKey) return result, nil } - endpoint = GetEndpoint(payload.Request.URL.Path, source) + endpoint = GetEndpoint(payload.Request.URL.Path, source, queryParams.Get(models.Agent)) if endpoint == models.EndpointHybrid { rCtx.Endpoint = models.EndpointHybrid return result, nil } + if endpoint == models.EndpointAppLovinMax { + // updating body locally to access updated fields from signal + payload.Body = updateAppLovinMaxRequest(payload.Body) + result.ChangeSet.AddMutation(func(ep hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) { + ep.Body = payload.Body + return ep, nil + }, hookstage.MutationUpdate, "update-max-app-lovin-request") + } + // init default for all modules result.Reject = true @@ -149,7 +158,7 @@ func GetRequestWrapper(payload hookstage.EntrypointPayload, result hookstage.Hoo fallthrough case models.EndpointVideo, models.EndpointVAST, models.EndpointJson: requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body, "ext", "wrapper") - case models.EndpointWebS2S: + case models.EndpointWebS2S, models.EndpointAppLovinMax: fallthrough default: requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body) @@ -158,13 +167,17 @@ func GetRequestWrapper(payload hookstage.EntrypointPayload, result hookstage.Hoo return requestExtWrapper, err } -func GetEndpoint(path, source string) string { +func GetEndpoint(path, source string, agent string) string { switch path { case hookexecution.EndpointAuction: switch source { case "pbjs": return models.EndpointWebS2S case "owsdk": + switch agent { + case models.AppLovinMaxAgent: + return models.EndpointAppLovinMax + } return models.EndpointV25 default: return models.EndpointHybrid diff --git a/modules/pubmatic/openwrap/entrypointhook_test.go b/modules/pubmatic/openwrap/entrypointhook_test.go index 317c73c1681..bea55d862b6 100644 --- a/modules/pubmatic/openwrap/entrypointhook_test.go +++ b/modules/pubmatic/openwrap/entrypointhook_test.go @@ -34,11 +34,13 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { setup func(*mock_metrics.MockMetricsEngine) } tests := []struct { - name string - fields fields - args args - want hookstage.HookResult[hookstage.EntrypointPayload] - wantErr error + name string + fields fields + args args + want hookstage.HookResult[hookstage.EntrypointPayload] + wantBody []byte + wantErr error + doMutate bool }{ { name: "request with sshb=1 should not execute entrypointhook", @@ -494,6 +496,73 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { }, wantErr: nil, }, + { + name: "valid request for applovin max /openrtb2/auction?source=owsdk&agent=max request", + fields: fields{ + cfg: config.Config{ + Tracker: config.Tracker{ + Endpoint: "t.pubmatic.com", + VideoErrorTrackerEndpoint: "t.pubmatic.com/error", + }, + }, + cache: nil, + }, + args: args{ + in0: context.Background(), + miCtx: hookstage.ModuleInvocationContext{}, + payload: hookstage.EntrypointPayload{ + Request: func() *http.Request { + r, err := http.NewRequest("POST", "http://localhost/openrtb2/auction?source=owsdk&agent=max&debug=1", nil) + if err != nil { + panic(err) + } + r.Header.Add("User-Agent", "go-test") + r.Header.Add("SOURCE_IP", "127.0.0.1") + r.Header.Add("Cookie", `KADUSERCOOKIE=7D75D25F-FAC9-443D-B2D1-B17FEE11E027; DPSync3=1684886400%3A248%7C1685491200%3A245_226_201; KRTBCOOKIE_80=16514-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&22987-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23025-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23386-CAESEMih0bN7ISRdZT8xX8LXzEw; KRTBCOOKIE_377=6810-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&22918-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&23031-59dc50c9-d658-44ce-b442-5a1f344d97c0; uids=eyJ0ZW1wVUlEcyI6eyIzM2Fjcm9zcyI6eyJ1aWQiOiIxMTkxNzkxMDk5Nzc2NjEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTo0My4zODg4Nzk5NVoifSwiYWRmIjp7InVpZCI6IjgwNDQ2MDgzMzM3Nzg4MzkwNzgiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMS4wMzMwNTQ3MjdaIn0sImFka2VybmVsIjp7InVpZCI6IkE5MTYzNTAwNzE0OTkyOTMyOTkwIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuMzczMzg1NjYyWiJ9LCJhZGtlcm5lbEFkbiI6eyJ1aWQiOiJBOTE2MzUwMDcxNDk5MjkzMjk5MCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEzLjQzNDkyNTg5NloifSwiYWRtaXhlciI6eyJ1aWQiOiIzNjZhMTdiMTJmMjI0ZDMwOGYzZTNiOGRhOGMzYzhhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjU5MjkxNDgwMVoifSwiYWRueHMiOnsidWlkIjoiNDE5Mjg5ODUzMDE0NTExOTMiLCJleHBpcmVzIjoiMjAyMy0wMS0xOFQwOTo1MzowOC44MjU0NDI2NzZaIn0sImFqYSI6eyJ1aWQiOiJzMnN1aWQ2RGVmMFl0bjJveGQ1aG9zS1AxVmV3IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTMuMjM5MTc2MDU0WiJ9LCJlcGxhbm5pbmciOnsidWlkIjoiQUoxRjBTOE5qdTdTQ0xWOSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjkyOTk2MDQ3M1oifSwiZ2Ftb3NoaSI6eyJ1aWQiOiJndXNyXzM1NmFmOWIxZDhjNjQyYjQ4MmNiYWQyYjdhMjg4MTYxIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuNTI0MTU3MjI1WiJ9LCJncmlkIjp7InVpZCI6IjRmYzM2MjUwLWQ4NTItNDU5Yy04NzcyLTczNTZkZTE3YWI5NyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE0LjY5NjMxNjIyN1oifSwiZ3JvdXBtIjp7InVpZCI6IjdENzVEMjVGLUZBQzktNDQzRC1CMkQxLUIxN0ZFRTExRTAyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjM5LjIyNjIxMjUzMloifSwiaXgiOnsidWlkIjoiWW9ORlNENlc5QkphOEh6eEdtcXlCUUFBXHUwMDI2Mjk3IiwiZXhwaXJlcyI6IjIwMjMtMDUtMzFUMDc6NTM6MzguNTU1ODI3MzU0WiJ9LCJqaXhpZSI6eyJ1aWQiOiI3MzY3MTI1MC1lODgyLTExZWMtYjUzOC0xM2FjYjdhZjBkZTQiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi4xOTEwOTk3MzJaIn0sImxvZ2ljYWQiOnsidWlkIjoiQVZ4OVROQS11c25pa3M4QURzTHpWa3JvaDg4QUFBR0JUREh0UUEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS40NTUxNDk2MTZaIn0sIm1lZGlhbmV0Ijp7InVpZCI6IjI5Nzg0MjM0OTI4OTU0MTAwMDBWMTAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMy42NzIyMTUxMjhaIn0sIm1naWQiOnsidWlkIjoibTU5Z1hyN0xlX1htIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTcuMDk3MDAxNDcxWiJ9LCJuYW5vaW50ZXJhY3RpdmUiOnsidWlkIjoiNmFlYzhjMTAzNzlkY2I3ODQxMmJjODBiNmRkOWM5NzMxNzNhYjdkNzEyZTQzMWE1YTVlYTcwMzRlNTZhNThhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE2LjcxNDgwNzUwNVoifSwib25ldGFnIjp7InVpZCI6IjdPelZoVzFOeC1LOGFVak1HMG52NXVNYm5YNEFHUXZQbnVHcHFrZ3k0ckEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS4xNDE3NDEyNjJaIn0sIm9wZW54Ijp7InVpZCI6IjVkZWNlNjIyLTBhMjMtMGRhYi0zYTI0LTVhNzcwMTBlNDU4MiIsImV4cGlyZXMiOiIyMDIzLTA1LTMxVDA3OjUyOjQ3LjE0MDQxNzM2M1oifSwicHVibWF0aWMiOnsidWlkIjoiN0Q3NUQyNUYtRkFDOS00NDNELUIyRDEtQjE3RkVFMTFFMDI3IiwiZXhwaXJlcyI6IjIwMjItMTAtMzFUMDk6MTQ6MjUuNzM3MjU2ODk5WiJ9LCJyaWNoYXVkaWVuY2UiOnsidWlkIjoiY2I2YzYzMjAtMzNlMi00Nzc0LWIxNjAtMXp6MTY1NDg0MDc0OSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjUyNTA3NDE4WiJ9LCJzbWFydHlhZHMiOnsidWlkIjoiMTJhZjE1ZTQ0ZjAwZDA3NjMwZTc0YzQ5MTU0Y2JmYmE0Zjg0N2U4ZDRhMTU0YzhjM2Q1MWY1OGNmNzJhNDYyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjgyNTAzMTg4NFoifSwic21pbGV3YW50ZWQiOnsidWlkIjoiZGQ5YzNmZTE4N2VmOWIwOWNhYTViNzExNDA0YzI4MzAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNC4yNTU2MDkzNjNaIn0sInN5bmFjb3JtZWRpYSI6eyJ1aWQiOiJHRFBSIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuOTc5NTgzNDM4WiJ9LCJ0cmlwbGVsaWZ0Ijp7InVpZCI6IjcwMjE5NzUwNTQ4MDg4NjUxOTQ2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA4Ljk4OTY3MzU3NFoifSwidmFsdWVpbXByZXNzaW9uIjp7InVpZCI6IjlkMDgxNTVmLWQ5ZmUtNGI1OC04OThlLWUyYzU2MjgyYWIzZSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjA2NzgzOTE2NFoifSwidmlzeCI6eyJ1aWQiOiIyN2UwYWMzYy1iNDZlLTQxYjMtOTkyYy1mOGQyNzE0OTQ5NWUiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi45ODk1MjM1NzNaIn0sInlpZWxkbGFiIjp7InVpZCI6IjY5NzE0ZDlmLWZiMDAtNGE1Zi04MTljLTRiZTE5MTM2YTMyNSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjExLjMwMzAyNjYxNVoifSwieWllbGRtbyI6eyJ1aWQiOiJnOTZjMmY3MTlmMTU1MWIzMWY2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjExMDUyODYwOVoifSwieWllbGRvbmUiOnsidWlkIjoiMmE0MmZiZDMtMmM3MC00ZWI5LWIxYmQtMDQ2OTY2NTBkOTQ4IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuMzE4MzMzOTM5WiJ9LCJ6ZXJvY2xpY2tmcmF1ZCI6eyJ1aWQiOiJiOTk5NThmZS0yYTg3LTJkYTQtOWNjNC05NjFmZDExM2JlY2UiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNS43MTk1OTQ1NjZaIn19LCJiZGF5IjoiMjAyMi0wNS0xN1QwNjo0ODozOC4wMTc5ODgyMDZaIn0=; KRTBCOOKIE_153=1923-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&19420-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&22979-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&23462-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse; KRTBCOOKIE_57=22776-41928985301451193&KRTB&23339-41928985301451193; KRTBCOOKIE_27=16735-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&16736-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23019-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23114-uid:3cab6283-4546-4500-a7b6-40ef605fe745; KRTBCOOKIE_18=22947-1978557989514665832; KRTBCOOKIE_466=16530-4fc36250-d852-459c-8772-7356de17ab97; KRTBCOOKIE_391=22924-8044608333778839078&KRTB&23263-8044608333778839078&KRTB&23481-8044608333778839078; KRTBCOOKIE_1310=23431-b81c3g7dr67i&KRTB&23446-b81c3g7dr67i&KRTB&23465-b81c3g7dr67i; KRTBCOOKIE_1290=23368-vkf3yv9lbbl; KRTBCOOKIE_22=14911-4554572065121110164&KRTB&23150-4554572065121110164; KRTBCOOKIE_860=16335-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23334-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23417-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23426-YGAqDU1zUTdjyAFxCoe3kctlNPo; KRTBCOOKIE_904=16787-KwJwE7NkCZClNJRysN2iYg; KRTBCOOKIE_1159=23138-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23328-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23427-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23445-5545f53f3d6e4ec199d8ed627ff026f3; KRTBCOOKIE_32=11175-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22713-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22715-AQEI_1QecY2ESAIjEW6KAQEBAQE; SyncRTB3=1685577600%3A35%7C1685491200%3A107_21_71_56_204_247_165_231_233_179_22_209_54_254_238_96_99_220_7_214_13_3_8_234_176_46_5%7C1684886400%3A2_223_15%7C1689465600%3A69%7C1685145600%3A63; KRTBCOOKIE_107=1471-uid:EK38R0PM1NQR0H5&KRTB&23421-uid:EK38R0PM1NQR0H5; KRTBCOOKIE_594=17105-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004&KRTB&17107-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004; SPugT=1684310122; chkChromeAb67Sec=133; KRTBCOOKIE_699=22727-AAFy2k7FBosAAEasbJoXnw; PugT=1684310473; origin=go-test`) + return r + }(), + Body: []byte(`{"id":"test-case-1","at":1,"bcat":["IAB26-4","IAB26-2","IAB25-6","IAB25-5","IAB25-4","IAB25-3","IAB25-1","IAB25-7","IAB8-18","IAB26-3","IAB26-1","IAB8-5","IAB25-2","IAB11-4"],"tmax":1000,"app":{"publisher":{"name":"New Story Inc.","id":"5890","ext":{"installed_sdk":{"id":"MOLOCO_BIDDING","sdk_version":{"major":1,"minor":0,"micro":0},"adapter_version":{"major":1,"minor":0,"micro":0}}}},"paid":0,"name":"DrawHappyAngel","ver":"0.5.4","bundle":"com.newstory.DrawHappyAngel","cat":["IAB9-30"],"id":"1234567","ext":{"orientation":1}},"device":{"ifa":"497a10d6-c4dd-4e04-a986-c32b7180d462","ip":"38.158.207.171","carrier":"MYTEL","language":"en_US","hwv":"ruby","ppi":440,"pxratio":2.75,"devicetype":4,"connectiontype":2,"js":1,"h":2400,"w":1080,"geo":{"type":2,"ipservice":3,"lat":40.7429,"lon":-73.9392,"long":-73.9392,"city":"Queens","country":"USA","region":"ny","dma":"501","metro":"501","zip":"11101","ext":{"org":"Myanmar Broadband Telecom Co.","isp":"Myanmar Broadband Telecom Co."}},"ext":{},"osv":"13.0.0","ua":"Mozilla/5.0 (Linux; Android 13; 22101316C Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.230 Mobile Safari/537.36","make":"xiaomi","model":"22101316c","os":"android"},"imp":[{"id":"1","displaymanager":"applovin_mediation","displaymanagerver":"11.8.2","instl":0,"secure":0,"tagid":"/43743431/DMDemo","bidfloor":0.01,"bidfloorcur":"USD","exp":14400,"banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"rwdd":0}],"user":{"data":[{"id":"1","name":"Publisher Passed","segment":[{"signal":"{\"id\":\"95d6643c-3da6-40a2-b9ca-12279393ffbf\",\"at\":1,\"tmax\":500,\"cur\":[\"USD\"],\"imp\":[{\"id\":\"imp176227948\",\"clickbrowser\":0,\"displaymanager\":\"PubMatic_OpenBid_SDK\",\"displaymanagerver\":\"1.4.0\",\"tagid\":\"\/43743431\/DMDemo\",\"secure\":0,\"banner\":{\"pos\":7,\"format\":[{\"w\":300,\"h\":250}],\"api\":[5,6,7]},\"instl\":1}],\"app\":{\"paid\":4,\"name\":\"OpenWrapperSample\",\"bundle\":\"com.pubmatic.openbid.app\",\"storeurl\":\"https:\/\/itunes.apple.com\/us\/app\/pubmatic-sdk-app\/id1175273098?appnexus_banner_fixedbid=1&fixedbid=1\",\"ver\":\"1.0\",\"publisher\":{\"id\":\"5890\"}},\"device\":{\"geo\":{\"type\":1,\"lat\":37.421998333333335,\"lon\":-122.08400000000002},\"pxratio\":2.625,\"mccmnc\":\"310-260\",\"lmt\":0,\"ifa\":\"07c387f2-e030-428f-8336-42f682150759\",\"connectiontype\":5,\"carrier\":\"Android\",\"js\":1,\"ua\":\"Mozilla\/5.0(Linux;Android9;AndroidSDKbuiltforx86Build\/PSR1.180720.075;wv)AppleWebKit\/537.36(KHTML,likeGecko)Version\/4.0Chrome\/69.0.3497.100MobileSafari\/537.36\",\"make\":\"Google\",\"model\":\"AndroidSDKbuiltforx86\",\"os\":\"Android\",\"osv\":\"9\",\"h\":1794,\"w\":1080,\"language\":\"en-US\",\"devicetype\":4,\"ext\":{\"atts\":3}},\"source\":{\"ext\":{\"omidpn\":\"PubMatic\",\"omidpv\":\"1.2.11-Pubmatic\"}},\"user\":{\"data\":[{\"id\":\"1234\"}]},\"ext\":{\"wrapper\":{\"ssauction\":1,\"sumry_disable\":0,\"profileid\":58135,\"versionid\":1,\"clientconfig\":1}}}"}]}],"ext":{"gdpr":0}},"regs":{"coppa":0,"ext":{"gdpr":0}},"source":{"ext":{"schain":{"ver":"1.0","complete":1,"nodes":[{"asi":"applovin.com","sid":"53bf468f18c5a0e2b7d4e3f748c677c1","rid":"494dbe15a3ce08c54f4e456363f35a022247f997","hp":1}]}}},"ext":{"prebid":{"bidderparams":{"pubmatic":{"wrapper":{"profileid":12929,"versionid":1}}}}}}`), + }, + setup: func(mme *mock_metrics.MockMetricsEngine) {}, + }, + want: hookstage.HookResult[hookstage.EntrypointPayload]{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + ProfileID: 12929, + DisplayID: 1, + DisplayVersionID: 1, + SSAuction: -1, + ClientConfigFlag: 1, + Debug: true, + UA: "go-test", + IP: "127.0.0.1", + IsCTVRequest: false, + TrackerEndpoint: "t.pubmatic.com", + VideoErrorTrackerEndpoint: "t.pubmatic.com/error", + UidCookie: &http.Cookie{ + Name: "uids", + Value: `eyJ0ZW1wVUlEcyI6eyIzM2Fjcm9zcyI6eyJ1aWQiOiIxMTkxNzkxMDk5Nzc2NjEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTo0My4zODg4Nzk5NVoifSwiYWRmIjp7InVpZCI6IjgwNDQ2MDgzMzM3Nzg4MzkwNzgiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMS4wMzMwNTQ3MjdaIn0sImFka2VybmVsIjp7InVpZCI6IkE5MTYzNTAwNzE0OTkyOTMyOTkwIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuMzczMzg1NjYyWiJ9LCJhZGtlcm5lbEFkbiI6eyJ1aWQiOiJBOTE2MzUwMDcxNDk5MjkzMjk5MCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEzLjQzNDkyNTg5NloifSwiYWRtaXhlciI6eyJ1aWQiOiIzNjZhMTdiMTJmMjI0ZDMwOGYzZTNiOGRhOGMzYzhhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjU5MjkxNDgwMVoifSwiYWRueHMiOnsidWlkIjoiNDE5Mjg5ODUzMDE0NTExOTMiLCJleHBpcmVzIjoiMjAyMy0wMS0xOFQwOTo1MzowOC44MjU0NDI2NzZaIn0sImFqYSI6eyJ1aWQiOiJzMnN1aWQ2RGVmMFl0bjJveGQ1aG9zS1AxVmV3IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTMuMjM5MTc2MDU0WiJ9LCJlcGxhbm5pbmciOnsidWlkIjoiQUoxRjBTOE5qdTdTQ0xWOSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjkyOTk2MDQ3M1oifSwiZ2Ftb3NoaSI6eyJ1aWQiOiJndXNyXzM1NmFmOWIxZDhjNjQyYjQ4MmNiYWQyYjdhMjg4MTYxIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuNTI0MTU3MjI1WiJ9LCJncmlkIjp7InVpZCI6IjRmYzM2MjUwLWQ4NTItNDU5Yy04NzcyLTczNTZkZTE3YWI5NyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE0LjY5NjMxNjIyN1oifSwiZ3JvdXBtIjp7InVpZCI6IjdENzVEMjVGLUZBQzktNDQzRC1CMkQxLUIxN0ZFRTExRTAyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjM5LjIyNjIxMjUzMloifSwiaXgiOnsidWlkIjoiWW9ORlNENlc5QkphOEh6eEdtcXlCUUFBXHUwMDI2Mjk3IiwiZXhwaXJlcyI6IjIwMjMtMDUtMzFUMDc6NTM6MzguNTU1ODI3MzU0WiJ9LCJqaXhpZSI6eyJ1aWQiOiI3MzY3MTI1MC1lODgyLTExZWMtYjUzOC0xM2FjYjdhZjBkZTQiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi4xOTEwOTk3MzJaIn0sImxvZ2ljYWQiOnsidWlkIjoiQVZ4OVROQS11c25pa3M4QURzTHpWa3JvaDg4QUFBR0JUREh0UUEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS40NTUxNDk2MTZaIn0sIm1lZGlhbmV0Ijp7InVpZCI6IjI5Nzg0MjM0OTI4OTU0MTAwMDBWMTAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMy42NzIyMTUxMjhaIn0sIm1naWQiOnsidWlkIjoibTU5Z1hyN0xlX1htIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTcuMDk3MDAxNDcxWiJ9LCJuYW5vaW50ZXJhY3RpdmUiOnsidWlkIjoiNmFlYzhjMTAzNzlkY2I3ODQxMmJjODBiNmRkOWM5NzMxNzNhYjdkNzEyZTQzMWE1YTVlYTcwMzRlNTZhNThhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE2LjcxNDgwNzUwNVoifSwib25ldGFnIjp7InVpZCI6IjdPelZoVzFOeC1LOGFVak1HMG52NXVNYm5YNEFHUXZQbnVHcHFrZ3k0ckEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS4xNDE3NDEyNjJaIn0sIm9wZW54Ijp7InVpZCI6IjVkZWNlNjIyLTBhMjMtMGRhYi0zYTI0LTVhNzcwMTBlNDU4MiIsImV4cGlyZXMiOiIyMDIzLTA1LTMxVDA3OjUyOjQ3LjE0MDQxNzM2M1oifSwicHVibWF0aWMiOnsidWlkIjoiN0Q3NUQyNUYtRkFDOS00NDNELUIyRDEtQjE3RkVFMTFFMDI3IiwiZXhwaXJlcyI6IjIwMjItMTAtMzFUMDk6MTQ6MjUuNzM3MjU2ODk5WiJ9LCJyaWNoYXVkaWVuY2UiOnsidWlkIjoiY2I2YzYzMjAtMzNlMi00Nzc0LWIxNjAtMXp6MTY1NDg0MDc0OSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjUyNTA3NDE4WiJ9LCJzbWFydHlhZHMiOnsidWlkIjoiMTJhZjE1ZTQ0ZjAwZDA3NjMwZTc0YzQ5MTU0Y2JmYmE0Zjg0N2U4ZDRhMTU0YzhjM2Q1MWY1OGNmNzJhNDYyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjgyNTAzMTg4NFoifSwic21pbGV3YW50ZWQiOnsidWlkIjoiZGQ5YzNmZTE4N2VmOWIwOWNhYTViNzExNDA0YzI4MzAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNC4yNTU2MDkzNjNaIn0sInN5bmFjb3JtZWRpYSI6eyJ1aWQiOiJHRFBSIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuOTc5NTgzNDM4WiJ9LCJ0cmlwbGVsaWZ0Ijp7InVpZCI6IjcwMjE5NzUwNTQ4MDg4NjUxOTQ2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA4Ljk4OTY3MzU3NFoifSwidmFsdWVpbXByZXNzaW9uIjp7InVpZCI6IjlkMDgxNTVmLWQ5ZmUtNGI1OC04OThlLWUyYzU2MjgyYWIzZSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjA2NzgzOTE2NFoifSwidmlzeCI6eyJ1aWQiOiIyN2UwYWMzYy1iNDZlLTQxYjMtOTkyYy1mOGQyNzE0OTQ5NWUiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi45ODk1MjM1NzNaIn0sInlpZWxkbGFiIjp7InVpZCI6IjY5NzE0ZDlmLWZiMDAtNGE1Zi04MTljLTRiZTE5MTM2YTMyNSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjExLjMwMzAyNjYxNVoifSwieWllbGRtbyI6eyJ1aWQiOiJnOTZjMmY3MTlmMTU1MWIzMWY2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjExMDUyODYwOVoifSwieWllbGRvbmUiOnsidWlkIjoiMmE0MmZiZDMtMmM3MC00ZWI5LWIxYmQtMDQ2OTY2NTBkOTQ4IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuMzE4MzMzOTM5WiJ9LCJ6ZXJvY2xpY2tmcmF1ZCI6eyJ1aWQiOiJiOTk5NThmZS0yYTg3LTJkYTQtOWNjNC05NjFmZDExM2JlY2UiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNS43MTk1OTQ1NjZaIn19LCJiZGF5IjoiMjAyMi0wNS0xN1QwNjo0ODozOC4wMTc5ODgyMDZaIn0=`, + }, + KADUSERCookie: &http.Cookie{ + Name: "KADUSERCOOKIE", + Value: `7D75D25F-FAC9-443D-B2D1-B17FEE11E027`, + }, + OriginCookie: "go-test", + Aliases: make(map[string]string), + ImpBidCtx: make(map[string]models.ImpCtx), + PrebidBidderCode: make(map[string]string), + BidderResponseTimeMillis: make(map[string]int), + ProfileIDStr: "12929", + Endpoint: models.EndpointAppLovinMax, + MetricsEngine: mockEngine, + SeatNonBids: make(map[string][]openrtb_ext.NonBid), + }, + }, + }, + doMutate: true, + wantErr: nil, + wantBody: []byte(`{"id":"test-case-1","imp":[{"id":"1","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900,"api":[5,6,7]},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"displaymanager":"PubMatic_OpenBid_SDK","displaymanagerver":"1.4.0","tagid":"/43743431/DMDemo","bidfloor":0.01,"bidfloorcur":"USD","clickbrowser":0,"secure":0,"exp":14400}],"app":{"id":"1234567","name":"DrawHappyAngel","bundle":"com.newstory.DrawHappyAngel","cat":["IAB9-30"],"ver":"0.5.4","paid":4,"publisher":{"id":"5890","name":"New Story Inc.","ext":{"installed_sdk":{"id":"MOLOCO_BIDDING","sdk_version":{"major":1,"minor":0,"micro":0},"adapter_version":{"major":1,"minor":0,"micro":0}}}},"ext":{"orientation":1}},"device":{"geo":{"lat":40.7429,"lon":-73.9392,"type":2,"ipservice":3,"country":"USA","region":"ny","metro":"501","city":"Queens","zip":"11101","ext":{"org":"Myanmar Broadband Telecom Co.","isp":"Myanmar Broadband Telecom Co."}},"ua":"Mozilla/5.0 (Linux; Android 13; 22101316C Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.230 Mobile Safari/537.36","ip":"38.158.207.171","devicetype":4,"make":"xiaomi","model":"22101316c","os":"android","osv":"13.0.0","hwv":"ruby","h":2400,"w":1080,"ppi":440,"pxratio":2.75,"js":1,"language":"en_US","carrier":"MYTEL","mccmnc":"310-260","connectiontype":5,"ifa":"497a10d6-c4dd-4e04-a986-c32b7180d462","ext":{"atts":3}},"user":{"data":[{"id":"1234"}],"ext":{"gdpr":0}},"at":1,"tmax":1000,"bcat":["IAB26-4","IAB26-2","IAB25-6","IAB25-5","IAB25-4","IAB25-3","IAB25-1","IAB25-7","IAB8-18","IAB26-3","IAB26-1","IAB8-5","IAB25-2","IAB11-4"],"source":{"ext":{"schain":{"ver":"1.0","complete":1,"nodes":[{"asi":"applovin.com","sid":"53bf468f18c5a0e2b7d4e3f748c677c1","rid":"494dbe15a3ce08c54f4e456363f35a022247f997","hp":1}]},"omidpn":"PubMatic","omidpv":"1.2.11-Pubmatic"}},"regs":{"ext":{"gdpr":0}},"ext":{"prebid":{"bidderparams":{"pubmatic":{"wrapper":{"profileid":12929,"versionid":1,"clientconfig":1}}}}}}`), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -526,7 +595,17 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { gotRctx.CurrencyConversion = nil // ignore currency-conversion got.ModuleContext["rctx"] = gotRctx } - assert.Equal(t, got, tt.want) + + if tt.doMutate { + mutations := got.ChangeSet.Mutations() + assert.NotEmpty(t, mutations, tt.name) + for _, mut := range mutations { + result, err := mut.Apply(tt.args.payload) + assert.Nil(t, err, tt.name) + assert.Equal(t, tt.wantBody, result.Body) + } + } + assert.Equal(t, tt.want.ModuleContext, got.ModuleContext) }) } } @@ -589,6 +668,7 @@ func TestGetEndpoint(t *testing.T) { type args struct { path string source string + agent string } tests := []struct { name string @@ -667,10 +747,19 @@ func TestGetEndpoint(t *testing.T) { }, want: models.EndpointJson, }, + { + name: "ApplovinMax", + args: args{ + path: hookexecution.EndpointAuction, + source: "owsdk", + agent: models.AppLovinMaxAgent, + }, + want: models.EndpointAppLovinMax, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := GetEndpoint(tt.args.path, tt.args.source) + got := GetEndpoint(tt.args.path, tt.args.source, tt.args.agent) assert.Equal(t, tt.want, got) }) } diff --git a/modules/pubmatic/openwrap/metrics/config/multimetrics.go b/modules/pubmatic/openwrap/metrics/config/multimetrics.go index 1c773f704b4..a8ae0176a37 100644 --- a/modules/pubmatic/openwrap/metrics/config/multimetrics.go +++ b/modules/pubmatic/openwrap/metrics/config/multimetrics.go @@ -363,9 +363,9 @@ func (me *MultiMetricsEngine) RecordVideoImpDisabledViaConnTypeStats(publisher, } // RecordGetProfileDataTime across all engines -func (me *MultiMetricsEngine) RecordGetProfileDataTime(requestType, profileid string, getTime time.Duration) { +func (me *MultiMetricsEngine) RecordGetProfileDataTime(getTime time.Duration) { for _, thisME := range *me { - thisME.RecordGetProfileDataTime(requestType, profileid, getTime) + thisME.RecordGetProfileDataTime(getTime) } } @@ -426,9 +426,9 @@ func (me *MultiMetricsEngine) RecordCtvUaAccuracy(pubId, status string) { } // RecordSendLoggerDataTime across all engines -func (me *MultiMetricsEngine) RecordSendLoggerDataTime(endpoint, profile string, sendTime time.Duration) { +func (me *MultiMetricsEngine) RecordSendLoggerDataTime(sendTime time.Duration) { for _, thisME := range *me { - thisME.RecordSendLoggerDataTime(endpoint, profile, sendTime) + thisME.RecordSendLoggerDataTime(sendTime) } } diff --git a/modules/pubmatic/openwrap/metrics/config/multimetrics_test.go b/modules/pubmatic/openwrap/metrics/config/multimetrics_test.go index 25c183e08c9..81aea23ae64 100644 --- a/modules/pubmatic/openwrap/metrics/config/multimetrics_test.go +++ b/modules/pubmatic/openwrap/metrics/config/multimetrics_test.go @@ -203,8 +203,8 @@ func TestRecordFunctionForMultiMetricsEngine(t *testing.T) { mockEngine.EXPECT().RecordBidResponseByDealCountInHB(publisher, profile, aliasBidder, dealId) mockEngine.EXPECT().RecordPartnerTimeoutInPBS(publisher, profile, aliasBidder) mockEngine.EXPECT().RecordVideoImpDisabledViaConnTypeStats(publisher, profile) - mockEngine.EXPECT().RecordGetProfileDataTime(endpoint, profile, getTime) - mockEngine.EXPECT().RecordSendLoggerDataTime(endpoint, profile, sendTime) + mockEngine.EXPECT().RecordGetProfileDataTime(getTime) + mockEngine.EXPECT().RecordSendLoggerDataTime(sendTime) mockEngine.EXPECT().RecordDBQueryFailure(queryType, publisher, profile) mockEngine.EXPECT().RecordAnalyticsTrackingThrottled(publisher, profile, "logger") mockEngine.EXPECT().Shutdown() @@ -215,7 +215,7 @@ func TestRecordFunctionForMultiMetricsEngine(t *testing.T) { mockEngine.EXPECT().RecordBids("pubid", "profileid", "bidder", "deal") mockEngine.EXPECT().RecordPartnerTimeoutRequests("pubid", "profileid", "bidder") mockEngine.EXPECT().RecordCtvUaAccuracy("pubId", "status") - mockEngine.EXPECT().RecordSendLoggerDataTime("requestType", "profileid", time.Second) + mockEngine.EXPECT().RecordSendLoggerDataTime(time.Second) mockEngine.EXPECT().RecordRequestTime("requestType", time.Second) mockEngine.EXPECT().RecordOWServerPanic("endpoint", "methodName", "nodeName", "podName") mockEngine.EXPECT().RecordAmpVideoRequests("pubid", "profileid") @@ -269,8 +269,8 @@ func TestRecordFunctionForMultiMetricsEngine(t *testing.T) { multiMetricEngine.RecordBidResponseByDealCountInHB(publisher, profile, aliasBidder, dealId) multiMetricEngine.RecordPartnerTimeoutInPBS(publisher, profile, aliasBidder) multiMetricEngine.RecordVideoImpDisabledViaConnTypeStats(publisher, profile) - multiMetricEngine.RecordGetProfileDataTime(endpoint, profile, getTime) - multiMetricEngine.RecordSendLoggerDataTime(endpoint, profile, sendTime) + multiMetricEngine.RecordGetProfileDataTime(getTime) + multiMetricEngine.RecordSendLoggerDataTime(sendTime) multiMetricEngine.RecordDBQueryFailure(queryType, publisher, profile) multiMetricEngine.RecordAnalyticsTrackingThrottled(publisher, profile, "logger") multiMetricEngine.Shutdown() @@ -281,7 +281,7 @@ func TestRecordFunctionForMultiMetricsEngine(t *testing.T) { multiMetricEngine.RecordBids("pubid", "profileid", "bidder", "deal") multiMetricEngine.RecordPartnerTimeoutRequests("pubid", "profileid", "bidder") multiMetricEngine.RecordCtvUaAccuracy("pubId", "status") - multiMetricEngine.RecordSendLoggerDataTime("requestType", "profileid", time.Second) + multiMetricEngine.RecordSendLoggerDataTime(time.Second) multiMetricEngine.RecordRequestTime("requestType", time.Second) multiMetricEngine.RecordOWServerPanic("endpoint", "methodName", "nodeName", "podName") multiMetricEngine.RecordAmpVideoRequests("pubid", "profileid") diff --git a/modules/pubmatic/openwrap/metrics/metrics.go b/modules/pubmatic/openwrap/metrics/metrics.go index 228dee246be..b6a96ca8335 100644 --- a/modules/pubmatic/openwrap/metrics/metrics.go +++ b/modules/pubmatic/openwrap/metrics/metrics.go @@ -57,7 +57,7 @@ type MetricsEngine interface { RecordBidResponseByDealCountInPBS(publisher, profile, aliasBidder, dealId string) RecordBidResponseByDealCountInHB(publisher, profile, aliasBidder, dealId string) - RecordGetProfileDataTime(endpoint, profile string, getTime time.Duration) + RecordGetProfileDataTime(getTime time.Duration) RecordDBQueryFailure(queryType, publisher, profile string) Shutdown() @@ -70,7 +70,7 @@ type MetricsEngine interface { RecordPrebidTimeoutRequests(pubid, profileid string) RecordPartnerTimeoutRequests(pubid, profileid, bidder string) RecordCtvUaAccuracy(pubId, status string) - RecordSendLoggerDataTime(requestType, profileid string, sendTime time.Duration) + RecordSendLoggerDataTime(sendTime time.Duration) RecordRequestTime(requestType string, requestTime time.Duration) RecordOWServerPanic(endpoint, methodName, nodeName, podName string) RecordAmpVideoRequests(pubid, profileid string) diff --git a/modules/pubmatic/openwrap/metrics/mock/mock.go b/modules/pubmatic/openwrap/metrics/mock/mock.go index 3ba1464ac93..270381b7abc 100644 --- a/modules/pubmatic/openwrap/metrics/mock/mock.go +++ b/modules/pubmatic/openwrap/metrics/mock/mock.go @@ -252,15 +252,15 @@ func (mr *MockMetricsEngineMockRecorder) RecordDBQueryFailure(arg0, arg1, arg2 i } // RecordGetProfileDataTime mocks base method. -func (m *MockMetricsEngine) RecordGetProfileDataTime(arg0, arg1 string, arg2 time.Duration) { +func (m *MockMetricsEngine) RecordGetProfileDataTime(arg0 time.Duration) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordGetProfileDataTime", arg0, arg1, arg2) + m.ctrl.Call(m, "RecordGetProfileDataTime", arg0) } // RecordGetProfileDataTime indicates an expected call of RecordGetProfileDataTime. -func (mr *MockMetricsEngineMockRecorder) RecordGetProfileDataTime(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockMetricsEngineMockRecorder) RecordGetProfileDataTime(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordGetProfileDataTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordGetProfileDataTime), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordGetProfileDataTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordGetProfileDataTime), arg0) } // RecordHTTPCounter mocks base method. @@ -660,15 +660,15 @@ func (mr *MockMetricsEngineMockRecorder) RecordSSTimeoutRequests(arg0, arg1 inte } // RecordSendLoggerDataTime mocks base method. -func (m *MockMetricsEngine) RecordSendLoggerDataTime(arg0, arg1 string, arg2 time.Duration) { +func (m *MockMetricsEngine) RecordSendLoggerDataTime(arg0 time.Duration) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordSendLoggerDataTime", arg0, arg1, arg2) + m.ctrl.Call(m, "RecordSendLoggerDataTime", arg0) } // RecordSendLoggerDataTime indicates an expected call of RecordSendLoggerDataTime. -func (mr *MockMetricsEngineMockRecorder) RecordSendLoggerDataTime(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockMetricsEngineMockRecorder) RecordSendLoggerDataTime(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordSendLoggerDataTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordSendLoggerDataTime), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordSendLoggerDataTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordSendLoggerDataTime), arg0) } // RecordStatsKeyCTVPrebidFailedImpression mocks base method. diff --git a/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go b/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go index 60d3baad4c8..dc6cb8cd614 100644 --- a/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go +++ b/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go @@ -49,7 +49,8 @@ type Metrics struct { // publisher-platform-endpoint level metrics pubPlatformEndpointRequests *prometheus.CounterVec - getProfileData *prometheus.HistogramVec + getProfileData prometheus.Histogram + sendLoggerData prometheus.Histogram dbQueryError *prometheus.CounterVec @@ -66,7 +67,6 @@ type Metrics struct { prebidTimeoutRequests *prometheus.CounterVec partnerTimeoutRequest *prometheus.CounterVec panicCounts *prometheus.CounterVec - sendLoggerData *prometheus.HistogramVec owRequestTime *prometheus.HistogramVec ampVideoRequests *prometheus.CounterVec ampVideoResponses *prometheus.CounterVec @@ -97,7 +97,7 @@ const ( analyticsTypeLabel = "an_type" ) -var standardTimeBuckets = []float64{0.05, 0.1, 0.15, 0.20, 0.25, 0.3, 0.4, 0.5, 0.75, 1} +var standardTimeBuckets = []float64{0.1, 0.3, 0.75, 1} var once sync.Once var metric *Metrics @@ -249,10 +249,9 @@ func newMetrics(cfg *config.PrometheusMetrics, promRegistry *prometheus.Registry []string{pubIDLabel, platformLabel, endpointLabel}, ) - metrics.getProfileData = newHistogramVec(cfg, promRegistry, + metrics.getProfileData = newHistogram(cfg, promRegistry, "profile_data_get_time", - "Time taken to get the profile data in seconds", []string{endpointLabel, profileIDLabel}, - standardTimeBuckets) + "Time taken to get the profile data in seconds", standardTimeBuckets) metrics.dbQueryError = newCounter(cfg, promRegistry, "db_query_failed", @@ -304,6 +303,19 @@ func newCounter(cfg *config.PrometheusMetrics, registry *prometheus.Registry, na return counter } +func newHistogram(cfg *config.PrometheusMetrics, registry *prometheus.Registry, name, help string, buckets []float64) prometheus.Histogram { + opts := prometheus.HistogramOpts{ + Namespace: cfg.Namespace, + Subsystem: cfg.Subsystem, + Name: name, + Help: help, + Buckets: buckets, + } + histogram := prometheus.NewHistogram(opts) + registry.MustRegister(histogram) + return histogram +} + func newHistogramVec(cfg *config.PrometheusMetrics, registry *prometheus.Registry, name, help string, labels []string, buckets []float64) *prometheus.HistogramVec { opts := prometheus.HistogramOpts{ Namespace: cfg.Namespace, @@ -465,11 +477,8 @@ func (m *Metrics) RecordInjectTrackerErrorCount(adformat, publisherID, partner s } // RecordGetProfileDataTime as a noop -func (m *Metrics) RecordGetProfileDataTime(endpoint, profileID string, getTime time.Duration) { - m.getProfileData.With(prometheus.Labels{ - endpointLabel: endpoint, - profileIDLabel: profileID, - }).Observe(float64(getTime.Seconds())) +func (m *Metrics) RecordGetProfileDataTime(getTime time.Duration) { + m.getProfileData.Observe(float64(getTime.Seconds())) } // RecordDBQueryFailure as a noop diff --git a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb.go b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb.go index a3cf81a3dd1..d8b9e7506fe 100644 --- a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb.go +++ b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb.go @@ -70,10 +70,9 @@ func newSSHBMetrics(metrics *Metrics, cfg *config.PrometheusMetrics, promRegistr "Count of total requests to header-bidding server labeled by type and status.", []string{requestTypeLabel, requestStatusLabel}) - metrics.sendLoggerData = newHistogramVec(cfg, promRegistry, + metrics.sendLoggerData = newHistogram(cfg, promRegistry, "sshb_logger_data_send_time", - "Time taken to send the wrapper logger body in seconds", []string{endpointLabel, profileIDLabel}, - standardTimeBuckets) + "Time taken to send the wrapper logger body in seconds", standardTimeBuckets) metrics.owRequestTime = newHistogramVec(cfg, promRegistry, "sshb_request_time", @@ -182,11 +181,8 @@ func (m *Metrics) RecordCtvUaAccuracy(pubId, status string) { } // RecordSendLoggerDataTime as a noop -func (m *Metrics) RecordSendLoggerDataTime(endpoint, profileID string, sendTime time.Duration) { - m.sendLoggerData.With(prometheus.Labels{ - endpointLabel: endpoint, - profileIDLabel: profileID, - }).Observe(float64(sendTime.Seconds())) +func (m *Metrics) RecordSendLoggerDataTime(sendTime time.Duration) { + m.sendLoggerData.Observe(float64(sendTime.Seconds())) } // RecordSendLoggerDataTime as a noop diff --git a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb_test.go b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb_test.go index 2fd059d2b25..ae4ebd90a4e 100644 --- a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb_test.go +++ b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb_test.go @@ -279,9 +279,8 @@ func TestRecordPartnerTimeoutRequests(t *testing.T) { func TestRecordSendLoggerDataTime(t *testing.T) { m := createMetricsForTesting() - m.RecordSendLoggerDataTime("v25", "59201", 300*time.Millisecond) - resultingHistogram := getHistogramFromHistogramVecByTwoKeys(m.sendLoggerData, - endpointLabel, "v25", profileIDLabel, "59201") + m.RecordSendLoggerDataTime(300 * time.Millisecond) + resultingHistogram := getHistogramFromHistogram(m.sendLoggerData) assertHistogram(t, "sshb_logger_data_send_time", resultingHistogram, 1, 0.3) } diff --git a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_test.go b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_test.go index 4706136b4c1..9d8abf28cec 100644 --- a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_test.go +++ b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_test.go @@ -304,9 +304,8 @@ func TestRecordPublisherResponseTimeStats(t *testing.T) { func TestRecordGetProfileDataTime(t *testing.T) { m := createMetricsForTesting() - m.RecordGetProfileDataTime("v25", "59201", 300*time.Millisecond) - resultingHistogram := getHistogramFromHistogramVecByTwoKeys(m.getProfileData, - endpointLabel, "v25", profileIDLabel, "59201") + m.RecordGetProfileDataTime(300 * time.Millisecond) + resultingHistogram := getHistogramFromHistogram(m.getProfileData) assertHistogram(t, "sshb_profile_data_get_time", resultingHistogram, 1, 0.3) } @@ -336,6 +335,14 @@ func TestRecordHTTPCounter(t *testing.T) { expectedCount, nil) } +func getHistogramFromHistogram(histogram prometheus.Histogram) dto.Histogram { + var result dto.Histogram + processMetrics(histogram, func(m dto.Metric) { + result = *m.GetHistogram() + }) + return result +} + func getHistogramFromHistogramVec(histogram *prometheus.HistogramVec, labelKey, labelValue string) dto.Histogram { var result dto.Histogram processMetrics(histogram, func(m dto.Metric) { diff --git a/modules/pubmatic/openwrap/metrics/stats/tcp_stats.go b/modules/pubmatic/openwrap/metrics/stats/tcp_stats.go index 59fbae851b5..e15fb26bf17 100644 --- a/modules/pubmatic/openwrap/metrics/stats/tcp_stats.go +++ b/modules/pubmatic/openwrap/metrics/stats/tcp_stats.go @@ -286,7 +286,7 @@ func (st *StatsTCP) RecordCacheErrorRequests(endpoint, publisher, profileID stri } } -func (st *StatsTCP) RecordGetProfileDataTime(requestType, profileid string, getTime time.Duration) {} +func (st *StatsTCP) RecordGetProfileDataTime(getTime time.Duration) {} func (st *StatsTCP) RecordDBQueryFailure(queryType, publisher, profile string) {} @@ -330,20 +330,20 @@ func (st *StatsTCP) Shutdown() { st.statsClient.ShutdownProcess() } -func (st *StatsTCP) RecordRequest(labels metrics.Labels) {} -func (st *StatsTCP) RecordLurlSent(labels metrics.LurlStatusLabels) {} -func (st *StatsTCP) RecordLurlBatchSent(labels metrics.LurlBatchStatusLabels) {} -func (st *StatsTCP) RecordBids(pubid, profileid, biddder, deal string) {} -func (st *StatsTCP) RecordPartnerTimeoutRequests(pubid, profileid, bidder string) {} -func (st *StatsTCP) RecordCtvUaAccuracy(pubId, status string) {} -func (st *StatsTCP) RecordSendLoggerDataTime(requestType, profileid string, sendTime time.Duration) {} -func (st *StatsTCP) RecordRequestTime(requestType string, requestTime time.Duration) {} -func (st *StatsTCP) RecordOWServerPanic(endpoint, methodName, nodeName, podName string) {} -func (st *StatsTCP) RecordAmpVideoRequests(pubid, profileid string) {} -func (st *StatsTCP) RecordAmpVideoResponses(pubid, profileid string) {} -func (st *StatsTCP) RecordHTTPCounter() {} -func (st *StatsTCP) RecordUnwrapRequestStatus(accountId, bidder, status string) {} -func (st *StatsTCP) RecordUnwrapWrapperCount(accountId, bidder, wrapper_count string) {} -func (st *StatsTCP) RecordUnwrapRequestTime(accountId, bidder string, respTime time.Duration) {} -func (st *StatsTCP) RecordUnwrapRespTime(accountId, wraperCnt string, respTime time.Duration) {} -func (st *StatsTCP) RecordAnalyticsTrackingThrottled(pubid, profileid, analyticsType string) {} +func (st *StatsTCP) RecordRequest(labels metrics.Labels) {} +func (st *StatsTCP) RecordLurlSent(labels metrics.LurlStatusLabels) {} +func (st *StatsTCP) RecordLurlBatchSent(labels metrics.LurlBatchStatusLabels) {} +func (st *StatsTCP) RecordBids(pubid, profileid, biddder, deal string) {} +func (st *StatsTCP) RecordPartnerTimeoutRequests(pubid, profileid, bidder string) {} +func (st *StatsTCP) RecordCtvUaAccuracy(pubId, status string) {} +func (st *StatsTCP) RecordSendLoggerDataTime(sendTime time.Duration) {} +func (st *StatsTCP) RecordRequestTime(requestType string, requestTime time.Duration) {} +func (st *StatsTCP) RecordOWServerPanic(endpoint, methodName, nodeName, podName string) {} +func (st *StatsTCP) RecordAmpVideoRequests(pubid, profileid string) {} +func (st *StatsTCP) RecordAmpVideoResponses(pubid, profileid string) {} +func (st *StatsTCP) RecordHTTPCounter() {} +func (st *StatsTCP) RecordUnwrapRequestStatus(accountId, bidder, status string) {} +func (st *StatsTCP) RecordUnwrapWrapperCount(accountId, bidder, wrapper_count string) {} +func (st *StatsTCP) RecordUnwrapRequestTime(accountId, bidder string, respTime time.Duration) {} +func (st *StatsTCP) RecordUnwrapRespTime(accountId, wraperCnt string, respTime time.Duration) {} +func (st *StatsTCP) RecordAnalyticsTrackingThrottled(pubid, profileid, analyticsType string) {} diff --git a/modules/pubmatic/openwrap/models/constants.go b/modules/pubmatic/openwrap/models/constants.go index 376a4b46824..4c0a5733511 100755 --- a/modules/pubmatic/openwrap/models/constants.go +++ b/modules/pubmatic/openwrap/models/constants.go @@ -446,21 +446,23 @@ const ( ) const ( - EndpointV25 = "v25" - EndpointAMP = "amp" - EndpintInappVideo = "inappvideo" - EndpointVideo = "video" - EndpointJson = "json" - EndpointORTB = "ortb" - EndpointVAST = "vast" - EndpointWebS2S = "webs2s" - EndPointCTV = "ctv" - EndpointHybrid = "hybrid" - Openwrap = "openwrap" - ImpTypeBanner = "banner" - ImpTypeVideo = "video" - ContentTypeSite = "site" - ContentTypeApp = "app" + EndpointV25 = "v25" + EndpointAMP = "amp" + EndpintInappVideo = "inappvideo" + EndpointVideo = "video" + EndpointJson = "json" + EndpointORTB = "ortb" + EndpointVAST = "vast" + EndpointWebS2S = "webs2s" + EndPointCTV = "ctv" + EndpointHybrid = "hybrid" + EndpointAppLovinMax = "applovinmax" + + Openwrap = "openwrap" + ImpTypeBanner = "banner" + ImpTypeVideo = "video" + ContentTypeSite = "site" + ContentTypeApp = "app" ) const ( @@ -529,3 +531,12 @@ const ( FeatureAMPMultiFormat = 3 FeatureAnalyticsThrottle = 4 ) + +// constants for applovinmax requests +const ( + Agent = "agent" + AppLovinMaxAgent = "max" + TypeRewarded = "rewarded" + SignalData = "signaldata" + OwSspBurl = "owsspburl" +) diff --git a/modules/pubmatic/openwrap/models/openwrap.go b/modules/pubmatic/openwrap/models/openwrap.go index 90b47315275..9467c8cddf5 100644 --- a/modules/pubmatic/openwrap/models/openwrap.go +++ b/modules/pubmatic/openwrap/models/openwrap.go @@ -101,6 +101,7 @@ type RequestCtx struct { IsTBFFeatureEnabled bool VastUnwrapEnabled bool VastUnwrapStatsEnabled bool + AppLovinMax AppLovinMax LoggerDisabled bool TrackerDisabled bool } @@ -198,3 +199,7 @@ type FeatureData struct { Enabled int // feature enabled/disabled Value string // feature value if any } + +type AppLovinMax struct { + Reject bool +} diff --git a/modules/pubmatic/openwrap/profiledata.go b/modules/pubmatic/openwrap/profiledata.go index 5ebc8c76ed5..caeeeee2aef 100644 --- a/modules/pubmatic/openwrap/profiledata.go +++ b/modules/pubmatic/openwrap/profiledata.go @@ -19,7 +19,7 @@ func (m OpenWrap) getProfileData(rCtx models.RequestCtx, bidRequest openrtb2.Bid return getTestModePartnerConfigMap(platform, m.cfg.Timeout.HBTimeout, rCtx.DisplayID), nil } - return m.cache.GetPartnerConfigMap(rCtx.PubID, rCtx.ProfileID, rCtx.DisplayID, rCtx.Endpoint) + return m.cache.GetPartnerConfigMap(rCtx.PubID, rCtx.ProfileID, rCtx.DisplayID) } func getTestModePartnerConfigMap(platform string, timeout int64, displayVersion int) map[int]map[string]string { diff --git a/modules/pubmatic/openwrap/profiledata_test.go b/modules/pubmatic/openwrap/profiledata_test.go index dace8c0d505..61d5a34ec91 100644 --- a/modules/pubmatic/openwrap/profiledata_test.go +++ b/modules/pubmatic/openwrap/profiledata_test.go @@ -130,7 +130,7 @@ func TestOpenWrap_getProfileData(t *testing.T) { }, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(5890, 123, 1, models.PLATFORM_APP).Return( + mockCache.EXPECT().GetPartnerConfigMap(5890, 123, 1).Return( map[int]map[string]string{ 1: { models.PARTNER_ID: "2", @@ -185,7 +185,7 @@ func TestOpenWrap_getProfileData(t *testing.T) { }, }, setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(5890, 123, 1, models.PLATFORM_APP).Return( + mockCache.EXPECT().GetPartnerConfigMap(5890, 123, 1).Return( nil, fmt.Errorf("error GetPartnerConfigMap")) }, want: nil, diff --git a/modules/pubmatic/openwrap/tracker/banner.go b/modules/pubmatic/openwrap/tracker/banner.go index 3ac1ba0262e..b092c2e5bdb 100644 --- a/modules/pubmatic/openwrap/tracker/banner.go +++ b/modules/pubmatic/openwrap/tracker/banner.go @@ -9,7 +9,11 @@ import ( "github.com/prebid/prebid-server/v2/openrtb_ext" ) -func injectBannerTracker(rctx models.RequestCtx, tracker models.OWTracker, bid openrtb2.Bid, seat string, pixels []adunitconfig.UniversalPixel) string { +func injectBannerTracker(rctx models.RequestCtx, tracker models.OWTracker, bid openrtb2.Bid, seat string, pixels []adunitconfig.UniversalPixel) (string, string) { + if rctx.Endpoint == models.EndpointAppLovinMax { + return bid.AdM, getBURL(bid.BURL, tracker.TrackerURL) + } + var replacedTrackerStr, trackerFormat string trackerFormat = models.TrackerCallWrap if trackerWithOM(tracker, rctx.Platform, seat) { @@ -17,7 +21,7 @@ func injectBannerTracker(rctx models.RequestCtx, tracker models.OWTracker, bid o } replacedTrackerStr = strings.Replace(trackerFormat, "${escapedUrl}", tracker.TrackerURL, 1) adm := applyTBFFeature(rctx, bid, replacedTrackerStr) - return appendUPixelinBanner(adm, pixels) + return appendUPixelinBanner(adm, pixels), bid.BURL } // append universal pixels in creative based on conditions diff --git a/modules/pubmatic/openwrap/tracker/banner_test.go b/modules/pubmatic/openwrap/tracker/banner_test.go index 345b1c29a22..c72f6c6b4e0 100644 --- a/modules/pubmatic/openwrap/tracker/banner_test.go +++ b/modules/pubmatic/openwrap/tracker/banner_test.go @@ -18,10 +18,30 @@ func Test_injectBannerTracker(t *testing.T) { pixels []adunitconfig.UniversalPixel } tests := []struct { - name string - args args - want string + name string + args args + wantAdm string + wantBurl string }{ + { + name: "endpoint_applovinmax", + args: args{ + rctx: models.RequestCtx{ + Platform: models.PLATFORM_APP, + Endpoint: models.EndpointAppLovinMax, + }, + tracker: models.OWTracker{ + TrackerURL: `sample.com/track?tid=1234`, + }, + bid: openrtb2.Bid{ + AdM: `
`, + BURL: `http://burl.com`, + }, + seat: "pubmatic", + }, + wantAdm: `
`, + wantBurl: `sample.com/track?tid=1234&owsspburl=http://burl.com`, + }, { name: "app_platform", args: args{ @@ -36,7 +56,7 @@ func Test_injectBannerTracker(t *testing.T) { }, seat: "test", }, - want: `sample_creative
`, + wantAdm: `sample_creative
`, }, { name: "app_platform_OM_Inactive_pubmatic", @@ -53,7 +73,7 @@ func Test_injectBannerTracker(t *testing.T) { }, seat: models.BidderPubMatic, }, - want: `sample_creative
`, + wantAdm: `sample_creative
`, }, { name: "app_platform_OM_Active_pubmatic", @@ -70,7 +90,7 @@ func Test_injectBannerTracker(t *testing.T) { }, seat: models.BidderPubMatic, }, - want: `sample_creative`, + wantAdm: `sample_creative`, }, { name: "tbf_feature_enabled", @@ -87,14 +107,14 @@ func Test_injectBannerTracker(t *testing.T) { AdM: `sample_creative`, }, }, - want: `
sample_creative`, + wantAdm: `
sample_creative`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := injectBannerTracker(tt.args.rctx, tt.args.tracker, tt.args.bid, tt.args.seat, tt.args.pixels); got != tt.want { - t.Errorf("injectBannerTracker() = %v, want %v", got, tt.want) - } + gotAdm, gotBurl := injectBannerTracker(tt.args.rctx, tt.args.tracker, tt.args.bid, tt.args.seat, tt.args.pixels) + assert.Equal(t, tt.wantAdm, gotAdm) + assert.Equal(t, tt.wantBurl, gotBurl) }) } } diff --git a/modules/pubmatic/openwrap/tracker/inject.go b/modules/pubmatic/openwrap/tracker/inject.go index 47d63afd41c..06cb70c25e3 100644 --- a/modules/pubmatic/openwrap/tracker/inject.go +++ b/modules/pubmatic/openwrap/tracker/inject.go @@ -31,13 +31,13 @@ func InjectTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) ( switch adformat { case models.Banner: - bidResponse.SeatBid[i].Bid[j].AdM = injectBannerTracker(rctx, tracker, bid, seatBid.Seat, pixels) + bidResponse.SeatBid[i].Bid[j].AdM, bidResponse.SeatBid[i].Bid[j].BURL = injectBannerTracker(rctx, tracker, bid, seatBid.Seat, pixels) case models.Video: trackers := []models.OWTracker{tracker} - bidResponse.SeatBid[i].Bid[j].AdM, err = injectVideoCreativeTrackers(bid, trackers) + bidResponse.SeatBid[i].Bid[j].AdM, bidResponse.SeatBid[i].Bid[j].BURL, err = injectVideoCreativeTrackers(rctx, bid, trackers) case models.Native: if impBidCtx, ok := rctx.ImpBidCtx[bid.ImpID]; ok { - bidResponse.SeatBid[i].Bid[j].AdM, err = injectNativeCreativeTrackers(impBidCtx.Native, bid.AdM, tracker) + bidResponse.SeatBid[i].Bid[j].AdM, bidResponse.SeatBid[i].Bid[j].BURL, err = injectNativeCreativeTrackers(impBidCtx.Native, bid, tracker, rctx.Endpoint) } else { errMsg = fmt.Sprintf("native obj not found for impid %s", bid.ImpID) } @@ -81,3 +81,15 @@ func getUniversalPixels(rctx models.RequestCtx, adformat string, bidderCode stri } return pixels } + +func getBURL(burl, trackerURL string) string { + if trackerURL == "" { + return burl + } + + if burl == "" { + return trackerURL + } + + return trackerURL + "&" + models.OwSspBurl + "=" + burl +} diff --git a/modules/pubmatic/openwrap/tracker/inject_test.go b/modules/pubmatic/openwrap/tracker/inject_test.go index 0738228b7d5..97c054f5f0c 100644 --- a/modules/pubmatic/openwrap/tracker/inject_test.go +++ b/modules/pubmatic/openwrap/tracker/inject_test.go @@ -1,15 +1,14 @@ package tracker import ( - "reflect" "testing" "github.com/golang/mock/gomock" - "github.com/magiconair/properties/assert" "github.com/prebid/openrtb/v20/openrtb2" mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/stretchr/testify/assert" ) func TestInjectTrackers(t *testing.T) { @@ -516,6 +515,143 @@ func TestInjectTrackers(t *testing.T) { }, wantErr: false, }, + { + name: "adformat_is_banner_and_AppLovinMax_request", + args: args{ + rctx: models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + Platform: "", + Trackers: map[string]models.OWTracker{ + "12345": { + BidType: "banner", + TrackerURL: `Tracking URL`, + }, + }, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + BURL: `http://burl.com`, + AdM: `
`, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + BURL: `Tracking URL&owsspburl=http://burl.com`, + AdM: `
`, + }, + }, + }, + }, + }, + wantErr: false, + }, + { + name: "adformat_is_video_and_AppLovinMax_request", + args: args{ + rctx: models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + Platform: "", + Trackers: map[string]models.OWTracker{ + "12345": { + BidType: "video", + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + BURL: `http://burl.com`, + AdM: ``, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + BURL: `Tracking URL&owsspburl=http://burl.com`, + AdM: ``, + }, + }, + }, + }, + }, + wantErr: false, + }, + { + name: "adformat_is_native_and_AppLovinMax_request", + args: args{ + rctx: models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + Platform: models.PLATFORM_APP, + Trackers: map[string]models.OWTracker{ + "12345": { + BidType: "native", + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp123": { + Native: &openrtb2.Native{ + Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", + }, + }, + }, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + ImpID: "imp123", + BURL: `http://burl.com`, + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + ImpID: "imp123", + BURL: `Tracking URL&owsspburl=http://burl.com`, + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + }, + }, + }, + }, + }, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -527,9 +663,7 @@ func TestInjectTrackers(t *testing.T) { t.Errorf("InjectTrackers() error = %v, wantErr %v", err, tt.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("InjectTrackers() = %v, want %v", got, tt.want) - } + assert.Equal(t, got, tt.want, tt.name) }) } } @@ -715,3 +849,46 @@ func Test_getUniversalPixels(t *testing.T) { }) } } + +func Test_getBurlAppLovinMax(t *testing.T) { + type args struct { + burl string + TrackerURL string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "empty_burl", + args: args{ + burl: "", + TrackerURL: `sample.com`, + }, + want: `sample.com`, + }, + { + name: "empty_tracker_url", + args: args{ + burl: `sample.com`, + TrackerURL: "", + }, + want: `sample.com`, + }, + { + name: "valid_burl_and_tracker_url", + args: args{ + burl: `sampleBurl.com`, + TrackerURL: `sampleTracker.com?id=123`, + }, + want: `sampleTracker.com?id=123&owsspburl=sampleBurl.com`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getBURL(tt.args.burl, tt.args.TrackerURL) + assert.Equal(t, got, tt.want) + }) + } +} diff --git a/modules/pubmatic/openwrap/tracker/native.go b/modules/pubmatic/openwrap/tracker/native.go index 1999699e24c..8d9e2ede379 100644 --- a/modules/pubmatic/openwrap/tracker/native.go +++ b/modules/pubmatic/openwrap/tracker/native.go @@ -11,12 +11,17 @@ import ( ) // Inject TrackerCall in Native Adm -func injectNativeCreativeTrackers(native *openrtb2.Native, adm string, tracker models.OWTracker) (string, error) { +func injectNativeCreativeTrackers(native *openrtb2.Native, bid openrtb2.Bid, tracker models.OWTracker, endpoint string) (string, string, error) { + adm := bid.AdM + var err error + if endpoint == models.EndpointAppLovinMax { + return adm, getBURL(bid.BURL, tracker.TrackerURL), nil + } if native == nil { - return adm, errors.New("native object is missing") + return adm, bid.BURL, errors.New("native object is missing") } if len(native.Request) == 0 { - return adm, errors.New("native request is empty") + return adm, bid.BURL, errors.New("native request is empty") } setTrackerURL := false callback := func(value []byte, dataType jsonparser.ValueType, offset int, err error) { @@ -31,9 +36,10 @@ func injectNativeCreativeTrackers(native *openrtb2.Native, adm string, tracker m jsonparser.ArrayEach([]byte(native.Request), callback, models.EventTrackers) if setTrackerURL { - return adm, nil + return adm, bid.BURL, nil } - return injectNativeImpressionTracker(&adm, tracker) + adm, err = injectNativeImpressionTracker(&adm, tracker) + return adm, bid.BURL, err } // inject tracker in EventTracker Object diff --git a/modules/pubmatic/openwrap/tracker/native_test.go b/modules/pubmatic/openwrap/tracker/native_test.go index 078ef4ec3a1..3c8a030d445 100644 --- a/modules/pubmatic/openwrap/tracker/native_test.go +++ b/modules/pubmatic/openwrap/tracker/native_test.go @@ -6,41 +6,64 @@ import ( "github.com/prebid/openrtb/v20/openrtb2" "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/stretchr/testify/assert" ) func Test_injectNativeCreativeTrackers(t *testing.T) { type args struct { - native *openrtb2.Native - adm string - tracker models.OWTracker + native *openrtb2.Native + bid openrtb2.Bid + tracker models.OWTracker + endpoint string } tests := []struct { - name string - args args - want string - wantErr bool + name string + args args + wantAdm string + wantBurl string + wantErr bool }{ + { + name: "AppLovinMax_endpoint_request", + args: args{ + native: &openrtb2.Native{}, + bid: openrtb2.Bid{ + BURL: `http://burl.com`, + AdM: `
`, + }, + tracker: models.OWTracker{ + TrackerURL: `t.pubmatic.com/track?tid=1234`, + }, + endpoint: models.EndpointAppLovinMax, + }, + wantBurl: `t.pubmatic.com/track?tid=1234&owsspburl=http://burl.com`, + wantAdm: `
`, + wantErr: false, + }, { name: "native_object_nil", args: args{ - adm: `creative`, + bid: openrtb2.Bid{ + AdM: `creative`, + }, tracker: models.OWTracker{ TrackerURL: `Tracking URL`, }, }, - want: `creative`, + wantAdm: `creative`, wantErr: true, }, { name: "empty_native_object", args: args{ native: &openrtb2.Native{}, - adm: `creative`, - tracker: models.OWTracker{ + bid: openrtb2.Bid{ + AdM: `creative`, + }, tracker: models.OWTracker{ TrackerURL: `Tracking URL`, }, }, - want: `creative`, + wantAdm: `creative`, wantErr: true, }, { @@ -49,12 +72,13 @@ func Test_injectNativeCreativeTrackers(t *testing.T) { native: &openrtb2.Native{ Request: `request`, }, - adm: `creative`, - tracker: models.OWTracker{ + bid: openrtb2.Bid{ + AdM: `creative`, + }, tracker: models.OWTracker{ TrackerURL: `Tracking URL`, }, }, - want: `creative`, + wantAdm: `creative`, wantErr: true, }, { @@ -63,12 +87,14 @@ func Test_injectNativeCreativeTrackers(t *testing.T) { native: &openrtb2.Native{ Request: "{\"context\":1,\"plcmttype\":1,\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", }, - adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + bid: openrtb2.Bid{ + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + }, tracker: models.OWTracker{ TrackerURL: `Tracking URL`, }, }, - want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35","Tracking URL"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + wantAdm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35","Tracking URL"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, wantErr: false, }, { @@ -77,12 +103,14 @@ func Test_injectNativeCreativeTrackers(t *testing.T) { native: &openrtb2.Native{ Request: "{\"context\":1,\"plcmttype\":1,\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", }, - adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + bid: openrtb2.Bid{ + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + }, tracker: models.OWTracker{ TrackerURL: `Tracking URL`, }, }, - want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}],"imptrackers":["Tracking URL"]}`, + wantAdm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}],"imptrackers":["Tracking URL"]}`, wantErr: false, }, { @@ -91,12 +119,14 @@ func Test_injectNativeCreativeTrackers(t *testing.T) { native: &openrtb2.Native{ Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[2]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", }, - adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":2,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.js"}]}`, + bid: openrtb2.Bid{ + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":2,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.js"}]}`, + }, tracker: models.OWTracker{ TrackerURL: `Tracking URL`, }, }, - want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":2,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.js"}],"imptrackers":["Tracking URL"]}`, + wantAdm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":2,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.js"}],"imptrackers":["Tracking URL"]}`, wantErr: false, }, { @@ -105,12 +135,14 @@ func Test_injectNativeCreativeTrackers(t *testing.T) { native: &openrtb2.Native{ Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":2,\"methods\":[1]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", }, - adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["abc.com"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":2,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.php"}]}`, + bid: openrtb2.Bid{ + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["abc.com"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":2,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.php"}]}`, + }, tracker: models.OWTracker{ TrackerURL: `Tracking URL`, }, }, - want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["abc.com","Tracking URL"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":2,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.php"}]}`, + wantAdm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["abc.com","Tracking URL"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":2,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.php"}]}`, wantErr: false, }, { @@ -119,12 +151,14 @@ func Test_injectNativeCreativeTrackers(t *testing.T) { native: &openrtb2.Native{ Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", }, - adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + bid: openrtb2.Bid{ + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + }, tracker: models.OWTracker{ TrackerURL: `Tracking URL`, }, }, - want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"Tracking URL"}]}`, + wantAdm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"Tracking URL"}]}`, wantErr: false, }, { @@ -133,12 +167,14 @@ func Test_injectNativeCreativeTrackers(t *testing.T) { native: &openrtb2.Native{ Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", }, - adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + bid: openrtb2.Bid{ + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + }, tracker: models.OWTracker{ TrackerURL: `Tracking URL`, }, }, - want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"Tracking URL"}]}`, + wantAdm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"Tracking URL"}]}`, wantErr: false, }, { @@ -147,12 +183,14 @@ func Test_injectNativeCreativeTrackers(t *testing.T) { native: &openrtb2.Native{ Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", }, - adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e"}`, + bid: openrtb2.Bid{ + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e"}`, + }, tracker: models.OWTracker{ TrackerURL: `Tracking URL`, }, }, - want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"Tracking URL"}]}`, + wantAdm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"Tracking URL"}]}`, wantErr: false, }, { @@ -161,25 +199,26 @@ func Test_injectNativeCreativeTrackers(t *testing.T) { native: &openrtb2.Native{ Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", }, - adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"abc.com"}]}`, + bid: openrtb2.Bid{ + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"abc.com"}]}`, + }, tracker: models.OWTracker{ TrackerURL: `Tracking URL`, }, }, - want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"abc.com"},{"event":1,"method":1,"url":"Tracking URL"}]}`, + wantAdm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"abc.com"},{"event":1,"method":1,"url":"Tracking URL"}]}`, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := injectNativeCreativeTrackers(tt.args.native, tt.args.adm, tt.args.tracker) + gotAdm, gotBurl, err := injectNativeCreativeTrackers(tt.args.native, tt.args.bid, tt.args.tracker, tt.args.endpoint) if (err != nil) != tt.wantErr { t.Errorf("injectNativeCreativeTrackers() error = %v, wantErr %v", err, tt.wantErr) return } - if got != tt.want { - t.Errorf("injectNativeCreativeTrackers() = %v, want %v", got, tt.want) - } + assert.Equal(t, tt.wantAdm, gotAdm, tt.name) + assert.Equal(t, tt.wantBurl, gotBurl, tt.name) }) } } diff --git a/modules/pubmatic/openwrap/tracker/video.go b/modules/pubmatic/openwrap/tracker/video.go index 72a0150bdc4..09f75afb3a8 100644 --- a/modules/pubmatic/openwrap/tracker/video.go +++ b/modules/pubmatic/openwrap/tracker/video.go @@ -11,28 +11,38 @@ import ( ) // Inject Trackers in Video Creative -func injectVideoCreativeTrackers(bid openrtb2.Bid, videoParams []models.OWTracker) (string, error) { +func injectVideoCreativeTrackers(rctx models.RequestCtx, bid openrtb2.Bid, videoParams []models.OWTracker) (string, string, error) { if bid.AdM == "" || len(videoParams) == 0 { - return "", errors.New("bid is nil or tracker data is missing") + return "", bid.BURL, errors.New("bid is nil or tracker data is missing") + } + + injectImpressionTracker := true + if rctx.Endpoint == models.EndpointAppLovinMax { + injectImpressionTracker = false } originalCreativeStr := bid.AdM if strings.HasPrefix(originalCreativeStr, models.HTTPProtocol) { originalCreativeStr = strings.Replace(models.VastWrapper, models.PartnerURLPlaceholder, originalCreativeStr, -1) - originalCreativeStr = strings.Replace(originalCreativeStr, models.TrackerPlaceholder, videoParams[0].TrackerURL, -1) + if injectImpressionTracker { + originalCreativeStr = strings.Replace(originalCreativeStr, models.TrackerPlaceholder, videoParams[0].TrackerURL, -1) + } else { + originalCreativeStr = strings.Replace(originalCreativeStr, models.VASTImpressionURLTemplate, "", -1) + bid.BURL = getBURL(bid.BURL, videoParams[0].TrackerURL) + } originalCreativeStr = strings.Replace(originalCreativeStr, models.ErrorPlaceholder, videoParams[0].ErrorURL, -1) bid.AdM = originalCreativeStr } else { originalCreativeStr = strings.TrimSpace(originalCreativeStr) doc := etree.NewDocument() if err := doc.ReadFromString(originalCreativeStr); err != nil { - return bid.AdM, errors.New("invalid creative format") + return bid.AdM, bid.BURL, errors.New("invalid creative format") } //Check VAST Object vast := doc.Element.FindElement(models.VideoVASTTag) if vast == nil { - return bid.AdM, errors.New("VAST Tag Not Found") + return bid.AdM, bid.BURL, errors.New("VAST Tag Not Found") } //GetVersion @@ -44,19 +54,23 @@ func injectVideoCreativeTrackers(bid openrtb2.Bid, videoParams []models.OWTracke element := adElement.FindElement(models.AdWrapperElement) isWrapper := (nil != element) - if nil == element { + if element == nil { element = adElement.FindElement(models.AdInlineElement) } - if nil == element { - return bid.AdM, errors.New("video creative not in required VAST format") + if element == nil { + return bid.AdM, bid.BURL, errors.New("video creative not in required VAST format") } if len(videoParams[i].TrackerURL) > 0 { // set tracker URL - newElement := etree.NewElement(models.ImpressionElement) - newElement.SetText(videoParams[i].TrackerURL) - element.InsertChild(element.SelectElement(models.ImpressionElement), newElement) + if injectImpressionTracker { + newElement := etree.NewElement(models.ImpressionElement) + newElement.SetText(videoParams[i].TrackerURL) + element.InsertChild(element.SelectElement(models.ImpressionElement), newElement) + } else { + bid.BURL = getBURL(bid.BURL, videoParams[i].TrackerURL) + } } if len(videoParams[i].ErrorURL) > 0 { @@ -66,7 +80,7 @@ func injectVideoCreativeTrackers(bid openrtb2.Bid, videoParams []models.OWTracke element.InsertChild(element.SelectElement(models.ErrorElement), newElement) } - if false == isWrapper && videoParams[i].Price != 0 { + if !isWrapper && videoParams[i].Price != 0 { if models.VideoVASTVersion2_0 == version { injectPricingNodeVAST20(element, videoParams[i].Price, videoParams[i].PriceModel, videoParams[i].PriceCurrency) } else { @@ -78,11 +92,11 @@ func injectVideoCreativeTrackers(bid openrtb2.Bid, videoParams []models.OWTracke updatedVastStr, err := doc.WriteToString() if err != nil { - return bid.AdM, err + return bid.AdM, bid.BURL, err } - return updatedVastStr, nil + return updatedVastStr, bid.BURL, nil } - return bid.AdM, nil + return bid.AdM, bid.BURL, nil } func injectPricingNodeVAST20(parent *etree.Element, price float64, model string, currency string) { diff --git a/modules/pubmatic/openwrap/tracker/video_test.go b/modules/pubmatic/openwrap/tracker/video_test.go index b69b4776010..1b3c188b3b9 100644 --- a/modules/pubmatic/openwrap/tracker/video_test.go +++ b/modules/pubmatic/openwrap/tracker/video_test.go @@ -9,16 +9,18 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_injectVideoCreativeTrackers(t *testing.T) { +func TestInjectVideoCreativeTrackers(t *testing.T) { type args struct { bid openrtb2.Bid videoParams []models.OWTracker + rctx models.RequestCtx } tests := []struct { - name string - args args - want string - wantErr bool + name string + args args + wantAdm string + wantBurl string + wantErr bool }{ { name: "empty_bid", @@ -31,7 +33,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: ``, + wantAdm: ``, wantErr: true, }, { @@ -46,7 +48,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: ``, + wantAdm: ``, wantErr: true, }, { @@ -63,7 +65,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: ``, + wantAdm: ``, wantErr: true, }, { @@ -80,7 +82,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: `PubMatic Wrapper`, + wantAdm: `PubMatic Wrapper`, wantErr: false, }, { @@ -92,7 +94,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, videoParams: []models.OWTracker{}, }, - want: ``, + wantAdm: ``, wantErr: true, }, { @@ -109,7 +111,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: `invalid_vast_creative`, + wantAdm: `invalid_vast_creative`, wantErr: true, }, { @@ -126,7 +128,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: ``, + wantAdm: ``, wantErr: true, }, { @@ -144,7 +146,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: ``, + wantAdm: ``, wantErr: false, }, { @@ -162,7 +164,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: ``, + wantAdm: ``, wantErr: false, }, { @@ -180,7 +182,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: ``, + wantAdm: ``, wantErr: false, }, { @@ -198,7 +200,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: ``, + wantAdm: ``, wantErr: false, }, { @@ -216,7 +218,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: ``, + wantAdm: ``, wantErr: false, }, { @@ -234,7 +236,7 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: ``, + wantAdm: ``, wantErr: false, }, { @@ -251,20 +253,179 @@ func Test_injectVideoCreativeTrackers(t *testing.T) { }, }, }, - want: ``, + wantAdm: ``, wantErr: false, }, + { + name: "vast_2.0_inline_pricing_with_EndpointAppLovinMax", + args: args{ + + bid: openrtb2.Bid{ + AdM: ``, + BURL: "https://burl.com", + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + rctx: models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + }, + }, + wantBurl: "Tracking URL&owsspburl=https://burl.com", + wantAdm: ``, + wantErr: false, + }, + { + name: "vast_3.0_inline_pricing_with_EndpointAppLovinMax", + args: args{ + + bid: openrtb2.Bid{ + AdM: ``, + BURL: "https://burl.com", + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracker URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + rctx: models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + }, + }, + wantBurl: "Tracker URL&owsspburl=https://burl.com", + wantAdm: ``, + wantErr: false, + }, + { + name: "inline_vast_3.0_with_EndpointAppLovinMax", + args: args{ + + bid: openrtb2.Bid{ + BURL: "https://burl.com", + AdM: `Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1https://dsptracker.com/{PSPM}00:00:04https://www.sample.com`, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracker URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + rctx: models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + }, + }, + wantBurl: "Tracker URL&owsspburl=https://burl.com", + wantAdm: ``, + wantErr: false, + }, + { + name: "wrapper_vast_2.0_with_EndpointAppLovinMax", + args: args{ + + bid: openrtb2.Bid{ + BURL: "https://burl.com", + AdM: `DSP`, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracker URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + rctx: models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + }, + }, + wantBurl: "Tracker URL&owsspburl=https://burl.com", + wantAdm: ``, + wantErr: false, + }, + { + name: "inline_vast_with_no_cdata_and_EndpointAppLovinMax", + args: args{ + + bid: openrtb2.Bid{ + BURL: "https://burl.com", + AdM: `Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1http://172.16.4.213/AdServer/AdDisplayTrackerServlethttps://dsptracker.com/{PSPM}http://172.16.4.213/trackhttps://Errortrack.com00:00:04http://172.16.4.213/trackhttps://www.sample.comhttps://stagingnyc.pubmatic.com:8443/video/Shashank/mediaFileHost/media/mp4-sample-1.mp4]https://stagingnyc.pubmatic.com:8443/video/Shashank/mediaFileHost/media/mp4-sample-2.mp4]`, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracker URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + rctx: models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + }, + }, + wantBurl: "Tracker URL&owsspburl=https://burl.com", + wantAdm: ``, + wantErr: false, + }, + { + name: "wrapper_vast_with_no_cdata_and_EndpointAppLovinMax", + args: args{ + + bid: openrtb2.Bid{ + BURL: "https://burl.com", + AdM: `DSPhttps://stagingnyc.pubmatic.com:8443/test/pub_vast.xmlhttps://track.dsp.com/er=[ERRORCODE]/tracker/errorhttps://track.dsp.com?e=impressionhttp://track.dsp.com/tracker/click`, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracker URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + rctx: models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + }, + }, + wantBurl: "Tracker URL&owsspburl=https://burl.com", + wantAdm: ``, + wantErr: false, + }, + { + name: "spaces_in_creative_with_EndpointAppLovinMax", + args: args{ + + bid: openrtb2.Bid{ + BURL: "https://burl.com", + AdM: ` `, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracker URL`, + ErrorURL: `Error URL`, + }, + }, + rctx: models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + }, + }, + wantBurl: "Tracker URL&owsspburl=https://burl.com", + wantAdm: ``, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := injectVideoCreativeTrackers(tt.args.bid, tt.args.videoParams) + gotAdm, gotBurl, err := injectVideoCreativeTrackers(tt.args.rctx, tt.args.bid, tt.args.videoParams) if (err != nil) != tt.wantErr { t.Errorf("injectVideoCreativeTrackers() error = %v, wantErr %v", err, tt.wantErr) return } - if got != tt.want { - t.Errorf("injectVideoCreativeTrackers() = %v, want %v", got, tt.want) - } + assert.Equal(t, tt.wantAdm, gotAdm, tt.name) + assert.Equal(t, tt.wantBurl, gotBurl, tt.name) }) } }