diff --git a/analytics/pubmatic/logger.go b/analytics/pubmatic/logger.go index f30ee4c0ec2..cb97d1e3b87 100644 --- a/analytics/pubmatic/logger.go +++ b/analytics/pubmatic/logger.go @@ -55,7 +55,8 @@ func GetLogAuctionObjectAsURL(ao analytics.AuctionObject, rCtx *models.RequestCt } wlog.logIntegrationType(rCtx.Endpoint) - wlog.logDeviceObject(rCtx, ao.RequestWrapper.BidRequest) + + wlog.logDeviceObject(&rCtx.DeviceCtx) if ao.RequestWrapper.User != nil { extUser := openrtb_ext.ExtUser{} @@ -360,8 +361,8 @@ func getPartnerRecordsByImp(ao analytics.AuctionObject, rCtx *models.RequestCtx) price := bid.Price if ao.Response.Cur != models.USD { - if bidCtx.EG != 0 { // valid-bids + dropped-bids+ default-bids - price = bidCtx.EG + if bidCtx.EN != 0 { // valid-bids + dropped-bids+ default-bids + price = bidCtx.EN } else if bidExt.OriginalBidCPMUSD != 0 { // valid non-bids price = bidExt.OriginalBidCPMUSD } @@ -403,11 +404,11 @@ func getPartnerRecordsByImp(ao analytics.AuctionObject, rCtx *models.RequestCtx) } if pr.NetECPM == 0 { - pr.NetECPM = models.GetNetEcpm(price, revShare) + pr.NetECPM = models.ToFixed(price, models.BID_PRECISION) } if pr.GrossECPM == 0 { - pr.GrossECPM = models.GetGrossEcpm(price) + pr.GrossECPM = models.GetGrossEcpmFromNetEcpm(price, revShare) } if pr.PartnerSize == "" { diff --git a/analytics/pubmatic/logger_test.go b/analytics/pubmatic/logger_test.go index a3c88615301..fed442d64f8 100644 --- a/analytics/pubmatic/logger_test.go +++ b/analytics/pubmatic/logger_test.go @@ -3024,8 +3024,8 @@ func TestGetPartnerRecordsByImpForRevShareAndBidCPM(t *testing.T) { partners: map[string][]PartnerRecord{ "imp1": { { - NetECPM: 90, - GrossECPM: 100, + NetECPM: 100, + GrossECPM: 111.11, OriginalCPM: 100, OriginalCur: "USD", PartnerID: "pubmatic", @@ -3198,8 +3198,8 @@ func TestGetPartnerRecordsByImpForRevShareAndBidCPM(t *testing.T) { partners: map[string][]PartnerRecord{ "imp1": { { - NetECPM: 90, - GrossECPM: 100, + NetECPM: 100, + GrossECPM: 111.11, OriginalCPM: 200, OriginalCur: "INR", PartnerID: "pubmatic", @@ -3513,8 +3513,10 @@ func TestGetLogAuctionObjectAsURL(t *testing.T) { Response: &openrtb2.BidResponse{}, }, rCtx: &models.RequestCtx{ - DevicePlatform: models.DevicePlatformMobileAppAndroid, - PubID: 5890, + PubID: 5890, + DeviceCtx: models.DeviceCtx{ + Platform: models.DevicePlatformMobileAppAndroid, + }, }, logInfo: true, forRespExt: true, @@ -3532,17 +3534,16 @@ func TestGetLogAuctionObjectAsURL(t *testing.T) { args: args{ ao: analytics.AuctionObject{ RequestWrapper: &openrtb_ext.RequestWrapper{ - BidRequest: &openrtb2.BidRequest{ - Device: &openrtb2.Device{ - Ext: json.RawMessage(`{"ifa_type":"sspid"}`), - }, - }, + BidRequest: &openrtb2.BidRequest{}, }, Response: &openrtb2.BidResponse{}, }, rCtx: &models.RequestCtx{ - DevicePlatform: models.DevicePlatformMobileAppAndroid, - PubID: 5890, + PubID: 5890, + DeviceCtx: models.DeviceCtx{ + Platform: models.DevicePlatformMobileAppAndroid, + IFATypeID: ptrutil.ToPtr(8), + }, }, logInfo: true, forRespExt: true, @@ -3555,6 +3556,36 @@ func TestGetLogAuctionObjectAsURL(t *testing.T) { }, }, }, + { + name: "log_device.ext.atts", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + DeviceCtx: models.DeviceCtx{ + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + ATTS: ptrutil.ToPtr(openrtb_ext.IOSAppTrackingStatusRestricted), + }, + }, + }, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: ow.cfg.PublicEndpoint + `?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{"atts":1},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, { name: "log content from site object", args: args{ diff --git a/analytics/pubmatic/record.go b/analytics/pubmatic/record.go index 13d72c16f8f..3d0083ae5ca 100644 --- a/analytics/pubmatic/record.go +++ b/analytics/pubmatic/record.go @@ -2,7 +2,6 @@ package pubmatic import ( "encoding/json" - "strings" "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/openrtb/v19/openrtb3" @@ -39,21 +38,22 @@ type record struct { AdPodPercentage *AdPodPercentage `json:"aps,omitempty"` Content *Content `json:"ct,omitempty"` TestConfigApplied int `json:"tgid,omitempty"` - //Geo GeoRecord `json:"geo,omitempty"` - FloorModelVersion string `json:"fmv,omitempty"` - FloorSource *int `json:"fsrc,omitempty"` - FloorType int `json:"ft"` - IntegrationType string `json:"it,omitempty"` - FloorFetchStatus *int `json:"ffs,omitempty"` - FloorProvider string `json:"fp,omitempty"` - PDC string `json:"pdc,omitempty"` - CustomDimensions string `json:"cds,omitempty"` + FloorModelVersion string `json:"fmv,omitempty"` + FloorSource *int `json:"fsrc,omitempty"` + FloorType int `json:"ft"` + IntegrationType string `json:"it,omitempty"` + FloorFetchStatus *int `json:"ffs,omitempty"` + FloorProvider string `json:"fp,omitempty"` + PDC string `json:"pdc,omitempty"` + CustomDimensions string `json:"cds,omitempty"` + //Geo GeoRecord `json:"geo,omitempty"` } // Device struct for storing device information type Device struct { - Platform models.DevicePlatform `json:"plt,omitempty"` - IFAType *models.DeviceIFAType `json:"ifty,omitempty"` //OTT-416, adding device.ext.ifa_type + Platform models.DevicePlatform `json:"plt,omitempty"` + IFAType *models.DeviceIFAType `json:"ifty,omitempty"` //OTT-416, adding device.ext.ifa_type + ATTS *openrtb_ext.IOSAppTrackingStatus `json:"atts,omitempty"` //device.ext.atts } /* @@ -173,32 +173,6 @@ var FetchStatusMap = map[string]int{ openrtb_ext.FetchTimeout: 4, } -// logDeviceObject is used to add device specific parameters like platform and ifa_type in logger -func (wlog *WloggerRecord) logDeviceObject(rctx *models.RequestCtx, ortbBidRequest *openrtb2.BidRequest) { - dvc := Device{ - Platform: rctx.DevicePlatform, - } - - if ortbBidRequest != nil && ortbBidRequest.Device != nil && ortbBidRequest.Device.Ext != nil { - ext := make(map[string]interface{}) - err := json.Unmarshal(ortbBidRequest.Device.Ext, &ext) - if err != nil { - return - } - - //use ext object for logging any other extension parameters - //log device.ext.ifa_type parameter to ifty in logger record - if value, ok := ext["ifa_type"].(string); ok { - //ifa_type checking is valid parameter and log its respective id - ifaType := models.DeviceIFATypeID[strings.ToLower(value)] - dvc.IFAType = &ifaType - } - } - - //settind device object - wlog.Device = dvc -} - // SetIntegrationType sets the integration type in WloggerRecord func (wlog *WloggerRecord) logIntegrationType(endpoint string) { switch endpoint { @@ -217,6 +191,18 @@ func (wlog *WloggerRecord) logIntegrationType(endpoint string) { } } +func (wlog *WloggerRecord) logDeviceObject(dvc *models.DeviceCtx) { + if dvc == nil { + return + } + + wlog.Device.Platform = dvc.Platform + wlog.Device.IFAType = dvc.IFATypeID + if dvc.Ext != nil { + wlog.record.Device.ATTS = dvc.Ext.ATTS + } +} + // logFloorType will be used to log floor type func (wlog *WloggerRecord) logFloorType(prebid *openrtb_ext.ExtRequestPrebid) { wlog.record.FloorType = models.SoftFloor diff --git a/analytics/pubmatic/record_test.go b/analytics/pubmatic/record_test.go index 7174074781a..067da0de7bb 100644 --- a/analytics/pubmatic/record_test.go +++ b/analytics/pubmatic/record_test.go @@ -1,7 +1,6 @@ package pubmatic import ( - "encoding/json" "testing" "github.com/prebid/openrtb/v19/openrtb2" @@ -11,168 +10,6 @@ import ( "github.com/stretchr/testify/assert" ) -func TestLogDeviceObject(t *testing.T) { - type args struct { - ortbBidRequest *openrtb2.BidRequest - rctx *models.RequestCtx - } - - type want struct { - device Device - } - - tests := []struct { - name string - args args - want want - }{ - { - name: `Nil request`, - args: args{ - rctx: &models.RequestCtx{ - DevicePlatform: models.DevicePlatformDesktop, - }, - }, - want: want{ - device: Device{ - Platform: models.DevicePlatformDesktop, - }, - }, - }, - { - name: `Empty uaFromHTTPReq`, - args: args{ - ortbBidRequest: &openrtb2.BidRequest{}, - rctx: &models.RequestCtx{ - DevicePlatform: models.DevicePlatformMobileWeb, - }, - }, - want: want{ - device: Device{ - Platform: models.DevicePlatformMobileWeb, - }, - }, - }, - { - name: `Invalid device ext`, - args: args{ - ortbBidRequest: &openrtb2.BidRequest{ - Device: &openrtb2.Device{ - Ext: json.RawMessage(`invalid ext`), - }, - }, - rctx: &models.RequestCtx{ - DevicePlatform: models.DevicePlatformMobileWeb, - }, - }, - want: want{ - device: Device{ - Platform: 0, - }, - }, - }, - { - name: `IFA Type key absent`, - args: args{ - ortbBidRequest: &openrtb2.BidRequest{ - Device: &openrtb2.Device{ - Ext: json.RawMessage(`{"anykey":"anyval"}`), - }, - }, - rctx: &models.RequestCtx{ - DevicePlatform: models.DevicePlatformMobileWeb, - }, - }, - want: want{ - device: Device{ - Platform: models.DevicePlatformMobileWeb, - }, - }, - }, - { - name: `Invalid data type for ifa_type key`, - args: args{ - ortbBidRequest: &openrtb2.BidRequest{ - Device: &openrtb2.Device{ - Ext: json.RawMessage(`{"ifa_type": 123}`)}, - }, - rctx: &models.RequestCtx{ - DevicePlatform: models.DevicePlatformMobileWeb, - }, - }, - want: want{ - device: Device{ - Platform: models.DevicePlatformMobileWeb, - }, - }, - }, - { - name: `ifa_type missing in DeviceIFATypeID mapping`, - args: args{ - ortbBidRequest: &openrtb2.BidRequest{ - Device: &openrtb2.Device{ - Ext: json.RawMessage(`{"ifa_type": "anything"}`), - }, - }, - rctx: &models.RequestCtx{ - DevicePlatform: models.DevicePlatformMobileWeb, - }, - }, - want: want{ - device: Device{ - Platform: models.DevicePlatformMobileWeb, - IFAType: ptrutil.ToPtr(0), - }, - }, - }, - { - name: `Case insensitive ifa_type`, - args: args{ - ortbBidRequest: &openrtb2.BidRequest{ - Device: &openrtb2.Device{ - Ext: json.RawMessage(`{"ifa_type": "DpId"}`), - }, - }, - rctx: &models.RequestCtx{ - DevicePlatform: models.DevicePlatformMobileWeb, - }, - }, - want: want{ - device: Device{ - Platform: models.DevicePlatformMobileWeb, - IFAType: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), - }, - }, - }, - { - name: `Valid ifa_type`, - args: args{ - ortbBidRequest: &openrtb2.BidRequest{ - Device: &openrtb2.Device{ - Ext: json.RawMessage(`{"ifa_type": "sessionid"}`), - }, - }, - rctx: &models.RequestCtx{ - DevicePlatform: models.DevicePlatformMobileWeb, - }, - }, - want: want{ - device: Device{ - Platform: models.DevicePlatformMobileWeb, - IFAType: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeSESSIONID]), - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - wlog := &WloggerRecord{} - wlog.logDeviceObject(tt.args.rctx, tt.args.ortbBidRequest) - assert.Equal(t, tt.want.device, wlog.Device) - }) - } -} - func TestLogIntegrationType(t *testing.T) { tests := []struct { name string @@ -517,3 +354,102 @@ func TestSetMetaDataObject(t *testing.T) { }) } } + +func TestLogDeviceObject(t *testing.T) { + type args struct { + dvc *models.DeviceCtx + } + tests := []struct { + name string + args args + want Device + }{ + { + name: `empty`, + args: args{ + dvc: nil, + }, + want: Device{}, + }, + { + name: `missing_ifatype`, + args: args{ + dvc: &models.DeviceCtx{ + Platform: models.DevicePlatformDesktop, + }, + }, + want: Device{ + Platform: models.DevicePlatformDesktop, + }, + }, + { + name: `missing_ext`, + args: args{ + dvc: &models.DeviceCtx{ + Platform: models.DevicePlatformDesktop, + IFATypeID: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), + }, + }, + want: Device{ + Platform: models.DevicePlatformDesktop, + IFAType: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), + }, + }, + { + name: `missing_device_ext`, + args: args{ + dvc: &models.DeviceCtx{ + Platform: models.DevicePlatformDesktop, + IFATypeID: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), + Ext: &models.ExtDevice{}, + }, + }, + want: Device{ + Platform: models.DevicePlatformDesktop, + IFAType: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), + }, + }, + { + name: `missing_atts`, + args: args{ + dvc: &models.DeviceCtx{ + Platform: models.DevicePlatformDesktop, + IFATypeID: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{}, + }, + }, + }, + want: Device{ + Platform: models.DevicePlatformDesktop, + IFAType: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), + }, + }, + { + name: `valid`, + args: args{ + dvc: &models.DeviceCtx{ + Platform: models.DevicePlatformDesktop, + IFATypeID: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + ATTS: ptrutil.ToPtr(openrtb_ext.IOSAppTrackingStatusNotDetermined), + }, + }, + }, + }, + want: Device{ + Platform: models.DevicePlatformDesktop, + IFAType: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), + ATTS: ptrutil.ToPtr(openrtb_ext.IOSAppTrackingStatusNotDetermined), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + wlog := &WloggerRecord{} + wlog.logDeviceObject(tt.args.dvc) + assert.Equal(t, tt.want, wlog.Device) + }) + } +} diff --git a/modules/pubmatic/openwrap/adapters/bidder_alias.go b/modules/pubmatic/openwrap/adapters/bidder_alias.go index ca19190fc31..4873aac7d59 100644 --- a/modules/pubmatic/openwrap/adapters/bidder_alias.go +++ b/modules/pubmatic/openwrap/adapters/bidder_alias.go @@ -5,14 +5,25 @@ import ( "github.com/prebid/prebid-server/v2/openrtb_ext" ) +// Alias will return copy of exisiting alias +func Alias() map[string]string { + + return map[string]string{ + models.BidderAdGenerationAlias: string(openrtb_ext.BidderAdgeneration), + models.BidderDistrictmDMXAlias: string(openrtb_ext.BidderDmx), + models.BidderPubMaticSecondaryAlias: string(openrtb_ext.BidderPubmatic), + models.BidderDistrictmAlias: string(openrtb_ext.BidderAppnexus), + models.BidderAndBeyondAlias: string(openrtb_ext.BidderAdkernel), + models.BidderMediaFuseAlias: string(openrtb_ext.BidderAppnexus), + } +} + //ResolveOWBidder it resolves hardcoded bidder alias names func ResolveOWBidder(bidderName string) string { - var coreBidderName string switch bidderName { - case models.BidderAdGenerationAlias: coreBidderName = string(openrtb_ext.BidderAdgeneration) case models.BidderDistrictmDMXAlias: @@ -25,8 +36,6 @@ func ResolveOWBidder(bidderName string) string { coreBidderName = string(openrtb_ext.BidderAdkernel) default: coreBidderName = bidderName - } - return coreBidderName } diff --git a/modules/pubmatic/openwrap/adapters/bidder_alias_test.go b/modules/pubmatic/openwrap/adapters/bidder_alias_test.go new file mode 100644 index 00000000000..17af57cc804 --- /dev/null +++ b/modules/pubmatic/openwrap/adapters/bidder_alias_test.go @@ -0,0 +1,29 @@ +package adapters + +import ( + "testing" + + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestAlias(t *testing.T) { + expected := map[string]string{ + models.BidderAdGenerationAlias: string(openrtb_ext.BidderAdgeneration), + models.BidderDistrictmDMXAlias: string(openrtb_ext.BidderDmx), + models.BidderPubMaticSecondaryAlias: string(openrtb_ext.BidderPubmatic), + models.BidderDistrictmAlias: string(openrtb_ext.BidderAppnexus), + models.BidderAndBeyondAlias: string(openrtb_ext.BidderAdkernel), + models.BidderMediaFuseAlias: string(openrtb_ext.BidderAppnexus), + } + assert.Equal(t, expected, Alias()) +} + +func TestResolveOWBidder(t *testing.T) { + assert.Equal(t, "", ResolveOWBidder("")) + assert.Equal(t, models.BidderPubMatic, ResolveOWBidder(models.BidderPubMatic)) + for alias, coreBidder := range Alias() { + assert.Equal(t, coreBidder, ResolveOWBidder(alias)) + } +} diff --git a/modules/pubmatic/openwrap/adapters/bidders.go b/modules/pubmatic/openwrap/adapters/bidders.go index 7b177051a85..68a49630e77 100644 --- a/modules/pubmatic/openwrap/adapters/bidders.go +++ b/modules/pubmatic/openwrap/adapters/bidders.go @@ -22,7 +22,7 @@ func PrepareBidParamJSONForPartner(width *int64, height *int64, fieldMap map[str } //get callback function and execute it - callback := getBuilder(params.AdapterName) + callback := GetBuilder(params.AdapterName) return callback(params) } @@ -602,3 +602,40 @@ func builderBoldwin(params BidderParameters) (json.RawMessage, error) { jsonStr.WriteByte('}') return jsonStr.Bytes(), nil } + +func builderColossus(params BidderParameters) (json.RawMessage, error) { + jsonStr := bytes.Buffer{} + jsonStr.WriteByte('{') + oneOf := []string{BidderParamColossusTagID, BidderParamColossusgroupID} + for _, param := range oneOf { + if key, ok := getString(params.FieldMap[param]); ok { + fmt.Fprintf(&jsonStr, `"%s":"%s"`, param, key) + break + } + } + // len=0 (no mandatory params present) + if jsonStr.Len() == 1 { + return nil, fmt.Errorf(errMandatoryParameterMissingFormat, params.AdapterName, oneOf) + } + + jsonStr.WriteByte('}') + return jsonStr.Bytes(), nil +} + +func builderNextmillennium(params BidderParameters) (json.RawMessage, error) { + jsonStr := bytes.Buffer{} + + anyOf := []string{"placement_id", "group_id"} + for _, param := range anyOf { + if val, ok := getString(params.FieldMap[param]); ok { + fmt.Fprintf(&jsonStr, `{"%s":"%s"}`, param, val) + break + } + } + + if jsonStr.Len() == 0 { + return nil, fmt.Errorf(errMandatoryParameterMissingFormat, params.AdapterName, anyOf) + } + + return jsonStr.Bytes(), nil +} diff --git a/modules/pubmatic/openwrap/adapters/bidders_test.go b/modules/pubmatic/openwrap/adapters/bidders_test.go new file mode 100644 index 00000000000..7f5e872568e --- /dev/null +++ b/modules/pubmatic/openwrap/adapters/bidders_test.go @@ -0,0 +1,2921 @@ +package adapters + +import ( + "encoding/json" + "fmt" + "os" + "strconv" + "testing" + + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/stretchr/testify/assert" +) + +func init() { + InitBidders("../../../../static/bidder-params") +} + +func AssertJSON(t *testing.T, expectedJSON, actualJSON json.RawMessage, msgAndArgs ...interface{}) bool { + msgAndArgs = append(msgAndArgs, fmt.Sprintf("expectedJSON: %s\nactualJSON: %s", string(expectedJSON), string(actualJSON))) + //assert.JSONq returns '('') invalid json' for empty string, to avoid these cases if actual and expected have equal zero lengths, we are returing as valid assertion + if len(expectedJSON) == 0 && len(actualJSON) == 0 { + return true + } + return assert.JSONEq(t, string(expectedJSON), string(actualJSON), msgAndArgs...) +} + +func AssertJSONObject(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) bool { + expectedJSON, _ := json.Marshal(expected) + actualJSON, _ := json.Marshal(actual) + return AssertJSON(t, expectedJSON, actualJSON, msgAndArgs...) +} + +func GetJSON(obj interface{}) string { + data, _ := json.Marshal(obj) + return string(data[:]) +} + +func readTestCasesFromFile(t *testing.T, filePath string, tests interface{}) { + //reading testcases from file + testData, err := os.ReadFile(filePath) + assert.NoError(t, err) + + err = json.Unmarshal(testData, tests) + assert.NoError(t, err) +} + +// getPrebidBidderParamsValidator getting prebid bidder params validator +func getPrebidBidderParamsValidator(t *testing.T, schemaDirectory string) openrtb_ext.BidderParamValidator { + validator, err := openrtb_ext.NewBidderParamsValidator(schemaDirectory) + if err != nil { + t.Logf("failed in getPrebidBidderParamsValidator, schemaDirectory:%s, error:%s", schemaDirectory, err.Error()) + t.FailNow() + } + return validator +} + +func formBidderKeywordsMap() map[string]*models.BidderExtension { + kvp1 := formKeyVal("key1", []string{"val1", "val2"}) + kvp2 := formKeyVal("key2", []string{"val3", "val4"}) + kvArr := []models.KeyVal{kvp1, kvp2} + bmap := map[string]*models.BidderExtension{ + "appnexus": {KeyWords: kvArr}, + "pubmatic": {KeyWords: kvArr}, + } + return bmap +} + +func formKeyVal(key string, values []string) models.KeyVal { + kv := models.KeyVal{ + Key: key, + Values: values, + } + return kv +} + +func formBidderKeywordsMapForAppnexusAlias() map[string]*models.BidderExtension { + kvp1 := formKeyVal("key1", []string{"val1", "val2"}) + kvp2 := formKeyVal("key2", []string{"val3", "val4"}) + kvArr := []models.KeyVal{kvp1, kvp2} + bmap := map[string]*models.BidderExtension{ + "appnexus-alias": {KeyWords: kvArr}, + "pubmatic": {KeyWords: kvArr}, + } + return bmap +} + +func TestPrepareBidParamJSONForPartner33across(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + wantErr bool + }{ + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "siteId": "testSite", + "productId": "testProduct", + }, + slotKey: "", + adapterName: string(openrtb_ext.Bidder33Across), + bidderCode: string(openrtb_ext.Bidder33Across), + }, + want: json.RawMessage(`{"productId":"testProduct","siteId":"testSite"}`), + wantErr: false, + }, + { + name: "siteId is missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "productId": "testProduct", + }, + slotKey: "", + adapterName: string(openrtb_ext.Bidder33Across), + bidderCode: string(openrtb_ext.Bidder33Across), + }, + want: nil, + wantErr: true, + }, + { + name: "productId is missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "siteId": "testSite", + }, + slotKey: "", + adapterName: string(openrtb_ext.Bidder33Across), + bidderCode: string(openrtb_ext.Bidder33Across), + }, + want: nil, + wantErr: true, + }, + { + name: "required params are missing", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: map[string]interface{}{}, + slotKey: "", + adapterName: string(openrtb_ext.Bidder33Across), + bidderCode: string(openrtb_ext.Bidder33Across), + }, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + assert.Equal(t, tt.wantErr, err != nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerAdf(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "mid": "1234", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderAdf), + }, + want: json.RawMessage(`{"mid":1234}`), + }, + { + name: "required param mid is missing", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: map[string]interface{}{}, + slotKey: "", + adapterName: string(openrtb_ext.BidderAdf), + }, + want: nil, + }, + { + name: "required param mid is not an integer", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "mid": "abc", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderAdf), + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, string(openrtb_ext.BidderAdf), nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerCriteo(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "Only zoneId present", + args: args{ + fieldMap: map[string]interface{}{ + "zoneId": "1", + }, + adapterName: string(openrtb_ext.BidderCriteo), + }, + want: json.RawMessage(`{"zoneId":1}`), + }, + { + name: "Only networkId present", + args: args{ + fieldMap: map[string]interface{}{ + "networkId": "4", + }, + adapterName: string(openrtb_ext.BidderCriteo), + }, + want: json.RawMessage(`{"networkId":4}`), + }, + { + name: "All params present", + args: args{ + fieldMap: map[string]interface{}{ + "zoneId": "1", + "zoneid": "2", + "networkid": "3", + "networkId": "4", + }, + adapterName: string(openrtb_ext.BidderCriteo), + }, + want: json.RawMessage(`{"zoneId":1}`), + }, + { + name: "No required params present", + args: args{ + fieldMap: map[string]interface{}{ + "ZoneId": "5", + }, + adapterName: string(openrtb_ext.BidderCriteo), + }, + want: nil, + }, + { + name: "Invalid params value", + args: args{ + fieldMap: map[string]interface{}{ + "zoneId": "a", + }, + adapterName: string(openrtb_ext.BidderCriteo), + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerAdform(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "mid": "1234", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderAdf), + }, + want: json.RawMessage(`{"mid":1234}`), + }, + { + name: "required param mid is missing", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: map[string]interface{}{}, + slotKey: "", + adapterName: string(openrtb_ext.BidderAdf), + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, string(openrtb_ext.BidderAdf), nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerSovrn(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "tagid": "315045", + "bidfloor": "0.04", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSovrn), + }, + want: json.RawMessage(`{"tagid":"315045","bidfloor":0.04}`), + }, + { + name: "param tagid is missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "bidfloor": "0.04", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSovrn), + }, + want: json.RawMessage(`{"bidfloor":0.04}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, string(openrtb_ext.BidderSovrn), nil) + AssertJSON(t, tt.want, got) + }) + } + +} + +func TestPrepareBidParamJSONForPartnerForConversant(t *testing.T) { + + type conversantTestObj struct { + SiteID string `json:"site_id"` + TagID string `json:"tag_id"` + Secure int `json:"secure"` + Position int `json:"position"` + BidFloor float64 `json:"bidfloor"` + API []int `json:"api"` + Protocols []int `json:"protocols"` + Mimes []string `json:"mimes"` + Maxduration int `json:"maxduration"` + } + + mapping := map[string]interface{}{ + "site_id": "12313", + "tag_id": "45343", + "secure": "1", + "position": "1", + "bidfloor": "0.12", + "maxduration": "100", + "mimes": "[video/mp4,video/mp3]", + "api": "[1,2,3]", + "protocols": "[1]", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + jsonString, _ := PrepareBidParamJSONForPartner(width, height, mapping, "adunit", string(openrtb_ext.BidderConversant), string(openrtb_ext.BidderConversant), nil) + conv := new(conversantTestObj) + if err := json.Unmarshal([]byte(jsonString), &conv); err != nil { + t.Error("Incorrect Json Formed") + return + } + + expected := conversantTestObj{ + SiteID: "12313", + TagID: "45343", + Secure: 1, + Position: 1, + BidFloor: 0.12, + Maxduration: 100, + Mimes: []string{"video/mp4", "video/mp3"}, + API: []int{1, 2, 3}, + Protocols: []int{1}, + } + + AssertJSON(t, json.RawMessage(GetJSON(expected)), jsonString) +} + +func TestPrepareBidParamJSONForPartnerForRubicon(t *testing.T) { + + type videoObj struct { + PlayerHeight int `json:"playerHeight,omitempty"` + PlayerWidth int `json:"playerWidth,omitempty"` + SizeID int `json:"size_id,omitempty"` + Language string `json:"language,omitempty"` + } + + type rubiconTestObj struct { + AccountID int `json:"accountId,omitempty"` + ZoneID int `json:"zoneId,omitempty"` + SiteID int `json:"siteId,omitempty"` + Video videoObj `json:"video,omitempty"` + } + + vMap := map[string]interface{}{ + "playerWidth": "1000", + "playerHeight": "1000", + "size_id": "10", + "language": "eng", + } + + mapping := map[string]interface{}{ + "accountId": "12313", + "zoneId": "45343", + "siteId": "12345", + "video": vMap, + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + jsonString, _ := PrepareBidParamJSONForPartner(width, height, mapping, "adunit", string(openrtb_ext.BidderRubicon), string(openrtb_ext.BidderRubicon), nil) + actualR := new(rubiconTestObj) + if err := json.Unmarshal([]byte(jsonString), &actualR); err != nil { + t.Error("Incorrect Json Formed") + return + } + + expected := rubiconTestObj{ + AccountID: 12313, + ZoneID: 45343, + SiteID: 12345, + Video: videoObj{ + PlayerHeight: 1000, + PlayerWidth: 1000, + SizeID: 10, + Language: "eng", + }, + } + + AssertJSON(t, json.RawMessage(GetJSON(expected)), jsonString) +} + +func TestPrepareBidParamJSONForPartnerForIndexExchange(t *testing.T) { + + type ixTestObj struct { + SiteID string `json:"siteId"` + Size [2]uint64 `json:"size"` + } + mapping := map[string]interface{}{ + "siteID": "12313", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + jsonString, _ := PrepareBidParamJSONForPartner(width, height, mapping, "adunit", string(openrtb_ext.BidderIx), string(openrtb_ext.BidderIx), nil) + ix := new(ixTestObj) + if err := json.Unmarshal([]byte(jsonString), &ix); err != nil { + t.Error("Incorrect Json Formed") + return + } + + if ix.SiteID != "12313" { + t.Error("Incorrect value set for site_id") + } + + if ix.Size[0] != 300 { + t.Error("Incorrect value set for size[0]") + } + + if ix.Size[1] != 250 { + t.Error("Incorrect value set for size[1]") + } +} + +func TestPrepareBidParamJSONForPartnerForIndexExchangeForMissingSiteID(t *testing.T) { + + type ixTestObj struct { + SiteID string `json:"siteId"` + Size [2]uint64 `json:"size"` + } + mapping := map[string]interface{}{ + "memberId": "12313", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + jsonString, _ := PrepareBidParamJSONForPartner(width, height, mapping, "adunit", string(openrtb_ext.BidderIx), string(openrtb_ext.BidderIx), nil) + + if jsonString != nil { + t.Error("Value should not be set when siteID is missing") + } +} + +func TestPrepareBidParamJSONForPartnerForShareThrough(t *testing.T) { + type shareThroughTestObj struct { + Pkey string `json:"pkey,omitempty"` + Badv []string `json:"badv,omitempty"` + Bcat []string `json:"bcat,omitempty"` + } + + fieldMap := map[string]interface{}{ + "pkey": "12", + "badv": []string{"1", "2"}, + "bcat": []string{"3", "4"}, + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonStrBuf, _ := PrepareBidParamJSONForPartner(width, height, fieldMap, "adunit", string(openrtb_ext.BidderSharethrough), string(openrtb_ext.BidderSharethrough), nil) + + var obj shareThroughTestObj + if err := json.Unmarshal([]byte(jsonStrBuf), &obj); err != nil { + t.Error("Failed to form json") + return + } + + if obj.Pkey != fieldMap["pkey"] { + t.Error("wrong pkey value set") + return + } + + assert.EqualValues(t, fieldMap["badv"].([]string), obj.Badv) + assert.EqualValues(t, fieldMap["bcat"].([]string), obj.Bcat) +} + +func TestPrepareBidParamJSONForPartnerForShareThroughWhenPkeyMissing(t *testing.T) { + type shareThroughTestObj struct { + Pkey string `json:"pkey,omitempty"` + Iframe bool `json:"iframe,omitempty"` + IframeSize [2]int `json:"iframeSize,omitempty"` + } + + fieldMap := map[string]interface{}{ + "iframe": "true", + "iframeSize": "[300, 250]", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonStrBuf, _ := PrepareBidParamJSONForPartner(width, height, fieldMap, "adunit", string(openrtb_ext.BidderSharethrough), string(openrtb_ext.BidderSharethrough), nil) + + if jsonStrBuf != nil { + t.Error("JSON should be empty string") + return + } +} + +func TestPrepareBidParamJSONForPartnerForTripleLift(t *testing.T) { + type tripleLiftTestObj struct { + InventoryCode string `json:"inventoryCode,omitempty"` + Floor float64 `json:"floor,omitempty"` + } + + fieldMap := map[string]interface{}{ + "inventoryCode": "121", + "floor": "9.11", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonStrBuf, _ := PrepareBidParamJSONForPartner(width, height, fieldMap, "adunit", string(openrtb_ext.BidderTriplelift), string(openrtb_ext.BidderTriplelift), nil) + var obj tripleLiftTestObj + if err := json.Unmarshal([]byte(jsonStrBuf), &obj); err != nil { + t.Error("Failed to form json") + return + } + + if obj.InventoryCode != "121" { + t.Error("wrong inventoryCode value set") + return + } + + floor, _ := strconv.ParseFloat(fmt.Sprintf("%v", fieldMap["floor"]), 64) + if floor != obj.Floor { + t.Error("wrong floor value set") + return + } +} + +func TestPrepareBidParamJSONForPartnerForTripleLiftWhenFloorMissing(t *testing.T) { + type tripleLiftTestObj struct { + InventoryCode string `json:"inventoryCode,omitempty"` + Floor float64 `json:"floor,omitempty"` + } + + fieldMap := map[string]interface{}{ + "inventoryCode": "121", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonStrBuf, _ := PrepareBidParamJSONForPartner(width, height, fieldMap, "adunit", string(openrtb_ext.BidderTriplelift), string(openrtb_ext.BidderTriplelift), nil) + var obj tripleLiftTestObj + if err := json.Unmarshal([]byte(jsonStrBuf), &obj); err != nil { + t.Error("Failed to form json") + return + } + + if obj.Floor != 0 { + t.Error("floor should be 0") + return + } +} + +func TestPrepareBidParamJSONForPartnerForTripleLiftWhenRequiredParamMissing(t *testing.T) { + type tripleLiftTestObj struct { + InventoryCode string `json:"inventoryCode,omitempty"` + Floor float64 `json:"floor,omitempty"` + } + + fieldMap := map[string]interface{}{ + "floor": "9.11", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonStrBuf, _ := PrepareBidParamJSONForPartner(width, height, fieldMap, "adunit", string(openrtb_ext.BidderTriplelift), string(openrtb_ext.BidderTriplelift), nil) + if jsonStrBuf != nil { + t.Error("JSON should be empty") + return + } +} + +func TestPrepareBidParamJSONForPartnerForImproveDigitalWithPlacementId(t *testing.T) { + type improveDigitalTestObj struct { + PlacementID int `json:"placementId,omitempty"` + PublisherID int `json:"publisherId,omitempty"` + PlacementKey string `json:"placementKey,omitempty"` + } + + fieldMap := map[string]interface{}{ + "placementId": "121", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonStrBuf, _ := PrepareBidParamJSONForPartner(width, height, fieldMap, "adunit", string(openrtb_ext.BidderImprovedigital), string(openrtb_ext.BidderImprovedigital), nil) + var obj improveDigitalTestObj + if err := json.Unmarshal([]byte(jsonStrBuf), &obj); err != nil { + t.Error("Failed to form json") + return + } + + if obj.PlacementID != 121 { + t.Error("wrong placementId value set") + return + } + + if obj.PublisherID != 0 { + t.Error("wrong publisherId value set") + return + } + + if obj.PlacementKey != "" { + t.Error("wrong placementKey value set") + return + } +} + +func TestPrepareBidParamJSONForPartnerTelaria(t *testing.T) { + type telariaTestObj struct { + SeatCode string `json:"seatCode"` + AdCode string `json:"adCode,omitempty"` + } + + fieldMap := map[string]interface{}{ + "seatCode": "test_seat_code", + "adCode": "test_ad_code", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonStrBuf, _ := PrepareBidParamJSONForPartner(width, height, fieldMap, "adunit", string(openrtb_ext.BidderTelaria), string(openrtb_ext.BidderTelaria), nil) + var obj telariaTestObj + if err := json.Unmarshal([]byte(jsonStrBuf), &obj); err != nil { + t.Errorf("Failed to form json: %v", err) + return + } + + if obj.AdCode != "test_ad_code" { + t.Error("wrong adCode value set") + return + } + + if obj.SeatCode != "test_seat_code" { + t.Error("wrong seatCode value set") + return + } +} + +func TestPrepareBidParamJSONForPartnerTelariaWithoutSeatCode(t *testing.T) { + type telariaTestObj struct { + SeatCode string `json:"seatCode"` + AdCode string `json:"adCode,omitempty"` + } + + fieldMap := map[string]interface{}{ + "adCode": "test_ad_code", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonStrBuf, _ := PrepareBidParamJSONForPartner(width, height, fieldMap, "adunit", string(openrtb_ext.BidderTelaria), string(openrtb_ext.BidderTelaria), nil) + if jsonStrBuf != nil { + t.Error("JSON should be empty") + return + } +} + +func TestPrepareBidParamJSONForPartnerForImproveDigitalWithoutPlacementId(t *testing.T) { + type improveDigitalTestObj struct { + PlacementID int `json:"placementId,omitempty"` + PublisherID int `json:"publisherId,omitempty"` + PlacementKey string `json:"placementKey,omitempty"` + } + + fieldMap := map[string]interface{}{ + "publisherId": "911", + "placementKey": "key_1", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonStrBuf, _ := PrepareBidParamJSONForPartner(width, height, fieldMap, "adunit", string(openrtb_ext.BidderImprovedigital), string(openrtb_ext.BidderImprovedigital), nil) + var obj improveDigitalTestObj + if err := json.Unmarshal([]byte(jsonStrBuf), &obj); err != nil { + t.Error("Failed to form json") + return + } + + if obj.PlacementID != 0 { + t.Error("wrong placementId value set") + return + } + + if obj.PublisherID != 911 { + t.Error("wrong publisherId value set") + return + } + + if obj.PlacementKey != "key_1" { + t.Error("wrong placementKey value set") + return + } +} + +func TestPrepareBidParamJSONForPartnerSpotx(t *testing.T) { + type spotxTestObject struct { + ChannelID string `json:"channel_id"` + AdUnit string `json:"ad_unit"` + Secure bool `json:"secure,omitempty"` + AdVolume float64 `json:"ad_volume,omitempty"` + PriceFloor int `json:"price_floor,omitempty"` + HideSkin bool `json:"hide_skin,omitempty"` + } + + fieldMap := map[string]interface{}{ + "channel_id": "12345", + "ad_unit": "outstream", + "secure": true, + "ad_volume": 19.1, + "price_floor": 12.0, + "hide_skin": false, + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonStrBuf, _ := PrepareBidParamJSONForPartner(width, height, fieldMap, "adunit", string(openrtb_ext.BidderSpotX), string(openrtb_ext.BidderSpotX), nil) + var obj spotxTestObject + if err := json.Unmarshal([]byte(jsonStrBuf), &obj); err != nil { + t.Errorf("Failed to form json: %v", err) + return + } + + if obj.ChannelID != "12345" { + t.Error("wrong channel_id value set") + return + } + + if obj.AdUnit != "outstream" { + t.Error("wrong ad_unit value set") + return + } + + if !obj.Secure { + t.Error("wrong secure value set") + return + } + + if obj.AdVolume != 19.1 { + t.Error("wrong ad_volume value set") + return + } + + if obj.PriceFloor != 12 { + t.Error("wrong price_floor value set") + return + } + + if obj.HideSkin { + t.Error("wrong secure value set") + return + } +} + +func TestPrepareBidParamJSONForPartnerSpotWithoutChannelId(t *testing.T) { + type spotxTestObject struct { + ChannelID string `json:"channel_id"` + AdUnit string `json:"ad_unit"` + Secure bool `json:"secure,omitempty"` + AdVolume float64 `json:"ad_volume,omitempty"` + PriceFloor int `json:"price_floor,omitempty"` + HideSkin bool `json:"hide_skin,omitempty"` + } + + fieldMap := map[string]interface{}{ + "ad_unit": "outstream", + "secure": true, + "ad_volume": 19.1, + "price_floor": 12.0, + "hide_skin": false, + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonStrBuf, _ := PrepareBidParamJSONForPartner(width, height, fieldMap, "adunit", string(openrtb_ext.BidderSpotX), string(openrtb_ext.BidderSpotX), nil) + if jsonStrBuf != nil { + t.Error("JSON should be empty string") + return + } +} + +func TestPrepareBidParamJSONForPartnerOpenX(t *testing.T) { + + type OpenXTestObj struct { + DelDomain string `json:"delDomain"` + Unit string `json:"unit"` + } + + mapping := map[string]interface{}{ + "delDomain": "test.openx.domain", + "unit": "45343", + } + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonString, _ := PrepareBidParamJSONForPartner(width, height, mapping, "adunit", string(openrtb_ext.BidderOpenx), string(openrtb_ext.BidderOpenx), nil) + openxObj := new(OpenXTestObj) + if err := json.Unmarshal([]byte(jsonString), &openxObj); err != nil { + t.Error("Incorrect Json Formed") + return + } + + if openxObj.DelDomain != "test.openx.domain" { + t.Error("Incorrect value set for delDomain") + } + + if openxObj.Unit != "45343" { + t.Error("Incorrect value set for unit") + } +} + +func TestPrepareBidParamJSONForPartnerOpenXMissingParameters(t *testing.T) { + + type OpenXTestObj struct { + DelDomain string `json:"delDomain"` + Unit string `json:"unit"` + } + + mapping := map[string]interface{}{} + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + jsonString, _ := PrepareBidParamJSONForPartner(width, height, mapping, "adunit", string(openrtb_ext.BidderOpenx), string(openrtb_ext.BidderOpenx), nil) + openxObj := new(OpenXTestObj) + if err := json.Unmarshal([]byte(jsonString), &openxObj); err != nil { + t.Error("Incorrect Json Formed") + return + } + + if openxObj.DelDomain != "" || openxObj.Unit != "" { + t.Error("Incorrect value set for delDomain or unit") + } +} + +func TestPrepareBidParamJSONForPartnerSynacorMedia(t *testing.T) { + type args struct { + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "seatId": "testSeatId", + "tagId": "testTagId", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderImds), + bidderCode: string(openrtb_ext.BidderImds), + }, + want: json.RawMessage(`{"seatId":"testSeatId","tagId":"testTagId"}`), + }, + { + name: "required param seatId missing", + args: args{ + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "tagId": "testTagId", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderImds), + bidderCode: string(openrtb_ext.BidderImds), + }, + want: nil, + }, + { + name: "param tagId missing", + args: args{ + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "seatId": "testSeatId", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderImds), + bidderCode: string(openrtb_ext.BidderImds), + }, + want: json.RawMessage(`{"seatId":"testSeatId"}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + + +func TestPrepareBidParamJSONForPartnerGumGum(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "zone": "testZone", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderGumGum), + bidderCode: string(openrtb_ext.BidderGumGum), + }, + want: json.RawMessage(`{"zone":"testZone"}`), + }, + { + name: "required params are missing", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: nil, + slotKey: "", + adapterName: string(openrtb_ext.BidderGumGum), + bidderCode: string(openrtb_ext.BidderGumGum), + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerYieldone(t *testing.T) { + type args struct { + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "placementId": "testplacementId", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderYieldone), + bidderCode: string(openrtb_ext.BidderYieldone), + }, + want: json.RawMessage(`{"placementId":"testplacementId"}`), + }, + { + name: "required param id is missing", + args: args{ + width: nil, + height: nil, + fieldMap: map[string]interface{}{}, + slotKey: "", + adapterName: string(openrtb_ext.BidderYieldone), + bidderCode: string(openrtb_ext.BidderYieldone), + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerDistrictmDMX(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All required params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "memberid": "testmemberid", + "dmxid": "testdmxid", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderDmx), + bidderCode: string(openrtb_ext.BidderDmx), + }, + want: json.RawMessage(`{"dmxid":"testdmxid","memberid":"testmemberid","tagid":"testdmxid"}`), + }, + { + name: "memberid is missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "dmxid": "testdmxid", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderDmx), + bidderCode: string(openrtb_ext.BidderDmx), + }, + want: nil, + }, + { + name: "dmxid is missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "memberid": "testmemberid", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderDmx), + bidderCode: string(openrtb_ext.BidderDmx), + }, + want: json.RawMessage(`{"memberid":"testmemberid"}`), + }, + { + name: "All params are missing", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: nil, + slotKey: "", + adapterName: string(openrtb_ext.BidderDmx), + bidderCode: string(openrtb_ext.BidderDmx), + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerAdGenration(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "id": "testid", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderAdgeneration), + bidderCode: string(openrtb_ext.BidderAdgeneration), + }, + want: json.RawMessage(`{"id":"testid"}`), + }, + { + name: "required param id is missing", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: map[string]interface{}{}, + slotKey: "", + adapterName: string(openrtb_ext.BidderAdgeneration), + bidderCode: string(openrtb_ext.BidderAdgeneration), + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerBeachfront(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "appId": "testAppID", + "bidfloor": "0.1", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderBeachfront), + bidderCode: string(openrtb_ext.BidderBeachfront), + }, + want: json.RawMessage(`{"appId":"testAppID","bidfloor":0.1,"videoResponseType":"adm"}`), + }, + { + name: "appId missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "bidfloor": "0.1", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderBeachfront), + bidderCode: string(openrtb_ext.BidderBeachfront), + }, + want: nil, + }, + { + name: "bidfloor missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "bidfloor": "0.1", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderBeachfront), + bidderCode: string(openrtb_ext.BidderBeachfront), + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, json.RawMessage(tt.want), json.RawMessage(got)) + }) + } +} + +func TestPrepareBidParamJSONForPartnerVRTCAL(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "Dummy param present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "dummyParam": "2", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderVrtcal), + bidderCode: string(openrtb_ext.BidderVrtcal), + }, + want: json.RawMessage(`{"just_an_unused_vrtcal_param":"2"}`), + }, + { + name: "Dummy param missing", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: map[string]interface{}{}, + slotKey: "", + adapterName: string(openrtb_ext.BidderVrtcal), + bidderCode: string(openrtb_ext.BidderVrtcal), + }, + want: json.RawMessage(`{"just_an_unused_vrtcal_param":"1"}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerInMobi(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "plc": "1234", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderInMobi), + bidderCode: string(openrtb_ext.BidderInMobi), + }, + want: json.RawMessage(`{"plc":"1234"}`), + }, + { + name: "required param plc is missing", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: map[string]interface{}{}, + slotKey: "", + adapterName: string(openrtb_ext.BidderInMobi), + bidderCode: string(openrtb_ext.BidderInMobi), + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerTappx(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "tappxkey": "key1", + "endpoint": "endpoint1", + "bidfloor": "0.2", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderTappx), + bidderCode: string(openrtb_ext.BidderTappx), + }, + want: json.RawMessage(`{"tappxkey":"key1","endpoint":"endpoint1","bidfloor":0.2}`), + }, + { + name: "tappxkey missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "endpoint": "endpoint1", + "bidfloor": "0.2", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderTappx), + bidderCode: string(openrtb_ext.BidderTappx), + }, + want: nil, + }, + { + name: "endpoint missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "tappxkey": "key1", + "bidfloor": "0.2", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderTappx), + bidderCode: string(openrtb_ext.BidderTappx), + }, + want: nil, + }, + { + name: "bidfloor missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "tappxkey": "key1", + "endpoint": "endpoint1", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderTappx), + bidderCode: string(openrtb_ext.BidderTappx), + }, + want: json.RawMessage(`{"tappxkey":"key1","endpoint":"endpoint1"}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerNobid(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present with correct mapping value datatype", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "siteId": 1234, + "placementId": 5678, + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderNoBid), + }, + want: json.RawMessage(`{"siteId":1234,"placementId":5678}`), + }, + { + name: "All params present with string mapping value datatype", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "siteId": "1234", + "placementId": "5678", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderNoBid), + bidderCode: string(openrtb_ext.BidderNoBid), + }, + want: json.RawMessage(`{"siteId":1234,"placementId":5678}`), + }, + { + name: "required param siteId is missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "placementId": "5678", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderNoBid), + bidderCode: string(openrtb_ext.BidderNoBid), + }, + want: nil, + }, + { + name: "required param siteId is not an integer", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "siteId": "abc", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderNoBid), + bidderCode: string(openrtb_ext.BidderNoBid), + }, + want: nil, + }, + { + name: "optional param placementId is missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "siteId": "1234", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderNoBid), + bidderCode: string(openrtb_ext.BidderNoBid), + }, + want: json.RawMessage(`{"siteId":1234}`), + }, + { + name: "optional param placementId is not an integer", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "siteId": "1234", + "placementId": "abc", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderNoBid), + bidderCode: string(openrtb_ext.BidderNoBid), + }, + want: json.RawMessage(`{"siteId":1234}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerAudienceNetwork(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "placementId": "testPlacementId", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderAudienceNetwork), + bidderCode: string(openrtb_ext.BidderAudienceNetwork), + }, + want: json.RawMessage(`{"placementId":"testPlacementId"}`), + }, + { + name: "required param id is missing", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: map[string]interface{}{}, + slotKey: "", + adapterName: string(openrtb_ext.BidderAudienceNetwork), + bidderCode: string(openrtb_ext.BidderAudienceNetwork), + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerGrid(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "uid": "1234", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderGrid), + bidderCode: string(openrtb_ext.BidderGrid), + }, + want: json.RawMessage(`{"uid":1234}`), + }, + { + name: "required param uid is missing", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: map[string]interface{}{}, + slotKey: "", + adapterName: string(openrtb_ext.BidderGrid), + bidderCode: string(openrtb_ext.BidderGrid), + }, + want: nil, + }, + { + name: "required param uid is not an integer", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "uid": "abc", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderGrid), + bidderCode: string(openrtb_ext.BidderGrid), + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerSmartAdServer(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "Network ID present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "networkId": "1234", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmartAdserver), + bidderCode: string(openrtb_ext.BidderSmartAdserver), + }, + want: json.RawMessage(`{"networkId":1234}`), + }, + { + name: "Network ID missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "siteId": "1234", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmartAdserver), + bidderCode: string(openrtb_ext.BidderSmartAdserver), + }, + want: nil, + }, + { + name: "Network ID not an integer", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "networkId": "test", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmartAdserver), + bidderCode: string(openrtb_ext.BidderSmartAdserver), + }, + want: nil, + }, + { + name: "All params present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "networkId": "1234", + "siteId": "3456", + "pageId": "8901", + "formatId": "7612", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmartAdserver), + bidderCode: string(openrtb_ext.BidderSmartAdserver), + }, + want: json.RawMessage(`{"networkId":1234,"siteId":3456,"pageId":8901,"formatId":7612}`), + }, + { + name: "Site ID and Format ID present, page ID missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "networkId": "1234", + "siteId": "3456", + "formatId": "7612", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmartAdserver), + bidderCode: string(openrtb_ext.BidderSmartAdserver), + }, + want: json.RawMessage(`{"networkId":1234}`), + }, + { + name: "Site ID and Page ID present, Format ID missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "networkId": "1234", + "siteId": "3456", + "pageId": "7612", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmartAdserver), + bidderCode: string(openrtb_ext.BidderSmartAdserver), + }, + want: json.RawMessage(`{"networkId":1234}`), + }, + { + name: "Format ID and Page ID present, Site ID missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "networkId": "1234", + "formatId": "3456", + "pageId": "7612", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmartAdserver), + bidderCode: string(openrtb_ext.BidderSmartAdserver), + }, + want: json.RawMessage(`{"networkId":1234}`), + }, + { + name: "Site ID and Format ID present, page ID not an integer", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "networkId": "1234", + "siteId": "3456", + "formatId": "7612", + "pageId": "test", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmartAdserver), + bidderCode: string(openrtb_ext.BidderSmartAdserver), + }, + want: json.RawMessage(`{"networkId":1234}`), + }, + { + name: "Site ID and Page ID present, Format ID not an integer", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "networkId": "1234", + "siteId": "3456", + "pageId": "7612", + "formatId": "test", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmartAdserver), + bidderCode: string(openrtb_ext.BidderSmartAdserver), + }, + want: json.RawMessage(`{"networkId":1234}`), + }, + { + name: "Format ID and Page ID present, Site ID not an integer", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "networkId": "1234", + "formatId": "3456", + "pageId": "7612", + "siteId": "test", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmartAdserver), + bidderCode: string(openrtb_ext.BidderSmartAdserver), + }, + want: json.RawMessage(`{"networkId":1234}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerSmaato(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "publisherId missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "adspaceId": "1234", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmaato), + bidderCode: string(openrtb_ext.BidderSmaato), + }, + want: nil, + }, + { + name: "adspaceId missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "publisherId": "1234", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmaato), + bidderCode: string(openrtb_ext.BidderSmaato), + }, + want: nil, + }, + { + name: "publisherId & adspaceId both are missing", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: map[string]interface{}{}, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmaato), + bidderCode: string(openrtb_ext.BidderSmaato), + }, + want: nil, + }, + { + name: "All params are present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "publisherId": "1234", + "adspaceId": "3456", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSmaato), + bidderCode: string(openrtb_ext.BidderSmaato), + }, + want: json.RawMessage(`{"publisherId": "1234","adspaceId": "3456"}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerPangle(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "Required param present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "token": "testToken", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderPangle), + bidderCode: string(openrtb_ext.BidderPangle), + }, + want: json.RawMessage(`{"token":"testToken"}`), + }, + { + name: "required params are missing", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: nil, + slotKey: "", + adapterName: string(openrtb_ext.BidderPangle), + bidderCode: string(openrtb_ext.BidderPangle), + }, + want: nil, + }, + { + name: "dependent param 'placementid' is missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "token": "testToken", + "appid": "testAppID", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderPangle), + bidderCode: string(openrtb_ext.BidderPangle), + }, + want: nil, + }, + { + name: "dependent param 'appid' is missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "token": "testToken", + "placementid": "testPlacementID", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderPangle), + bidderCode: string(openrtb_ext.BidderPangle), + }, + want: nil, + }, + { + name: "all params are present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "token": "testToken", + "placementid": "testPlacementID", + "appid": "testAppID", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderPangle), + bidderCode: string(openrtb_ext.BidderPangle), + }, + want: json.RawMessage(`{"token":"testToken","placementid":"testPlacementID","appid":"testAppID"}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerSonobi(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "ad_unit missing but placement_id present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "placement_id": "testPlacementId", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSonobi), + }, + want: json.RawMessage(`{"TagID":"testPlacementId"}`), + }, + { + name: "placement_id missing but ad_unit present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "ad_unit": "testAdUnit", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSonobi), + }, + want: json.RawMessage(`{"TagID":"testAdUnit"}`), + }, + { + name: "empty fieldmap", + args: args{ + reqID: "", + width: nil, + height: nil, + fieldMap: map[string]interface{}{}, + slotKey: "", + adapterName: string(openrtb_ext.BidderSonobi), + }, + want: nil, + }, + { + name: "ad_unit & placement_id both are present", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "ad_unit": "testAdUnit", + "placement_id": "testPlacementId", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderSonobi), + }, + want: json.RawMessage(`{"TagID":"testAdUnit"}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, string(openrtb_ext.BidderSonobi), nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerImproveDigital(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "All params present", + args: args{ + + width: ptrutil.ToPtr[int64](300), + height: ptrutil.ToPtr[int64](250), + fieldMap: map[string]interface{}{ + "placementId": "1234", + "publisherId": "5678", + "placementKey": "key1", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderImprovedigital), + bidderCode: string(openrtb_ext.BidderImprovedigital), + }, + want: json.RawMessage(`{"placementId":1234,"size":{"w":300,"h":250}}`), + }, + { + name: "PlacementId present, publisherId and placementKey missing", + args: args{ + + width: ptrutil.ToPtr[int64](300), + height: ptrutil.ToPtr[int64](250), + fieldMap: map[string]interface{}{ + "placementId": "1234", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderImprovedigital), + bidderCode: string(openrtb_ext.BidderImprovedigital), + }, + want: json.RawMessage(`{"placementId":1234,"size":{"w":300,"h":250}}`), + }, + { + name: "PlacementId absent, publisherId and placementKey present", + args: args{ + + width: ptrutil.ToPtr[int64](300), + height: ptrutil.ToPtr[int64](250), + fieldMap: map[string]interface{}{ + "publisherId": "5678", + "placementKey": "key1", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderImprovedigital), + bidderCode: string(openrtb_ext.BidderImprovedigital), + }, + want: json.RawMessage(`{"publisherId":5678,"placementKey":"key1","size":{"w":300,"h":250}}`), + }, + { + name: "required params missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "placementKey": "key1", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderImprovedigital), + bidderCode: string(openrtb_ext.BidderImprovedigital), + }, + want: nil, + }, + { + name: "required param placementId is not an integer", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "placementId": "abc", + "placementKey": "key1", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderImprovedigital), + bidderCode: string(openrtb_ext.BidderImprovedigital), + }, + want: nil, + }, + { + name: "required param publisherId is not an integer", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "publisherId": "abc", + "placementKey": "key1", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderImprovedigital), + bidderCode: string(openrtb_ext.BidderImprovedigital), + }, + want: nil, + }, + { + name: "optional param size is missing", + args: args{ + + width: nil, + height: nil, + fieldMap: map[string]interface{}{ + "placementId": "1234", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderImprovedigital), + bidderCode: string(openrtb_ext.BidderImprovedigital), + }, + want: json.RawMessage(`{"placementId":1234}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerOutbrain(t *testing.T) { + type args struct { + reqID string + width *int64 + height *int64 + fieldMap map[string]interface{} + slotKey string + adapterName string + bidderCode string + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "id within publisher object present", + args: args{ + fieldMap: map[string]interface{}{ + "publisher": map[string]interface{}{ + "id": "myid", + }, + }, + adapterName: string(openrtb_ext.BidderOutbrain), + }, + want: json.RawMessage(`{"publisher":{"id":"myid"}}`), + }, + { + name: "Empty publisher object", + args: args{ + fieldMap: map[string]interface{}{ + "publisher": map[string]interface{}{}, + }, + adapterName: string(openrtb_ext.BidderOutbrain), + }, + want: nil, + }, + { + name: "NO publisher object", + args: args{ + fieldMap: map[string]interface{}{ + "PubLisheR": map[string]interface{}{ + "ID": "myid", + }, + }, + adapterName: string(openrtb_ext.BidderOutbrain), + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) + AssertJSON(t, tt.want, got) + }) + } +} + +func TestPrepareBidParamJSONForPartnerForAppnexus(t *testing.T) { + + mapping := map[string]interface{}{ + "invCode": "abc", + "placementId": "4978056", + "query": "def", + "reserve": "1", + "usePaymentRule": "true", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + impExt := new(models.ImpExtension) + impExt.Bidder = formBidderKeywordsMap() + + jsonString, _ := PrepareBidParamJSONForPartner(width, height, mapping, "adunit", string(openrtb_ext.BidderAppnexus), string(openrtb_ext.BidderAppnexus), impExt) + actualR := new(openrtb_ext.ExtImpAppnexus) + if err := json.Unmarshal([]byte(jsonString), &actualR); err != nil { + + t.Error("Incorrect Json Formed: ERR: ", err) + t.Error("Incorrect Json Formed: ", jsonString) + return + } + + upr := new(bool) + *upr = true + var kw openrtb_ext.ExtImpAppnexusKeywords = "key1=val1,key1=val2,key2=val3,key2=val4" + + expected := openrtb_ext.ExtImpAppnexus{ + DeprecatedPlacementId: 4978056, + LegacyInvCode: "abc", + Reserve: 1.0, + UsePaymentRule: upr, + Keywords: kw, + } + + AssertJSON(t, json.RawMessage(GetJSON(expected)), json.RawMessage(GetJSON(actualR))) +} + +func TestPrepareBidParamJSONForPartnerForAppnexusAlias(t *testing.T) { + + mapping := map[string]interface{}{ + "invCode": "abc", + "placementId": "4978056", + "query": "def", + "reserve": "1", + "usePaymentRule": "true", + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + impExt := new(models.ImpExtension) + impExt.Bidder = formBidderKeywordsMapForAppnexusAlias() + + jsonString, _ := PrepareBidParamJSONForPartner(width, height, mapping, "adunit", string(openrtb_ext.BidderAppnexus), "appnexus-alias", impExt) + actualR := new(openrtb_ext.ExtImpAppnexus) + if err := json.Unmarshal([]byte(jsonString), &actualR); err != nil { + + t.Error("Incorrect Json Formed: ERR: ", err) + t.Error("Incorrect Json Formed: ", jsonString) + return + } + + upr := new(bool) + *upr = true + var kw openrtb_ext.ExtImpAppnexusKeywords = "key1=val1,key1=val2,key2=val3,key2=val4" + + expected := openrtb_ext.ExtImpAppnexus{ + DeprecatedPlacementId: 4978056, + LegacyInvCode: "abc", + Reserve: 1.0, + UsePaymentRule: upr, + Keywords: kw, + } + + AssertJSON(t, json.RawMessage(GetJSON(expected)), json.RawMessage(GetJSON(actualR))) +} + +func TestPrepareBidParamJSONForPartnerForAppnexusForIgnoredKeys(t *testing.T) { + + mapping := map[string]interface{}{ + "placementId": "4978056", + "video": map[string]interface{}{ + "frameworks": []int{0, 1, 2}, + "playback_method": []string{"auto_play_sound_on"}, + "skippable": true, + }, + } + + width := new(int64) + *width = 300 + height := new(int64) + *height = 250 + + expectedJSONString := json.RawMessage(`{"placementId":4978056}`) + actualJSONString, _ := PrepareBidParamJSONForPartner(width, height, mapping, "adunit", string(openrtb_ext.BidderAppnexus), string(openrtb_ext.BidderAppnexus), nil) + AssertJSON(t, expectedJSONString, actualJSONString) +} + +func Test_builderPubMatic(t *testing.T) { + expected := JSONObject{"publisherId": "301", "pmzoneid": "Zone1", "dctr": "value1", "keywords": "test"} + + type args struct { + params BidderParameters + } + tests := []struct { + name string + args args + want json.RawMessage + wantErr bool + }{ + { + name: "TEST1", + args: args{params: BidderParameters{FieldMap: JSONObject{"publisherId": "301", "pmzoneid": "Zone1", "dctr": "value1", "keywords": "test"}}}, + want: json.RawMessage(GetJSON(expected)), + wantErr: false, + }, + { + name: "When bidderparam BidviewabilityScore is present, read and pass it", + args: args{params: BidderParameters{FieldMap: JSONObject{"publisherId": "301", "bidViewability": JSONObject{"createdAt": 1666155076240, "lastViewed": 3171.100000023842, "rendered": 131, "totalViewTime": 15468, "updatedAt": 1666296333802, "viewed": 80}}}}, + want: json.RawMessage(`{"publisherId": "301", "bidViewability": {"createdAt": 1666155076240, "lastViewed": 3171.100000023842, "rendered": 131, "totalViewTime": 15468, "updatedAt": 1666296333802, "viewed": 80}}`), + wantErr: false, + }, + { + name: "When bidderparam BidviewabilityScore is present, but with limited fields ,read and pass it", + args: args{params: BidderParameters{FieldMap: JSONObject{"publisherId": "301", "bidViewability": JSONObject{"createdAt": 1666155076240, "rendered": 131, "updatedAt": 1666296333802, "viewed": 0}}}}, + want: json.RawMessage(`{"publisherId": "301", "bidViewability": {"createdAt": 1666155076240, "rendered": 131,"updatedAt": 1666296333802, "viewed": 0}}`), + wantErr: false, + }, + { + name: "When bidderparam BidviewabilityScore is present with invalid json fields,ignore passing bidviewability object", + args: args{params: BidderParameters{FieldMap: JSONObject{"publisherId": "301", "bidViewability": json.RawMessage(`{"createdAt": 1666155076240, "rendered: 131, "updatedAt: 1666296333802, "viewed}": 0}`)}}}, + want: json.RawMessage(`{"publisherId": "301"}`), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := builderPubMatic(tt.args.params) + if (err != nil) != tt.wantErr { + t.Errorf("builderPubMatic() error = %v, wantErr %v", err, tt.wantErr) + return + } + AssertJSON(t, tt.want, got) + + }) + } +} + +func Test_builderAppNexus(t *testing.T) { + expected := JSONObject{"placementId": 0, "keywords": "test", "generate_ad_pod_id": true, "member": "958"} + + type args struct { + params BidderParameters + } + tests := []struct { + name string + args args + want json.RawMessage + wantErr bool + }{ + { + name: "TEST1", + args: args{params: BidderParameters{FieldMap: JSONObject{"keywords": "test", "generate_ad_pod_id": true, "member": "958"}}}, + want: json.RawMessage(GetJSON(expected)), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := builderAppNexus(tt.args.params) + if (err != nil) != tt.wantErr { + t.Errorf("builderAppNexus() error = %v, wantErr %v", err, tt.wantErr) + return + } + AssertJSON(t, tt.want, got) + }) + } +} + +func Test_builderPulsePoint(t *testing.T) { + expected := JSONObject{"cp": 0, "ct": 0, "cf": "70"} + type args struct { + params BidderParameters + } + tests := []struct { + name string + args args + want json.RawMessage + wantErr bool + }{ + { + name: "TEST1", + args: args{params: BidderParameters{SlotKey: "adunit@70"}}, + want: json.RawMessage(GetJSON(expected)), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := builderPulsePoint(tt.args.params) + if (err != nil) != tt.wantErr { + t.Errorf("builderPulsePoint() error = %v, wantErr %v", err, tt.wantErr) + return + } + AssertJSON(t, tt.want, got) + }) + } +} + +func Test_builderApacdex(t *testing.T) { + type args struct { + params BidderParameters + } + tests := []struct { + name string + args args + want json.RawMessage + wantErr bool + }{ + { + name: "Valid Scenerio (oneOf siteId or PlacementId) is present with FloorPrice and geo Object", + args: args{params: BidderParameters{FieldMap: JSONObject{"siteId": "test123", "floorPrice": 0.999223, "geo": JSONObject{"lat": 17.98928, "lon": 99.7741712, "accuracy": 20}}}}, + want: json.RawMessage(GetJSON(JSONObject{"siteId": "test123", "floorPrice": 0.999223, "geo": JSONObject{"lat": 17.98928, "lon": 99.7741712, "accuracy": 20}})), + wantErr: false, + }, + { + name: "Invalid Scenerio both siteId and PlacementId are present with FloorPrice, select siteId & ignore placementId", + args: args{params: BidderParameters{FieldMap: JSONObject{"siteId": "test123", "placementId": "testPlacementid", "floorPrice": 0.999223}}}, + want: json.RawMessage(GetJSON(JSONObject{"siteId": "test123", "floorPrice": 0.999223})), + wantErr: false, + }, + { + name: "Valid Scenerio (oneOf siteId or PlacementId) is present and FloorPrice is absent, ignore FloorPrice param", + args: args{params: BidderParameters{FieldMap: JSONObject{"siteId": "test123"}}}, + want: json.RawMessage(GetJSON(JSONObject{"siteId": "test123"})), + wantErr: false, + }, + { + name: "Valid Scenerio only PlacementId is present, expect only placementId", + args: args{params: BidderParameters{FieldMap: JSONObject{"placementId": "test123"}}}, + want: json.RawMessage(GetJSON(JSONObject{"placementId": "test123"})), + wantErr: false, + }, + { + name: "Invalid Scenerio only floorPrice is present", + args: args{params: BidderParameters{FieldMap: JSONObject{"floorPrice": 0.9292}}}, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := builderApacdex(tt.args.params) + if (err != nil) != tt.wantErr { + t.Errorf("builderApacdex() error = %v, wantErr %v", err, tt.wantErr) + return + } + AssertJSON(t, tt.want, got) + }) + } +} + +func Test_builderUnruly(t *testing.T) { + type args struct { + params BidderParameters + } + tests := []struct { + name string + args args + want json.RawMessage + wantErr bool + }{ + { + name: "Valid Scenerio (oneOf siteId or siteid) is present-siteId", + args: args{params: BidderParameters{FieldMap: JSONObject{"siteId": 123}}}, + want: json.RawMessage(`{"siteId": 123}`), + wantErr: false, + }, + { + name: "Valid Scenerio (oneOf siteId or siteid) is present-siteid", + args: args{params: BidderParameters{FieldMap: JSONObject{"siteid": 123}}}, + want: json.RawMessage(`{"siteid": 123}`), + wantErr: false, + }, + { + name: "Valid Scenerio (oneOf siteId or siteid) is present with Optional field featureoverides", + args: args{params: BidderParameters{FieldMap: JSONObject{"siteid": 123, "featureOverrides": JSONObject{"canRunUnmissable": true}}}}, + want: json.RawMessage(`{"siteid": 123, "featureOverrides": {"canRunUnmissable": true}}`), + wantErr: false, + }, + { + name: "Invalid Scenerio (None Of siteId or siteid) is present", + args: args{params: BidderParameters{FieldMap: JSONObject{}}}, + want: json.RawMessage(``), + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := builderUnruly(tt.args.params) + if (err != nil) != tt.wantErr { + t.Errorf("builderUnruly() error = %v, wantErr %v", err, tt.wantErr) + return + } + AssertJSON(t, tt.want, got) + }) + } +} + +func Test_builderBoldwin(t *testing.T) { + type args struct { + params BidderParameters + } + tests := []struct { + name string + args args + want json.RawMessage + wantErr bool + }{ + { + name: "Valid Scenerio (oneOf placementId or endpointId) is present-placementId", + args: args{params: BidderParameters{FieldMap: JSONObject{"placementId": "1234"}}}, + want: json.RawMessage(`{"placementId": "1234"}`), + wantErr: false, + }, + { + name: "Valid Scenerio (oneOf placementId or endpointId) is present-endpointId", + args: args{params: BidderParameters{FieldMap: JSONObject{"endpointId": "0"}}}, + want: json.RawMessage(`{"endpointId": "0"}`), + wantErr: false, + }, + { + name: "Valid Scenerio (oneOf placementId or endpointId), Both are present", + args: args{params: BidderParameters{FieldMap: JSONObject{"endpointId": "0", "placementId": "1234"}}}, + want: json.RawMessage(`{"placementId": "1234"}`), + wantErr: false, + }, + { + name: "Invalid Scenerio (None Of placementId or endpointId) is present", + args: args{params: BidderParameters{FieldMap: JSONObject{}}}, + want: json.RawMessage(``), + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := builderBoldwin(tt.args.params) + if (err != nil) != tt.wantErr { + t.Errorf("builderBoldwin() error = %v, wantErr %v", err, tt.wantErr) + return + } + AssertJSON(t, tt.want, got) + }) + } +} + +func TestBuilderColossus(t *testing.T) { + type args struct { + params BidderParameters + } + tests := []struct { + name string + args args + want json.RawMessage + wantErr bool + }{ + { + name: "Valid Scenerio (oneOf TagID or groupId) is present-TagID", + args: args{params: BidderParameters{FieldMap: JSONObject{"TagID": "0"}}}, + want: json.RawMessage(`{"TagID": "0"}`), + wantErr: false, + }, + { + name: "Valid Scenerio (oneOf TagID or groupId) is present-groupId", + args: args{params: BidderParameters{FieldMap: JSONObject{"groupId": "0"}}}, + want: json.RawMessage(`{"groupId": "0"}`), + wantErr: false, + }, + { + name: "Valid Scenerio (oneOf TagID or groupId), Both are present", + args: args{params: BidderParameters{FieldMap: JSONObject{"groupId": "0", "TagID": "0"}}}, + want: json.RawMessage(`{"TagID": "0"}`), + wantErr: false, + }, + { + name: "Invalid Scenerio (None Of TagID or groupId) is present", + args: args{params: BidderParameters{FieldMap: JSONObject{}}}, + want: json.RawMessage(``), + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := builderColossus(tt.args.params) + if (err != nil) != tt.wantErr { + t.Errorf("builderColossus() error = %v, wantErr %v", err, tt.wantErr) + return + } + AssertJSON(t, tt.want, got) + }) + } +} + +func TestBuilderNextmillennium(t *testing.T) { + type args struct { + params BidderParameters + } + tests := []struct { + name string + args args + want json.RawMessage + wantErr bool + }{ + { + name: "Valid Scenerio (anyOf placement_id or group_id) is present-placement_id", + args: args{ + params: BidderParameters{FieldMap: JSONObject{"placement_id": "1234"}}, + }, + want: json.RawMessage(`{"placement_id":"1234"}`), + wantErr: false, + }, + { + name: "Valid Scenerio (anyOf placement_id or group_id) is present-group_id", + args: args{ + params: BidderParameters{FieldMap: JSONObject{"group_id": "1234"}}, + }, + want: json.RawMessage(`{"group_id":"1234"}`), + wantErr: false, + }, + { + name: "Valid Scenerio (anyOf placement_id or group_id) both are present", + args: args{ + params: BidderParameters{FieldMap: JSONObject{"placement_id": "1234", "group_id": "45567"}}, + }, + want: json.RawMessage(`{"placement_id":"1234"}`), + wantErr: false, + }, + { + name: "Invalid Scenerio (None Of placement_id or group_id) is present", + args: args{params: BidderParameters{FieldMap: JSONObject{}}}, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := builderNextmillennium(tt.args.params) + if (err != nil) != tt.wantErr { + t.Errorf("builderNextmillennium() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/modules/pubmatic/openwrap/adapters/builder.go b/modules/pubmatic/openwrap/adapters/builder.go index e13bc0af483..c29a3eefa0d 100644 --- a/modules/pubmatic/openwrap/adapters/builder.go +++ b/modules/pubmatic/openwrap/adapters/builder.go @@ -3,7 +3,6 @@ package adapters import ( "encoding/json" - "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/v2/openrtb_ext" ) @@ -54,11 +53,13 @@ func initBidderBuilderFactory() { string(openrtb_ext.BidderUnruly): builderUnruly, string(openrtb_ext.BidderMediafuse): builderAppNexus, string(openrtb_ext.BidderBoldwin): builderBoldwin, + string(openrtb_ext.BidderColossus): builderColossus, + string(openrtb_ext.BidderNextMillennium): builderNextmillennium, } } // getBuilder will return core bidder hard coded builder, if not found then returns default builder -func getBuilder(adapterName string) builder { +func GetBuilder(adapterName string) builder { //resolve hardcoded bidder alias adapterName = ResolveOWBidder(adapterName) @@ -69,7 +70,7 @@ func getBuilder(adapterName string) builder { } // InitBidders will initialise bidder alias, default bidder parameter json and builders for each bidder -func InitBidders(cfg config.Config) error { +func InitBidders(schemaDirectory string) error { initBidderBuilderFactory() - return parseBidderParams(cfg) + return parseBidderParams(schemaDirectory) } diff --git a/modules/pubmatic/openwrap/adapters/constant.go b/modules/pubmatic/openwrap/adapters/constant.go index d8bf27d379c..8ba2ead8913 100644 --- a/modules/pubmatic/openwrap/adapters/constant.go +++ b/modules/pubmatic/openwrap/adapters/constant.go @@ -23,10 +23,14 @@ var ignoreAppnexusKeys = map[string]bool{ // Bidder Params const ( - BidderParamApacdex_siteId = "siteId" - BidderParamApacdex_placementId = "placementId" - BidderParamApacdex_geo = "geo" - BidderParamApacdex_floorPrice = "floorPrice" - BidderParamBoldwinPlacementID = "placementId" - BidderParamBoldwinEndpointID = "endpointId" + BidderParamApacdex_siteId = "siteId" + BidderParamApacdex_placementId = "placementId" + BidderParamApacdex_geo = "geo" + BidderParamApacdex_floorPrice = "floorPrice" + BidderParamBoldwinPlacementID = "placementId" + BidderParamBoldwinEndpointID = "endpointId" + BidderParamColossusTagID = "TagID" + BidderParamColossusgroupID = "groupId" + BidderNextmillenniumPlacementID = "placement_id" + BidderNextmillenniumgroupID = "group_id" ) diff --git a/modules/pubmatic/openwrap/adapters/converter.go b/modules/pubmatic/openwrap/adapters/converter.go index 3bc2ad067ad..ce39a273307 100644 --- a/modules/pubmatic/openwrap/adapters/converter.go +++ b/modules/pubmatic/openwrap/adapters/converter.go @@ -26,7 +26,7 @@ func FixBidderParams(reqID, adapterName, bidderCode string, ext json.RawMessage) fieldMap := convertExtToFieldMap(bidderCode, ext) //get callback function and execute it - callback := getBuilder(adapterName) + callback := GetBuilder(adapterName) //executing callback function return callback(BidderParameters{ diff --git a/modules/pubmatic/openwrap/adapters/converter_test.go b/modules/pubmatic/openwrap/adapters/converter_test.go new file mode 100644 index 00000000000..6c8a2ddd4d8 --- /dev/null +++ b/modules/pubmatic/openwrap/adapters/converter_test.go @@ -0,0 +1,109 @@ +package adapters + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestFixBidderParams(t *testing.T) { + + type Args struct { + AdapterName string `json:"adapterName"` + RequestJSON json.RawMessage `json:"requestJSON"` + } + type Want struct { + ExpectedJSON json.RawMessage `json:"expectedJSON"` + ExpectedError string `json:"error"` + } + type test struct { + Name string `json:"name"` + Args Args `json:"args"` + Want Want `json:"want"` + } + + var tests []test + //reading test cases from file + readTestCasesFromFile(t, `./tests/hybrid_bidders.json`, &tests) + + //prerequisite + validator := getPrebidBidderParamsValidator(t, `../../../../static/bidder-params`) + + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + //resolving alias only + bidderCode := ResolveOWBidder(tt.Args.AdapterName) + + //FixBidderParams fixing bidder parameters + result, err := FixBidderParams("req-id", tt.Args.AdapterName, bidderCode, tt.Args.RequestJSON) + + //Verify error check + if len(tt.Want.ExpectedError) > 0 { + if assert.Error(t, err) { + assert.Equal(t, err.Error(), tt.Want.ExpectedError) + } + } else { + assert.NoError(t, err) + } + + //verify json + AssertJSON(t, tt.Want.ExpectedJSON, result) + + //validate schema for resultant string + err = validator.Validate(openrtb_ext.BidderName(bidderCode), result) + assert.NoError(t, err) + }) + } +} + +func TestFixBidderParamsS2S(t *testing.T) { + + type Args struct { + AdapterName string `json:"adapterName"` + RequestJSON json.RawMessage `json:"requestJSON"` + } + type Want struct { + ExpectedJSON json.RawMessage `json:"expectedJSON"` + ExpectedError string `json:"error"` + } + type test struct { + Name string `json:"name"` + Args Args `json:"args"` + Want Want `json:"want"` + } + + var tests []test + //reading test cases from file + readTestCasesFromFile(t, `./tests/s2s_bidders.json`, &tests) + + //prerequisite + validator := getPrebidBidderParamsValidator(t, `../../../../static/bidder-params`) + + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + //resolving alias only + bidderCode := ResolveOWBidder(tt.Args.AdapterName) + + //FixBidderParams fixing bidder parameters + result, err := FixBidderParams("req-id", tt.Args.AdapterName, bidderCode, tt.Args.RequestJSON) + + //Verify error check + if len(tt.Want.ExpectedError) > 0 { + if assert.Error(t, err) { + assert.Equal(t, err.Error(), tt.Want.ExpectedError) + } + } else { + assert.NoError(t, err) + } + + //verify json + AssertJSON(t, tt.Want.ExpectedJSON, result) + + //validate schema for resultant string + err = validator.Validate(openrtb_ext.BidderName(bidderCode), result) + assert.NoError(t, err) + }) + } +} diff --git a/modules/pubmatic/openwrap/adapters/default_bidder_parameter.go b/modules/pubmatic/openwrap/adapters/default_bidder_parameter.go index 20572c8d60f..e94d71e6d91 100644 --- a/modules/pubmatic/openwrap/adapters/default_bidder_parameter.go +++ b/modules/pubmatic/openwrap/adapters/default_bidder_parameter.go @@ -9,7 +9,6 @@ import ( "path/filepath" "strings" - "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" "github.com/prebid/prebid-server/v2/openrtb_ext" ) @@ -44,8 +43,8 @@ type ArrayItemsType struct { Type string `json:"type"` } -func parseBidderParams(cfg config.Config) error { - schemas, err := parseBidderSchemaDefinitions() +func parseBidderParams(schemaDirectory string) error { + schemas, err := parseBidderSchemaDefinitions(schemaDirectory) if err != nil { return err } @@ -92,14 +91,12 @@ func parseBidderParams(cfg config.Config) error { parameter.DefaultValue = propertyDef.DefaultValue } parameter.Required = propertyDef.Required - } else { } } for _, propertyName := range jsonSchema.Required { if parameters[propertyName] != nil { parameters[propertyName].Required = true - } else { } } @@ -129,10 +126,10 @@ func getType(param BidderParameter) string { return tp } -func parseBidderSchemaDefinitions() (map[string]*BidderParamJSON, error) { +func parseBidderSchemaDefinitions(schemaDirectory string) (map[string]*BidderParamJSON, error) { schemas := make(map[string]*BidderParamJSON) - schemaDirectory := getBidderParamsDirectory() + schemaDirectory = getBidderParamsDirectory(schemaDirectory) if schemaDirectory == "" { return schemas, errors.New("error failed to parse bidder params files") } @@ -174,8 +171,7 @@ func parseBidderSchemaDefinitions() (map[string]*BidderParamJSON, error) { return schemas, nil } -func getBidderParamsDirectory() string { - schemaDirectory := "./static/bidder-params" +func getBidderParamsDirectory(schemaDirectory string) string { if isDirectoryExists(schemaDirectory) { return schemaDirectory } diff --git a/modules/pubmatic/openwrap/adapters/default_bidder_parameter_test.go b/modules/pubmatic/openwrap/adapters/default_bidder_parameter_test.go new file mode 100644 index 00000000000..5ff8e5e35e7 --- /dev/null +++ b/modules/pubmatic/openwrap/adapters/default_bidder_parameter_test.go @@ -0,0 +1,142 @@ +package adapters + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func getExpectedOpenWrapParameterMappings() map[string]map[string]*ParameterMapping { + mapping := make(map[string]map[string]*ParameterMapping) + dmxMap := make(map[string]*ParameterMapping) + dmxMap["tagid"] = &ParameterMapping{ + KeyName: "dmxid", + } + mapping["dmx"] = dmxMap + + vrtcalMap := make(map[string]*ParameterMapping) + vrtcalMap["just_an_unused_vrtcal_param"] = &ParameterMapping{ + KeyName: "dummyParam", + DefaultValue: "1", + } + mapping["vrtcal"] = vrtcalMap + + gridMap := make(map[string]*ParameterMapping) + gridMap["uid"] = &ParameterMapping{ + Required: true, + } + mapping["grid"] = gridMap + + adkernelMap := make(map[string]*ParameterMapping) + adkernelMap["zoneId"] = &ParameterMapping{ + Datatype: "integer", + } + mapping["adkernel"] = adkernelMap + + return mapping +} + +func TestGetType(t *testing.T) { + type args struct { + param BidderParameter + } + tests := []struct { + name string + args args + want string + }{ + { + name: "Array of strings", + args: args{ + param: BidderParameter{ + Type: "array", + Items: ArrayItemsType{ + Type: "string", + }, + }, + }, + want: "[]string", + }, + { + name: "Array of integers", + args: args{ + param: BidderParameter{ + Type: "array", + Items: ArrayItemsType{ + Type: "integer", + }, + }, + }, + want: "[]integer", + }, + { + name: "Array of numbers", + args: args{ + param: BidderParameter{ + Type: "array", + Items: ArrayItemsType{ + Type: "number", + }, + }, + }, + want: "[]number", + }, + { + name: "String from array of options", + args: args{ + param: BidderParameter{ + Type: []string{"integer", "string"}, + }, + }, + want: "string", + }, + { + name: "First item from array of options", + args: args{ + param: BidderParameter{ + Type: []string{"integer", "number"}, + }, + }, + want: "integer", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getType(tt.args.param); got != tt.want { + t.Errorf("getType() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestParseBidderParams(t *testing.T) { + parseBidderParams("../../static/bidder-params") + assert.Equal(t, 157, len(adapterParams), "Length of expected entries should match") + // calculate this number using X-Y + // where X is calculated using command - `ls -l | wc -l` (substract 1 from result) + // Y is calculated using command `grep -EinR 'oneof|not|anyof|dependenc' static/bidder-params | grep -v "description" | grep -oE './.*.json' | uniq | wc -l` +} + +func TestParseBidderSchemaDefinitions(t *testing.T) { + schemaDefinitions, _ := parseBidderSchemaDefinitions("../../../../static/bidder-params") + assert.Equal(t, 192, len(schemaDefinitions), "Length of expected entries should match") + // calculate this number using command - `ls -l | wc -l` (substract 1 from result) +} + +func TestParseOpenWrapParameterMappings(t *testing.T) { + tests := []struct { + name string + want map[string]map[string]*ParameterMapping + }{ + { + name: "Verify mappings are correctly parsed", + want: getExpectedOpenWrapParameterMappings(), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := parseOpenWrapParameterMappings() + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/modules/pubmatic/openwrap/adapters/default_bidder_test.go b/modules/pubmatic/openwrap/adapters/default_bidder_test.go new file mode 100644 index 00000000000..fa0fe6125c2 --- /dev/null +++ b/modules/pubmatic/openwrap/adapters/default_bidder_test.go @@ -0,0 +1,275 @@ +package adapters + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/prebid/prebid-server/util/ptrutil" +) + +type prepareBidParamJSONDefaultArgs struct { + adapterName string + slotMappings map[string]interface{} + width *int + height *int +} + +func TestPrepareBidParamJSONDefault(t *testing.T) { + //Skip this test for adapters having entry in OpenWrap parameter mappings JSON file + adaptersToSkip := make(map[string]bool) + for bidderName := range parseOpenWrapParameterMappings() { + adaptersToSkip[bidderName] = true + } + + var tests []struct { + name string + args prepareBidParamJSONDefaultArgs + want string + } + for adapterName, adapterParams := range adapterParams { + if adaptersToSkip[adapterName] { + continue + } + + skipRequiredParam := false + tests = append(tests, struct { + name string + args prepareBidParamJSONDefaultArgs + want string + }{name: getTestName(adapterName, skipRequiredParam), args: getTestArgs(adapterName, adapterParams, skipRequiredParam), + want: getExpectedJSON(adapterParams, skipRequiredParam)}) + + skipRequiredParam = true + tests = append(tests, struct { + name string + args prepareBidParamJSONDefaultArgs + want string + }{name: getTestName(adapterName, skipRequiredParam), args: getTestArgs(adapterName, adapterParams, skipRequiredParam), + want: getExpectedJSON(adapterParams, skipRequiredParam)}) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + params := BidderParameters{ + AdapterName: tt.args.adapterName, + FieldMap: tt.args.slotMappings, + Width: func() *int64 { + if tt.args.width != nil { + return ptrutil.ToPtr(int64(*tt.args.width)) + } + return nil + }(), + Height: func() *int64 { + if tt.args.width != nil { + return ptrutil.ToPtr((int64(*tt.args.height))) + } + return nil + }(), + } + got, _ := prepareBidParamJSONDefault(params) + AssertJSON(t, json.RawMessage(tt.want), got) + }) + } +} + +func getTestName(adapterName string, skipRequiredParam bool) string { + if skipRequiredParam { + return fmt.Sprintf("Test for adapter %s with required param missing", adapterName) + } + return fmt.Sprintf("Test for adapter %s", adapterName) +} + +func getTestArgs(adapterName string, params map[string]*ParameterMapping, skipRequiredParam bool) prepareBidParamJSONDefaultArgs { + return prepareBidParamJSONDefaultArgs{ + adapterName: adapterName, + slotMappings: getDummySlotMappings(params, skipRequiredParam), + width: nil, + height: nil, + } + +} + +func getExpectedJSON(params map[string]*ParameterMapping, skipRequiredParam bool) string { + allParamsOptional := true + for _, mapping := range params { + if mapping.Required { + allParamsOptional = false + } + } + if !allParamsOptional && skipRequiredParam { + return "" + } + return GetJSON(getExpectedResponseSlotMappings(params, skipRequiredParam)) +} + +func getExpectedResponseSlotMappings(params map[string]*ParameterMapping, skipRequiredParam bool) map[string]interface{} { + targetMap := make(map[string]interface{}) + for _, mapping := range params { + if mapping.Required && skipRequiredParam { + continue + } + targetMap[mapping.KeyName] = getExpectedResponseValue(mapping.Datatype) + } + return targetMap +} + +func getExpectedResponseValue(datatype string) interface{} { + switch datatype { + case "string": + return "dummyString" + case "number": + return 0.10 + case "integer": + return 1 + case "boolean": + return true + case "[]string": + return []string{"dummyValue1", "dummyValue2"} + case "[]integer": + return []int{1, 2, 3} + case "[]number": + return []float64{1.1, 2.2} + default: + return "defaultDummyString" + } +} + +func getDummySlotMappings(params map[string]*ParameterMapping, skipRequiredParam bool) map[string]interface{} { + targetMap := make(map[string]interface{}) + for _, mapping := range params { + if mapping.Required && skipRequiredParam { + continue + } + targetMap[mapping.KeyName] = getDummyValue(mapping.Datatype) + } + return targetMap +} + +func getDummyValue(datatype string) interface{} { + switch datatype { + case "string": + return "dummyString" + case "number": + return 0.10011 + case "integer": + return 1 + case "boolean": + return true + case "[]string": + return []string{"dummyValue1", "dummyValue2"} + case "[]integer": + return []int{1, 2, 3} + case "[]number": + return []float64{1.1, 2.2} + default: + return "defaultDummyString" + } +} + +func Test_getDataType(t *testing.T) { + type args struct { + paramType string + } + tests := []struct { + name string + args args + want int + }{ + { + name: "TEST paramType []number", + args: args{paramType: "[]number"}, + want: 6, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getDataType(tt.args.paramType); got != tt.want { + t.Errorf("getDataType() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_addBidParam(t *testing.T) { + type args struct { + bidParams map[string]interface{} + name string + paramType string + value interface{} + } + tests := []struct { + name string + args args + wantErr bool + }{ + + { + name: "Empty string input for paramType", + args: args{bidParams: map[string]interface{}{"test": "test1"}, name: "test", paramType: "string", value: ""}, + wantErr: true, + }, + { + name: "Invalid float input for paramType", + args: args{bidParams: map[string]interface{}{"test": "test1"}, name: "test", paramType: "number", value: "abc"}, + wantErr: true, + }, + { + name: "Invalid boolean input for paramType", + args: args{bidParams: map[string]interface{}{"test": "test1"}, name: "test", paramType: "boolean", value: "abc"}, + wantErr: true, + }, + { + name: "Invalid array of int input for paramType", + args: args{bidParams: map[string]interface{}{"test": "test1"}, name: "test", paramType: "[]integer", value: true}, + wantErr: true, + }, + { + name: "array of float input for paramType", + args: args{bidParams: map[string]interface{}{"test": "test1"}, name: "test", paramType: "[]number", value: []float64{11.6}}, + wantErr: false, + }, + { + name: "Invalid array of float input for paramType", + args: args{bidParams: map[string]interface{}{"test": "test1"}, name: "test", paramType: "[]number", value: "11.6"}, + wantErr: true, + }, + { + name: "Invalid array of float input for paramType1", + args: args{bidParams: map[string]interface{}{"test": "test1"}, name: "test", paramType: "[]number", value: []int64{11}}, + wantErr: true, + }, + { + name: "Invalid array of float input for paramType2", + args: args{bidParams: map[string]interface{}{"test": "test1"}, name: "test", paramType: "[]number", value: []interface{}{11}}, + wantErr: true, + }, + { + name: "valid array of float input for paramType", + args: args{bidParams: map[string]interface{}{"test": "test1"}, name: "test", paramType: "[]number", value: []interface{}{11.5}}, + wantErr: false, + }, + { + name: "Invalid array of string input for paramType2", + args: args{bidParams: map[string]interface{}{"test": "test1"}, name: "test", paramType: "[]string", value: []interface{}{nil}}, + wantErr: true, + }, + { + name: "valid array of string input for paramType", + args: args{bidParams: map[string]interface{}{"test": "test1"}, name: "test", paramType: "[]string", value: []interface{}{"test"}}, + wantErr: false, + }, + { + name: "valid array of string input for paramType", + args: args{bidParams: map[string]interface{}{"test": "test1"}, name: "test", paramType: "[]string", value: 5}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := addBidParam(tt.args.bidParams, tt.args.name, tt.args.paramType, tt.args.value); (err != nil) != tt.wantErr { + t.Errorf("addBidParam() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/modules/pubmatic/openwrap/adapters/tests/hybrid_bidders.json b/modules/pubmatic/openwrap/adapters/tests/hybrid_bidders.json index 02f3ba27a5e..f96346584d2 100644 --- a/modules/pubmatic/openwrap/adapters/tests/hybrid_bidders.json +++ b/modules/pubmatic/openwrap/adapters/tests/hybrid_bidders.json @@ -348,5 +348,19 @@ } } } + }, + { + "name": "mediafuse_client_json", + "args": { + "adapterName": "mediafuse", + "requestJSON": { + "placementId": "9880618" + } + }, + "want": { + "expectedJSON": { + "placementId": 9880618 + } + } } ] \ No newline at end of file diff --git a/modules/pubmatic/openwrap/adapters/tests/s2s_bidders.json b/modules/pubmatic/openwrap/adapters/tests/s2s_bidders.json index 183c8491319..4edda100c3b 100644 --- a/modules/pubmatic/openwrap/adapters/tests/s2s_bidders.json +++ b/modules/pubmatic/openwrap/adapters/tests/s2s_bidders.json @@ -10,9 +10,70 @@ }, "want": { "expectedJSON": { - "uid": 123, + "uid": "123", + "size": [160,600] + } + } + }, + { + "name": "visx_client_json_string_uid", + "args" : { + "adapterName": "visx", + "requestJSON": { + "uid": "123", + "size": [160, 600] + } + }, + "want": { + "expectedJSON": { + "uid": "123", "size": [160,600] } } + }, + { + "name": "boldwin_json_OneOf - placementId/endpointId", + "args" : { + "adapterName": "boldwin", + "requestJSON": { + "placementId": "0", + "endpointId":"0" + } + }, + "want": { + "expectedJSON": { + "placementId": "0" + } + } + }, + { + "name": "colossus_json_OneOf - TagID/groupId", + "args" : { + "adapterName": "colossus", + "requestJSON": { + "TagID": "1", + "groupId":"1" + } + }, + "want": { + "expectedJSON": { + "TagID": "1" + } + } + }, + { + "name": "nextmillennium_json_AnyOf - placement_id/group_id", + "args" : { + "adapterName": "nextmillennium", + "requestJSON": { + "placement_id": "1234", + "group_id":"5678" + } + }, + "want": { + "expectedJSON": { + "placement_id": "1234" + } + } } ] \ No newline at end of file diff --git a/modules/pubmatic/openwrap/adapters/vastbidder.go b/modules/pubmatic/openwrap/adapters/vastbidder.go index f656df521f2..8de0d58bb2e 100644 --- a/modules/pubmatic/openwrap/adapters/vastbidder.go +++ b/modules/pubmatic/openwrap/adapters/vastbidder.go @@ -5,32 +5,24 @@ import ( "strconv" "strings" - "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/v2/openrtb_ext" ) -func PrepareVASTBidderParamJSON(request *openrtb2.BidRequest, imp *openrtb2.Imp, - pubVASTTags models.PublisherVASTTags, - matchedSlotKeys []string, slotMap map[string]models.SlotMapping, - adpod *models.AdPod) json.RawMessage { - - if nil == imp.Video { - return nil - } +func PrepareVASTBidderParamJSON(pubVASTTags models.PublisherVASTTags, matchedSlotKeys []string, slotMap map[string]models.SlotMapping) json.RawMessage { bidderExt := openrtb_ext.ExtImpVASTBidder{} bidderExt.Tags = make([]*openrtb_ext.ExtImpVASTBidderTag, len(matchedSlotKeys)) var tagIndex int = 0 for _, slotKey := range matchedSlotKeys { vastTagID := getVASTTagID(slotKey) - if 0 == vastTagID { + if vastTagID == 0 { continue } vastTag, ok := pubVASTTags[vastTagID] - if false == ok { + if !ok { continue } diff --git a/modules/pubmatic/openwrap/adapters/vastbidder_test.go b/modules/pubmatic/openwrap/adapters/vastbidder_test.go new file mode 100644 index 00000000000..b6126a0d380 --- /dev/null +++ b/modules/pubmatic/openwrap/adapters/vastbidder_test.go @@ -0,0 +1,134 @@ +package adapters + +import ( + "encoding/json" + "testing" + + "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/stretchr/testify/assert" +) + +func formImp(isVideo bool) *openrtb2.Imp { + imp := &openrtb2.Imp{} + imp.ID = "impId" + + banner := new(openrtb2.Banner) + banner.Format = []openrtb2.Format{ + { + W: 600, + H: 800, + }, + } + imp.Banner = banner + + if isVideo { + imp.Video = new(openrtb2.Video) + imp.Video.W = 300 + imp.Video.H = 250 + imp.Video.MIMEs = []string{"video/mp4"} + } + + imp.TagID = "4" + return imp +} +func createSlotMapping(slotName string, mappings map[string]interface{}) models.SlotMapping { + return models.SlotMapping{ + PartnerId: 0, + AdapterId: 0, + VersionId: 0, + SlotName: slotName, + SlotMappings: mappings, + Hash: "", + OrderID: 0, + } +} + +func TestPrepareVASTBidderParamJSON(t *testing.T) { + type args struct { + imp *openrtb2.Imp + pubVASTTags models.PublisherVASTTags + matchedSlotKeys []string + slotMap map[string]models.SlotMapping + } + tests := []struct { + name string + args args + want json.RawMessage + }{ + { + name: "VAST Tag ID not found in slot key", + args: args{ + imp: formImp(true), + pubVASTTags: models.PublisherVASTTags{ + 101: &models.VASTTag{URL: `vast-tag-url-1`, Duration: 15}, + 102: &models.VASTTag{URL: `vast-tag-url-2`, Duration: 20}, + }, + matchedSlotKeys: []string{"`abc@123`"}, + slotMap: map[string]models.SlotMapping{ + "abc@123": createSlotMapping("abc@123", + map[string]interface{}{"param1": "85394", "param2": "test", "param3": "example1"}), + }, + }, + want: nil, + }, + { + name: "VAST Tag slot mapping found", + args: args{ + imp: formImp(true), + pubVASTTags: models.PublisherVASTTags{ + 123: &models.VASTTag{URL: `vast-tag-url-1`, Duration: 15}, + }, + matchedSlotKeys: []string{"abc@123"}, + slotMap: map[string]models.SlotMapping{ + "abc@123": createSlotMapping("abc@123", + map[string]interface{}{"param1": "85394", "param2": "test", "param3": "example1"}), + }, + }, + want: json.RawMessage(`{"tags":[{"tagid":"abc@123","url":"vast-tag-url-1","dur":15,"price":0,"params":{"param1":"85394","param2":"test","param3":"example1"}}]}`), + }, + { + name: "VAST Tag slot mapping not found", + args: args{ + imp: formImp(true), + pubVASTTags: models.PublisherVASTTags{ + 123: &models.VASTTag{URL: `vast-tag-url-1`, Duration: 15}, + }, + matchedSlotKeys: []string{"abc@123"}, + slotMap: map[string]models.SlotMapping{ + "abcd@123": createSlotMapping("abcd@123", + map[string]interface{}{"param1": "85394", "param2": "test", "param3": "example1"}), + }, + }, + want: nil, + }, + { + name: "VAST Tag ID not found", + args: args{ + imp: formImp(true), + pubVASTTags: models.PublisherVASTTags{ + 1234: &models.VASTTag{URL: `vast-tag-url-1`, Duration: 15}, + }, + matchedSlotKeys: []string{"abc@123"}, + slotMap: map[string]models.SlotMapping{ + "abc@123": createSlotMapping("abc@123", + map[string]interface{}{"param1": "85394", "param2": "test", "param3": "example1"}), + }, + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := PrepareVASTBidderParamJSON(tt.args.pubVASTTags, tt.args.matchedSlotKeys, tt.args.slotMap) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestGetVASTTagID(t *testing.T) { + assert.Equal(t, 0, getVASTTagID(""), "key: ") + assert.Equal(t, 0, getVASTTagID("abc"), "key: abc") + assert.Equal(t, 0, getVASTTagID("abc@xyz"), "key: abc@xyz") + assert.Equal(t, 123, getVASTTagID("abc@123"), "key: abc@123") +} diff --git a/modules/pubmatic/openwrap/allprocessedbidresponsehook.go b/modules/pubmatic/openwrap/allprocessedbidresponsehook.go index 764ba2d76e1..7a701a5706a 100644 --- a/modules/pubmatic/openwrap/allprocessedbidresponsehook.go +++ b/modules/pubmatic/openwrap/allprocessedbidresponsehook.go @@ -5,6 +5,7 @@ import ( "github.com/prebid/prebid-server/v2/exchange/entities" "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/utils" "github.com/prebid/prebid-server/v2/openrtb_ext" ) @@ -26,6 +27,17 @@ func (m OpenWrap) handleAllProcessedBidResponsesHook( return result, nil } + rCtx, ok := moduleCtx.ModuleContext["rctx"].(models.RequestCtx) + if !ok { + result.DebugMessages = append(result.DebugMessages, "error: request-ctx not found in handleAllProcessedBidResponsesHook()") + return result, nil + } + + //Do not execute the module for requests processed in SSHB(8001) + if rCtx.Sshb == "1" || rCtx.Endpoint == models.EndpointHybrid { + return result, nil + } + result.ChangeSet.AddMutation(func(apbrp hookstage.AllProcessedBidResponsesPayload) (hookstage.AllProcessedBidResponsesPayload, error) { updateBidIds(apbrp.Responses) return apbrp, nil diff --git a/modules/pubmatic/openwrap/allprocessedbidresponsehook_test.go b/modules/pubmatic/openwrap/allprocessedbidresponsehook_test.go index 28dc988034f..2c1214dd2f6 100644 --- a/modules/pubmatic/openwrap/allprocessedbidresponsehook_test.go +++ b/modules/pubmatic/openwrap/allprocessedbidresponsehook_test.go @@ -1,10 +1,16 @@ package openwrap import ( + "context" "testing" + "github.com/golang/mock/gomock" "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -67,3 +73,175 @@ func TestUpdateBidIds(t *testing.T) { }) } } + +func TestOpenWrap_handleAllProcessedBidResponsesHook(t *testing.T) { + ctrl := gomock.NewController(t) + mockCache := mock_cache.NewMockCache(ctrl) + defer ctrl.Finish() + + type args struct { + ctx context.Context + moduleCtx hookstage.ModuleInvocationContext + payload hookstage.AllProcessedBidResponsesPayload + } + tests := []struct { + name string + args args + mutationApplied bool + want hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload] + wantErr bool + wantResponse map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid + }{ + { + name: "empty module context", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{}, + payload: hookstage.AllProcessedBidResponsesPayload{}, + }, + want: hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload]{ + Reject: false, + ChangeSet: hookstage.ChangeSet[hookstage.AllProcessedBidResponsesPayload]{}, + DebugMessages: []string{"error: module-ctx not found in handleAllProcessedBidResponsesHook()"}, + AnalyticsTags: hookanalytics.Analytics{}, + }, + wantErr: false, + }, + { + name: "empty request context", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: map[string]interface{}{ + "rctx": nil, + }, + }, + }, + want: hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload]{ + Reject: false, + ChangeSet: hookstage.ChangeSet[hookstage.AllProcessedBidResponsesPayload]{}, + DebugMessages: []string{"error: request-ctx not found in handleAllProcessedBidResponsesHook()"}, + AnalyticsTags: hookanalytics.Analytics{}, + }, + wantErr: false, + }, + { + name: "SSHb request", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: map[string]interface{}{ + "rctx": models.RequestCtx{ + Sshb: "1", + }, + }, + }, + }, + + want: hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload]{ + Reject: false, + ChangeSet: hookstage.ChangeSet[hookstage.AllProcessedBidResponsesPayload]{}, + DebugMessages: nil, + AnalyticsTags: hookanalytics.Analytics{}, + }, + }, + { + name: "Hybrid request", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: map[string]interface{}{ + "rctx": models.RequestCtx{ + Endpoint: models.EndpointHybrid, + }, + }, + }, + }, + want: hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload]{ + Reject: false, + ChangeSet: hookstage.ChangeSet[hookstage.AllProcessedBidResponsesPayload]{}, + DebugMessages: nil, + AnalyticsTags: hookanalytics.Analytics{}, + }, + }, + { + name: "All bidIds are updated", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: map[string]interface{}{ + "rctx": models.RequestCtx{ + Endpoint: models.EndpointV25, + }, + }, + }, + payload: hookstage.AllProcessedBidResponsesPayload{ + Responses: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{ + "pubmatic": { + Bids: []*entities.PbsOrtbBid{ + { + Bid: &openrtb2.Bid{ + ID: "bid-1", + }, + GeneratedBidID: "gen-1", + }, + { + Bid: &openrtb2.Bid{ + ID: "bid-2", + }, + GeneratedBidID: "gen-2", + }, + }, + }, + }, + }, + }, + mutationApplied: true, + want: hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload]{ + Reject: false, + ChangeSet: hookstage.ChangeSet[hookstage.AllProcessedBidResponsesPayload]{}, + DebugMessages: nil, + AnalyticsTags: hookanalytics.Analytics{}, + }, + wantErr: false, + wantResponse: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{ + "pubmatic": { + Bids: []*entities.PbsOrtbBid{ + { + Bid: &openrtb2.Bid{ + ID: "bid-1::gen-1", + }, + GeneratedBidID: "gen-1", + }, + { + Bid: &openrtb2.Bid{ + ID: "bid-2::gen-2", + }, + GeneratedBidID: "gen-2", + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := OpenWrap{ + cache: mockCache, + } + got, err := m.handleAllProcessedBidResponsesHook(tt.args.ctx, tt.args.moduleCtx, tt.args.payload) + assert.Equal(t, tt.wantErr, err != nil, "handleAllProcessedBidResponsesHook() error = %v, wantErr %v", err, tt.wantErr) + if tt.mutationApplied { + 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.wantResponse, result.Responses, tt.name) + } + } + assert.Equal(t, tt.want.DebugMessages, got.DebugMessages, "Debug messages should be equal") + assert.Equal(t, tt.want.Reject, false, "Reject should be equal") + }) + } +} diff --git a/modules/pubmatic/openwrap/auctionresponsehook.go b/modules/pubmatic/openwrap/auctionresponsehook.go index 8c99cfc9a47..9b940728bc6 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook.go +++ b/modules/pubmatic/openwrap/auctionresponsehook.go @@ -117,12 +117,12 @@ func (m OpenWrap) handleAuctionResponseHook( // set response netecpm and logger/tracker en revShare := models.GetRevenueShare(rctx.PartnerConfigMap[partnerID]) - bidExt.NetECPM = models.GetNetEcpm(bid.Price, revShare) - eg = bid.Price + bidExt.NetECPM = models.ToFixed(bid.Price, models.BID_PRECISION) + eg = models.GetGrossEcpmFromNetEcpm(bid.Price, revShare) en = bidExt.NetECPM if payload.BidResponse.Cur != "USD" { - eg = bidExt.OriginalBidCPMUSD - en = models.GetNetEcpm(bidExt.OriginalBidCPMUSD, revShare) + eg = models.GetGrossEcpmFromNetEcpm(bidExt.OriginalBidCPMUSD, revShare) + en = bidExt.OriginalBidCPMUSD bidExt.OriginalBidCPMUSD = 0 } diff --git a/modules/pubmatic/openwrap/auctionresponsehook_test.go b/modules/pubmatic/openwrap/auctionresponsehook_test.go index fe2fc62acf4..e7b643fd4c7 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook_test.go +++ b/modules/pubmatic/openwrap/auctionresponsehook_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/golang/mock/gomock" + "github.com/prebid/openrtb/v19/adcom1" "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/openrtb/v19/openrtb3" "github.com/prebid/prebid-server/v2/hooks/hookstage" @@ -1418,11 +1419,8 @@ func TestResetBidIdtoOriginal(t *testing.T) { func TestAuctionResponseHookForEndpointWebS2S(t *testing.T) { ctrl := gomock.NewController(t) mockCache := mock_cache.NewMockCache(ctrl) - tbf.Init(1, mockCache) - defer func() { - ctrl.Finish() - tbf.StopTBFReloaderService() - }() + tbf.SetAndResetTBFConfig(mockCache, nil) + defer ctrl.Finish() type args struct { ctx context.Context @@ -1554,7 +1552,6 @@ func TestAuctionResponseHookForEndpointWebS2S(t *testing.T) { metricEngine: tt.getMetricsEngine(), cache: mockCache, } - mockCache.EXPECT().GetTBFTrafficForPublishers().Return(map[int]map[int]int{1: {2: 3}}, nil).AnyTimes() 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() @@ -1567,3 +1564,342 @@ func TestAuctionResponseHookForEndpointWebS2S(t *testing.T) { }) } } + +func TestOpenWrap_handleAuctionResponseHook(t *testing.T) { + ctrl := gomock.NewController(t) + mockCache := mock_cache.NewMockCache(ctrl) + tbf.SetAndResetTBFConfig(mockCache, nil) + defer ctrl.Finish() + + type want struct { + result hookstage.HookResult[hookstage.AuctionResponsePayload] + bidResponse json.RawMessage + err error + } + type args struct { + ctx context.Context + moduleCtx hookstage.ModuleInvocationContext + payload hookstage.AuctionResponsePayload + } + tests := []struct { + name string + args args + want want + doMutate bool + setup func() *mock_metrics.MockMetricsEngine + }{ + { + name: "empty moduleContext", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{}, + payload: hookstage.AuctionResponsePayload{}, + }, + doMutate: false, + want: want{ + result: hookstage.HookResult[hookstage.AuctionResponsePayload]{ + DebugMessages: []string{"error: module-ctx not found in handleAuctionResponseHook()"}, + }, + err: nil, + }, + }, + { + name: "empty requestContext", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": nil, + }, + }, + payload: hookstage.AuctionResponsePayload{}, + }, + doMutate: false, + want: want{ + result: hookstage.HookResult[hookstage.AuctionResponsePayload]{ + DebugMessages: []string{"error: request-ctx not found in handleAuctionResponseHook()"}, + }, + err: nil, + }, + }, + { + name: "requestContext is not of type RequestCtx", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": "request-ctx", // request-ctx is not of type RequestCtx + }, + }, + payload: hookstage.AuctionResponsePayload{}, + }, + doMutate: false, + want: want{ + result: hookstage.HookResult[hookstage.AuctionResponsePayload]{ + DebugMessages: []string{"error: request-ctx not found in handleAuctionResponseHook()"}, + }, + err: nil, + }, + }, + { + name: "requestContext has sshb=1(request should not execute module hook)", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + Sshb: "1", + }, + }, + }, + payload: hookstage.AuctionResponsePayload{}, + }, + doMutate: false, + want: want{ + result: hookstage.HookResult[hookstage.AuctionResponsePayload]{}, + err: nil, + }, + }, + { + name: "empty bidResponse", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + Sshb: "0", + PubID: 5890, + PubIDStr: "5890", + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{}, + }, + }, + }, + doMutate: true, + setup: func() *mock_metrics.MockMetricsEngine { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordNobidErrPrebidServerResponse("5890") + mockEngine.EXPECT().RecordPublisherResponseTimeStats("5890", gomock.Any()) + return mockEngine + }, + want: want{ + result: hookstage.HookResult[hookstage.AuctionResponsePayload]{}, + err: nil, + bidResponse: json.RawMessage(`{"id":"","ext":{"matchedimpression":{}}}`), + }, + }, + { + name: "valid bidResponse with banner bids", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + PubID: 5890, + PubIDStr: "5890", + Platform: "web", + ImpBidCtx: map[string]models.ImpCtx{ + "Div1": { + Bidders: map[string]models.PartnerData{ + "pubmatic": { + PartnerID: 123, + PrebidBidderCode: "pubmatic", + }, + }, + Video: &openrtb2.Video{}, + Type: "video", + Banner: true, + }, + }, + BidderResponseTimeMillis: map[string]int{}, + SeatNonBids: map[string][]openrtb_ext.NonBid{}, + LogInfoFlag: 1, + ReturnAllBidStatus: true, + Debug: true, + ClientConfigFlag: 1, + PartnerConfigMap: map[int]map[string]string{ + 123: { + models.PARTNER_ID: "123", + models.PREBID_PARTNER_NAME: "pubmatic", + models.BidderCode: "pubmatic", + models.SERVER_SIDE_FLAG: "1", + models.KEY_GEN_PATTERN: "_AU_@_W_x_H_", + models.TIMEOUT: "200", + }, + -1: { + models.DisplayVersionID: "1", + "refreshInterval": "30", + "rev_share": "0.5", + }, + }, + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + ID: "12345", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "Div1", + Price: 5, + AdM: "
", + Ext: json.RawMessage(`{"bidtype":0,"deal_channel":1,"dspid":6,"origbidcpm":8,"origbidcur":"USD","prebid":{"bidid":"bb57a9e3-fdc2-4772-8071-112dd7f50a6a","meta":{"adaptercode":"pubmatic","advertiserId":4098,"agencyId":4098,"demandSource":"6","mediaType":"banner","networkId":6},"targeting":{"hb_bidder_pubmatic":"pubmatic","hb_deal_pubmatic":"PUBDEAL1","hb_pb_pubmatic":"8.00","hb_size_pubmatic":"728x90"},"type":"banner","video":{"duration":0,"primary_category":"","vasttagid":""}}}`), + }, + }, + Seat: "pubmatic", + }, + }, + Ext: json.RawMessage(`{"responsetimemillis":{"pubmatic":8}}`), + }, + }, + }, + setup: func() *mock_metrics.MockMetricsEngine { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("web", "5890", "pubmatic") + mockEngine.EXPECT().RecordPartnerResponseTimeStats("5890", "pubmatic", 8) + mockEngine.EXPECT().RecordPublisherResponseTimeStats("5890", gomock.Any()) + mockEngine.EXPECT().RecordPublisherPartnerNoCookieStats("5890", gomock.Any()).AnyTimes() + return mockEngine + }, + doMutate: true, + want: want{ + result: hookstage.HookResult[hookstage.AuctionResponsePayload]{ + DebugMessages: []string{`[{"PubID":5890,"ProfileID":0,"DisplayID":0,"VersionID":0,"DisplayVersionID":0,"SSAuction":0,"SummaryDisable":0,"LogInfoFlag":1,"SSAI":"","PartnerConfigMap":{"-1":{"displayVersionId":"1","refreshInterval":"30","rev_share":"0.5"},"123":{"bidderCode":"pubmatic","kgp":"_AU_@_W_x_H_","partnerId":"123","prebidPartnerName":"pubmatic","serverSideEnabled":"1","timeout":"200"}},"SupportDeals":false,"Platform":"web","LoggerImpressionID":"","ClientConfigFlag":1,"IP":"","TMax":0,"IsTestRequest":0,"ABTestConfig":0,"ABTestConfigApplied":0,"IsCTVRequest":false,"TrackerEndpoint":"","VideoErrorTrackerEndpoint":"","UA":"","Cookies":"","UidCookie":null,"KADUSERCookie":null,"ParsedUidCookie":null,"OriginCookie":"","Debug":true,"Trace":false,"PageURL":"","StartTime":0,"DevicePlatform":0,"Trackers":{"bid-id-1":{"Tracker":{"PubID":5890,"PageURL":"","Timestamp":0,"IID":"","ProfileID":"0","VersionID":"0","SlotID":"","Adunit":"","PartnerInfo":{"PartnerID":"pubmatic","BidderCode":"pubmatic","KGPV":"","GrossECPM":0,"NetECPM":0,"BidID":"bb57a9e3-fdc2-4772-8071-112dd7f50a6a","OrigBidID":"bid-id-1","AdSize":"0x0","AdDuration":0,"Adformat":"banner","ServerSide":1,"Advertiser":"","FloorValue":0,"FloorRuleValue":0,"DealID":"-1"},"RewardedInventory":0,"SURL":"","Platform":0,"SSAI":"","AdPodSlot":0,"TestGroup":0,"Origin":"","FloorSkippedFlag":null,"FloorModelVersion":"","FloorSource":null,"FloorType":0,"CustomDimensions":"","LoggerData":{"KGPSV":"","FloorProvider":"","FloorFetchStatus":null}},"TrackerURL":"https:?adv=\u0026af=banner\u0026aps=0\u0026au=\u0026bc=pubmatic\u0026bidid=bb57a9e3-fdc2-4772-8071-112dd7f50a6a\u0026di=-1\u0026eg=0\u0026en=0\u0026ft=0\u0026iid=\u0026kgpv=\u0026orig=\u0026origbidid=bid-id-1\u0026pdvid=0\u0026pid=0\u0026plt=0\u0026pn=pubmatic\u0026psz=0x0\u0026pubid=5890\u0026purl=\u0026sl=1\u0026slot=\u0026ss=1\u0026tgid=0\u0026tst=0","ErrorURL":"","Price":5,"PriceModel":"CPM","PriceCurrency":""}},"PrebidBidderCode":null,"ImpBidCtx":{"Div1":{"ImpID":"","TagID":"","Div":"","SlotName":"","AdUnitName":"","Secure":0,"BidFloor":0,"BidFloorCur":"","IsRewardInventory":null,"Banner":true,"Video":{"mimes":null},"Native":null,"IncomingSlots":null,"Type":"video","Bidders":{"pubmatic":{"PartnerID":123,"PrebidBidderCode":"pubmatic","MatchedSlot":"","KGP":"","KGPV":"","IsRegex":false,"Params":null,"VASTTagFlag":false,"VASTTagFlags":null}},"NonMapped":null,"NewExt":null,"BidCtx":{"bid-id-1":{"prebid":{"meta":{"adaptercode":"pubmatic","advertiserId":4098,"agencyId":4098,"demandSource":"6","mediaType":"banner","networkId":6},"type":"banner","bidid":"bb57a9e3-fdc2-4772-8071-112dd7f50a6a"},"refreshInterval":30,"crtype":"banner","dspid":6,"netecpm":5,"origbidcpm":8,"origbidcur":"USD","EG":0,"EN":0}},"BannerAdUnitCtx":{"MatchedSlot":"","IsRegex":false,"MatchedRegex":"","SelectedSlotAdUnitConfig":null,"AppliedSlotAdUnitConfig":null,"UsingDefaultConfig":false,"AllowedConnectionTypes":null},"VideoAdUnitCtx":{"MatchedSlot":"","IsRegex":false,"MatchedRegex":"","SelectedSlotAdUnitConfig":null,"AppliedSlotAdUnitConfig":null,"UsingDefaultConfig":false,"AllowedConnectionTypes":null},"BidderError":"","IsAdPodRequest":false}},"Aliases":null,"NewReqExt":null,"ResponseExt":{"responsetimemillis":{"pubmatic":8}},"MarketPlaceBidders":null,"AdapterThrottleMap":null,"AdUnitConfig":null,"Source":"","Origin":"","SendAllBids":false,"WinningBids":{"Div1":{"ID":"bid-id-1","NetEcpm":5,"BidDealTierSatisfied":false,"Nbr":null}},"DroppedBids":null,"DefaultBids":{},"SeatNonBids":{},"BidderResponseTimeMillis":{"pubmatic":8},"Endpoint":"","PubIDStr":"5890","ProfileIDStr":"","MetricsEngine":{},"ReturnAllBidStatus":true,"Sshb":"","DCName":"","CachePutMiss":0,"MatchedImpression":{"pubmatic":0},"CustomDimensions":null}]`}, + }, + err: nil, + bidResponse: json.RawMessage(`{"id":"12345","seatbid":[{"bid":[{"id":"bid-id-1","impid":"Div1","price":5,"adm":"\u003cimg src=\"http://ads.pubmatic.com/AdTag/728x90.png\"\u003e\u003c/img\u003e\u003cdiv style=\"position:absolute;left:0px;top:0px;visibility:hidden;\"\u003e\u003cimg src=\"https://t.pubmatic.com/wt?adv=\u0026af=banner\u0026aps=0\u0026au=%2F43743431%2FDMDemo\u0026bc=appnexus\u0026bidid=4033c510-6d67-4af6-b53f-682ff1a580c3\u0026di=-1\u0026eg=14\u0026en=14\u0026frv=1.57\u0026ft=0\u0026fv=1.57\u0026iid=429d469d-8cfb-495a-9f0c-5f48aa0ede40\u0026kgpv=\u0026orig=ebay.com\u0026origbidid=718825584\u0026pdvid=1\u0026pid=22503\u0026plt=1\u0026pn=appnexus\u0026psz=728x90\u0026pubid=5890\u0026purl=http%3A%2F%2Febay.com%2Finte%2Fautomation%2Fs2s_activation%2Fbanner-with-gdpr-pubmatic-denied-defaultbidder.html%3Fprofileid%3D22503%26pwtv%3D1%26pwtvc%3D1%26appnexus_banner_fixedbid%3D14%26fixedbid%3D1%26debug%3D1\u0026sl=1\u0026slot=%2F43743431%2FDMDemo\u0026ss=1\u0026tgid=0\u0026tst=1704357774\"\u003e\u003c/div\u003e\u003cdiv style=\"position:absolute;left:0px;top:0px;visibility:hidden;\"\u003e\u003cimg src=\"https:?adv=\u0026af=banner\u0026aps=0\u0026au=\u0026bc=pubmatic\u0026bidid=bb57a9e3-fdc2-4772-8071-112dd7f50a6a\u0026di=-1\u0026eg=0\u0026en=0\u0026ft=0\u0026iid=\u0026kgpv=\u0026orig=\u0026origbidid=bid-id-1\u0026pdvid=0\u0026pid=0\u0026plt=0\u0026pn=pubmatic\u0026psz=0x0\u0026pubid=5890\u0026purl=\u0026sl=1\u0026slot=\u0026ss=1\u0026tgid=0\u0026tst=0\"\u003e\u003c/div\u003e","ext":{"prebid":{"meta":{"adaptercode":"pubmatic","advertiserId":4098,"agencyId":4098,"demandSource":"6","mediaType":"banner","networkId":6},"type":"banner","bidid":"bb57a9e3-fdc2-4772-8071-112dd7f50a6a"},"refreshInterval":30,"crtype":"banner","dspid":6,"netecpm":5,"origbidcpm":8,"origbidcur":"USD"}}],"seat":"pubmatic"}],"ext":{"responsetimemillis":{"pubmatic":8},"matchedimpression":{"pubmatic":0},"loginfo":{"tracker":"?adv=\u0026af=\u0026aps=0\u0026au=%24%7BADUNIT%7D\u0026bc=%24%7BBIDDER_CODE%7D\u0026bidid=%24%7BBID_ID%7D\u0026di=\u0026eg=%24%7BG_ECPM%7D\u0026en=%24%7BN_ECPM%7D\u0026ft=0\u0026iid=\u0026kgpv=%24%7BKGPV%7D\u0026orig=\u0026origbidid=%24%7BORIGBID_ID%7D\u0026pdvid=0\u0026pid=0\u0026plt=0\u0026pn=%24%7BPARTNER_NAME%7D\u0026psz=\u0026pubid=5890\u0026purl=\u0026rwrd=%24%7BREWARDED%7D\u0026sl=1\u0026slot=%24%7BSLOT_ID%7D\u0026ss=0\u0026tgid=0\u0026tst=0"}}}`), + }, + }, + { + name: "valid bidResponse with video bids", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + PubID: 5890, + PubIDStr: "5890", + Platform: "web", + ImpBidCtx: map[string]models.ImpCtx{ + "Div1": { + Bidders: map[string]models.PartnerData{ + "pubmatic": { + PartnerID: 123, + PrebidBidderCode: "pubmatic", + }, + }, + Video: &openrtb2.Video{ + MaxDuration: 20, + MinDuration: 10, + SkipAfter: 2, + Skip: ptrutil.ToPtr[int8](1), + SkipMin: 1, + BAttr: []adcom1.CreativeAttribute{adcom1.CreativeAttribute(1)}, + PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackPageLoadSoundOn}, + }, + Type: "video", + Banner: false, + }, + }, + BidderResponseTimeMillis: map[string]int{}, + SeatNonBids: map[string][]openrtb_ext.NonBid{}, + LogInfoFlag: 1, + ReturnAllBidStatus: true, + Debug: true, + PartnerConfigMap: map[int]map[string]string{ + 123: { + models.PARTNER_ID: "123", + models.PREBID_PARTNER_NAME: "pubmatic", + models.BidderCode: "pubmatic", + models.SERVER_SIDE_FLAG: "1", + models.KEY_GEN_PATTERN: "_AU_@_W_x_H_", + models.TIMEOUT: "200", + }, + -1: { + models.DisplayVersionID: "1", + "refreshInterval": "30", + "rev_share": "0.5", + }, + }, + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + ID: "12345", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "Div1", + Price: 5, + AdM: "", + Ext: json.RawMessage(`{"bidtype":0,"deal_channel":1,"dspid":6,"origbidcpm":8,"origbidcur":"USD","prebid":{"bidid":"bb57a9e3-fdc2-4772-8071-112dd7f50a6a","meta":{"adaptercode":"pubmatic","advertiserId":4098,"agencyId":4098,"demandSource":"6","mediaType":"banner","networkId":6},"targeting":{"hb_bidder_pubmatic":"pubmatic","hb_deal_pubmatic":"PUBDEAL1","hb_pb_pubmatic":"8.00","hb_size_pubmatic":"728x90"},"type":"video","video":{"duration":0,"primary_category":"","vasttagid":""}}}`), + }, + }, + Seat: "pubmatic", + }, + }, + Ext: json.RawMessage(`{"responsetimemillis":{"pubmatic":8}}`), + }, + }, + }, + setup: func() *mock_metrics.MockMetricsEngine { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("web", "5890", "pubmatic") + mockEngine.EXPECT().RecordPartnerResponseTimeStats("5890", "pubmatic", 8) + mockEngine.EXPECT().RecordPublisherResponseTimeStats("5890", gomock.Any()) + mockEngine.EXPECT().RecordPublisherPartnerNoCookieStats("5890", gomock.Any()).AnyTimes() + return mockEngine + }, + doMutate: true, + want: want{ + result: hookstage.HookResult[hookstage.AuctionResponsePayload]{}, + err: nil, + bidResponse: json.RawMessage(`{"id":"12345","seatbid":[{"bid":[{"id":"bid-id-1","impid":"Div1","price":5,"adm":"\u003cVAST version=\"3.0\"\u003e\u003cAd\u003e\u003cWrapper\u003e\u003cImpression\u003e\u003c![CDATA[https:?adv=\u0026af=video\u0026aps=0\u0026au=\u0026bc=pubmatic\u0026bidid=bb57a9e3-fdc2-4772-8071-112dd7f50a6a\u0026di=-1\u0026eg=0\u0026en=0\u0026ft=0\u0026iid=\u0026kgpv=\u0026orig=\u0026origbidid=bid-id-1\u0026pdvid=0\u0026pid=0\u0026plt=0\u0026pn=pubmatic\u0026psz=0x0\u0026pubid=5890\u0026purl=\u0026sl=1\u0026slot=\u0026ss=1\u0026tgid=0\u0026tst=0]]\u003e\u003c/Impression\u003e\u003c/Wrapper\u003e\u003c/Ad\u003e\u003c/VAST\u003e","ext":{"prebid":{"meta":{"adaptercode":"pubmatic","advertiserId":4098,"agencyId":4098,"demandSource":"6","mediaType":"banner","networkId":6},"type":"video","bidid":"bb57a9e3-fdc2-4772-8071-112dd7f50a6a"},"refreshInterval":30,"crtype":"video","video":{"minduration":10,"maxduration":20,"skip":1,"skipmin":1,"skipafter":2,"battr":[1],"playbackmethod":[1]},"dspid":6,"netecpm":5,"origbidcpm":8,"origbidcur":"USD"}}],"seat":"pubmatic"}],"ext":{"responsetimemillis":{"pubmatic":8},"matchedimpression":{"pubmatic":0},"loginfo":{"tracker":"?adv=\u0026af=\u0026aps=0\u0026au=%24%7BADUNIT%7D\u0026bc=%24%7BBIDDER_CODE%7D\u0026bidid=%24%7BBID_ID%7D\u0026di=\u0026eg=%24%7BG_ECPM%7D\u0026en=%24%7BN_ECPM%7D\u0026ft=0\u0026iid=\u0026kgpv=%24%7BKGPV%7D\u0026orig=\u0026origbidid=%24%7BORIGBID_ID%7D\u0026pdvid=0\u0026pid=0\u0026plt=0\u0026pn=%24%7BPARTNER_NAME%7D\u0026psz=\u0026pubid=5890\u0026purl=\u0026rwrd=%24%7BREWARDED%7D\u0026sl=1\u0026slot=%24%7BSLOT_ID%7D\u0026ss=0\u0026tgid=0\u0026tst=0"}}}`), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var mockEngine *mock_metrics.MockMetricsEngine + if tt.setup != nil { + mockEngine = tt.setup() + } + m := OpenWrap{ + cache: mockCache, + metricEngine: mockEngine, + } + moduleCtx, ok := tt.args.moduleCtx.ModuleContext["rctx"] + if ok { + rCtx, ok := moduleCtx.(models.RequestCtx) + if ok { + rCtx.MetricsEngine = mockEngine + tt.args.moduleCtx.ModuleContext["rctx"] = rCtx + } + } + hookResult, err := m.handleAuctionResponseHook(tt.args.ctx, tt.args.moduleCtx, tt.args.payload) + assert.Equal(t, tt.want.err, err, tt.name) + if tt.doMutate { + mutations := hookResult.ChangeSet.Mutations() + assert.NotEmpty(t, mutations, tt.name) + for _, mut := range mutations { + result, err := mut.Apply(tt.args.payload) + gotBidResponse, _ := json.Marshal(result.BidResponse) + assert.Nil(t, err, tt.name) + assert.Equal(t, tt.want.bidResponse, json.RawMessage(gotBidResponse), tt.name) + } + return + } + assert.Equal(t, tt.want.result.DebugMessages, hookResult.DebugMessages, tt.name) + }) + } +} diff --git a/modules/pubmatic/openwrap/beforevalidationhook.go b/modules/pubmatic/openwrap/beforevalidationhook.go index 16654bd8d02..307081b515a 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook.go +++ b/modules/pubmatic/openwrap/beforevalidationhook.go @@ -48,14 +48,6 @@ func (m OpenWrap) handleBeforeValidationHook( } }() - // return prebid validation error - if len(payload.BidRequest.Imp) == 0 || (payload.BidRequest.Site == nil && payload.BidRequest.App == nil) { - result.Reject = false - m.metricEngine.RecordBadRequests(rCtx.Endpoint, getPubmaticErrorCode(nbr.InvalidRequestExt)) - m.metricEngine.RecordNobidErrPrebidServerRequests(rCtx.PubIDStr, nbr.InvalidRequestExt) - return result, nil - } - //Do not execute the module for requests processed in SSHB(8001) if rCtx.Sshb == "1" { result.Reject = false @@ -68,6 +60,14 @@ func (m OpenWrap) handleBeforeValidationHook( return result, nil } + // return prebid validation error + if len(payload.BidRequest.Imp) == 0 || (payload.BidRequest.Site == nil && payload.BidRequest.App == nil) { + result.Reject = false + m.metricEngine.RecordBadRequests(rCtx.Endpoint, getPubmaticErrorCode(nbr.InvalidRequestExt)) + m.metricEngine.RecordNobidErrPrebidServerRequests(rCtx.PubIDStr, nbr.InvalidRequestExt) + return result, nil + } + pubID, err := getPubID(*payload.BidRequest) if err != nil { result.NbrCode = nbr.InvalidPublisherID @@ -79,7 +79,9 @@ func (m OpenWrap) handleBeforeValidationHook( rCtx.Source, rCtx.Origin = getSourceAndOrigin(payload.BidRequest) rCtx.PageURL = getPageURL(payload.BidRequest) rCtx.Platform = getPlatformFromRequest(payload.BidRequest) - rCtx.DevicePlatform = GetDevicePlatform(rCtx, payload.BidRequest) + rCtx.UA = getUserAgent(payload.BidRequest, rCtx.UA) + rCtx.DeviceCtx.Platform = getDevicePlatform(rCtx, payload.BidRequest) + populateDeviceContext(&rCtx.DeviceCtx, payload.BidRequest.Device) if rCtx.UidCookie == nil { m.metricEngine.RecordUidsCookieNotPresentErrorStats(rCtx.PubIDStr, rCtx.ProfileIDStr) @@ -131,9 +133,8 @@ func (m OpenWrap) handleBeforeValidationHook( return result, err } rCtx.Platform = platform - rCtx.DevicePlatform = GetDevicePlatform(rCtx, payload.BidRequest) + rCtx.DeviceCtx.Platform = getDevicePlatform(rCtx, payload.BidRequest) rCtx.SendAllBids = isSendAllBids(rCtx) - rCtx.TMax = m.setTimeout(rCtx, payload.BidRequest) m.metricEngine.RecordPublisherRequests(rCtx.Endpoint, rCtx.PubIDStr, rCtx.Platform) @@ -143,6 +144,9 @@ func (m OpenWrap) handleBeforeValidationHook( result.Warnings = append(result.Warnings, "update the rCtx.PartnerConfigMap with ABTest data") } + //TMax should be updated after ABTest processing + rCtx.TMax = m.setTimeout(rCtx, payload.BidRequest) + var allPartnersThrottledFlag bool rCtx.AdapterThrottleMap, allPartnersThrottledFlag = GetAdapterThrottleMap(rCtx.PartnerConfigMap) if allPartnersThrottledFlag { @@ -175,6 +179,7 @@ func (m OpenWrap) handleBeforeValidationHook( isAdPodRequest := false disabledSlots := 0 serviceSideBidderPresent := false + requestExt.Prebid.BidAdjustmentFactors = map[string]float64{} aliasgvlids := make(map[string]uint16) for i := 0; i < len(payload.BidRequest.Imp); i++ { @@ -340,19 +345,24 @@ func (m OpenWrap) handleBeforeValidationHook( bidderMeta[bidder].VASTTagFlags[bidder] = false } + isAlias := false if alias, ok := partnerConfig[models.IsAlias]; ok && alias == "1" { if prebidPartnerName, ok := partnerConfig[models.PREBID_PARTNER_NAME]; ok { rCtx.Aliases[bidderCode] = adapters.ResolveOWBidder(prebidPartnerName) + isAlias = true } } if alias, ok := IsAlias(bidderCode); ok { rCtx.Aliases[bidderCode] = alias + isAlias = true } - if partnerConfig[models.PREBID_PARTNER_NAME] == models.BidderVASTBidder { + if isAlias || partnerConfig[models.PREBID_PARTNER_NAME] == models.BidderVASTBidder { updateAliasGVLIds(aliasgvlids, bidderCode, partnerConfig) } + revShare := models.GetRevenueShare(rCtx.PartnerConfigMap[partnerID]) + requestExt.Prebid.BidAdjustmentFactors[bidderCode] = models.GetBidAdjustmentValue(revShare) serviceSideBidderPresent = true } // for(rctx.PartnerConfigMap @@ -522,7 +532,7 @@ func (m *OpenWrap) applyProfileChanges(rctx models.RequestCtx, bidRequest *openr bidRequest.Device.IP = rctx.IP bidRequest.Device.Language = getValidLanguage(bidRequest.Device.Language) - validateDevice(bidRequest.Device) + amendDeviceObject(bidRequest.Device, &rctx.DeviceCtx) if bidRequest.User == nil { bidRequest.User = &openrtb2.User{} @@ -836,9 +846,9 @@ func getPageURL(bidRequest *openrtb2.BidRequest) string { func getVASTEventMacros(rctx models.RequestCtx) map[string]string { macros := map[string]string{ string(models.MacroProfileID): fmt.Sprintf("%d", rctx.ProfileID), - string(models.MacroProfileVersionID): fmt.Sprintf("%d", rctx.DisplayID), + string(models.MacroProfileVersionID): fmt.Sprintf("%d", rctx.DisplayVersionID), string(models.MacroUnixTimeStamp): fmt.Sprintf("%d", rctx.StartTime), - string(models.MacroPlatform): fmt.Sprintf("%d", rctx.DevicePlatform), + string(models.MacroPlatform): fmt.Sprintf("%d", rctx.DeviceCtx.Platform), string(models.MacroWrapperImpressionID): rctx.LoggerImpressionID, } diff --git a/modules/pubmatic/openwrap/beforevalidationhook_test.go b/modules/pubmatic/openwrap/beforevalidationhook_test.go index b56e9e4a458..6327a440e80 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook_test.go +++ b/modules/pubmatic/openwrap/beforevalidationhook_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "net/http" + "sort" "testing" "github.com/golang/mock/gomock" @@ -12,7 +13,7 @@ import ( "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/prebid-server/v2/hooks/hookanalytics" "github.com/prebid/prebid-server/v2/hooks/hookstage" - ow_adapters "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adapters" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adapters" "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" @@ -164,11 +165,13 @@ func TestGetVASTEventMacros(t *testing.T) { args: args{ rctx: models.RequestCtx{ ProfileID: 1234, - DisplayID: 1234, + DisplayVersionID: 1234, StartTime: 1234, - DevicePlatform: 1234, LoggerImpressionID: "1234", SSAI: "", + DeviceCtx: models.DeviceCtx{ + Platform: 1234, + }, }, }, want: map[string]string{ @@ -184,11 +187,13 @@ func TestGetVASTEventMacros(t *testing.T) { args: args{ rctx: models.RequestCtx{ ProfileID: 1234, - DisplayID: 1234, + DisplayVersionID: 1234, StartTime: 1234, - DevicePlatform: 1234, LoggerImpressionID: "1234", SSAI: "1234", + DeviceCtx: models.DeviceCtx{ + Platform: 1234, + }, }, }, want: map[string]string{ @@ -1851,6 +1856,7 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { defer ctrl.Finish() mockCache := mock_cache.NewMockCache(ctrl) mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + adapters.InitBidders("./static/bidder-params/") type fields struct { cfg config.Config @@ -1864,13 +1870,14 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { bidrequest json.RawMessage } tests := []struct { - name string - fields fields - args args - want hookstage.HookResult[hookstage.BeforeValidationRequestPayload] - setup func() - isRequestNotRejected bool - wantErr bool + name string + fields fields + args args + want hookstage.HookResult[hookstage.BeforeValidationRequestPayload] + setup func() + wantErr bool + wantBidRequest json.RawMessage + doMutate bool }{ { name: "request_with_sshb=1", @@ -1888,6 +1895,7 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ Reject: false, }, + wantErr: false, }, { name: "empty_module_context", @@ -1965,9 +1973,10 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { mockEngine.EXPECT().RecordNobidErrPrebidServerRequests("", nbr.InvalidPublisherID) }, want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ - Reject: true, - NbrCode: nbr.InvalidPublisherID, - Errors: []string{"ErrInvalidPublisherID"}, + Reject: true, + NbrCode: nbr.InvalidPublisherID, + Errors: []string{"ErrInvalidPublisherID"}, + DebugMessages: nil, }, wantErr: true, }, @@ -2482,12 +2491,15 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { }, want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ Reject: false, + NbrCode: 0, + Message: "", ChangeSet: hookstage.ChangeSet[hookstage.BeforeValidationRequestPayload]{}, - DebugMessages: []string{"new imp: {\"123\":{\"ImpID\":\"123\",\"TagID\":\"adunit\",\"Div\":\"\",\"SlotName\":\"adunit\",\"AdUnitName\":\"adunit\",\"Secure\":0,\"BidFloor\":0,\"BidFloorCur\":\"\",\"IsRewardInventory\":null,\"Banner\":true,\"Video\":{\"mimes\":[\"video/mp4\",\"video/mpeg\"],\"w\":640,\"h\":480},\"Native\":{\"request\":\"\"},\"IncomingSlots\":[\"700x900\",\"728x90\",\"300x250\",\"640x480v\"],\"Type\":\"video\",\"Bidders\":{\"appnexus\":{\"PartnerID\":2,\"PrebidBidderCode\":\"appnexus\",\"MatchedSlot\":\"adunit@700x900\",\"KGP\":\"_AU_@_W_x_H_\",\"KGPV\":\"\",\"IsRegex\":false,\"Params\":{\"placementId\":0,\"site\":\"12313\",\"adtag\":\"45343\"},\"VASTTagFlag\":false,\"VASTTagFlags\":null}},\"NonMapped\":{},\"NewExt\":{\"data\":{\"pbadslot\":\"adunit\"},\"prebid\":{\"bidder\":{\"appnexus\":{\"placementId\":0,\"site\":\"12313\",\"adtag\":\"45343\"}}}},\"BidCtx\":{},\"BannerAdUnitCtx\":{\"MatchedSlot\":\"adunit@700x900\",\"IsRegex\":false,\"MatchedRegex\":\"\",\"SelectedSlotAdUnitConfig\":{\"banner\":{\"enabled\":false}},\"AppliedSlotAdUnitConfig\":{\"banner\":{\"enabled\":false}},\"UsingDefaultConfig\":false,\"AllowedConnectionTypes\":null},\"VideoAdUnitCtx\":{\"MatchedSlot\":\"adunit@640x480\",\"IsRegex\":false,\"MatchedRegex\":\"\",\"SelectedSlotAdUnitConfig\":{\"video\":{\"enabled\":false}},\"AppliedSlotAdUnitConfig\":{\"video\":{\"enabled\":false}},\"UsingDefaultConfig\":false,\"AllowedConnectionTypes\":null},\"BidderError\":\"\",\"IsAdPodRequest\":false}}"}, - AnalyticsTags: hookanalytics.Analytics{}, + DebugMessages: []string{`new imp: {"123":{"ImpID":"123","TagID":"adunit","Div":"","SlotName":"adunit","AdUnitName":"adunit","Secure":0,"BidFloor":0,"BidFloorCur":"","IsRewardInventory":null,"Banner":true,"Video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"Native":{"request":""},"IncomingSlots":["640x480v","700x900","728x90","300x250"],"Type":"video","Bidders":{"appnexus":{"PartnerID":2,"PrebidBidderCode":"appnexus","MatchedSlot":"adunit@700x900","KGP":"_AU_@_W_x_H_","KGPV":"","IsRegex":false,"Params":{"placementId":0,"site":"12313","adtag":"45343"},"VASTTagFlag":false,"VASTTagFlags":null}},"NonMapped":{},"NewExt":{"data":{"pbadslot":"adunit"},"prebid":{"bidder":{"appnexus":{"placementId":0,"site":"12313","adtag":"45343"}}}},"BidCtx":{},"BannerAdUnitCtx":{"MatchedSlot":"adunit@700x900","IsRegex":false,"MatchedRegex":"","SelectedSlotAdUnitConfig":{"banner":{"enabled":false}},"AppliedSlotAdUnitConfig":{"banner":{"enabled":false}},"UsingDefaultConfig":false,"AllowedConnectionTypes":null},"VideoAdUnitCtx":{"MatchedSlot":"adunit@640x480","IsRegex":false,"MatchedRegex":"","SelectedSlotAdUnitConfig":{"video":{"enabled":false}},"AppliedSlotAdUnitConfig":{"video":{"enabled":false}},"UsingDefaultConfig":false,"AllowedConnectionTypes":null},"BidderError":"","IsAdPodRequest":false}}`, `new request.ext: {"prebid":{"bidadjustmentfactors":{"appnexus":1},"bidderparams":{"pubmatic":{"wiid":""}},"debug":true,"floors":{"enforcement":{"enforcepbs":true},"enabled":true},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":5,"increment":0.05},{"min":5,"max":10,"increment":0.1},{"min":10,"max":20,"increment":0.5}]},"mediatypepricegranularity":{},"includewinners":true,"includebidderkeys":true},"macros":{"[PLATFORM]":"3","[PROFILE_ID]":"1234","[PROFILE_VERSION]":"1","[UNIX_TIMESTAMP]":"0","[WRAPPER_IMPRESSION_ID]":""}}}`}, + AnalyticsTags: hookanalytics.Analytics{Activities: nil}, }, - wantErr: false, - isRequestNotRejected: true, + wantErr: false, + doMutate: true, + wantBidRequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","native":{"request":""},"tagid":"adunit","ext":{"data":{"pbadslot":"adunit"},"prebid":{"bidder":{"appnexus":{"placementId":0,"site":"12313","adtag":"45343"}}}}}],"site":{"domain":"test.com","page":"www.test.com","publisher":{"id":"5890"}},"device":{"ua":"Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36","ip":"127.0.0.1"},"user":{"id":"119208432","buyeruid":"1rwe432","yob":1980,"gender":"F","customdata":"7D75D25F-FAC9-443D-B2D1-B17FEE11E027","geo":{"country":"US","region":"CA","metro":"90001","city":"Alamo"}},"wseat":["Wseat_0","Wseat_1"],"bseat":["Bseat_0","Bseat_1"],"cur":["cur_0","cur_1"],"wlang":["Wlang_0","Wlang_1"],"bcat":["bcat_0","bcat_1"],"badv":["badv_0","badv_1"],"bapp":["bapp_0","bapp_1"],"source":{"tid":"123-456-789","ext":{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}},"ext":{"prebid":{"bidadjustmentfactors":{"appnexus":1},"bidderparams":{"pubmatic":{"wiid":""}},"debug":true,"floors":{"enforcement":{"enforcepbs":true},"enabled":true},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":5,"increment":0.05},{"min":5,"max":10,"increment":0.1},{"min":10,"max":20,"increment":0.5}]},"mediatypepricegranularity":{},"includewinners":true,"includebidderkeys":true},"macros":{"[PLATFORM]":"3","[PROFILE_ID]":"1234","[PROFILE_VERSION]":"1","[UNIX_TIMESTAMP]":"0","[WRAPPER_IMPRESSION_ID]":""}}}}`), }, { name: "no_serviceSideBidderPresent", @@ -2560,6 +2572,119 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { }, wantErr: false, }, + { + name: "if-partner-is-alias-update-req.ext.prebid.aliasgvlid", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + ProfileID: 1234, + DisplayID: 1, + SSAuction: -1, + Platform: "in-app", + 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: "1234", + Endpoint: models.EndpointV25, + SeatNonBids: make(map[string][]openrtb_ext.NonBid), + MetricsEngine: mockEngine, + }, + }, + }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"tagid":"adunit","bidfloor":4.3,"bidfloorcur":"USD","ext":{"bidder":{"pubmatic":{"keywords":[{"key":"pmzoneid","value":["val1","val2"]}]}},"prebid":{}}}],"site":{"domain":"test.com","page":"www.test.com","publisher":{"id":"5890"}},"device":{"ua":"Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36","ip":"123.145.167.10"},"user":{"id":"119208432","buyeruid":"1rwe432","yob":1980,"gender":"F","geo":{"country":"US","region":"CA","metro":"90001","city":"Alamo"}},"wseat":["Wseat_0","Wseat_1"],"bseat":["Bseat_0","Bseat_1"],"cur":["cur_0","cur_1"],"wlang":["Wlang_0","Wlang_1"],"bcat":["bcat_0","bcat_1"],"badv":["badv_0","badv_1"],"bapp":["bapp_0","bapp_1"],"source":{"ext":{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}},"ext":{"prebid":{},"wrapper":{"test":123,"profileid":123,"versionid":1,"wiid":"test_display_wiid"}}}`), + }, + fields: fields{ + cache: mockCache, + metricEngine: mockEngine, + }, + setup: func() { + mockCache.EXPECT().GetMappingsFromCacheV25(gomock.Any(), gomock.Any()).Return(map[string]models.SlotMapping{ + "adunit@700x900": { + SlotName: "adunit@700x900", + SlotMappings: map[string]interface{}{ + models.SITE_CACHE_KEY: "12313", + models.TAG_CACHE_KEY: "45343", + }, + }, + }).Times(3) + mockCache.EXPECT().GetSlotToHashValueMapFromCacheV25(gomock.Any(), gomock.Any()).Return(models.SlotMappingInfo{ + OrderedSlotList: []string{"adunit@700x900"}, + HashValueMap: map[string]string{ + "adunit@700x900": "1232433543534543", + }, + }).Times(3) + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + 1: { + models.PARTNER_ID: "1", + models.PREBID_PARTNER_NAME: "pubmatic2", + models.BidderCode: "pub2-alias", + models.IsAlias: "1", + models.TIMEOUT: "200", + models.KEY_GEN_PATTERN: "_AU_@_W_x_H_", + models.SERVER_SIDE_FLAG: "1", + models.VENDORID: "130", + }, + 2: { + models.PARTNER_ID: "2", + models.PREBID_PARTNER_NAME: "appnexus", + models.BidderCode: "appnexus", + models.SERVER_SIDE_FLAG: "1", + models.KEY_GEN_PATTERN: "_AU_@_W_x_H_", + models.TIMEOUT: "200", + models.VENDORID: "100", + }, + 3: { + models.PARTNER_ID: "3", + models.PREBID_PARTNER_NAME: "districtm", + models.BidderCode: "dm-alias", + models.IsAlias: "1", + models.TIMEOUT: "200", + models.KEY_GEN_PATTERN: "_AU_@_W_x_H_", + models.SERVER_SIDE_FLAG: "1", + models.VENDORID: "99", + }, + -1: { + models.DisplayVersionID: "1", + models.PLATFORM_KEY: models.PLATFORM_APP, + }, + }, nil) + mockCache.EXPECT().GetAdunitConfigFromCache(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&adunitconfig.AdUnitConfig{}) + //prometheus metrics + mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") + mockEngine.EXPECT().RecordPublisherRequests(rctx.Endpoint, "5890", rctx.Platform) + mockEngine.EXPECT().RecordPlatformPublisherPartnerReqStats(rctx.Platform, "5890", "pub2-alias") + mockEngine.EXPECT().RecordPlatformPublisherPartnerReqStats(rctx.Platform, "5890", "appnexus") + mockEngine.EXPECT().RecordPlatformPublisherPartnerReqStats(rctx.Platform, "5890", "dm-alias") + }, + want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ + Reject: false, + NbrCode: 0, + ChangeSet: hookstage.ChangeSet[hookstage.BeforeValidationRequestPayload]{}, + DebugMessages: []string{`new imp: {"123":{"ImpID":"123","TagID":"adunit","Div":"","SlotName":"adunit","AdUnitName":"adunit","Secure":0,"BidFloor":4.3,"BidFloorCur":"USD","IsRewardInventory":null,"Banner":true,"Video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"Native":null,"IncomingSlots":["640x480v","700x900","728x90","300x250"],"Type":"video","Bidders":{"appnexus":{"PartnerID":2,"PrebidBidderCode":"appnexus","MatchedSlot":"adunit@700x900","KGP":"_AU_@_W_x_H_","KGPV":"","IsRegex":false,"Params":{"placementId":0,"adtag":"45343","site":"12313"},"VASTTagFlag":false,"VASTTagFlags":null},"dm-alias":{"PartnerID":3,"PrebidBidderCode":"districtm","MatchedSlot":"adunit@700x900","KGP":"_AU_@_W_x_H_","KGPV":"","IsRegex":false,"Params":{"placementId":0,"site":"12313","adtag":"45343"},"VASTTagFlag":false,"VASTTagFlags":null},"pub2-alias":{"PartnerID":1,"PrebidBidderCode":"pubmatic2","MatchedSlot":"adunit@700x900","KGP":"_AU_@_W_x_H_","KGPV":"","IsRegex":false,"Params":{"publisherId":"5890","adSlot":"adunit@700x900","wrapper":{"version":1,"profile":1234}},"VASTTagFlag":false,"VASTTagFlags":null}},"NonMapped":{},"NewExt":{"data":{"pbadslot":"adunit"},"prebid":{"bidder":{"appnexus":{"placementId":0,"adtag":"45343","site":"12313"},"dm-alias":{"placementId":0,"site":"12313","adtag":"45343"},"pub2-alias":{"publisherId":"5890","adSlot":"adunit@700x900","wrapper":{"version":1,"profile":1234}}}}},"BidCtx":{},"BannerAdUnitCtx":{"MatchedSlot":"","IsRegex":false,"MatchedRegex":"","SelectedSlotAdUnitConfig":null,"AppliedSlotAdUnitConfig":null,"UsingDefaultConfig":false,"AllowedConnectionTypes":null},"VideoAdUnitCtx":{"MatchedSlot":"","IsRegex":false,"MatchedRegex":"","SelectedSlotAdUnitConfig":null,"AppliedSlotAdUnitConfig":null,"UsingDefaultConfig":false,"AllowedConnectionTypes":null},"BidderError":"","IsAdPodRequest":false}}`, `new request.ext: {"prebid":{"aliases":{"dm-alias":"appnexus","pub2-alias":"pubmatic"},"aliasgvlids":{"dm-alias":99,"pub2-alias":130},"bidadjustmentfactors":{"appnexus":1,"dm-alias":1,"pub2-alias":1},"bidderparams":{"pubmatic":{"wiid":""}},"debug":true,"floors":{"enforcement":{"enforcepbs":true},"enabled":true},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":5,"increment":0.05},{"min":5,"max":10,"increment":0.1},{"min":10,"max":20,"increment":0.5}]},"mediatypepricegranularity":{},"includewinners":true,"includebidderkeys":true},"macros":{"[PLATFORM]":"3","[PROFILE_ID]":"1234","[PROFILE_VERSION]":"1","[UNIX_TIMESTAMP]":"0","[WRAPPER_IMPRESSION_ID]":""}}}`}, + AnalyticsTags: hookanalytics.Analytics{}, + }, + wantBidRequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"tagid":"adunit","bidfloor":4.3,"bidfloorcur":"USD","ext":{"data":{"pbadslot":"adunit"},"prebid":{"bidder":{"appnexus":{"placementId":0,"adtag":"45343","site":"12313"},"dm-alias":{"placementId":0,"site":"12313","adtag":"45343"},"pub2-alias":{"publisherId":"5890","adSlot":"adunit@700x900","wrapper":{"version":1,"profile":1234}}}}}}],"site":{"domain":"test.com","page":"www.test.com","publisher":{"id":"5890"}},"device":{"ua":"Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36","ip":"127.0.0.1"},"user":{"id":"119208432","buyeruid":"1rwe432","yob":1980,"gender":"F","customdata":"7D75D25F-FAC9-443D-B2D1-B17FEE11E027","geo":{"country":"US","region":"CA","metro":"90001","city":"Alamo"}},"wseat":["Wseat_0","Wseat_1"],"bseat":["Bseat_0","Bseat_1"],"cur":["cur_0","cur_1"],"wlang":["Wlang_0","Wlang_1"],"bcat":["bcat_0","bcat_1"],"badv":["badv_0","badv_1"],"bapp":["bapp_0","bapp_1"],"source":{"tid":"123-456-789","ext":{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}},"ext":{"prebid":{"aliases":{"dm-alias":"appnexus","pub2-alias":"pubmatic"},"aliasgvlids":{"dm-alias":99,"pub2-alias":130},"bidadjustmentfactors":{"appnexus":1,"dm-alias":1,"pub2-alias":1},"bidderparams":{"pubmatic":{"wiid":""}},"debug":true,"floors":{"enforcement":{"enforcepbs":true},"enabled":true},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":5,"increment":0.05},{"min":5,"max":10,"increment":0.1},{"min":10,"max":20,"increment":0.5}]},"mediatypepricegranularity":{},"includewinners":true,"includebidderkeys":true},"macros":{"[PLATFORM]":"3","[PROFILE_ID]":"1234","[PROFILE_VERSION]":"1","[UNIX_TIMESTAMP]":"0","[WRAPPER_IMPRESSION_ID]":""}}}}`), + wantErr: false, + doMutate: true, + }, { name: "happy_path_request_not_rejected_and_successfully_updted_from_DB", args: args{ @@ -2643,11 +2768,12 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { Reject: false, NbrCode: 0, ChangeSet: hookstage.ChangeSet[hookstage.BeforeValidationRequestPayload]{}, - DebugMessages: []string{"new imp: {\"123\":{\"ImpID\":\"\",\"TagID\":\"adunit\",\"Div\":\"\",\"Secure\":0,\"IsRewardInventory\":null,\"Banner\":true,\"Video\":{\"mimes\":[\"video/mp4\",\"video/mpeg\"],\"w\":640,\"h\":480},\"IncomingSlots\":[\"700x900\",\"728x90\",\"300x250\",\"640x480v\"],\"Type\":\"video\",\"Bidders\":{\"appnexus\":{\"PartnerID\":2,\"PrebidBidderCode\":\"appnexus\",\"MatchedSlot\":\"adunit@700x900\",\"KGP\":\"_AU_@_W_x_H_\",\"KGPV\":\"\",\"IsRegex\":false,\"Params\":{\"placementId\":0,\"site\":\"12313\",\"adtag\":\"45343\"}}},\"NonMapped\":{},\"NewExt\":{\"prebid\":{\"bidder\":{\"appnexus\":{\"placementId\":0,\"site\":\"12313\",\"adtag\":\"45343\"}}}},\"BidCtx\":{},\"BannerAdUnitCtx\":{\"MatchedSlot\":\"\",\"IsRegex\":false,\"MatchedRegex\":\"\",\"SelectedSlotAdUnitConfig\":null,\"AppliedSlotAdUnitConfig\":null,\"UsingDefaultConfig\":false,\"AllowedConnectionTypes\":null},\"VideoAdUnitCtx\":{\"MatchedSlot\":\"\",\"IsRegex\":false,\"MatchedRegex\":\"\",\"SelectedSlotAdUnitConfig\":null,\"AppliedSlotAdUnitConfig\":null,\"UsingDefaultConfig\":false,\"AllowedConnectionTypes\":null}}}", "new request.ext: {\"prebid\":{\"bidderparams\":{\"pubmatic\":{\"wiid\":\"\"}},\"debug\":true,\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"min\":0,\"max\":5,\"increment\":0.05},{\"min\":5,\"max\":10,\"increment\":0.1},{\"min\":10,\"max\":20,\"increment\":0.5}]},\"mediatypepricegranularity\":{},\"includewinners\":true,\"includebidderkeys\":true},\"floors\":{\"enforcement\":{\"enforcepbs\":true},\"enabled\":true},\"macros\":{\"[PLATFORM]\":\"3\",\"[PROFILE_ID]\":\"1234\",\"[PROFILE_VERSION]\":\"1\",\"[UNIX_TIMESTAMP]\":\"0\",\"[WRAPPER_IMPRESSION_ID]\":\"\"}}}"}, + DebugMessages: []string{`new imp: {"123":{"ImpID":"123","TagID":"adunit","Div":"","SlotName":"adunit","AdUnitName":"adunit","Secure":0,"BidFloor":4.3,"BidFloorCur":"USD","IsRewardInventory":null,"Banner":true,"Video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"Native":null,"IncomingSlots":["300x250","640x480v","700x900","728x90"],"Type":"video","Bidders":{"appnexus":{"PartnerID":2,"PrebidBidderCode":"appnexus","MatchedSlot":"adunit@700x900","KGP":"_AU_@_W_x_H_","KGPV":"","IsRegex":false,"Params":{"placementId":0,"site":"12313","adtag":"45343"},"VASTTagFlag":false,"VASTTagFlags":null}},"NonMapped":{},"NewExt":{"data":{"pbadslot":"adunit"},"prebid":{"bidder":{"appnexus":{"placementId":0,"site":"12313","adtag":"45343"}}}},"BidCtx":{},"BannerAdUnitCtx":{"MatchedSlot":"","IsRegex":false,"MatchedRegex":"","SelectedSlotAdUnitConfig":null,"AppliedSlotAdUnitConfig":null,"UsingDefaultConfig":false,"AllowedConnectionTypes":null},"VideoAdUnitCtx":{"MatchedSlot":"","IsRegex":false,"MatchedRegex":"","SelectedSlotAdUnitConfig":null,"AppliedSlotAdUnitConfig":null,"UsingDefaultConfig":false,"AllowedConnectionTypes":null},"BidderError":"","IsAdPodRequest":false}}`, `new request.ext: {"prebid":{"bidadjustmentfactors":{"appnexus":1},"bidderparams":{"pubmatic":{"wiid":""}},"debug":true,"floors":{"enforcement":{"enforcepbs":true},"enabled":true},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":5,"increment":0.05},{"min":5,"max":10,"increment":0.1},{"min":10,"max":20,"increment":0.5}]},"mediatypepricegranularity":{},"includewinners":true,"includebidderkeys":true},"macros":{"[PLATFORM]":"3","[PROFILE_ID]":"1234","[PROFILE_VERSION]":"1","[UNIX_TIMESTAMP]":"0","[WRAPPER_IMPRESSION_ID]":""}}}`}, AnalyticsTags: hookanalytics.Analytics{}, }, - wantErr: false, - isRequestNotRejected: true, + wantBidRequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"tagid":"adunit","bidfloor":4.3,"bidfloorcur":"USD","ext":{"data":{"pbadslot":"adunit"},"prebid":{"bidder":{"appnexus":{"placementId":0,"site":"12313","adtag":"45343"}}}}}],"site":{"domain":"test.com","page":"www.test.com","publisher":{"id":"5890"}},"device":{"ua":"Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36","ip":"127.0.0.1"},"user":{"id":"119208432","buyeruid":"1rwe432","yob":1980,"gender":"F","customdata":"7D75D25F-FAC9-443D-B2D1-B17FEE11E027","geo":{"country":"US","region":"CA","metro":"90001","city":"Alamo"}},"wseat":["Wseat_0","Wseat_1"],"bseat":["Bseat_0","Bseat_1"],"cur":["cur_0","cur_1"],"wlang":["Wlang_0","Wlang_1"],"bcat":["bcat_0","bcat_1"],"badv":["badv_0","badv_1"],"bapp":["bapp_0","bapp_1"],"source":{"tid":"123-456-789","ext":{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}},"ext":{"prebid":{"bidadjustmentfactors":{"appnexus":1},"bidderparams":{"pubmatic":{"wiid":""}},"debug":true,"floors":{"enforcement":{"enforcepbs":true},"enabled":true},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":5,"increment":0.05},{"min":5,"max":10,"increment":0.1},{"min":10,"max":20,"increment":0.5}]},"mediatypepricegranularity":{},"includewinners":true,"includebidderkeys":true},"macros":{"[PLATFORM]":"3","[PROFILE_ID]":"1234","[PROFILE_VERSION]":"1","[UNIX_TIMESTAMP]":"0","[WRAPPER_IMPRESSION_ID]":""}}}}`), + wantErr: false, + doMutate: true, }, { name: "prebid-validation-errors-imp-missing", @@ -2705,27 +2831,149 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { if tt.setup != nil { tt.setup() } - ow_adapters.InitBidders(tt.fields.cfg) m := OpenWrap{ cfg: tt.fields.cfg, cache: tt.fields.cache, metricEngine: tt.fields.metricEngine, } + bidrequest := &openrtb2.BidRequest{} json.Unmarshal(tt.args.bidrequest, bidrequest) tt.args.payload.BidRequest = bidrequest got, err := m.handleBeforeValidationHook(tt.args.ctx, tt.args.moduleCtx, tt.args.payload) - if tt.isRequestNotRejected { - assert.Equal(t, tt.want.Reject, got.Reject) - assert.Equal(t, tt.want.NbrCode, got.NbrCode) - assert.NotEmpty(t, tt.want.DebugMessages) - return + assert.Equal(t, tt.wantErr, err != nil) + assert.Equal(t, tt.want.Reject, got.Reject) + assert.Equal(t, tt.want.NbrCode, got.NbrCode) + for i := 0; i < len(got.DebugMessages); i++ { + gotDebugMessage, _ := json.Marshal(got.DebugMessages[i]) + wantDebugMessage, _ := json.Marshal(tt.want.DebugMessages[i]) + sort.Slice(gotDebugMessage, func(i, j int) bool { + return gotDebugMessage[i] < gotDebugMessage[j] + }) + sort.Slice(wantDebugMessage, func(i, j int) bool { + return wantDebugMessage[i] < wantDebugMessage[j] + }) + assert.Equal(t, wantDebugMessage, gotDebugMessage) } - if (err != nil) != tt.wantErr { - assert.Equal(t, tt.wantErr, err != nil) - return + + 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) + gotBidRequest, _ := json.Marshal(result.BidRequest) + assert.JSONEq(t, string(tt.wantBidRequest), string(gotBidRequest)) + } } - assert.Equal(t, tt.want, got) + }) + } +} + +func TestUserAgent_handleBeforeValidationHook(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockCache := mock_cache.NewMockCache(ctrl) + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + + type fields struct { + cfg config.Config + cache cache.Cache + metricEngine metrics.MetricsEngine + } + type args struct { + ctx context.Context + moduleCtx hookstage.ModuleInvocationContext + payload hookstage.BeforeValidationRequestPayload + bidrequest json.RawMessage + } + type want struct { + rctx *models.RequestCtx + error bool + } + tests := []struct { + name string + fields fields + args args + want want + setup func() + }{ + { + name: "bidRequest.Device.UA_is_present", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": rctx, + }, + }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"tagid":"adunit","ext":{"wrapper":{"div":"div"},"bidder":{"pubmatic":{"keywords":[{"key":"pmzoneid","value":["val1","val2"]}]}},"prebid":{}}}],"site":{"domain":"test.com","page":"www.test.com","publisher":{"id":"5890"}},"device":{"ua":"Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36","ip":"123.145.167.10"},"user":{"id":"119208432","buyeruid":"1rwe432","yob":1980,"gender":"F","geo":{"country":"US","region":"CA","metro":"90001","city":"Alamo"}},"wseat":["Wseat_0","Wseat_1"],"bseat":["Bseat_0","Bseat_1"],"cur":["cur_0","cur_1"],"wlang":["Wlang_0","Wlang_1"],"bcat":["bcat_0","bcat_1"],"badv":["badv_0","badv_1"],"bapp":["bapp_0","bapp_1"],"source":{"ext":{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}},"ext":1}`), + }, + fields: fields{ + cache: mockCache, + metricEngine: mockEngine, + }, + setup: func() { + mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") + mockEngine.EXPECT().RecordBadRequests(rctx.Endpoint, getPubmaticErrorCode(nbr.InvalidRequestExt)) + mockEngine.EXPECT().RecordNobidErrPrebidServerRequests("5890", nbr.InvalidRequestExt) + }, + want: want{ + rctx: &models.RequestCtx{ + UA: "Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36", + }, + error: true, + }, + }, + { + name: "bidRequest.Device.UA_is_absent", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": rctx, + }, + }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"tagid":"adunit","ext":{"wrapper":{"div":"div"},"bidder":{"pubmatic":{"keywords":[{"key":"pmzoneid","value":["val1","val2"]}]}},"prebid":{}}}],"site":{"domain":"test.com","page":"www.test.com","publisher":{"id":"5890"}},"device":{"ip":"123.145.167.10"},"user":{"id":"119208432","buyeruid":"1rwe432","yob":1980,"gender":"F","geo":{"country":"US","region":"CA","metro":"90001","city":"Alamo"}},"wseat":["Wseat_0","Wseat_1"],"bseat":["Bseat_0","Bseat_1"],"cur":["cur_0","cur_1"],"wlang":["Wlang_0","Wlang_1"],"bcat":["bcat_0","bcat_1"],"badv":["badv_0","badv_1"],"bapp":["bapp_0","bapp_1"],"source":{"ext":{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}},"ext":1}`), + }, + fields: fields{ + cache: mockCache, + metricEngine: mockEngine, + }, + setup: func() { + mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") + mockEngine.EXPECT().RecordBadRequests(rctx.Endpoint, getPubmaticErrorCode(nbr.InvalidRequestExt)) + mockEngine.EXPECT().RecordNobidErrPrebidServerRequests("5890", nbr.InvalidRequestExt) + }, + want: want{ + rctx: &models.RequestCtx{ + UA: "go-test", + PubID: 1, + }, + error: true, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.setup != nil { + tt.setup() + } + adapters.InitBidders("./static/bidder-params/") + m := OpenWrap{ + cfg: tt.fields.cfg, + cache: tt.fields.cache, + metricEngine: tt.fields.metricEngine, + } + tt.args.payload.BidRequest = &openrtb2.BidRequest{} + json.Unmarshal(tt.args.bidrequest, tt.args.payload.BidRequest) + + _, err := m.handleBeforeValidationHook(tt.args.ctx, tt.args.moduleCtx, tt.args.payload) + assert.Equal(t, tt.want.error, err != nil, "mismatched error received from handleBeforeValidationHook") + iRctx := tt.args.moduleCtx.ModuleContext["rctx"] + assert.Equal(t, tt.want.rctx == nil, iRctx == nil, "mismatched rctx received from handleBeforeValidationHook") + gotRctx := iRctx.(models.RequestCtx) + assert.Equal(t, tt.want.rctx.UA, gotRctx.UA, "mismatched rctx.UA received from handleBeforeValidationHook") }) } } diff --git a/modules/pubmatic/openwrap/bidderparams/common.go b/modules/pubmatic/openwrap/bidderparams/common.go index 81ea475d9f3..335194bb5ce 100644 --- a/modules/pubmatic/openwrap/bidderparams/common.go +++ b/modules/pubmatic/openwrap/bidderparams/common.go @@ -25,6 +25,7 @@ var ignoreKeys = map[string]bool{ models.THROTTLE: true, models.BidderCode: true, models.IsAlias: true, + models.VENDORID: true, } func getSlotMeta(rctx models.RequestCtx, cache cache.Cache, bidRequest openrtb2.BidRequest, imp openrtb2.Imp, impExt models.ImpExtension, partnerID int) ([]string, map[string]models.SlotMapping, models.SlotMappingInfo, [][2]int64) { diff --git a/modules/pubmatic/openwrap/bidderparams/vast.go b/modules/pubmatic/openwrap/bidderparams/vast.go index c172b286f84..e64a3b9250d 100644 --- a/modules/pubmatic/openwrap/bidderparams/vast.go +++ b/modules/pubmatic/openwrap/bidderparams/vast.go @@ -33,7 +33,19 @@ func PrepareVASTBidderParams(rctx models.RequestCtx, cache cache.Cache, bidReque return "", nil, nil, err } - bidParams := adapters.PrepareVASTBidderParamJSON(&bidRequest, &imp, pubVASTTags, matchedSlotKeys, slotMap, adpodExt) + // NYC_TODO: + //setting flagmap + // bidderWrapper := &BidderWrapper{VASTagFlags: make(map[string]bool)} + // for _, key := range matchedSlotKeys { + // bidderWrapper.VASTagFlags[key] = false + // } + // impWrapper.Bidder[bidderCode] = bidderWrapper + var bidParams json.RawMessage + if imp.Video != nil { + bidParams = adapters.PrepareVASTBidderParamJSON(pubVASTTags, matchedSlotKeys, slotMap) + } else { + bidParams = nil + } /* Sample Values diff --git a/modules/pubmatic/openwrap/database/mysql/partner_config.go b/modules/pubmatic/openwrap/database/mysql/partner_config.go index 4089900a7db..97d7e33cf2f 100644 --- a/modules/pubmatic/openwrap/database/mysql/partner_config.go +++ b/modules/pubmatic/openwrap/database/mysql/partner_config.go @@ -40,10 +40,10 @@ func (db *mySqlDB) getActivePartnerConfigurations(versionID int) (map[int]map[st partnerConfigMap := make(map[int]map[string]string, 0) for rows.Next() { var ( - keyName, value, prebidPartnerName, bidderCode string - partnerID, entityTypeID, testConfig, isAlias int + keyName, value, prebidPartnerName, bidderCode string + partnerID, entityTypeID, testConfig, isAlias, vendorID int ) - if err := rows.Scan(&partnerID, &prebidPartnerName, &bidderCode, &isAlias, &entityTypeID, &testConfig, &keyName, &value); err != nil { + if err := rows.Scan(&partnerID, &prebidPartnerName, &bidderCode, &isAlias, &entityTypeID, &testConfig, &vendorID, &keyName, &value); err != nil { continue } @@ -65,6 +65,9 @@ func (db *mySqlDB) getActivePartnerConfigurations(versionID int) (map[int]map[st partnerConfigMap[partnerID][models.PREBID_PARTNER_NAME] = prebidPartnerName partnerConfigMap[partnerID][models.BidderCode] = bidderCode partnerConfigMap[partnerID][models.IsAlias] = strconv.Itoa(isAlias) + if prebidPartnerName != models.BidderVASTBidder { + partnerConfigMap[partnerID][models.VENDORID] = strconv.Itoa(vendorID) + } } } diff --git a/modules/pubmatic/openwrap/database/mysql/partner_config_test.go b/modules/pubmatic/openwrap/database/mysql/partner_config_test.go index 061880ff2d8..331b61a5811 100644 --- a/modules/pubmatic/openwrap/database/mysql/partner_config_test.go +++ b/modules/pubmatic/openwrap/database/mysql/partner_config_test.go @@ -111,6 +111,7 @@ func Test_mySqlDB_GetActivePartnerConfigurations(t *testing.T) { "serverSideEnabled": "1", "isAlias": "0", "partnerId": "101", + "vendorId": "76", }, -1: { "bidderCode": "ALL", @@ -120,6 +121,7 @@ func Test_mySqlDB_GetActivePartnerConfigurations(t *testing.T) { "partnerId": "-1", "displayVersionId": "9", "platform": "display", + "vendorId": "-1", }, }, wantErr: false, @@ -132,12 +134,12 @@ func Test_mySqlDB_GetActivePartnerConfigurations(t *testing.T) { rowsWrapperVersion := sqlmock.NewRows([]string{"versionId", "displayVersionId"}).AddRow("251", "9") mock.ExpectQuery(regexp.QuoteMeta("^SELECT (.+) FROM wrapper_version (.+) LIVE")).WithArgs(19109, 5890).WillReturnRows(rowsWrapperVersion) - rowsPartnerConfig := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "keyName", "value"}). - AddRow("-1", "ALL", "ALL", 0, -1, 0, "platform", "display"). - AddRow("-1", "ALL", "ALL", 0, -1, 0, "gdpr", "0"). - AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, "kgp", "_AU_@_W_x_H_"). - AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, "timeout", "200"). - AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, "serverSideEnabled", "1") + rowsPartnerConfig := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "vendorId", "keyName", "value"}). + AddRow("-1", "ALL", "ALL", 0, -1, 0, -1, "platform", "display"). + AddRow("-1", "ALL", "ALL", 0, -1, 0, -1, "gdpr", "0"). + AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, 76, "kgp", "_AU_@_W_x_H_"). + AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, 76, "timeout", "200"). + AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, 76, "serverSideEnabled", "1") mock.ExpectQuery(regexp.QuoteMeta("^SELECT (.+) FROM wrapper_config_map (.+)")).WillReturnRows(rowsPartnerConfig) return db }, @@ -168,6 +170,7 @@ func Test_mySqlDB_GetActivePartnerConfigurations(t *testing.T) { "serverSideEnabled": "1", "isAlias": "0", "partnerId": "101", + "vendorId": "76", }, -1: { "bidderCode": "ALL", @@ -177,6 +180,7 @@ func Test_mySqlDB_GetActivePartnerConfigurations(t *testing.T) { "partnerId": "-1", "displayVersionId": "9", "platform": "display", + "vendorId": "-1", }, }, wantErr: false, @@ -189,12 +193,81 @@ func Test_mySqlDB_GetActivePartnerConfigurations(t *testing.T) { rowsWrapperVersion := sqlmock.NewRows([]string{"versionId", "displayVersionId"}).AddRow("251", "9") mock.ExpectQuery(regexp.QuoteMeta("^SELECT (.+) FROM wrapper_version (.+)")).WithArgs(19109, 3, 5890).WillReturnRows(rowsWrapperVersion) - rowsPartnerConfig := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "keyName", "value"}). - AddRow("-1", "ALL", "ALL", 0, -1, 0, "platform", "display"). - AddRow("-1", "ALL", "ALL", 0, -1, 0, "gdpr", "0"). - AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, "kgp", "_AU_@_W_x_H_"). - AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, "timeout", "200"). - AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, "serverSideEnabled", "1") + rowsPartnerConfig := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "vendorId", "keyName", "value"}). + AddRow("-1", "ALL", "ALL", 0, -1, 0, -1, "platform", "display"). + AddRow("-1", "ALL", "ALL", 0, -1, 0, -1, "gdpr", "0"). + AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, 76, "kgp", "_AU_@_W_x_H_"). + AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, 76, "timeout", "200"). + AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, 76, "serverSideEnabled", "1") + mock.ExpectQuery(regexp.QuoteMeta("^SELECT (.+) FROM wrapper_config_map (.+)")).WillReturnRows(rowsPartnerConfig) + return db + }, + }, + { + name: "vastbidder present with publisher key vendorId", + fields: fields{ + cfg: config.Database{ + Queries: config.Queries{ + DisplayVersionInnerQuery: "^SELECT (.+) FROM wrapper_version (.+)", + GetParterConfig: "^SELECT (.+) FROM wrapper_config_map (.+)", + }, + MaxDbContextTimeout: 1000, + }, + }, + args: args{ + pubID: 5890, + profileID: 19109, + displayVersion: 3, + }, + + want: map[int]map[string]string{ + 234: { + "bidderCode": "test-vastbidder", + "prebidPartnerName": "vastbidder", + "serverSideEnabled": "1", + "isAlias": "0", + "partnerId": "234", + "vendorId": "999", + }, + 101: { + "bidderCode": "pubmatic", + "prebidPartnerName": "pubmatic", + "timeout": "200", + "kgp": "_AU_@_W_x_H_", + "serverSideEnabled": "1", + "isAlias": "0", + "partnerId": "101", + "vendorId": "76", + }, + -1: { + "bidderCode": "ALL", + "prebidPartnerName": "ALL", + "gdpr": "0", + "isAlias": "0", + "partnerId": "-1", + "displayVersionId": "9", + "platform": "display", + "vendorId": "-1", + }, + }, + wantErr: false, + setup: func() *sql.DB { + db, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) + } + + rowsWrapperVersion := sqlmock.NewRows([]string{"versionId", "displayVersionId"}).AddRow("251", "9") + mock.ExpectQuery(regexp.QuoteMeta("^SELECT (.+) FROM wrapper_version (.+)")).WithArgs(19109, 3, 5890).WillReturnRows(rowsWrapperVersion) + + rowsPartnerConfig := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "vendorId", "keyName", "value"}). + AddRow("-1", "ALL", "ALL", 0, -1, 0, -1, "platform", "display"). + AddRow("-1", "ALL", "ALL", 0, -1, 0, -1, "gdpr", "0"). + AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, 76, "kgp", "_AU_@_W_x_H_"). + AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, 76, "timeout", "200"). + AddRow("101", "pubmatic", "pubmatic", 0, 3, 0, 76, "serverSideEnabled", "1"). + AddRow("234", "vastbidder", "test-vastbidder", 0, 3, 0, -1, "serverSideEnabled", "1"). + AddRow("234", "vastbidder", "test-vastbidder", 0, 3, 0, -1, "vendorId", "999") mock.ExpectQuery(regexp.QuoteMeta("^SELECT (.+) FROM wrapper_config_map (.+)")).WillReturnRows(rowsPartnerConfig) return db }, @@ -263,8 +336,8 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } - rows := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "keyName", "value"}). - AddRow("11_11", "openx", "openx", 0, -1, 0, "k1", "v1") + rows := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "vendorId", "keyName", "value"}). + AddRow("11_11", "openx", "openx", 0, -1, 0, -1, "k1", "v1") mock.ExpectQuery(regexp.QuoteMeta("^SELECT (.+) FROM wrapper_config_map (.+)")).WillReturnRows(rows) return db }, @@ -290,6 +363,7 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { "prebidPartnerName": "openx", "bidderCode": "openx", "isAlias": "0", + "vendorId": "152", }, 102: { "k1": "v2", @@ -297,6 +371,7 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { "prebidPartnerName": "pubmatic", "bidderCode": "pubmatic", "isAlias": "0", + "vendorId": "76", }, }, wantErr: false, @@ -305,10 +380,10 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } - rows := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "keyName", "value"}). - AddRow(101, "openx", "openx", 0, -1, 0, "k1", "v1"). - AddRow(101, "openx", "openx", 0, -1, 0, "k2", "v2"). - AddRow(102, "pubmatic", "pubmatic", 0, -1, 0, "k1", "v2") + rows := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "vendorId", "keyName", "value"}). + AddRow(101, "openx", "openx", 0, -1, 0, 152, "k1", "v1"). + AddRow(101, "openx", "openx", 0, -1, 0, 152, "k2", "v2"). + AddRow(102, "pubmatic", "pubmatic", 0, -1, 0, 76, "k1", "v2") mock.ExpectQuery(regexp.QuoteMeta("^SELECT (.+) FROM wrapper_config_map (.+)")).WillReturnRows(rows) return db }, @@ -335,6 +410,7 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { "prebidPartnerName": "FirstPartnerName", "bidderCode": "FirstBidder", "isAlias": "0", + "vendorId": "152", }, 102: { "k1": "v1", @@ -343,6 +419,7 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { "prebidPartnerName": "SecondPartnerName", "bidderCode": "SecondBidder", "isAlias": "0", + "vendorId": "100", }, }, wantErr: false, @@ -351,14 +428,14 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } - rows := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "keyName", "value"}). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 0, "accountId", "1234"). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, "accountId", "9876"). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 0, "pubId", "9999"). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, "pubId", "8888"). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, "rev_share", "10"). - AddRow(102, "SecondPartnerName", "SecondBidder", 0, -1, 0, "k1", "v1"). - AddRow(102, "SecondPartnerName", "SecondBidder", 0, -1, 0, "k2", "v2") + rows := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "vendorId", "keyName", "value"}). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 0, 152, "accountId", "1234"). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, 152, "accountId", "9876"). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 0, 152, "pubId", "9999"). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, 152, "pubId", "8888"). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, 152, "rev_share", "10"). + AddRow(102, "SecondPartnerName", "SecondBidder", 0, -1, 0, 100, "k1", "v1"). + AddRow(102, "SecondPartnerName", "SecondBidder", 0, -1, 0, 100, "k2", "v2") mock.ExpectQuery(regexp.QuoteMeta("^SELECT (.+) FROM wrapper_config_map (.+)")).WillReturnRows(rows) return db }, @@ -388,6 +465,7 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { "sstimeout_test": "350", "testEnabled": "1", "isAlias": "0", + "vendorId": "76", }, 102: { "k1": "v1", @@ -396,6 +474,7 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { "prebidPartnerName": "SecondPartnerName", "bidderCode": "SecondBidder", "isAlias": "0", + "vendorId": "100", }, }, wantErr: false, @@ -404,14 +483,14 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } - rows := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "keyName", "value"}). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 0, "accountId", "1234"). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 0, "sstimeout", "200"). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 1, "sstimeout", "350"). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, "pubId", "8888"). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, "rev_share", "10"). - AddRow(102, "SecondPartnerName", "SecondBidder", 0, -1, 0, "k1", "v1"). - AddRow(102, "SecondPartnerName", "SecondBidder", 0, -1, 0, "k2", "v2") + rows := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "vendorId", "keyName", "value"}). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 0, 76, "accountId", "1234"). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 0, 76, "sstimeout", "200"). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 1, 76, "sstimeout", "350"). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, 76, "pubId", "8888"). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, 76, "rev_share", "10"). + AddRow(102, "SecondPartnerName", "SecondBidder", 0, -1, 0, 100, "k1", "v1"). + AddRow(102, "SecondPartnerName", "SecondBidder", 0, -1, 0, 100, "k2", "v2") mock.ExpectQuery(regexp.QuoteMeta("^SELECT (.+) FROM wrapper_config_map (.+)")).WillReturnRows(rows) return db }, @@ -440,6 +519,7 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { "bidderCode": "FirstBidder", "sstimeout": "200", "isAlias": "0", + "vendorId": "76", }, 102: { "k1": "v1", @@ -448,6 +528,7 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { "prebidPartnerName": "SecondPartnerName", "bidderCode": "SecondBidder", "isAlias": "1", + "vendorId": "100", }, }, wantErr: false, @@ -456,13 +537,13 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } - rows := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "keyName", "value"}). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 0, "accountId", "1234"). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 0, "sstimeout", "200"). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, "pubId", "8888"). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, "rev_share", "10"). - AddRow(102, "SecondPartnerName", "SecondBidder", 1, -1, 0, "k1", "v1"). - AddRow(102, "SecondPartnerName", "SecondBidder", 1, -1, 0, "k2", "v2") + rows := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "vendorId", "keyName", "value"}). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 0, 76, "accountId", "1234"). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 1, 0, 76, "sstimeout", "200"). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, 76, "pubId", "8888"). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, 76, "rev_share", "10"). + AddRow(102, "SecondPartnerName", "SecondBidder", 1, -1, 0, 100, "k1", "v1"). + AddRow(102, "SecondPartnerName", "SecondBidder", 1, -1, 0, 100, "k2", "v2") mock.ExpectQuery(regexp.QuoteMeta("^SELECT (.+) FROM wrapper_config_map (.+)")).WillReturnRows(rows) return db }, @@ -490,6 +571,7 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { "bidderCode": "FirstBidder", "sstimeout": "200", "isAlias": "0", + "vendorId": "76", }, 102: { "k1": "v1", @@ -498,6 +580,7 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { "prebidPartnerName": "SecondPartnerName", "bidderCode": "SecondBidder", "isAlias": "0", + "vendorId": "100", }, }, wantErr: false, @@ -506,14 +589,14 @@ func Test_mySqlDB_getActivePartnerConfigurations(t *testing.T) { if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } - rows := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "keyName", "value"}). - AddRow(101, "-", "-", 0, 1, 0, "accountId", "1234"). - AddRow(101, "-", "-", 0, 1, 0, "sstimeout", "200"). - AddRow(101, "-", "-", 0, 1, 0, "pubId", "8888"). - AddRow(101, "-", "-", 0, 3, 0, "pubId", "12345"). - AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, "rev_share", "10"). - AddRow(102, "-", "-", 0, -1, 0, "k1", "v1"). - AddRow(102, "SecondPartnerName", "SecondBidder", 0, -1, 0, "k2", "v2") + rows := sqlmock.NewRows([]string{"partnerId", "prebidPartnerName", "bidderCode", "isAlias", "entityTypeID", "testConfig", "vendorId", "keyName", "value"}). + AddRow(101, "-", "-", 0, 1, 0, 76, "accountId", "1234"). + AddRow(101, "-", "-", 0, 1, 0, 76, "sstimeout", "200"). + AddRow(101, "-", "-", 0, 1, 0, 76, "pubId", "8888"). + AddRow(101, "-", "-", 0, 3, 0, 76, "pubId", "12345"). + AddRow(101, "FirstPartnerName", "FirstBidder", 0, 3, 0, 76, "rev_share", "10"). + AddRow(102, "-", "-", 0, -1, 0, 100, "k1", "v1"). + AddRow(102, "SecondPartnerName", "SecondBidder", 0, -1, 0, 100, "k2", "v2") mock.ExpectQuery(regexp.QuoteMeta("^SELECT (.+) FROM wrapper_config_map (.+)")).WillReturnRows(rows) return db }, diff --git a/modules/pubmatic/openwrap/device.go b/modules/pubmatic/openwrap/device.go index a01170819a0..04e333a3ba2 100644 --- a/modules/pubmatic/openwrap/device.go +++ b/modules/pubmatic/openwrap/device.go @@ -6,43 +6,73 @@ import ( "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/v2/openrtb_ext" ) -func validateDevice(device *openrtb2.Device) { +func populateDeviceContext(dvc *models.DeviceCtx, device *openrtb2.Device) { + if device == nil { + return + } + //this is needed in determine ifa_type parameter + dvc.DeviceIFA = device.IFA + + if device.Ext == nil { + return + } + //unmarshal device ext var deviceExt models.ExtDevice - err := json.Unmarshal(device.Ext, &deviceExt) - if err != nil { + if err := json.Unmarshal(device.Ext, &deviceExt); err != nil { + return + } + dvc.Ext = &deviceExt + + //update device IFA Details + updateDeviceIFADetails(dvc) +} + +func updateDeviceIFADetails(dvc *models.DeviceCtx) { + if dvc == nil || dvc.Ext == nil { return } - if deviceExt.ExtDevice != nil { - deviceExt.IFAType = strings.TrimSpace(deviceExt.IFAType) - deviceExt.SessionID = strings.TrimSpace(deviceExt.SessionID) - - //refactor below condition - if deviceExt.IFAType != "" { - if device.IFA != "" { - if _, ok := models.DeviceIFATypeID[strings.ToLower(deviceExt.IFAType)]; !ok { - deviceExt.IFAType = "" - } - } else if deviceExt.SessionID != "" { - device.IFA = deviceExt.SessionID - deviceExt.IFAType = models.DeviceIFATypeSESSIONID - } else { + deviceExt := dvc.Ext + deviceExt.IFAType = strings.TrimSpace(deviceExt.IFAType) + deviceExt.SessionID = strings.TrimSpace(deviceExt.SessionID) + + //refactor below condition + if deviceExt.IFAType != "" { + if dvc.DeviceIFA != "" { + if _, ok := models.DeviceIFATypeID[strings.ToLower(deviceExt.IFAType)]; !ok { deviceExt.IFAType = "" } } else if deviceExt.SessionID != "" { - device.IFA = deviceExt.SessionID + dvc.DeviceIFA = deviceExt.SessionID deviceExt.IFAType = models.DeviceIFATypeSESSIONID + } else { + deviceExt.IFAType = "" } } else if deviceExt.SessionID != "" { - deviceExt.ExtDevice = &openrtb_ext.ExtDevice{ - IFAType: models.DeviceIFATypeSESSIONID, - } - device.IFA = deviceExt.SessionID + dvc.DeviceIFA = deviceExt.SessionID + deviceExt.IFAType = models.DeviceIFATypeSESSIONID + } + + if ifaTypeID, ok := models.DeviceIFATypeID[strings.ToLower(deviceExt.IFAType)]; ok { + dvc.IFATypeID = &ifaTypeID } +} - device.Ext, _ = json.Marshal(deviceExt) +func amendDeviceObject(device *openrtb2.Device, dvc *models.DeviceCtx) { + if device == nil || dvc == nil { + return + } + + //update device IFA + if len(dvc.DeviceIFA) > 0 { + device.IFA = dvc.DeviceIFA + } + + //update device extension + if dvc.Ext != nil { + device.Ext, _ = json.Marshal(dvc.Ext) + } } diff --git a/modules/pubmatic/openwrap/device_test.go b/modules/pubmatic/openwrap/device_test.go new file mode 100644 index 00000000000..bc9b22c6043 --- /dev/null +++ b/modules/pubmatic/openwrap/device_test.go @@ -0,0 +1,502 @@ +package openwrap + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/stretchr/testify/assert" +) + +func TestPopulateDeviceExt(t *testing.T) { + type args struct { + device *openrtb2.Device + } + + type want struct { + deviceCtx models.DeviceCtx + } + + tests := []struct { + name string + args args + want want + }{ + { + name: `nil_request`, + args: args{}, + want: want{ + deviceCtx: models.DeviceCtx{}, + }, + }, + { + name: `invalid_device_ext`, + args: args{ + device: &openrtb2.Device{ + Ext: json.RawMessage(`invalid ext`), + }, + }, + want: want{ + deviceCtx: models.DeviceCtx{}, + }, + }, + { + name: `ifa_present`, + args: args{ + device: &openrtb2.Device{ + IFA: `test_ifa`, + }, + }, + want: want{ + deviceCtx: models.DeviceCtx{ + DeviceIFA: `test_ifa`, + }, + }, + }, + { + name: `ifa_type_key_absent`, + args: args{ + device: &openrtb2.Device{ + IFA: `test_ifa`, + Ext: json.RawMessage(`{"anykey":"anyval"}`), + }, + }, + want: want{ + deviceCtx: models.DeviceCtx{ + DeviceIFA: `test_ifa`, + Ext: &models.ExtDevice{}, + }, + }, + }, + { + name: `invalid_data_type_for_ifa_type_key`, + args: args{ + device: &openrtb2.Device{ + IFA: `test_ifa`, + Ext: json.RawMessage(`{"ifa_type": 123}`)}, + }, + want: want{ + deviceCtx: models.DeviceCtx{ + DeviceIFA: `test_ifa`, + }, + }, + }, + { + name: `ifa_type_missing_in_DeviceIFATypeID_mapping`, + args: args{ + device: &openrtb2.Device{ + IFA: `test_ifa`, + Ext: json.RawMessage(`{"ifa_type": "anything"}`), + }, + }, + want: want{ + /* removed_invalid_ifatype */ + deviceCtx: models.DeviceCtx{ + DeviceIFA: `test_ifa`, + Ext: &models.ExtDevice{}, + }, + }, + }, + { + name: `case_insensitive_ifa_type`, + args: args{ + device: &openrtb2.Device{ + IFA: `test_ifa`, + Ext: json.RawMessage(`{"ifa_type": "DpId"}`), + }, + }, + want: want{ + deviceCtx: models.DeviceCtx{ + DeviceIFA: `test_ifa`, + IFATypeID: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: `DpId`, + }, + }, + }, + }, + }, + { + name: `valid_ifa_type`, + args: args{ + device: &openrtb2.Device{ + IFA: `test_ifa`, + Ext: json.RawMessage(`{"ifa_type": "sessionid"}`), + }, + }, + want: want{ + deviceCtx: models.DeviceCtx{ + DeviceIFA: `test_ifa`, + IFATypeID: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeSESSIONID]), + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: `sessionid`, + }, + }, + }, + }, + }, + { + name: `valid_ifa_type_missing_device_ifa`, + args: args{ + device: &openrtb2.Device{ + Ext: json.RawMessage(`{"ifa_type": "sessionid"}`), + }, + }, + want: want{ + deviceCtx: models.DeviceCtx{ + Ext: &models.ExtDevice{}, + }, + }, + }, + { + name: `invalid_device.ext.atts`, + args: args{ + device: &openrtb2.Device{ + IFA: `test_ifa`, + Ext: json.RawMessage(`{"atts": "invalid_value"}`), + }, + }, + want: want{ + deviceCtx: models.DeviceCtx{ + DeviceIFA: `test_ifa`, + }, + }, + }, + { + name: `valid_device.ext.atts`, + args: args{ + device: &openrtb2.Device{ + Ext: json.RawMessage(`{"atts": 1}`), + }, + }, + want: want{ + deviceCtx: models.DeviceCtx{ + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + ATTS: ptrutil.ToPtr(openrtb_ext.IOSAppTrackingStatusRestricted), + }, + }, + }, + }, + }, + { + name: `all_valid_ext_parameters`, + args: args{ + device: &openrtb2.Device{ + IFA: `test_ifa`, + Ext: json.RawMessage(`{"ifa_type": "sessionid","atts": 1}`), + }, + }, + want: want{ + deviceCtx: models.DeviceCtx{ + DeviceIFA: `test_ifa`, + IFATypeID: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeSESSIONID]), + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: `sessionid`, + ATTS: ptrutil.ToPtr(openrtb_ext.IOSAppTrackingStatusRestricted), + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dvc := models.DeviceCtx{} + populateDeviceContext(&dvc, tt.args.device) + assert.Equal(t, tt.want.deviceCtx, dvc) + }) + } +} + +func TestUpdateDeviceIFADetails(t *testing.T) { + type args struct { + dvc *models.DeviceCtx + } + tests := []struct { + name string + args args + want *models.DeviceCtx + }{ + { + name: `empty`, + args: args{}, + want: nil, + }, + { + name: `device_ext_nil`, + args: args{ + dvc: &models.DeviceCtx{}, + }, + want: &models.DeviceCtx{}, + }, + { + name: `device_ext_nil`, + args: args{ + dvc: &models.DeviceCtx{}, + }, + want: &models.DeviceCtx{}, + }, + { + name: `ifa_type_missing`, + args: args{ + dvc: &models.DeviceCtx{ + Ext: &models.ExtDevice{}, + }, + }, + want: &models.DeviceCtx{ + Ext: &models.ExtDevice{}, + }, + }, + { + name: `ifa_type_present_ifa_missing`, + args: args{ + dvc: &models.DeviceCtx{ + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: models.DeviceIFATypeDPID, + }, + }, + }, + }, + want: &models.DeviceCtx{ + Ext: &models.ExtDevice{}, + }, + }, + { + name: `wrong_ifa_type`, + args: args{ + dvc: &models.DeviceCtx{ + DeviceIFA: `sample_ifa_value`, + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: `wrong_ifa_type`, + }, + }, + }, + }, + want: &models.DeviceCtx{ + DeviceIFA: `sample_ifa_value`, + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{}, + }, + }, + }, + { + name: `valid_ifa_type`, + args: args{ + dvc: &models.DeviceCtx{ + DeviceIFA: `sample_ifa_value`, + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: models.DeviceIFATypeDPID, + }, + }, + }, + }, + want: &models.DeviceCtx{ + DeviceIFA: `sample_ifa_value`, + IFATypeID: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: models.DeviceIFATypeDPID, + }, + }, + }, + }, + { + name: `case_insensitive_ifa_type`, + args: args{ + dvc: &models.DeviceCtx{ + DeviceIFA: `sample_ifa_value`, + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: strings.ToUpper(models.DeviceIFATypeDPID), + }, + }, + }, + }, + want: &models.DeviceCtx{ + DeviceIFA: `sample_ifa_value`, + IFATypeID: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: strings.ToUpper(models.DeviceIFATypeDPID), + }, + }, + }, + }, + { + name: `ifa_type_present_session_id_present`, + args: args{ + dvc: &models.DeviceCtx{ + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: models.DeviceIFATypeDPID, + }, + SessionID: `sample_session_id`, + }, + }, + }, + want: &models.DeviceCtx{ + DeviceIFA: `sample_session_id`, + IFATypeID: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeSESSIONID]), + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: models.DeviceIFATypeSESSIONID, + }, + SessionID: `sample_session_id`, + }, + }, + }, + { + name: `ifa_type_present_session_id_missing`, + args: args{ + dvc: &models.DeviceCtx{ + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: models.DeviceIFATypeDPID, + }, + }, + }, + }, + want: &models.DeviceCtx{ + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{}, + }, + }, + }, + { + name: `ifa_type_missing_session_id_present`, + args: args{ + dvc: &models.DeviceCtx{ + DeviceIFA: `existing_ifa_id`, + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{}, + SessionID: `sample_session_id`, + }, + }, + }, + want: &models.DeviceCtx{ + DeviceIFA: `sample_session_id`, + IFATypeID: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeSESSIONID]), + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + IFAType: models.DeviceIFATypeSESSIONID, + }, + SessionID: `sample_session_id`, + }, + }, + }, + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + updateDeviceIFADetails(tt.args.dvc) + assert.Equal(t, tt.want, tt.args.dvc) + }) + } +} + +func TestAmendDeviceObject(t *testing.T) { + type args struct { + device *openrtb2.Device + dvc *models.DeviceCtx + } + tests := []struct { + name string + args args + want *openrtb2.Device + }{ + { + name: `any_empty`, + args: args{}, + want: nil, + }, + { + name: `update_ifa_details_1`, + args: args{ + device: &openrtb2.Device{ + UA: `sample_ua`, + }, + dvc: &models.DeviceCtx{ + DeviceIFA: `new_ifa`, + }, + }, + want: &openrtb2.Device{ + UA: `sample_ua`, + IFA: `new_ifa`, + }, + }, + { + name: `update_ifa_details_2`, + args: args{ + device: &openrtb2.Device{ + UA: `sample_ua`, + IFA: `old_ifa`, + }, + dvc: &models.DeviceCtx{ + DeviceIFA: `new_ifa`, + }, + }, + want: &openrtb2.Device{ + UA: `sample_ua`, + IFA: `new_ifa`, + }, + }, + { + name: `update_ext_1`, + args: args{ + device: &openrtb2.Device{ + UA: `sample_ua`, + IFA: `old_ifa`, + }, + dvc: &models.DeviceCtx{ + DeviceIFA: `new_ifa`, + Ext: &models.ExtDevice{ + SessionID: `sample_session`, + }, + }, + }, + want: &openrtb2.Device{ + UA: `sample_ua`, + IFA: `new_ifa`, + Ext: json.RawMessage(`{"session_id":"sample_session"}`), + }, + }, + { + name: `update_ext_2`, + args: args{ + device: &openrtb2.Device{ + UA: `sample_ua`, + IFA: `old_ifa`, + Ext: json.RawMessage(`{"extra_key":"missing"}`), + }, + dvc: &models.DeviceCtx{ + DeviceIFA: `new_ifa`, + Ext: &models.ExtDevice{ + SessionID: `sample_session`, + }, + }, + }, + want: &openrtb2.Device{ + UA: `sample_ua`, + IFA: `new_ifa`, + Ext: json.RawMessage(`{"session_id":"sample_session"}`), + }, + }, + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + amendDeviceObject(tt.args.device, tt.args.dvc) + }) + } +} diff --git a/modules/pubmatic/openwrap/endpoints/legacy/openrtb/v25/video.go b/modules/pubmatic/openwrap/endpoints/legacy/openrtb/v25/video.go index 1c895292382..4de6c265821 100644 --- a/modules/pubmatic/openwrap/endpoints/legacy/openrtb/v25/video.go +++ b/modules/pubmatic/openwrap/endpoints/legacy/openrtb/v25/video.go @@ -221,7 +221,7 @@ func ConvertVideoToAuctionRequest(payload hookstage.EntrypointPayload, result *h } if ifaType := GetValueFromRequest(values, redirectQueryParams, models.DeviceExtIfaType); ifaType != nil { - deviceExt.ExtDevice = &openrtb_ext.ExtDevice{ + deviceExt.ExtDevice = openrtb_ext.ExtDevice{ IFAType: GetString(ifaType), } } diff --git a/modules/pubmatic/openwrap/entrypointhook.go b/modules/pubmatic/openwrap/entrypointhook.go index aa669d08f48..b54ca401fdd 100644 --- a/modules/pubmatic/openwrap/entrypointhook.go +++ b/modules/pubmatic/openwrap/entrypointhook.go @@ -61,7 +61,7 @@ func (m OpenWrap) handleEntrypointHook( case "pbjs": endpoint = models.EndpointWebS2S requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body) - case "inapp": + case "owsdk": requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body, "ext", "wrapper") endpoint = models.EndpointV25 default: diff --git a/modules/pubmatic/openwrap/entrypointhook_test.go b/modules/pubmatic/openwrap/entrypointhook_test.go index c2e76a4289e..6f6bb2358b5 100644 --- a/modules/pubmatic/openwrap/entrypointhook_test.go +++ b/modules/pubmatic/openwrap/entrypointhook_test.go @@ -216,7 +216,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { wantErr: nil, }, { - name: "Valid /openrtb2/auction?source=pbjs request(ows2s)", + name: "Valid /openrtb2/auction?source=pbjs request(webs2s)", fields: fields{ cfg: config.Config{}, cache: nil, @@ -275,7 +275,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { wantErr: nil, }, { - name: "Valid /openrtb2/auction?source=pbjs request(ows2s) debug set from request body instead of queryparam", + name: "Valid /openrtb2/auction?source=pbjs request(webs2s) debug set from request body instead of queryparam", fields: fields{ cfg: config.Config{}, cache: nil, @@ -334,7 +334,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { wantErr: nil, }, { - name: "/openrtb2/auction?source=pbjs request(ows2s) without profileid", + name: "/openrtb2/auction?source=pbjs request(webs2s) without profileid", fields: fields{ cfg: config.Config{}, cache: nil, @@ -367,7 +367,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { wantErr: nil, }, { - name: "/openrtb2/auction without source=pbjs/inapp. new hybrid endpoint should not execute module", + name: "/openrtb2/auction with source!=(pbjs or owsdk). new hybrid endpoint should not execute module", fields: fields{ cfg: config.Config{}, cache: nil, @@ -429,7 +429,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { wantErr: nil, }, { - name: "valid /openrtb2/auction?source=inapp request directly on port 8000", + name: "valid /openrtb2/auction?source=owsdk request directly on port 8000", fields: fields{ cfg: config.Config{ Tracker: config.Tracker{ @@ -444,7 +444,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { miCtx: hookstage.ModuleInvocationContext{}, payload: hookstage.EntrypointPayload{ Request: func() *http.Request { - r, err := http.NewRequest("POST", "http://localhost/openrtb2/auction?source=inapp&debug=1", nil) + r, err := http.NewRequest("POST", "http://localhost/openrtb2/auction?source=owsdk&debug=1", nil) if err != nil { panic(err) } diff --git a/modules/pubmatic/openwrap/models/constants.go b/modules/pubmatic/openwrap/models/constants.go index 6fb4108d457..935bf4f92d1 100755 --- a/modules/pubmatic/openwrap/models/constants.go +++ b/modules/pubmatic/openwrap/models/constants.go @@ -334,8 +334,10 @@ const ( DataTypeArrayOfFloats = 6 DataTypeArrayOfStrings = 7 - Device = "device" - DeviceType = "deviceType" + Device = "device" + DeviceType = "deviceType" + DeviceExtIFAType = "ifa_type" + DeviceExtATTS = "atts" //constant for native tracker EventTrackers = "eventtrackers" diff --git a/modules/pubmatic/openwrap/models/device.go b/modules/pubmatic/openwrap/models/device.go index d62d2a33934..45bcfd933c4 100644 --- a/modules/pubmatic/openwrap/models/device.go +++ b/modules/pubmatic/openwrap/models/device.go @@ -57,7 +57,7 @@ const ( ) type ExtDevice struct { - *openrtb_ext.ExtDevice + openrtb_ext.ExtDevice SessionID string `json:"session_id,omitempty"` IDFV string `json:"idfv,omitempty"` } diff --git a/modules/pubmatic/openwrap/models/openwrap.go b/modules/pubmatic/openwrap/models/openwrap.go index 9fdef9b6609..6f43e40b443 100644 --- a/modules/pubmatic/openwrap/models/openwrap.go +++ b/modules/pubmatic/openwrap/models/openwrap.go @@ -55,9 +55,9 @@ type RequestCtx struct { Trace bool //tracker - PageURL string - StartTime int64 - DevicePlatform DevicePlatform + PageURL string + StartTime int64 + DeviceCtx DeviceCtx //trackers per bid Trackers map[string]OWTracker @@ -114,6 +114,14 @@ func (r RequestCtx) GetVersionLevelKey(key string) string { return v } +// DeviceCtx to cache device specific parameters +type DeviceCtx struct { + DeviceIFA string + IFATypeID *DeviceIFAType + Platform DevicePlatform + Ext *ExtDevice +} + type ImpCtx struct { ImpID string TagID string diff --git a/modules/pubmatic/openwrap/models/tracker.go b/modules/pubmatic/openwrap/models/tracker.go index c22481ba7ce..e2714b9e604 100644 --- a/modules/pubmatic/openwrap/models/tracker.go +++ b/modules/pubmatic/openwrap/models/tracker.go @@ -1,5 +1,7 @@ package models +import "github.com/prebid/prebid-server/v2/openrtb_ext" + // OWTracker vast video parameters to be injected type OWTracker struct { Tracker Tracker @@ -37,6 +39,7 @@ type Tracker struct { FloorSource *int FloorType int CustomDimensions string + ATTS *openrtb_ext.IOSAppTrackingStatus LoggerData LoggerData // need this in logger to avoid duplicate computation ImpID string `json:"-"` diff --git a/modules/pubmatic/openwrap/models/tracking.go b/modules/pubmatic/openwrap/models/tracking.go index 0d78f04ccf1..b1e362d5f3d 100644 --- a/modules/pubmatic/openwrap/models/tracking.go +++ b/modules/pubmatic/openwrap/models/tracking.go @@ -44,6 +44,7 @@ const ( TRKServerLogger = "sl" TRKDealID = "di" TRKCustomDimensions = "cds" + TRKATTS = "atts" ) // video error tracker url parameters diff --git a/modules/pubmatic/openwrap/models/utils.go b/modules/pubmatic/openwrap/models/utils.go index 2e825c230d3..9c012481981 100644 --- a/modules/pubmatic/openwrap/models/utils.go +++ b/modules/pubmatic/openwrap/models/utils.go @@ -139,19 +139,14 @@ func GetRevenueShare(partnerConfig map[string]string) float64 { func GetNetEcpm(price float64, revShare float64) float64 { if revShare == 0 { - return toFixed(price, BID_PRECISION) + return ToFixed(price, BID_PRECISION) } price = price * (1 - revShare/100) - return toFixed(price, BID_PRECISION) + return ToFixed(price, BID_PRECISION) } func GetGrossEcpm(price float64) float64 { - return toFixed(price, BID_PRECISION) -} - -func toFixed(num float64, precision int) float64 { - output := math.Pow(10, float64(precision)) - return float64(round(num*output)) / output + return ToFixed(price, BID_PRECISION) } func round(num float64) int { @@ -413,3 +408,21 @@ func GetFloorsDetails(responseExt openrtb_ext.ExtBidResponse) (floorDetails Floo } return floorDetails } + +func GetGrossEcpmFromNetEcpm(netEcpm float64, revShare float64) float64 { + + if revShare == 100 { + return 0 + } + originalBidPrice := netEcpm / (1 - revShare/100) + return ToFixed(originalBidPrice, BID_PRECISION) +} + +func GetBidAdjustmentValue(revShare float64) float64 { + return (1 - revShare/100) +} + +func ToFixed(num float64, precision int) float64 { + output := math.Pow(10, float64(precision)) + return float64(round(num*output)) / output +} diff --git a/modules/pubmatic/openwrap/models/utils_test.go b/modules/pubmatic/openwrap/models/utils_test.go index a02e0ad6e39..26f6470b316 100644 --- a/modules/pubmatic/openwrap/models/utils_test.go +++ b/modules/pubmatic/openwrap/models/utils_test.go @@ -1331,3 +1331,107 @@ func TestGetKGPSV(t *testing.T) { }) } } + +func TestGetGrossEcpmFromNetEcpm(t *testing.T) { + type args struct { + netEcpm float64 + revShare float64 + } + tests := []struct { + name string + args args + want float64 + }{ + { + name: "When netcpm is 100 and revShare is 0", + args: args{ + netEcpm: 100, + revShare: 0, + }, + want: 100, + }, + { + name: "When netcpm is 0 and revShare is 100", + args: args{ + netEcpm: 0, + revShare: 100, + }, + want: 0, + }, + { + name: "When netcpm is 100 and revShare is 50", + args: args{ + netEcpm: 100, + revShare: 50, + }, + want: 200, + }, + { + name: "When netcpm is 80 and revShare is 20", + args: args{ + netEcpm: 80, + revShare: 20, + }, + want: 100, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetGrossEcpmFromNetEcpm(tt.args.netEcpm, tt.args.revShare); got != tt.want { + t.Errorf("GetGrossEcpmFromNetEcpm() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestToFixed(t *testing.T) { + type args struct { + num float64 + precision int + } + tests := []struct { + name string + args args + want float64 + }{ + { + name: "Rounding of 0.1", + args: args{ + num: 0.1, + precision: 2, + }, + want: 0.10, + }, + { + name: "Rounding of 0.1101", + args: args{ + num: 0.1101, + precision: 2, + }, + want: 0.11, + }, + { + name: "Rounding of 0.10000000149011612", + args: args{ + num: 0.10000000149011612, + precision: 2, + }, + want: 0.10, + }, + { + name: "Rounding of 0.10000000149011612", + args: args{ + num: 0.10000000149011612, + precision: 3, + }, + want: 0.100, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ToFixed(tt.args.num, tt.args.precision); got != tt.want { + t.Errorf("toFixed() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/modules/pubmatic/openwrap/openwrap.go b/modules/pubmatic/openwrap/openwrap.go index d673aca8ee0..9c43678e3f8 100644 --- a/modules/pubmatic/openwrap/openwrap.go +++ b/modules/pubmatic/openwrap/openwrap.go @@ -58,7 +58,7 @@ func initOpenWrap(rawCfg json.RawMessage, moduleDeps moduledeps.ModuleDeps) (Ope } // NYC_TODO: remove this dependency - if err := ow_adapters.InitBidders(cfg); err != nil { + if err := ow_adapters.InitBidders("./static/bidder-params"); err != nil { return OpenWrap{}, errors.New("error while initializing bidder params") } diff --git a/modules/pubmatic/openwrap/processedauctionhook.go b/modules/pubmatic/openwrap/processedauctionhook.go index 2108f67e07a..80f79d9ab5a 100644 --- a/modules/pubmatic/openwrap/processedauctionhook.go +++ b/modules/pubmatic/openwrap/processedauctionhook.go @@ -16,12 +16,17 @@ func (m OpenWrap) HandleProcessedAuctionHook( result.ChangeSet = hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{} if len(moduleCtx.ModuleContext) == 0 { - result.DebugMessages = append(result.DebugMessages, "error: module-ctx not found in handleBeforeValidationHook()") + result.DebugMessages = append(result.DebugMessages, "error: module-ctx not found in handleProcessedAuctionHook()") return result, nil } rctx, ok := moduleCtx.ModuleContext["rctx"].(models.RequestCtx) if !ok { - result.DebugMessages = append(result.DebugMessages, "error: request-ctx not found in handleBeforeValidationHook()") + result.DebugMessages = append(result.DebugMessages, "error: request-ctx not found in handleProcessedAuctionHook()") + return result, nil + } + + //Do not execute the module for requests processed in SSHB(8001) + if rctx.Sshb == "1" || rctx.Endpoint == models.EndpointHybrid { return result, nil } diff --git a/modules/pubmatic/openwrap/processedauctionhook_test.go b/modules/pubmatic/openwrap/processedauctionhook_test.go new file mode 100644 index 00000000000..93326579f63 --- /dev/null +++ b/modules/pubmatic/openwrap/processedauctionhook_test.go @@ -0,0 +1,164 @@ +package openwrap + +import ( + "context" + "testing" + + "github.com/golang/mock/gomock" + "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestOpenWrap_HandleProcessedAuctionHook(t *testing.T) { + ctrl := gomock.NewController(t) + mockCache := mock_cache.NewMockCache(ctrl) + defer ctrl.Finish() + + type args struct { + ctx context.Context + moduleCtx hookstage.ModuleInvocationContext + payload hookstage.ProcessedAuctionRequestPayload + } + tests := []struct { + name string + args args + mutationApplied bool + want hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload] + wantErr bool + wantBidRequest *openrtb2.BidRequest + }{ + { + name: "empty module context", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{}, + payload: hookstage.ProcessedAuctionRequestPayload{}, + }, + want: hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload]{ + Reject: false, + ChangeSet: hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}, + DebugMessages: []string{"error: module-ctx not found in handleProcessedAuctionHook()"}, + AnalyticsTags: hookanalytics.Analytics{}, + }, + wantErr: false, + }, + { + name: "empty request context", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: map[string]interface{}{ + "rctx": nil, + }, + }, + }, + want: hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload]{ + Reject: false, + ChangeSet: hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}, + DebugMessages: []string{"error: request-ctx not found in handleProcessedAuctionHook()"}, + AnalyticsTags: hookanalytics.Analytics{}, + }, + wantErr: false, + }, + { + name: "SSHb request", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: map[string]interface{}{ + "rctx": models.RequestCtx{ + Sshb: "1", + }, + }, + }, + }, + + want: hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload]{ + Reject: false, + ChangeSet: hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}, + DebugMessages: nil, + AnalyticsTags: hookanalytics.Analytics{}, + }, + }, + { + name: "Hybrid request", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: map[string]interface{}{ + "rctx": models.RequestCtx{ + Endpoint: models.EndpointHybrid, + }, + }, + }, + }, + want: hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload]{ + Reject: false, + ChangeSet: hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}, + DebugMessages: nil, + AnalyticsTags: hookanalytics.Analytics{}, + }, + }, + { + name: "empty device ip updated with request ip", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: map[string]interface{}{ + "rctx": models.RequestCtx{ + Endpoint: models.EndpointV25, + IP: "10.20.30.40", + }, + }, + }, + payload: hookstage.ProcessedAuctionRequestPayload{ + Request: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + IP: "", + }, + }, + }, + }, + }, + mutationApplied: true, + want: hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload]{ + Reject: false, + ChangeSet: hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}, + DebugMessages: nil, + AnalyticsTags: hookanalytics.Analytics{}, + }, + wantBidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + IP: "10.20.30.40", + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := OpenWrap{ + cache: mockCache, + } + got, err := m.HandleProcessedAuctionHook(tt.args.ctx, tt.args.moduleCtx, tt.args.payload) + assert.Equal(t, tt.wantErr, err != nil, "handleAllProcessedBidResponsesHook() error = %v, wantErr %v", err, tt.wantErr) + if tt.mutationApplied { + 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.wantBidRequest, result.Request.BidRequest, tt.name) + } + } + assert.Equal(t, tt.want.DebugMessages, got.DebugMessages, "Debug messages should be equal") + assert.Equal(t, tt.want.Reject, false, "Reject should be equal") + }) + } +} diff --git a/modules/pubmatic/openwrap/tbf/tbf_test.go b/modules/pubmatic/openwrap/tbf/tbf_test.go index c4c32935eb7..5f1511da0d9 100644 --- a/modules/pubmatic/openwrap/tbf/tbf_test.go +++ b/modules/pubmatic/openwrap/tbf/tbf_test.go @@ -41,7 +41,6 @@ func TestInitAndReloader(t *testing.T) { } for _, tt := range tests { tt.runBefore() - tbfConfigs.serviceStop = make(chan struct{}) Init(tt.args.defaultExpiry, tt.args.cache) time.Sleep(2 * time.Second) StopTBFReloaderService() diff --git a/modules/pubmatic/openwrap/tracker/create.go b/modules/pubmatic/openwrap/tracker/create.go index f62eddcc61c..19974016c81 100644 --- a/modules/pubmatic/openwrap/tracker/create.go +++ b/modules/pubmatic/openwrap/tracker/create.go @@ -61,7 +61,7 @@ func createTrackers(rctx models.RequestCtx, trackers map[string]models.OWTracker PageURL: rctx.PageURL, Timestamp: rctx.StartTime, IID: rctx.LoggerImpressionID, - Platform: int(rctx.DevicePlatform), + Platform: int(rctx.DeviceCtx.Platform), SSAI: rctx.SSAI, ImpID: bid.ImpID, Origin: rctx.Origin, @@ -86,6 +86,10 @@ func createTrackers(rctx models.RequestCtx, trackers map[string]models.OWTracker eg, en float64 ) + if rctx.DeviceCtx.Ext != nil { + tracker.ATTS = rctx.DeviceCtx.Ext.ATTS + } + if impCtx, ok := rctx.ImpBidCtx[bid.ImpID]; ok { if bidderMeta, ok := impCtx.Bidders[seatBid.Seat]; ok { matchedSlot = bidderMeta.MatchedSlot @@ -267,6 +271,9 @@ func constructTrackerURL(rctx models.RequestCtx, tracker models.Tracker) string if tracker.CustomDimensions != "" { v.Set(models.TRKCustomDimensions, tracker.CustomDimensions) } + if tracker.ATTS != nil { + v.Set(models.TRKATTS, strconv.Itoa(int(*tracker.ATTS))) + } queryString := v.Encode() //Code for making tracker call http/https based on secure flag for in-app platform diff --git a/modules/pubmatic/openwrap/tracker/create_test.go b/modules/pubmatic/openwrap/tracker/create_test.go index 991883494e7..e338d818861 100644 --- a/modules/pubmatic/openwrap/tracker/create_test.go +++ b/modules/pubmatic/openwrap/tracker/create_test.go @@ -20,10 +20,12 @@ var rctx = models.RequestCtx{ DisplayVersionID: 1, PageURL: "abc.com", LoggerImpressionID: "loggerIID", - DevicePlatform: 5, SSAI: "mediatailor", Origin: "publisher.com", ABTestConfigApplied: 1, + DeviceCtx: models.DeviceCtx{ + Platform: 5, + }, PrebidBidderCode: map[string]string{ "pubmatic": "pubmatic", }, @@ -108,6 +110,11 @@ func Test_createTrackers(t *testing.T) { rctx: func() models.RequestCtx { testRctx := rctx testRctx.StartTime = startTime + testRctx.DeviceCtx.Ext = &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + ATTS: ptrutil.ToPtr(openrtb_ext.IOSAppTrackingStatusRestricted), + }, + } return testRctx }(), bidResponse: &openrtb2.BidResponse{ @@ -169,8 +176,9 @@ func Test_createTrackers(t *testing.T) { KGPSV: "adunit-1@250x300", }, CustomDimensions: "author=henry", + ATTS: ptrutil.ToPtr(openrtb_ext.IOSAppTrackingStatusRestricted), }, - TrackerURL: "https:?adv=domain.com&af=banner&aps=0&au=adunit-1&bc=pubmatic&bidid=bidID-1&cds=author%3Dhenry&di=deal-id-1&dur=20&eg=8.7&en=8.7&frv=4.4&ft=0&fv=6.4&iid=loggerIID&kgpv=adunit-1%40250x300&orig=publisher.com&origbidid=bidID-1&pdvid=1&pid=1234&plt=5&pn=prebidBidderCode&psz=250x300&pubid=5890&purl=abc.com&sl=1&slot=impID-1_adunit-1&ss=1&ssai=mediatailor&tgid=1&tst=" + strconv.FormatInt(startTime, 10), + TrackerURL: "https:?adv=domain.com&af=banner&aps=0&atts=1&au=adunit-1&bc=pubmatic&bidid=bidID-1&cds=author%3Dhenry&di=deal-id-1&dur=20&eg=8.7&en=8.7&frv=4.4&ft=0&fv=6.4&iid=loggerIID&kgpv=adunit-1%40250x300&orig=publisher.com&origbidid=bidID-1&pdvid=1&pid=1234&plt=5&pn=prebidBidderCode&psz=250x300&pubid=5890&purl=abc.com&sl=1&slot=impID-1_adunit-1&ss=1&ssai=mediatailor&tgid=1&tst=" + strconv.FormatInt(startTime, 10), Price: 8.7, PriceModel: "CPM", PriceCurrency: "USD", diff --git a/modules/pubmatic/openwrap/tracker/tracker.go b/modules/pubmatic/openwrap/tracker/tracker.go index ed4a9881ec6..ed48ec8096b 100644 --- a/modules/pubmatic/openwrap/tracker/tracker.go +++ b/modules/pubmatic/openwrap/tracker/tracker.go @@ -13,11 +13,11 @@ func GetTrackerInfo(rCtx models.RequestCtx, responseExt openrtb_ext.ExtBidRespon tracker := models.Tracker{ PubID: rCtx.PubID, ProfileID: fmt.Sprintf("%d", rCtx.ProfileID), - VersionID: fmt.Sprintf("%d", rCtx.DisplayID), + VersionID: fmt.Sprintf("%d", rCtx.DisplayVersionID), PageURL: rCtx.PageURL, Timestamp: rCtx.StartTime, IID: rCtx.LoggerImpressionID, - Platform: int(rCtx.DevicePlatform), + Platform: int(rCtx.DeviceCtx.Platform), Origin: rCtx.Origin, TestGroup: rCtx.ABTestConfigApplied, FloorModelVersion: floorsDetails.FloorModelVersion, @@ -26,6 +26,10 @@ func GetTrackerInfo(rCtx models.RequestCtx, responseExt openrtb_ext.ExtBidRespon FloorSource: floorsDetails.FloorSource, } + if rCtx.DeviceCtx.Ext != nil { + tracker.ATTS = rCtx.DeviceCtx.Ext.ATTS + } + constructedURLString := constructTrackerURL(rCtx, tracker) trackerURL, err := url.Parse(constructedURLString) diff --git a/modules/pubmatic/openwrap/tracker/tracker_test.go b/modules/pubmatic/openwrap/tracker/tracker_test.go index 948acf4ab28..41ae04dbfa2 100644 --- a/modules/pubmatic/openwrap/tracker/tracker_test.go +++ b/modules/pubmatic/openwrap/tracker/tracker_test.go @@ -8,6 +8,7 @@ import ( "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/stretchr/testify/assert" ) func TestGetTrackerInfo(t *testing.T) { @@ -28,17 +29,25 @@ func TestGetTrackerInfo(t *testing.T) { TrackerEndpoint: "localhost:8080/wt", PubID: 123, ProfileID: 1, + DisplayVersionID: 2, VersionID: 1, PageURL: "www.test.com", LoggerImpressionID: "iid123", StartTime: startTime, - DevicePlatform: models.DevicePlatformMobileAppAndroid, Origin: "www.publisher.com", ABTestConfigApplied: 1, + DeviceCtx: models.DeviceCtx{ + Platform: models.DevicePlatformMobileAppAndroid, + Ext: &models.ExtDevice{ + ExtDevice: openrtb_ext.ExtDevice{ + ATTS: ptrutil.ToPtr(openrtb_ext.IOSAppTrackingStatusRestricted), + }, + }, + }, }, responseExt: openrtb_ext.ExtBidResponse{}, }, - want: "localhost:8080/wt?adv=&af=&aps=0&au=%24%7BADUNIT%7D&bc=%24%7BBIDDER_CODE%7D&bidid=%24%7BBID_ID%7D&di=&eg=%24%7BG_ECPM%7D&en=%24%7BN_ECPM%7D&ft=0&iid=iid123&kgpv=%24%7BKGPV%7D&orig=www.publisher.com&origbidid=%24%7BORIGBID_ID%7D&pdvid=0&pid=1&plt=5&pn=%24%7BPARTNER_NAME%7D&psz=&pubid=123&purl=www.test.com&rwrd=%24%7BREWARDED%7D&sl=1&slot=%24%7BSLOT_ID%7D&ss=0&tgid=1&tst=" + strconv.FormatInt(startTime, 10), + want: "localhost:8080/wt?adv=&af=&aps=0&atts=1&au=%24%7BADUNIT%7D&bc=%24%7BBIDDER_CODE%7D&bidid=%24%7BBID_ID%7D&di=&eg=%24%7BG_ECPM%7D&en=%24%7BN_ECPM%7D&ft=0&iid=iid123&kgpv=%24%7BKGPV%7D&orig=www.publisher.com&origbidid=%24%7BORIGBID_ID%7D&pdvid=2&pid=1&plt=5&pn=%24%7BPARTNER_NAME%7D&psz=&pubid=123&purl=www.test.com&rwrd=%24%7BREWARDED%7D&sl=1&slot=%24%7BSLOT_ID%7D&ss=0&tgid=1&tst=" + strconv.FormatInt(startTime, 10), }, { name: "all_tracker_info_with_floors", @@ -48,12 +57,15 @@ func TestGetTrackerInfo(t *testing.T) { PubID: 123, ProfileID: 1, VersionID: 1, + DisplayVersionID: 2, PageURL: "www.test.com", LoggerImpressionID: "iid123", StartTime: startTime, - DevicePlatform: models.DevicePlatformMobileAppAndroid, Origin: "www.publisher.com", ABTestConfigApplied: 1, + DeviceCtx: models.DeviceCtx{ + Platform: models.DevicePlatformMobileAppAndroid, + }, }, responseExt: openrtb_ext.ExtBidResponse{ Prebid: &openrtb_ext.ExtResponsePrebid{ @@ -74,14 +86,13 @@ func TestGetTrackerInfo(t *testing.T) { }, }, }, - want: "localhost:8080/wt?adv=&af=&aps=0&au=%24%7BADUNIT%7D&bc=%24%7BBIDDER_CODE%7D&bidid=%24%7BBID_ID%7D&di=&eg=%24%7BG_ECPM%7D&en=%24%7BN_ECPM%7D&fmv=version+1&fskp=1&fsrc=2&ft=1&iid=iid123&kgpv=%24%7BKGPV%7D&orig=www.publisher.com&origbidid=%24%7BORIGBID_ID%7D&pdvid=0&pid=1&plt=5&pn=%24%7BPARTNER_NAME%7D&psz=&pubid=123&purl=www.test.com&rwrd=%24%7BREWARDED%7D&sl=1&slot=%24%7BSLOT_ID%7D&ss=0&tgid=1&tst=" + strconv.FormatInt(startTime, 10), + want: "localhost:8080/wt?adv=&af=&aps=0&au=%24%7BADUNIT%7D&bc=%24%7BBIDDER_CODE%7D&bidid=%24%7BBID_ID%7D&di=&eg=%24%7BG_ECPM%7D&en=%24%7BN_ECPM%7D&fmv=version+1&fskp=1&fsrc=2&ft=1&iid=iid123&kgpv=%24%7BKGPV%7D&orig=www.publisher.com&origbidid=%24%7BORIGBID_ID%7D&pdvid=2&pid=1&plt=5&pn=%24%7BPARTNER_NAME%7D&psz=&pubid=123&purl=www.test.com&rwrd=%24%7BREWARDED%7D&sl=1&slot=%24%7BSLOT_ID%7D&ss=0&tgid=1&tst=" + strconv.FormatInt(startTime, 10), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := GetTrackerInfo(tt.args.rCtx, tt.args.responseExt); got != tt.want { - t.Errorf("GetTrackerInfo() = %v, want %v", got, tt.want) - } + got := GetTrackerInfo(tt.args.rCtx, tt.args.responseExt) + assert.Equal(t, tt.want, got) }) } } diff --git a/modules/pubmatic/openwrap/util.go b/modules/pubmatic/openwrap/util.go index e67a61b1737..9021d42da94 100644 --- a/modules/pubmatic/openwrap/util.go +++ b/modules/pubmatic/openwrap/util.go @@ -27,7 +27,9 @@ var ( ctvRegex *regexp.Regexp ) -const test = "_test" +const ( + test = "_test" +) func init() { widthRegEx = regexp.MustCompile(models.MACRO_WIDTH) @@ -45,15 +47,11 @@ func init() { ctvRegex = regexp.MustCompile(models.ConnectedDeviceUARegexPattern) } -// rCtx.DevicePlatform = GetDevicePlatform(rCtx.UA, payload.BidRequest, rCtx.Platform, rCtx.PubIDStr, m.metricEngine) +// rCtx.DevicePlatform = getDevicePlatform(rCtx.UA, payload.BidRequest, rCtx.Platform, rCtx.PubIDStr, m.metricEngine) // -// GetDevicePlatform determines the device from which request has been generated -func GetDevicePlatform(rCtx models.RequestCtx, bidRequest *openrtb2.BidRequest) models.DevicePlatform { +// getDevicePlatform determines the device from which request has been generated +func getDevicePlatform(rCtx models.RequestCtx, bidRequest *openrtb2.BidRequest) models.DevicePlatform { userAgentString := rCtx.UA - if bidRequest != nil && bidRequest.Device != nil && len(bidRequest.Device.UA) != 0 { - userAgentString = bidRequest.Device.UA - rCtx.UA = userAgentString - } switch rCtx.Platform { case models.PLATFORM_AMP: @@ -285,6 +283,15 @@ func isCTV(userAgent string) bool { return ctvRegex.Match([]byte(userAgent)) } +// getUserAgent returns value of bidRequest.Device.UA if present else returns empty string +func getUserAgent(bidRequest *openrtb2.BidRequest, defaultUA string) string { + userAgent := defaultUA + if bidRequest != nil && bidRequest.Device != nil && len(bidRequest.Device.UA) > 0 { + userAgent = bidRequest.Device.UA + } + return userAgent +} + func getPlatformFromRequest(request *openrtb2.BidRequest) string { var platform string if request.Site != nil { diff --git a/modules/pubmatic/openwrap/util_test.go b/modules/pubmatic/openwrap/util_test.go index 3207029b9cc..de5021ae678 100644 --- a/modules/pubmatic/openwrap/util_test.go +++ b/modules/pubmatic/openwrap/util_test.go @@ -261,7 +261,7 @@ func TestGetDevicePlatform(t *testing.T) { name: "Test_platform_in-app_with_device.ua_for_ios", args: args{ rCtx: models.RequestCtx{ - UA: "", + UA: "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", Platform: "in-app", }, bidRequest: getORTBRequest("", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", 0, false, true), @@ -305,7 +305,7 @@ func TestGetDevicePlatform(t *testing.T) { name: "Test_platform_display_with_device.ua_for_mobile", args: args{ rCtx: models.RequestCtx{ - UA: "", + UA: "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", Platform: "display", }, bidRequest: getORTBRequest("", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", 0, true, false), @@ -385,7 +385,7 @@ func TestGetDevicePlatform(t *testing.T) { name: "Test_platform_video_with_site_entry_and_mobile_UA", args: args{ rCtx: models.RequestCtx{ - UA: "", + UA: "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", Platform: "video", }, bidRequest: getORTBRequest("", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", 0, true, false), @@ -396,7 +396,7 @@ func TestGetDevicePlatform(t *testing.T) { name: "Test_platform_video_with_app_entry_and_iOS_mobile_UA", args: args{ rCtx: models.RequestCtx{ - UA: "", + UA: "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", Platform: "video", }, bidRequest: getORTBRequest("", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", 0, false, true), @@ -407,7 +407,7 @@ func TestGetDevicePlatform(t *testing.T) { name: "Test_platform_video_with_app_entry_and_android_mobile_UA", args: args{ rCtx: models.RequestCtx{ - UA: "", + UA: "Mozilla/5.0 (Linux; Android 7.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Focus/1.0 Chrome/59.0.3029.83 Mobile Safari/537.36", Platform: "video", }, @@ -441,7 +441,7 @@ func TestGetDevicePlatform(t *testing.T) { name: "Test_platform_video_with_CTV_and_device_type", args: args{ rCtx: models.RequestCtx{ - UA: "", + UA: "Mozilla/5.0 (SMART-TV; Linux; Tizen 4.0) AppleWebKit/538.1 (KHTML, like Gecko) Version/4.0 TV Safari/538.1", Platform: "video", PubIDStr: "5890", }, @@ -453,7 +453,7 @@ func TestGetDevicePlatform(t *testing.T) { name: "Test_platform_video_with_CTV_and_no_device_type", args: args{ rCtx: models.RequestCtx{ - UA: "", + UA: "AppleCoreMedia/1.0.0.20L498 (Apple TV; U; CPU OS 16_4_1 like Mac OS X; en_us)", Platform: "video", }, bidRequest: getORTBRequest("", "AppleCoreMedia/1.0.0.20L498 (Apple TV; U; CPU OS 16_4_1 like Mac OS X; en_us)", 0, true, false), @@ -464,7 +464,7 @@ func TestGetDevicePlatform(t *testing.T) { name: "Test_platform_video_for_non_CTV_User_agent_with_device_type_7", args: args{ rCtx: models.RequestCtx{ - UA: "", + UA: "AppleCoreMedia/1.0.0.20L498 (iphone ; U; CPU OS 16_4_1 like Mac OS X; en_us)", Platform: "video", PubIDStr: "5890", }, @@ -475,7 +475,7 @@ func TestGetDevicePlatform(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := GetDevicePlatform(tt.args.rCtx, tt.args.bidRequest) + got := getDevicePlatform(tt.args.rCtx, tt.args.bidRequest) assert.Equal(t, tt.want, got) }) } @@ -572,6 +572,68 @@ func TestIsMobile(t *testing.T) { } } +func Test_getUserAgent(t *testing.T) { + type args struct { + request *openrtb2.BidRequest + defaultUA string + } + tests := []struct { + name string + args args + wantUA string + }{ + { + name: "request_is_nil", + args: args{ + request: nil, + defaultUA: "default-ua", + }, + + wantUA: "default-ua", + }, + { + name: "req.device_is_nil", + args: args{ + request: &openrtb2.BidRequest{ + Device: nil, + }, + defaultUA: "default-ua", + }, + wantUA: "default-ua", + }, + { + name: "req.device.ua_empty", + args: args{ + request: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + UA: "", + }, + }, + defaultUA: "default-ua", + }, + wantUA: "default-ua", + }, + { + name: "req.device.ua_valid", + args: args{ + request: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + UA: "Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36", + }, + }, + defaultUA: "default-ua", + }, + wantUA: "Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ua := getUserAgent(tt.args.request, tt.args.defaultUA) + assert.Equal(t, tt.wantUA, ua, "mismatched UA") + }) + } +} + func TestIsIos(t *testing.T) { type args struct { os string diff --git a/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go b/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go index 643e5b4cd0d..06c71b338ad 100644 --- a/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go +++ b/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go @@ -23,9 +23,6 @@ func (m VastUnwrapModule) handleRawBidderResponseHook( result.DebugMessages = append(result.DebugMessages, "error: vast unwrap flag is not enabled in handleRawBidderResponseHook()") return result, nil } - defer func() { - miCtx.ModuleContext[RequestContext] = vastRequestContext - }() // Below code collects stats only if vastRequestContext.VastUnwrapStatsEnabled { diff --git a/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go b/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go index 657fd8ecd9e..311ffbbca9c 100644 --- a/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go +++ b/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go @@ -97,6 +97,7 @@ func TestHandleRawBidderResponseHook(t *testing.T) { mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes() mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()).AnyTimes() }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") @@ -171,6 +172,7 @@ func TestHandleRawBidderResponseHook(t *testing.T) { mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes() mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()).AnyTimes() }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") @@ -221,6 +223,7 @@ func TestHandleRawBidderResponseHook(t *testing.T) { mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes() mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()).AnyTimes() }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") @@ -295,6 +298,7 @@ func TestHandleRawBidderResponseHook(t *testing.T) { mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "0").AnyTimes() mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "0", gomock.Any()).AnyTimes() }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") diff --git a/modules/pubmatic/vastunwrap/module_test.go b/modules/pubmatic/vastunwrap/module_test.go index 479ae7723dd..e8332ad87b2 100644 --- a/modules/pubmatic/vastunwrap/module_test.go +++ b/modules/pubmatic/vastunwrap/module_test.go @@ -159,6 +159,8 @@ func TestVastUnwrapModuleHandleRawBidderResponseHook(t *testing.T) { mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0") mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1") mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()) + }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") diff --git a/modules/pubmatic/vastunwrap/stats/metrics.go b/modules/pubmatic/vastunwrap/stats/metrics.go index c6a34380f58..d3117ca8748 100644 --- a/modules/pubmatic/vastunwrap/stats/metrics.go +++ b/modules/pubmatic/vastunwrap/stats/metrics.go @@ -23,14 +23,16 @@ type MetricsEngine interface { RecordRequestStatus(accountId, bidder, status string) RecordWrapperCount(accountId, bidder string, wrapper_count string) RecordRequestTime(accountId, bidder string, readTime time.Duration) + RecordUnwrapRespTime(accountId, wraperCnt string, respTime time.Duration) } // Metrics defines the datatype which will implement MetricsEngine type Metrics struct { - Registry *prometheus.Registry - requests *prometheus.CounterVec - wrapperCount *prometheus.CounterVec - requestTime *prometheus.HistogramVec + Registry *prometheus.Registry + requests *prometheus.CounterVec + wrapperCount *prometheus.CounterVec + requestTime *prometheus.HistogramVec + unwrapRespTime *prometheus.HistogramVec } // NewMetricsEngine reads the configuration and returns the appropriate metrics engine @@ -57,6 +59,10 @@ func NewMetricsEngine(cfg moduledeps.ModuleDeps) (*Metrics, error) { "vastunwrap_request_time", "Time taken to serve the vast unwrap request in Milliseconds", []string{pubIdLabel, bidderLabel}, []float64{50, 100, 200, 300, 500}) + metrics.unwrapRespTime = newHistogramVec(cfg.MetricsCfg.Prometheus, metrics.Registry, + "vastunwrap_resp_time", + "Time taken to serve the vast unwrap request in Milliseconds at wrapper count level", []string{pubIdLabel, wrapperCountLabel}, + []float64{50, 100, 150, 200}) return &metrics, nil } @@ -110,3 +116,11 @@ func (m *Metrics) RecordRequestTime(accountId, bidder string, requestTime time.D bidderLabel: bidder, }).Observe(float64(requestTime.Milliseconds())) } + +// RecordUnwrapRespTime records time takent to complete vast unwrap per wrapper count level +func (m *Metrics) RecordUnwrapRespTime(accountId, wraperCnt string, respTime time.Duration) { + m.unwrapRespTime.With(prometheus.Labels{ + pubIdLabel: accountId, + wrapperCountLabel: wraperCnt, + }).Observe(float64(respTime.Milliseconds())) +} diff --git a/modules/pubmatic/vastunwrap/stats/metrics_test.go b/modules/pubmatic/vastunwrap/stats/metrics_test.go index b26f058024c..f33e448d2e6 100644 --- a/modules/pubmatic/vastunwrap/stats/metrics_test.go +++ b/modules/pubmatic/vastunwrap/stats/metrics_test.go @@ -41,6 +41,16 @@ func TestRecordRequestTime(t *testing.T) { result := getHistogramFromHistogramVec(m.requestTime, "bidder", "pubmatic") assertHistogram(t, result, 1, 250) } + +func TestRecordRespTime(t *testing.T) { + m := createMetricsForTesting() + + m.RecordUnwrapRespTime("1234", "1", time.Millisecond*100) + + result := getHistogramFromHistogramVec(m.unwrapRespTime, "pub_id", "1234") + assertHistogram(t, result, 1, 100) +} + func TestRecordRequestStatus(t *testing.T) { m := createMetricsForTesting() diff --git a/modules/pubmatic/vastunwrap/stats/mock/mock.go b/modules/pubmatic/vastunwrap/stats/mock/mock.go index ae18d8fe3af..6fa4d69fab1 100644 --- a/modules/pubmatic/vastunwrap/stats/mock/mock.go +++ b/modules/pubmatic/vastunwrap/stats/mock/mock.go @@ -57,6 +57,18 @@ func (mr *MockMetricsEngineMockRecorder) RecordWrapperCount(arg0, arg1, arg2 int return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordWrapperCount", reflect.TypeOf((*MockMetricsEngine)(nil).RecordWrapperCount), arg0, arg1, arg2) } +// RecordUnwrapRespTime mocks base method +func (m *MockMetricsEngine) RecordUnwrapRespTime(arg0, arg1 string, arg2 time.Duration) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RecordUnwrapRespTime", arg0, arg1, arg2) +} + +// RecordUnwrapRespTime indicates an expected call of RecordRequestStatus +func (mr *MockMetricsEngineMockRecorder) RecordUnwrapRespTime(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordUnwrapRespTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordUnwrapRespTime), arg0, arg1, arg2) +} + // RecordRequestTime mocks base method func (m *MockMetricsEngine) RecordRequestTime(arg0, arg1 string, arg2 time.Duration) { m.ctrl.T.Helper() diff --git a/modules/pubmatic/vastunwrap/unwrap_service.go b/modules/pubmatic/vastunwrap/unwrap_service.go index c9e0423aeda..aff7cd3b79c 100644 --- a/modules/pubmatic/vastunwrap/unwrap_service.go +++ b/modules/pubmatic/vastunwrap/unwrap_service.go @@ -27,6 +27,7 @@ func (m VastUnwrapModule) doUnwrapandUpdateBid(isStatsEnabled bool, bid *adapter m.MetricsEngine.RecordRequestStatus(accountID, bidder, respStatus) if respStatus == "0" { m.MetricsEngine.RecordWrapperCount(accountID, bidder, strconv.Itoa(int(wrapperCnt))) + m.MetricsEngine.RecordUnwrapRespTime(accountID, strconv.Itoa(int(wrapperCnt)), respTime) } }() headers := http.Header{} diff --git a/modules/pubmatic/vastunwrap/unwrap_service_test.go b/modules/pubmatic/vastunwrap/unwrap_service_test.go index 2058e29bc4e..50f8a13d505 100644 --- a/modules/pubmatic/vastunwrap/unwrap_service_test.go +++ b/modules/pubmatic/vastunwrap/unwrap_service_test.go @@ -117,6 +117,7 @@ func TestDoUnwrap(t *testing.T) { mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0") mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1") mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()) }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0")