Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Currency conversion on adapter JSON tests #2071

Merged
merged 3 commits into from
Nov 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 37 additions & 4 deletions adapters/adapterstest/test_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/mitchellh/copystructure"
"github.com/mxmCherry/openrtb/v15/openrtb2"
"github.com/prebid/prebid-server/adapters"
"github.com/prebid/prebid-server/currency"
"github.com/prebid/prebid-server/openrtb_ext"
"github.com/stretchr/testify/assert"
"github.com/yudai/gojsondiff"
"github.com/yudai/gojsondiff/formatter"
Expand Down Expand Up @@ -103,17 +105,48 @@ func loadFile(filename string) (*testSpec, error) {
//
// More assertions will almost certainly be added in the future, as bugs come up.
func runSpec(t *testing.T, filename string, spec *testSpec, bidder adapters.Bidder, isAmpTest, isVideoTest bool) {
reqInfo := adapters.ExtraRequestInfo{}
reqInfo := getTestExtraRequestInfo(t, filename, spec, isAmpTest, isVideoTest)
requests := testMakeRequestsImpl(t, filename, spec, bidder, reqInfo)

testMakeBidsImpl(t, filename, spec, bidder, requests)
}

// getTestExtraRequestInfo builds the ExtraRequestInfo object that will be passed to testMakeRequestsImpl
func getTestExtraRequestInfo(t *testing.T, filename string, spec *testSpec, isAmpTest, isVideoTest bool) *adapters.ExtraRequestInfo {
t.Helper()

var reqInfo adapters.ExtraRequestInfo

// If test request.ext defines its own currency rates, add currency conversion to reqInfo
reqWrapper := &openrtb_ext.RequestWrapper{}
reqWrapper.BidRequest = &spec.BidRequest

reqExt, err := reqWrapper.GetRequestExt()
assert.NoError(t, err, "Could not unmarshall test request ext. %s", filename)

reqPrebid := reqExt.GetPrebid()
if reqPrebid != nil && reqPrebid.CurrencyConversions != nil && len(reqPrebid.CurrencyConversions.ConversionRates) > 0 {
err = currency.ValidateCustomRates(reqPrebid.CurrencyConversions)
assert.NoError(t, err, "Error validating currency rates in the test request: %s", filename)

// Get currency rates conversions from the test request.ext
conversions := currency.NewRates(reqPrebid.CurrencyConversions.ConversionRates)

// Create return adapters.ExtraRequestInfo object
reqInfo = adapters.NewExtraRequestInfo(conversions)
} else {
reqInfo = adapters.ExtraRequestInfo{}
}

// Set PbsEntryPoint if either isAmpTest or isVideoTest is true
if isAmpTest {
// simulates AMP entry point
reqInfo.PbsEntryPoint = "amp"
} else if isVideoTest {
reqInfo.PbsEntryPoint = "video"
}

requests := testMakeRequestsImpl(t, filename, spec, bidder, &reqInfo)

testMakeBidsImpl(t, filename, spec, bidder, requests)
return &reqInfo
}

type testSpec struct {
Expand Down
32 changes: 30 additions & 2 deletions adapters/impactify/impactifytest/exemplary/sample_banner.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
}
]
},
"bidfloor": 1.00,
"bidfloorcur": "MXN",
"ext":{
"bidder": {
"appId": "impactify.io",
Expand All @@ -22,7 +24,19 @@
}
}
}
]
],
"ext": {
"prebid": {
"currency": {
"rates": {
"MXN": {
"USD": 0.05
}
},
"usepbsrates": false
}
}
}
},

"httpCalls": [
Expand All @@ -45,6 +59,8 @@
}
]
},
"bidfloor": 0.05,
"bidfloorcur": "USD",
"ext": {
"impactify": {
"appId": "impactify.io",
Expand All @@ -53,7 +69,19 @@
}
}
}
]
],
"ext": {
"prebid": {
"currency": {
"rates": {
"MXN": {
"USD": 0.05
}
},
"usepbsrates": false
}
}
}
}
},
"mockResponse": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"mockBidRequest": {
"id": "test-request-id",
"imp": [
{
"id": "test-imp-id",
"banner": {
"format": [
{
"w": 300,
"h": 250
}
]
},
"bidfloor": 1.00,
"bidfloorcur": "JPY",
"ext":{
"bidder": {
"appId": "impactify.io",
"format": "screen",
"style": "impact"
}
}
}
],
"ext": {
"prebid": {
"currency": {
"rates": {
"MXN": {
"USD": 0.05
}
},
"usepbsrates": false
}
}
}
},
"expectedMakeRequestsErrors": [
{
"value": "Currency conversion rate not found: 'JPY' => 'USD'",
"comparison": "literal"
}
]
}
34 changes: 34 additions & 0 deletions currency/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package currency

import (
"fmt"

"golang.org/x/text/currency"

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

// ValidateCustomRates throws a bad input error if any of the 3-digit currency codes found in
// the bidRequest.ext.prebid.currency field is invalid, malfomed or does not represent any actual
// currency. No error is thrown if bidRequest.ext.prebid.currency is invalid or empty.
func ValidateCustomRates(bidReqCurrencyRates *openrtb_ext.ExtRequestCurrency) error {
if bidReqCurrencyRates == nil {
return nil
}

for fromCurrency, rates := range bidReqCurrencyRates.ConversionRates {
// Check if fromCurrency is a valid 3-letter currency code
if _, err := currency.ParseISO(fromCurrency); err != nil {
return &errortypes.BadInput{Message: fmt.Sprintf("currency code %s is not recognized or malformed", fromCurrency)}
}

// Check if currencies mapped to fromCurrency are valid 3-letter currency codes
for toCurrency := range rates {
if _, err := currency.ParseISO(toCurrency); err != nil {
return &errortypes.BadInput{Message: fmt.Sprintf("currency code %s is not recognized or malformed", toCurrency)}
}
}
}
return nil
}
116 changes: 116 additions & 0 deletions currency/validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package currency

import (
"testing"

"github.com/prebid/prebid-server/errortypes"
"github.com/prebid/prebid-server/openrtb_ext"
"github.com/stretchr/testify/assert"
)

func TestValidateCustomRates(t *testing.T) {
boolTrue := true
boolFalse := false

testCases := []struct {
desc string
inBidReqCurrencies *openrtb_ext.ExtRequestCurrency
outCurrencyError error
}{
{
desc: "nil input, no errors expected",
inBidReqCurrencies: nil,
outCurrencyError: nil,
},
{
desc: "empty custom currency rates but UsePBSRates is set to false, we don't return error nor warning",
inBidReqCurrencies: &openrtb_ext.ExtRequestCurrency{
ConversionRates: map[string]map[string]float64{},
UsePBSRates: &boolFalse,
},
outCurrencyError: nil,
},
{
desc: "empty custom currency rates but UsePBSRates is set to true, no need to return error because we can use PBS rates",
inBidReqCurrencies: &openrtb_ext.ExtRequestCurrency{
ConversionRates: map[string]map[string]float64{},
UsePBSRates: &boolTrue,
},
outCurrencyError: nil,
},
{
desc: "UsePBSRates is nil and defaults to true, bidExt fromCurrency is invalid, expect bad input error",
inBidReqCurrencies: &openrtb_ext.ExtRequestCurrency{
ConversionRates: map[string]map[string]float64{
"FOO": {
"GBP": 1.2,
"MXN": 0.05,
"JPY": 0.95,
},
},
},
outCurrencyError: &errortypes.BadInput{Message: "currency code FOO is not recognized or malformed"},
},
{
desc: "UsePBSRates set to false, bidExt fromCurrency is invalid, expect bad input error",
inBidReqCurrencies: &openrtb_ext.ExtRequestCurrency{
ConversionRates: map[string]map[string]float64{
"FOO": {
"GBP": 1.2,
"MXN": 0.05,
"JPY": 0.95,
},
},
UsePBSRates: &boolFalse,
},
outCurrencyError: &errortypes.BadInput{Message: "currency code FOO is not recognized or malformed"},
},
{
desc: "UsePBSRates set to false, some of the bidExt 'to' Currencies are invalid, expect bad input error when parsing the first invalid currency code",
inBidReqCurrencies: &openrtb_ext.ExtRequestCurrency{
ConversionRates: map[string]map[string]float64{
"USD": {
"FOO": 10.0,
"MXN": 0.05,
},
},
UsePBSRates: &boolFalse,
},
outCurrencyError: &errortypes.BadInput{Message: "currency code FOO is not recognized or malformed"},
},
{
desc: "UsePBSRates set to false, some of the bidExt 'from' and 'to' currencies are invalid, expect bad input error when parsing the first invalid currency code",
inBidReqCurrencies: &openrtb_ext.ExtRequestCurrency{
ConversionRates: map[string]map[string]float64{
"FOO": {
"MXN": 0.05,
"CAD": 0.95,
},
},
UsePBSRates: &boolFalse,
},
outCurrencyError: &errortypes.BadInput{Message: "currency code FOO is not recognized or malformed"},
},
{
desc: "All 3-digit currency codes exist, expect no error",
inBidReqCurrencies: &openrtb_ext.ExtRequestCurrency{
ConversionRates: map[string]map[string]float64{
"USD": {
"MXN": 0.05,
},
"MXN": {
"JPY": 10.0,
"EUR": 10.95,
},
},
UsePBSRates: &boolFalse,
},
},
}

for _, tc := range testCases {
actualErr := ValidateCustomRates(tc.inBidReqCurrencies)

assert.Equal(t, tc.outCurrencyError, actualErr, tc.desc)
}
}
28 changes: 2 additions & 26 deletions endpoints/openrtb2/auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
accountService "github.com/prebid/prebid-server/account"
"github.com/prebid/prebid-server/analytics"
"github.com/prebid/prebid-server/config"
"github.com/prebid/prebid-server/currency"
"github.com/prebid/prebid-server/errortypes"
"github.com/prebid/prebid-server/exchange"
"github.com/prebid/prebid-server/metrics"
Expand All @@ -39,7 +40,6 @@ import (
"github.com/prebid/prebid-server/util/iputil"
"github.com/prebid/prebid-server/util/uuidutil"
"golang.org/x/net/publicsuffix"
"golang.org/x/text/currency"
)

const storedRequestTimeoutMillis = 50
Expand Down Expand Up @@ -377,7 +377,7 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper) []err
return []error{err}
}

if err := validateCustomRates(reqPrebid.CurrencyConversions); err != nil {
if err := currency.ValidateCustomRates(reqPrebid.CurrencyConversions); err != nil {
return []error{err}
}
}
Expand Down Expand Up @@ -475,30 +475,6 @@ func validateSChains(sChains []*openrtb_ext.ExtRequestPrebidSChain) error {
return err
}

// validateCustomRates throws a bad input error if any of the 3-digit currency codes found in
// the bidRequest.ext.prebid.currency field is invalid, malfomed or does not represent any actual
// currency. No error is thrown if bidRequest.ext.prebid.currency is invalid or empty.
func validateCustomRates(bidReqCurrencyRates *openrtb_ext.ExtRequestCurrency) error {
if bidReqCurrencyRates == nil {
return nil
}

for fromCurrency, rates := range bidReqCurrencyRates.ConversionRates {
// Check if fromCurrency is a valid 3-letter currency code
if _, err := currency.ParseISO(fromCurrency); err != nil {
return &errortypes.BadInput{Message: fmt.Sprintf("currency code %s is not recognized or malformed", fromCurrency)}
}

// Check if currencies mapped to fromCurrency are valid 3-letter currency codes
for toCurrency := range rates {
if _, err := currency.ParseISO(toCurrency); err != nil {
return &errortypes.BadInput{Message: fmt.Sprintf("currency code %s is not recognized or malformed", toCurrency)}
}
}
}
return nil
}

func (deps *endpointDeps) validateEidPermissions(prebid *openrtb_ext.ExtRequestPrebidData, aliases map[string]string) error {
if prebid == nil {
return nil
Expand Down