Repository: prebid/prebid-server
Pull request #1901 (2021-06-24 18:45:04)

Currency Conversion Utility Function
Implements https://github.com/prebid/prebid-server/issues/1482


Patch:
diff --git a/adapters/bidder.go b/adapters/bidder.go
index 2e5ec83c..b7bde4bc 100644
--- a/adapters/bidder.go
+++ b/adapters/bidder.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/mxmCherry/openrtb/v15/openrtb2"
 	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/currency"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
@@ -138,6 +139,25 @@ func (r *RequestData) SetBasicAuth(username string, password string) {
 type ExtraRequestInfo struct {
 	PbsEntryPoint              metrics.RequestType
 	GlobalPrivacyControlHeader string
+	currencyConversions        currency.Conversions
+}
+
+func NewExtraRequestInfo(c currency.Conversions) ExtraRequestInfo {
+	return ExtraRequestInfo{
+		currencyConversions: c,
+	}
+}
+
+// ConvertCurrency converts a given amount from one currency to another, or returns:
+//  - Error if the `from` or `to` arguments are malformed or unknown ISO-4217 codes.
+//  - ConversionNotFoundError if the conversion mapping is unknown to Prebid Server
+//    and not provided in the bid request.
+func (r ExtraRequestInfo) ConvertCurrency(value float64, from, to string) (float64, error) {
+	if rate, err := r.currencyConversions.GetRate(from, to); err == nil {
+		return value * rate, nil
+	} else {
+		return 0, err
+	}
 }
 
 type Builder func(openrtb_ext.BidderName, config.Adapter) (Bidder, error)
diff --git a/adapters/bidmyadz/bidmyadz.go b/adapters/bidmyadz/bidmyadz.go
new file mode 100644
index 00000000..829d57e6
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadz.go
@@ -0,0 +1,157 @@
+package bidmyadz
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"net/http"
+)
+
+type adapter struct {
+	endpoint string
+}
+
+type bidExt struct {
+	MediaType string `json:"mediaType"`
+}
+
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
+	}
+	return bidder, nil
+}
+
+func (a *adapter) MakeRequests(
+	openRTBRequest *openrtb2.BidRequest,
+	reqInfo *adapters.ExtraRequestInfo,
+) (
+	requestsToBidder []*adapters.RequestData,
+	errs []error,
+) {
+
+	var errors []error
+
+	if len(openRTBRequest.Imp) > 1 {
+		errors = append(errors, &errortypes.BadInput{
+			Message: "Bidder does not support multi impression",
+		})
+	}
+
+	if openRTBRequest.Device.IP == "" && openRTBRequest.Device.IPv6 == "" {
+		errors = append(errors, &errortypes.BadInput{
+			Message: "IP/IPv6 is a required field",
+		})
+	}
+
+	if openRTBRequest.Device.UA == "" {
+		errors = append(errors, &errortypes.BadInput{
+			Message: "User-Agent is a required field",
+		})
+	}
+
+	if len(errors) != 0 {
+		return nil, errors
+	}
+
+	reqJSON, err := json.Marshal(openRTBRequest)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	headers.Add("X-Openrtb-Version", "2.5")
+
+	return []*adapters.RequestData{{
+		Method:  http.MethodPost,
+		Body:    reqJSON,
+		Uri:     a.endpoint,
+		Headers: headers,
+	}}, nil
+}
+
+func (a *adapter) MakeBids(
+	openRTBRequest *openrtb2.BidRequest,
+	requestToBidder *adapters.RequestData,
+	bidderRawResponse *adapters.ResponseData,
+) (
+	bidderResponse *adapters.BidderResponse,
+	errs []error,
+) {
+	if bidderRawResponse.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if bidderRawResponse.StatusCode == http.StatusBadRequest {
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Bad Request. %s", string(bidderRawResponse.Body)),
+		}}
+	}
+
+	if bidderRawResponse.StatusCode == http.StatusServiceUnavailable {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Bidder is unavailable. Please contact your account manager.",
+		}}
+	}
+
+	if bidderRawResponse.StatusCode != http.StatusOK {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Something went wrong. Status Code: [ %d ] %s", bidderRawResponse.StatusCode, string(bidderRawResponse.Body)),
+		}}
+	}
+
+	responseBody := bidderRawResponse.Body
+	var bidResp openrtb2.BidResponse
+	if err := json.Unmarshal(responseBody, &bidResp); err != nil {
+		return nil, []error{err}
+	}
+
+	if len(bidResp.SeatBid) == 0 {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Empty SeatBid",
+		}}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
+
+	bids := bidResp.SeatBid[0].Bid
+
+	if len(bids) == 0 {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Empty SeatBid.Bids",
+		}}
+	}
+
+	bid := bids[0]
+
+	var bidExt bidExt
+	var bidType openrtb_ext.BidType
+
+	if err := json.Unmarshal(bid.Ext, &bidExt); err != nil {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("BidExt parsing error. %s", err.Error()),
+		}}
+	}
+
+	bidType, err := getBidType(bidExt)
+
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+		Bid:     &bid,
+		BidType: bidType,
+	})
+	return bidResponse, nil
+}
+
+func getBidType(ext bidExt) (openrtb_ext.BidType, error) {
+	return openrtb_ext.ParseBidType(ext.MediaType)
+}
diff --git a/adapters/bidmyadz/usersync.go b/adapters/bidmyadz/usersync.go
new file mode 100644
index 00000000..755a184d
--- /dev/null
+++ b/adapters/bidmyadz/usersync.go
@@ -0,0 +1,12 @@
+package bidmyadz
+
+import (
+	"text/template"
+
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/usersync"
+)
+
+func NewBidmyadzSyncer(temp *template.Template) usersync.Usersyncer {
+	return adapters.NewSyncer("bidmyadz", temp, adapters.SyncTypeRedirect)
+}
diff --git a/config/config.go b/config/config.go
index 58bb40a5..47db6b57 100644
--- a/config/config.go
+++ b/config/config.go
@@ -587,6 +587,7 @@ func (cfg *Configuration) setDerivedDefaults() {
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAvocet, "https://ads.avct.cloud/getuid?&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Davocet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7BUUID%7D%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeachfront, "https://sync.bfmio.com/sync_s2s?gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeachfront%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bio_cid%5D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeintoo, "https://ib.beintoo.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeintoo%26uid%3D%24UID")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBidmyadz, "https://cookie-sync.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&ccpa={{.USPrivacy}}&red="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbidmyadz%26uid%3D%5BUID%5D%26us_privacy%3D{{.USPrivacy}}%26gdpr_consent%3D{{.GDPRConsent}}%26gdpr%3D{{.GDPR}}")
 	// openrtb_ext.BidderBidsCube doesn't have a good default.
 	// openrtb_ext.BidderBmtm doesn't have a good default.
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBrightroll, "https://pr-bh.ybp.yahoo.com/sync/appnexusprebidserver/?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbrightroll%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
@@ -842,6 +843,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.beintoo.endpoint", "https://ib.beintoo.com/um")
 	v.SetDefault("adapters.between.endpoint", "http://{{.Host}}.betweendigital.com/openrtb_bid?sspId={{.PublisherID}}")
 	v.SetDefault("adapters.bidmachine.endpoint", "https://{{.Host}}.bidmachine.io")
+	v.SetDefault("adapters.bidmyadz.endpoint", "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc")
 	v.SetDefault("adapters.bidscube.endpoint", "http://supply.bidscube.com/?c=o&m=rtb")
 	v.SetDefault("adapters.bmtm.endpoint", "https://one.elitebidder.com/api/pbs")
 	v.SetDefault("adapters.brightroll.endpoint", "http://east-bid.ybp.yahoo.com/bid/appnexuspbs")
diff --git a/currency/aggregate_conversions.go b/currency/aggregate_conversions.go
index 53c5ebff..a15404fe 100644
--- a/currency/aggregate_conversions.go
+++ b/currency/aggregate_conversions.go
@@ -23,7 +23,7 @@ func (re *AggregateConversions) GetRate(from string, to string) (float64, error)
 	rate, err := re.customRates.GetRate(from, to)
 	if err == nil {
 		return rate, nil
-	} else if _, isMissingRateErr := err.(ConversionRateNotFound); !isMissingRateErr {
+	} else if _, isMissingRateErr := err.(ConversionNotFoundError); !isMissingRateErr {
 		// other error, return the error
 		return 0, err
 	}
diff --git a/currency/constant_rates.go b/currency/constant_rates.go
index dde317d8..ccc5c24d 100644
--- a/currency/constant_rates.go
+++ b/currency/constant_rates.go
@@ -27,7 +27,7 @@ func (r *ConstantRates) GetRate(from string, to string) (float64, error) {
 	}
 
 	if fromUnit.String() != toUnit.String() {
-		return 0, ConversionRateNotFound{fromUnit.String(), toUnit.String()}
+		return 0, ConversionNotFoundError{FromCur: fromUnit.String(), ToCur: toUnit.String()}
 	}
 
 	return 1, nil
diff --git a/currency/errors.go b/currency/errors.go
index d764c15b..bb4c42aa 100644
--- a/currency/errors.go
+++ b/currency/errors.go
@@ -2,12 +2,12 @@ package currency
 
 import "fmt"
 
-// ConversionRateNotFound is thrown by the currency.Conversions GetRate(from string, to string) method
+// ConversionNotFoundError is thrown by the currency.Conversions GetRate(from string, to string) method
 // when the conversion rate between the two currencies, nor its reciprocal, can be found.
-type ConversionRateNotFound struct {
+type ConversionNotFoundError struct {
 	FromCur, ToCur string
 }
 
-func (err ConversionRateNotFound) Error() string {
+func (err ConversionNotFoundError) Error() string {
 	return fmt.Sprintf("Currency conversion rate not found: '%s' => '%s'", err.FromCur, err.ToCur)
 }
diff --git a/currency/rates.go b/currency/rates.go
index 62914c4b..b9cb0201 100644
--- a/currency/rates.go
+++ b/currency/rates.go
@@ -47,9 +47,9 @@ func (r *Rates) UnmarshalJSON(b []byte) error {
 // GetRate returns the conversion rate between two currencies or:
 //  - An error if one of the currency strings is not well-formed
 //  - An error if any of the currency strings is not a recognized currency code.
-//  - A MissingConversionRate error in case the conversion rate between the two
+//  - A ConversionNotFoundError in case the conversion rate between the two
 //    given currencies is not in the currencies rates map
-func (r *Rates) GetRate(from string, to string) (float64, error) {
+func (r *Rates) GetRate(from, to string) (float64, error) {
 	var err error
 	fromUnit, err := currency.ParseISO(from)
 	if err != nil {
@@ -70,7 +70,7 @@ func (r *Rates) GetRate(from string, to string) (float64, error) {
 			// In case we have an entry TO -> FROM
 			return 1 / conversion, nil
 		}
-		return 0, ConversionRateNotFound{fromUnit.String(), toUnit.String()}
+		return 0, ConversionNotFoundError{FromCur: fromUnit.String(), ToCur: toUnit.String()}
 	}
 	return 0, errors.New("rates are nil")
 }
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 1fdb7c24..dd663812 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -33,6 +33,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/beintoo"
 	"github.com/prebid/prebid-server/adapters/between"
 	"github.com/prebid/prebid-server/adapters/bidmachine"
+	"github.com/prebid/prebid-server/adapters/bidmyadz"
 	"github.com/prebid/prebid-server/adapters/bidscube"
 	"github.com/prebid/prebid-server/adapters/bmtm"
 	"github.com/prebid/prebid-server/adapters/brightroll"
@@ -154,6 +155,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderBeintoo:           beintoo.Builder,
 		openrtb_ext.BidderBetween:           between.Builder,
 		openrtb_ext.BidderBidmachine:        bidmachine.Builder,
+		openrtb_ext.BidderBidmyadz:          bidmyadz.Builder,
 		openrtb_ext.BidderBidsCube:          bidscube.Builder,
 		openrtb_ext.BidderBmtm:              bmtm.Builder,
 		openrtb_ext.BidderBrightroll:        brightroll.Builder,
diff --git a/exchange/exchange.go b/exchange/exchange.go
index e96aadbc..6aaafcd1 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -449,7 +449,7 @@ func (e *exchange) getAllBids(
 			if givenAdjustment, ok := bidAdjustments[string(bidderRequest.BidderName)]; ok {
 				adjustmentFactor = givenAdjustment
 			}
-			var reqInfo adapters.ExtraRequestInfo
+			reqInfo := adapters.NewExtraRequestInfo(conversions)
 			reqInfo.PbsEntryPoint = bidderRequest.BidderLabels.RType
 			reqInfo.GlobalPrivacyControlHeader = globalPrivacyControlHeader
 			bids, err := e.adapterMap[bidderRequest.BidderCoreName].requestBid(ctx, bidderRequest.BidRequest, bidderRequest.BidderName, adjustmentFactor, conversions, &reqInfo, accountDebugAllowed, headerDebugAllowed)
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 91ce9f0d..d6075bb1 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -104,6 +104,7 @@ const (
 	BidderBeintoo           BidderName = "beintoo"
 	BidderBetween           BidderName = "between"
 	BidderBidmachine        BidderName = "bidmachine"
+	BidderBidmyadz          BidderName = "bidmyadz"
 	BidderBidsCube          BidderName = "bidscube"
 	BidderBmtm              BidderName = "bmtm"
 	BidderBrightroll        BidderName = "brightroll"
@@ -225,6 +226,7 @@ func CoreBidderNames() []BidderName {
 		BidderBeintoo,
 		BidderBetween,
 		BidderBidmachine,
+		BidderBidmyadz,
 		BidderBidsCube,
 		BidderBmtm,
 		BidderBrightroll,
diff --git a/static/bidder-info/bidmyadz.yaml b/static/bidder-info/bidmyadz.yaml
new file mode 100644
index 00000000..70a995a2
--- /dev/null
+++ b/static/bidder-info/bidmyadz.yaml
@@ -0,0 +1,13 @@
+maintainer:
+  email: "contact@bidmyadz.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
\ No newline at end of file
diff --git a/static/bidder-params/bidmyadz.json b/static/bidder-params/bidmyadz.json
new file mode 100644
index 00000000..4e7b1119
--- /dev/null
+++ b/static/bidder-params/bidmyadz.json
@@ -0,0 +1,12 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "BidMyAdz Adapter Params",
+    "description": "A schema which validates params accepted by the BidMyAdz adapter",
+    "type": "object",
+    "properties": {
+      "placementId": {
+        "type": "string"
+      }
+    },
+    "required": ["placementId"]
+  }
\ No newline at end of file
diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go
index 169417e0..7de59491 100644
--- a/usersync/usersyncers/syncer.go
+++ b/usersync/usersyncers/syncer.go
@@ -28,6 +28,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/beachfront"
 	"github.com/prebid/prebid-server/adapters/beintoo"
 	"github.com/prebid/prebid-server/adapters/between"
+	"github.com/prebid/prebid-server/adapters/bidmyadz"
 	"github.com/prebid/prebid-server/adapters/bmtm"
 	"github.com/prebid/prebid-server/adapters/brightroll"
 	"github.com/prebid/prebid-server/adapters/colossus"
@@ -128,6 +129,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderBeintoo, beintoo.NewBeintooSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderBmtm, bmtm.NewBmtmSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderBrightroll, brightroll.NewBrightrollSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderBidmyadz, bidmyadz.NewBidmyadzSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderColossus, colossus.NewColossusSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderConnectAd, connectad.NewConnectAdSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderConsumable, consumable.NewConsumableSyncer)


Test patch:
diff --git a/adapters/bidder_test.go b/adapters/bidder_test.go
new file mode 100644
index 00000000..f0b833ce
--- /dev/null
+++ b/adapters/bidder_test.go
@@ -0,0 +1,63 @@
+package adapters
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+func TestExtraRequestInfoConvertCurrency(t *testing.T) {
+	var (
+		givenValue float64 = 2
+		givenFrom  string  = "AAA"
+		givenTo    string  = "BBB"
+	)
+
+	testCases := []struct {
+		description   string
+		setMock       func(m *mock.Mock)
+		expectedValue float64
+		expectedError error
+	}{
+		{
+			description:   "Success",
+			setMock:       func(m *mock.Mock) { m.On("GetRate", "AAA", "BBB").Return(2.5, nil) },
+			expectedValue: 5,
+			expectedError: nil,
+		},
+		{
+			description:   "Error",
+			setMock:       func(m *mock.Mock) { m.On("GetRate", "AAA", "BBB").Return(2.5, errors.New("some error")) },
+			expectedValue: 0,
+			expectedError: errors.New("some error"),
+		},
+	}
+
+	for _, test := range testCases {
+		mockConversions := &mockConversions{}
+		test.setMock(&mockConversions.Mock)
+
+		extraRequestInfo := NewExtraRequestInfo(mockConversions)
+		result, err := extraRequestInfo.ConvertCurrency(givenValue, givenFrom, givenTo)
+
+		mockConversions.AssertExpectations(t)
+		assert.Equal(t, test.expectedValue, result, test.description+":result")
+		assert.Equal(t, test.expectedError, err, test.description+":err")
+	}
+}
+
+type mockConversions struct {
+	mock.Mock
+}
+
+func (m mockConversions) GetRate(from string, to string) (float64, error) {
+	args := m.Called(from, to)
+	return args.Get(0).(float64), args.Error(1)
+}
+
+func (m mockConversions) GetRates() *map[string]map[string]float64 {
+	args := m.Called()
+	return args.Get(0).(*map[string]map[string]float64)
+}
diff --git a/adapters/bidmyadz/bidmyadz_test.go b/adapters/bidmyadz/bidmyadz_test.go
new file mode 100644
index 00000000..b0de6a02
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadz_test.go
@@ -0,0 +1,18 @@
+package bidmyadz
+
+import (
+	"github.com/stretchr/testify/assert"
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderBidmyadz, config.Adapter{
+		Endpoint: "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc"})
+
+	assert.NoError(t, buildErr)
+	adapterstest.RunJSONBidderTest(t, "bidmyadztest", bidder)
+}
diff --git a/adapters/bidmyadz/bidmyadztest/exemplary/banner.json b/adapters/bidmyadz/bidmyadztest/exemplary/banner.json
new file mode 100644
index 00000000..460291bc
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/exemplary/banner.json
@@ -0,0 +1,146 @@
+{
+  "mockBidRequest": {
+    "id": "12345",
+    "imp": [{
+      "id": "1",
+      "secure": 1,
+      "bidfloor": 0.31,
+      "bidfloorcur": "USD",
+      "banner": {
+        "w": 300,
+        "h": 250
+      },
+      "ext": {
+        "bidder": {
+          "placementId": "tbanner"
+        }
+      }
+    }],
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "ip": "18.146.18.241",
+      "devicetype": 2
+    },
+    "user": {
+      "id": "a66e61ff8c2ab5460ecf156c4b892fd84d41fad5"
+    },
+    "site": {
+      "id": "banner",
+      "domain": "test.com",
+      "cat": ["IAB1"],
+      "page": "https://test.com",
+      "publisher": {
+        "id": "1"
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc",
+        "body": {
+          "id": "12345",
+          "imp": [{
+            "id": "1",
+            "secure": 1,
+            "bidfloor": 0.31,
+            "bidfloorcur": "USD",
+            "banner": {
+              "w": 300,
+              "h": 250
+            },
+            "ext": {
+              "bidder": {
+                "placementId": "tbanner"
+              }
+            }
+          }],
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+            "geo": {
+              "country": "USA",
+              "lat": 47.627500,
+              "lon": -122.346200
+            },
+            "ip": "18.146.18.241",
+            "devicetype": 2
+          },
+          "user": {
+            "id": "a66e61ff8c2ab5460ecf156c4b892fd84d41fad5"
+          },
+          "site": {
+            "id": "banner",
+            "domain": "test.com",
+            "cat": ["IAB1"],
+            "page": "https://test.com",
+            "publisher": {
+              "id": "1"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "d910ceef496ff2d746176506e296f4b3_z53356_a126365",
+          "bidid": "880184da7ec02b1f42802acb46b63b3c",
+          "seatbid": [{
+            "bid": [{
+              "id": "6ca51ef38eb42820d27503ee96b634b5",
+              "impid": "126365",
+              "price": 0.1,
+              "nurl": "http://test.com/nurl",
+              "burl": "http://test.com/burl",
+              "adm": "<ADM>",
+              "adomain": ["test.com"],
+              "cat": ["IAB1"],
+              "cid": "1",
+              "crid": "1",
+              "w": 300,
+              "h": 250,
+              "ext": {
+                "mediaType": "banner"
+              }
+            }],
+            "seat": "1"
+          }],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "6ca51ef38eb42820d27503ee96b634b5",
+            "impid": "126365",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<ADM>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "1",
+            "crid": "1",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "mediaType": "banner"
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/exemplary/native.json b/adapters/bidmyadz/bidmyadztest/exemplary/native.json
new file mode 100644
index 00000000..98480260
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/exemplary/native.json
@@ -0,0 +1,141 @@
+{
+  "mockBidRequest": {
+    "id": "111",
+    "tmax": 150,
+    "at": 1,
+    "device": {
+      "dnt": 0,
+      "devicetype": 2,
+      "ip": "71.106.52.124",
+      "ua": "Mozilla/5.0 (X11; Linux x86_64; Ubuntu 14.04.2 LTS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.0 Maxthon/1.0.5.3 Safari/537.36"
+    },
+    "user": {
+      "id": "user-id"
+    },
+    "site": {
+      "id": "native",
+      "domain": "test.com",
+      "cat": ["IAB1"],
+      "page": "https://test.com",
+      "publisher": {
+        "id": "1"
+      }
+    },
+    "cur": [
+      "USD"
+    ],
+    "imp": [{
+      "id": "1",
+      "bidfloor": 0.01,
+      "bidfloorcur": "USD",
+      "instl": 0,
+      "secure": 1,
+      "native": {
+        "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":15}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":300,\"hmin\":300,\"type\":3}}, {\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+        "ver": "1.1"
+      },
+      "ext": {
+        "bidder": {
+          "placementId": "tnative"
+        }
+      }
+    }]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc",
+      "body": {
+        "id": "111",
+        "tmax": 150,
+        "at": 1,
+        "device": {
+          "dnt": 0,
+          "devicetype": 2,
+          "ip": "71.106.52.124",
+          "ua": "Mozilla/5.0 (X11; Linux x86_64; Ubuntu 14.04.2 LTS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.0 Maxthon/1.0.5.3 Safari/537.36"
+        },
+        "user": {
+          "id": "user-id"
+        },
+        "site": {
+          "id": "native",
+          "domain": "test.com",
+          "cat": ["IAB1"],
+          "page": "https://test.com",
+          "publisher": {
+            "id": "1"
+          }
+        },
+        "cur": [
+          "USD"
+        ],
+        "imp": [{
+          "id": "1",
+          "bidfloor": 0.01,
+          "bidfloorcur": "USD",
+          "secure": 1,
+          "native": {
+            "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":15}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":300,\"hmin\":300,\"type\":3}}, {\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+            "ver": "1.1"
+          },
+          "ext": {
+            "bidder": {
+              "placementId": "tnative"
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "a1580f2f-be6d-11eb-a150-d094662c1c35",
+        "bidid": "359da97d0384d8a14767029c18fd840d",
+        "seatbid": [{
+          "bid": [{
+            "id": "1",
+            "impid": "1",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "{native-ads}",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "1",
+            "crid": "2",
+            "w": 0,
+            "h": 0,
+            "ext": {
+              "mediaType": "native"
+            }
+          }],
+          "seat": "1"
+        }],
+        "cur": "USD"
+      }
+    }
+  }],
+
+  "expectedBidResponses": [{
+    "currency": "USD",
+    "bids": [{
+      "bid": {
+        "id": "1",
+        "impid": "1",
+        "price": 0.1,
+        "nurl": "http://test.com/nurl",
+        "burl": "http://test.com/burl",
+        "adm": "{native-ads}",
+        "adomain": ["test.com"],
+        "cat": ["IAB1"],
+        "cid": "1",
+        "crid": "2",
+        "ext": {
+          "mediaType": "native"
+        }
+      },
+      "type": "native"
+    }]
+  }]
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/exemplary/video.json b/adapters/bidmyadz/bidmyadztest/exemplary/video.json
new file mode 100644
index 00000000..92c42f83
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/exemplary/video.json
@@ -0,0 +1,160 @@
+{
+  "mockBidRequest": {
+    "id": "12345",
+    "imp": [{
+      "id": "1",
+      "video": {
+        "mimes": ["video/mp4", "video/ogg", "video/webm"],
+        "minduration": 5,
+        "maxduration": 60,
+        "protocols": [2, 3, 5, 6, 7, 8],
+        "w": 300,
+        "h": 250,
+        "linearity": 1,
+        "playbackmethod": [2]
+      },
+      "bidfloor": 0.001,
+      "bidfloorcur": "USD",
+      "secure": 1,
+      "exp": 3600,
+      "ext": {
+        "bidder": {
+          "placementId": "tvideo"
+        }
+      }
+    }],
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "ip": "18.146.18.241",
+      "devicetype": 2
+    },
+    "user": {
+      "id": "a66e61ff8c2ab5460ecf156c4b892fd84d41fad5"
+    },
+    "site": {
+      "id": "banner",
+      "domain": "test.com",
+      "cat": ["IAB1"],
+      "page": "https://test.com",
+      "publisher": {
+        "id": "1"
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc",
+        "body": {
+          "id": "12345",
+          "imp": [{
+            "id": "1",
+            "video": {
+              "mimes": ["video/mp4", "video/ogg", "video/webm"],
+              "minduration": 5,
+              "maxduration": 60,
+              "protocols": [2, 3, 5, 6, 7, 8],
+              "w": 300,
+              "h": 250,
+              "linearity": 1,
+              "playbackmethod": [2]
+            },
+            "bidfloor": 0.001,
+            "bidfloorcur": "USD",
+            "secure": 1,
+            "exp": 3600,
+            "ext": {
+              "bidder": {
+                "placementId": "tvideo"
+              }
+            }
+          }],
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+            "geo": {
+              "country": "USA",
+              "lat": 47.627500,
+              "lon": -122.346200
+            },
+            "ip": "18.146.18.241",
+            "devicetype": 2
+          },
+          "user": {
+            "id": "a66e61ff8c2ab5460ecf156c4b892fd84d41fad5"
+          },
+          "site": {
+            "id": "banner",
+            "domain": "test.com",
+            "cat": ["IAB1"],
+            "page": "https://test.com",
+            "publisher": {
+              "id": "1"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "d910ceef496ff2d746176506e296f4b3_z53356_a126365",
+          "bidid": "880184da7ec02b1f42802acb46b63b3c",
+          "seatbid": [{
+            "bid": [{
+              "id": "6ca51ef38eb42820d27503ee96b634b5",
+              "impid": "126365",
+              "price": 0.1,
+              "nurl": "http://test.com/nurl",
+              "burl": "http://test.com/burl",
+              "adm": "<VAST>",
+              "adomain": ["test.com"],
+              "cat": ["IAB1"],
+              "cid": "1",
+              "crid": "1",
+              "w": 300,
+              "h": 250,
+              "ext": {
+                "mediaType": "video"
+              }
+            }],
+            "seat": "1"
+          }],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "6ca51ef38eb42820d27503ee96b634b5",
+            "impid": "126365",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<VAST>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "1",
+            "crid": "1",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "mediaType": "video"
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/params/race/banner.json b/adapters/bidmyadz/bidmyadztest/params/race/banner.json
new file mode 100644
index 00000000..18dce42f
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/params/race/banner.json
@@ -0,0 +1,3 @@
+{
+  "placementId": "tbanner"
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/params/race/native.json b/adapters/bidmyadz/bidmyadztest/params/race/native.json
new file mode 100644
index 00000000..0600af3a
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/params/race/native.json
@@ -0,0 +1,3 @@
+{
+  "placementId": "tnative"
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/params/race/video.json b/adapters/bidmyadz/bidmyadztest/params/race/video.json
new file mode 100644
index 00000000..85478bf2
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/params/race/video.json
@@ -0,0 +1,3 @@
+{
+  "placementId": "tvideo"
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/supplemental/invalid-device-fields.json b/adapters/bidmyadz/bidmyadztest/supplemental/invalid-device-fields.json
new file mode 100644
index 00000000..d620a050
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/supplemental/invalid-device-fields.json
@@ -0,0 +1,48 @@
+{
+  "mockBidRequest": {
+    "id": "12345",
+    "imp": [{
+      "id": "1",
+      "secure": 1,
+      "bidfloor": 0.31,
+      "bidfloorcur": "USD",
+      "banner": {
+        "w": 300,
+        "h": 250
+      },
+      "ext": {
+        "bidder": {
+          "placementId": "tbanner"
+        }
+      }
+    }],
+    "device": {
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "devicetype": 2
+    },
+    "user": {
+      "id": "460ecf1d41fa6656c4b892fd84e61ff8c2ab5ad5"
+    },
+    "site": {
+      "id": "banner",
+      "domain": "test.com",
+      "cat": ["IAB1"],
+      "page": "https://test.com",
+      "publisher": {
+        "id": "test"
+      }
+    }
+  },
+
+  "expectedMakeRequestsErrors": [{
+    "value": "IP/IPv6 is a required field",
+    "comparison": "literal"
+  }, {
+    "value": "User-Agent is a required field",
+    "comparison": "literal"
+  }]
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/supplemental/invalid-multi-imps.json b/adapters/bidmyadz/bidmyadztest/supplemental/invalid-multi-imps.json
new file mode 100644
index 00000000..09020fc8
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/supplemental/invalid-multi-imps.json
@@ -0,0 +1,61 @@
+{
+  "mockBidRequest": {
+    "id": "12345",
+    "imp": [{
+      "id": "1",
+      "secure": 1,
+      "bidfloor": 0.31,
+      "bidfloorcur": "USD",
+      "banner": {
+        "w": 300,
+        "h": 250
+      },
+      "ext": {
+        "bidder": {
+          "placementId": "tbanner"
+        }
+      }
+    }, {
+      "id": "1",
+      "secure": 1,
+      "bidfloor": 0.11,
+      "bidfloorcur": "USD",
+      "banner": {
+        "w": 320,
+        "h": 50
+      },
+      "ext": {
+        "bidder": {
+          "placementId": "3234"
+        }
+      }
+    }],
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "ip": "18.146.18.241",
+      "devicetype": 2
+    },
+    "user": {
+      "id": "460ecf1d41fa6656c4b892fd84e61ff8c2ab5ad5"
+    },
+    "site": {
+      "id": "banner",
+      "domain": "test.com",
+      "cat": ["IAB1"],
+      "page": "https://test.com",
+      "publisher": {
+        "id": "test"
+      }
+    }
+  },
+
+  "expectedMakeRequestsErrors": [{
+    "value": "Bidder does not support multi impression",
+    "comparison": "literal"
+  }]
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/supplemental/missing-mediatype.json b/adapters/bidmyadz/bidmyadztest/supplemental/missing-mediatype.json
new file mode 100644
index 00000000..486d7324
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/supplemental/missing-mediatype.json
@@ -0,0 +1,122 @@
+{
+  "mockBidRequest": {
+    "id": "12345",
+    "imp": [{
+      "id": "1",
+      "secure": 1,
+      "bidfloor": 0.31,
+      "bidfloorcur": "USD",
+      "banner": {
+        "w": 300,
+        "h": 250
+      },
+      "ext": {
+        "bidder": {
+          "placementId": "tbanner"
+        }
+      }
+    }],
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "ip": "18.146.18.241",
+      "devicetype": 2
+    },
+    "user": {
+      "id": "a66e61ff8c2ab5460ecf156c4b892fd84d41fad5"
+    },
+    "site": {
+      "id": "banner",
+      "domain": "test.com",
+      "cat": ["IAB1"],
+      "page": "https://test.com",
+      "publisher": {
+        "id": "1"
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc",
+        "body": {
+          "id": "12345",
+          "imp": [{
+            "id": "1",
+            "secure": 1,
+            "bidfloor": 0.31,
+            "bidfloorcur": "USD",
+            "banner": {
+              "w": 300,
+              "h": 250
+            },
+            "ext": {
+              "bidder": {
+                "placementId": "tbanner"
+              }
+            }
+          }],
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+            "geo": {
+              "country": "USA",
+              "lat": 47.627500,
+              "lon": -122.346200
+            },
+            "ip": "18.146.18.241",
+            "devicetype": 2
+          },
+          "user": {
+            "id": "a66e61ff8c2ab5460ecf156c4b892fd84d41fad5"
+          },
+          "site": {
+            "id": "banner",
+            "domain": "test.com",
+            "cat": ["IAB1"],
+            "page": "https://test.com",
+            "publisher": {
+              "id": "1"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "d910ceef496ff2d746176506e296f4b3_z53356_a126365",
+          "bidid": "880184da7ec02b1f42802acb46b63b3c",
+          "seatbid": [{
+            "bid": [{
+              "id": "6ca51ef38eb42820d27503ee96b634b5",
+              "impid": "126365",
+              "price": 0.1,
+              "nurl": "http://test.com/nurl",
+              "burl": "http://test.com/burl",
+              "adm": "<ADM>",
+              "adomain": ["test.com"],
+              "cat": ["IAB1"],
+              "cid": "1",
+              "crid": "1",
+              "w": 300,
+              "h": 250
+            }],
+            "seat": "1"
+          }],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedMakeBidsErrors": [
+    {
+      "value": "BidExt parsing error. unexpected end of JSON input",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/supplemental/response-without-bids.json b/adapters/bidmyadz/bidmyadztest/supplemental/response-without-bids.json
new file mode 100644
index 00000000..fe3361f6
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/supplemental/response-without-bids.json
@@ -0,0 +1,109 @@
+{
+  "mockBidRequest": {
+    "id": "12345",
+    "imp": [{
+      "id": "1",
+      "secure": 1,
+      "bidfloor": 0.31,
+      "bidfloorcur": "USD",
+      "banner": {
+        "w": 300,
+        "h": 250
+      },
+      "ext": {
+        "bidder": {
+          "placementId": "tbanner"
+        }
+      }
+    }],
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "ip": "18.146.18.241",
+      "devicetype": 2
+    },
+    "user": {
+      "id": "a66e61ff8c2ab5460ecf156c4b892fd84d41fad5"
+    },
+    "site": {
+      "id": "banner",
+      "domain": "test.com",
+      "cat": ["IAB1"],
+      "page": "https://test.com",
+      "publisher": {
+        "id": "1"
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc",
+        "body": {
+          "id": "12345",
+          "imp": [{
+            "id": "1",
+            "secure": 1,
+            "bidfloor": 0.31,
+            "bidfloorcur": "USD",
+            "banner": {
+              "w": 300,
+              "h": 250
+            },
+            "ext": {
+              "bidder": {
+                "placementId": "tbanner"
+              }
+            }
+          }],
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+            "geo": {
+              "country": "USA",
+              "lat": 47.627500,
+              "lon": -122.346200
+            },
+            "ip": "18.146.18.241",
+            "devicetype": 2
+          },
+          "user": {
+            "id": "a66e61ff8c2ab5460ecf156c4b892fd84d41fad5"
+          },
+          "site": {
+            "id": "banner",
+            "domain": "test.com",
+            "cat": ["IAB1"],
+            "page": "https://test.com",
+            "publisher": {
+              "id": "1"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "d910ceef496ff2d746176506e296f4b3_z53356_a126365",
+          "bidid": "880184da7ec02b1f42802acb46b63b3c",
+          "seatbid": [{
+            "bid": [],
+            "seat": "1"
+          }],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Empty SeatBid.Bids",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/supplemental/response-without-seatbid.json b/adapters/bidmyadz/bidmyadztest/supplemental/response-without-seatbid.json
new file mode 100644
index 00000000..727e745e
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/supplemental/response-without-seatbid.json
@@ -0,0 +1,106 @@
+{
+  "mockBidRequest": {
+    "id": "12345",
+    "imp": [{
+      "id": "1",
+      "secure": 1,
+      "bidfloor": 0.31,
+      "bidfloorcur": "USD",
+      "banner": {
+        "w": 300,
+        "h": 250
+      },
+      "ext": {
+        "bidder": {
+          "placementId": "tbanner"
+        }
+      }
+    }],
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "ip": "18.146.18.241",
+      "devicetype": 2
+    },
+    "user": {
+      "id": "a66e61ff8c2ab5460ecf156c4b892fd84d41fad5"
+    },
+    "site": {
+      "id": "banner",
+      "domain": "test.com",
+      "cat": ["IAB1"],
+      "page": "https://test.com",
+      "publisher": {
+        "id": "1"
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc",
+        "body": {
+          "id": "12345",
+          "imp": [{
+            "id": "1",
+            "secure": 1,
+            "bidfloor": 0.31,
+            "bidfloorcur": "USD",
+            "banner": {
+              "w": 300,
+              "h": 250
+            },
+            "ext": {
+              "bidder": {
+                "placementId": "tbanner"
+              }
+            }
+          }],
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+            "geo": {
+              "country": "USA",
+              "lat": 47.627500,
+              "lon": -122.346200
+            },
+            "ip": "18.146.18.241",
+            "devicetype": 2
+          },
+          "user": {
+            "id": "a66e61ff8c2ab5460ecf156c4b892fd84d41fad5"
+          },
+          "site": {
+            "id": "banner",
+            "domain": "test.com",
+            "cat": ["IAB1"],
+            "page": "https://test.com",
+            "publisher": {
+              "id": "1"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "d910ceef496ff2d746176506e296f4b3_z53356_a126365",
+          "bidid": "880184da7ec02b1f42802acb46b63b3c",
+          "seatbid": [],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Empty SeatBid",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/supplemental/status-204.json b/adapters/bidmyadz/bidmyadztest/supplemental/status-204.json
new file mode 100644
index 00000000..05efda0e
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/supplemental/status-204.json
@@ -0,0 +1,94 @@
+{
+  "mockBidRequest": {
+    "id": "12345",
+    "imp": [{
+      "id": "1",
+      "secure": 1,
+      "bidfloor": 0.31,
+      "bidfloorcur": "USD",
+      "banner": {
+        "w": 300,
+        "h": 250
+      },
+      "ext": {
+        "bidder": {
+          "placementId": "tbanner"
+        }
+      }
+    }],
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "ip": "18.146.18.241",
+      "devicetype": 2
+    },
+    "user": {
+      "id": "460ecf1d41fa6656c4b892fd84e61ff8c2ab5ad5"
+    },
+    "site": {
+      "id": "banner",
+      "domain": "test.com",
+      "cat": ["IAB1"],
+      "page": "https://test.com",
+      "publisher": {
+        "id": "test"
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc",
+        "body": {
+          "id": "12345",
+          "imp": [{
+            "id": "1",
+            "secure": 1,
+            "bidfloor": 0.31,
+            "bidfloorcur": "USD",
+            "banner": {
+              "w": 300,
+              "h": 250
+            },
+            "ext": {
+              "bidder": {
+                "placementId": "tbanner"
+              }
+            }
+          }],
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+            "geo": {
+              "country": "USA",
+              "lat": 47.627500,
+              "lon": -122.346200
+            },
+            "ip": "18.146.18.241",
+            "devicetype": 2
+          },
+          "user": {
+            "id": "460ecf1d41fa6656c4b892fd84e61ff8c2ab5ad5"
+          },
+          "site": {
+            "id": "banner",
+            "domain": "test.com",
+            "cat": ["IAB1"],
+            "page": "https://test.com",
+            "publisher": {
+              "id": "test"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 204,
+        "body": {}
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/supplemental/status-400.json b/adapters/bidmyadz/bidmyadztest/supplemental/status-400.json
new file mode 100644
index 00000000..e24acdc6
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/supplemental/status-400.json
@@ -0,0 +1,101 @@
+{
+  "mockBidRequest": {
+    "id": "12345",
+    "imp": [{
+      "id": "1",
+      "secure": 1,
+      "bidfloor": 0.31,
+      "bidfloorcur": "USD",
+      "banner": {
+        "w": 300,
+        "h": 250
+      },
+      "ext": {
+        "bidder": {
+          "placementId": "tbanner"
+        }
+      }
+    }],
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "ip": "18.146.18.241",
+      "devicetype": 2
+    },
+    "user": {
+      "id": "460ecf1d41fa6656c4b892fd84e61ff8c2ab5ad5"
+    },
+    "site": {
+      "id": "banner",
+      "domain": "test.com",
+      "cat": ["IAB1"],
+      "page": "https://test.com",
+      "publisher": {
+        "id": "test"
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc",
+        "body": {
+          "id": "12345",
+          "imp": [{
+            "id": "1",
+            "secure": 1,
+            "bidfloor": 0.31,
+            "bidfloorcur": "USD",
+            "banner": {
+              "w": 300,
+              "h": 250
+            },
+            "ext": {
+              "bidder": {
+                "placementId": "tbanner"
+              }
+            }
+          }],
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+            "geo": {
+              "country": "USA",
+              "lat": 47.627500,
+              "lon": -122.346200
+            },
+            "ip": "18.146.18.241",
+            "devicetype": 2
+          },
+          "user": {
+            "id": "460ecf1d41fa6656c4b892fd84e61ff8c2ab5ad5"
+          },
+          "site": {
+            "id": "banner",
+            "domain": "test.com",
+            "cat": ["IAB1"],
+            "page": "https://test.com",
+            "publisher": {
+              "id": "test"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 400,
+        "body": "Source blocked"
+      }
+    }
+  ],
+
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bad Request. \"Source blocked\"",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/supplemental/status-service-unavailable.json b/adapters/bidmyadz/bidmyadztest/supplemental/status-service-unavailable.json
new file mode 100644
index 00000000..13e22f55
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/supplemental/status-service-unavailable.json
@@ -0,0 +1,100 @@
+{
+  "mockBidRequest": {
+    "id": "12345",
+    "imp": [{
+      "id": "1",
+      "secure": 1,
+      "bidfloor": 0.31,
+      "bidfloorcur": "USD",
+      "banner": {
+        "w": 300,
+        "h": 250
+      },
+      "ext": {
+        "bidder": {
+          "placementId": "tbanner"
+        }
+      }
+    }],
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "ip": "18.146.18.241",
+      "devicetype": 2
+    },
+    "user": {
+      "id": "460ecf1d41fa6656c4b892fd84e61ff8c2ab5ad5"
+    },
+    "site": {
+      "id": "banner",
+      "domain": "test.com",
+      "cat": ["IAB1"],
+      "page": "https://test.com",
+      "publisher": {
+        "id": "test"
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc",
+        "body": {
+          "id": "12345",
+          "imp": [{
+            "id": "1",
+            "secure": 1,
+            "bidfloor": 0.31,
+            "bidfloorcur": "USD",
+            "banner": {
+              "w": 300,
+              "h": 250
+            },
+            "ext": {
+              "bidder": {
+                "placementId": "tbanner"
+              }
+            }
+          }],
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+            "geo": {
+              "country": "USA",
+              "lat": 47.627500,
+              "lon": -122.346200
+            },
+            "ip": "18.146.18.241",
+            "devicetype": 2
+          },
+          "user": {
+            "id": "460ecf1d41fa6656c4b892fd84e61ff8c2ab5ad5"
+          },
+          "site": {
+            "id": "banner",
+            "domain": "test.com",
+            "cat": ["IAB1"],
+            "page": "https://test.com",
+            "publisher": {
+              "id": "test"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 503
+      }
+    }
+  ],
+
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bidder is unavailable. Please contact your account manager.",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/bidmyadztest/supplemental/status-unknown.json b/adapters/bidmyadz/bidmyadztest/supplemental/status-unknown.json
new file mode 100644
index 00000000..69b649e1
--- /dev/null
+++ b/adapters/bidmyadz/bidmyadztest/supplemental/status-unknown.json
@@ -0,0 +1,101 @@
+{
+  "mockBidRequest": {
+    "id": "12345",
+    "imp": [{
+      "id": "1",
+      "secure": 1,
+      "bidfloor": 0.31,
+      "bidfloorcur": "USD",
+      "banner": {
+        "w": 300,
+        "h": 250
+      },
+      "ext": {
+        "bidder": {
+          "placementId": "tbanner"
+        }
+      }
+    }],
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "ip": "18.146.18.241",
+      "devicetype": 2
+    },
+    "user": {
+      "id": "460ecf1d41fa6656c4b892fd84e61ff8c2ab5ad5"
+    },
+    "site": {
+      "id": "banner",
+      "domain": "test.com",
+      "cat": ["IAB1"],
+      "page": "https://test.com",
+      "publisher": {
+        "id": "test"
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc",
+        "body": {
+          "id": "12345",
+          "imp": [{
+            "id": "1",
+            "secure": 1,
+            "bidfloor": 0.31,
+            "bidfloorcur": "USD",
+            "banner": {
+              "w": 300,
+              "h": 250
+            },
+            "ext": {
+              "bidder": {
+                "placementId": "tbanner"
+              }
+            }
+          }],
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
+            "geo": {
+              "country": "USA",
+              "lat": 47.627500,
+              "lon": -122.346200
+            },
+            "ip": "18.146.18.241",
+            "devicetype": 2
+          },
+          "user": {
+            "id": "460ecf1d41fa6656c4b892fd84e61ff8c2ab5ad5"
+          },
+          "site": {
+            "id": "banner",
+            "domain": "test.com",
+            "cat": ["IAB1"],
+            "page": "https://test.com",
+            "publisher": {
+              "id": "test"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 403,
+        "body": "Forbidden"
+      }
+    }
+  ],
+
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Something went wrong. Status Code: [ 403 ] \"Forbidden\"",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bidmyadz/params_test.go b/adapters/bidmyadz/params_test.go
new file mode 100644
index 00000000..857cde86
--- /dev/null
+++ b/adapters/bidmyadz/params_test.go
@@ -0,0 +1,49 @@
+package bidmyadz
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+var validParams = []string{
+	`{ "placementId": "1234" }`,
+}
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderBidmyadz, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected bidmyadz params: %s", validParam)
+		}
+	}
+}
+
+var invalidParams = []string{
+	`1234`,
+	``,
+	`true`,
+	`null`,
+	`[]`,
+	`{}`,
+	`{ "anyparam": "anyvalue" }`,
+	`{ "placementId": null }`,
+}
+
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderBidmyadz, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
diff --git a/adapters/bidmyadz/usersync_test.go b/adapters/bidmyadz/usersync_test.go
new file mode 100644
index 00000000..11b5fedd
--- /dev/null
+++ b/adapters/bidmyadz/usersync_test.go
@@ -0,0 +1,33 @@
+package bidmyadz
+
+import (
+	"testing"
+	"text/template"
+
+	"github.com/prebid/prebid-server/privacy"
+	"github.com/prebid/prebid-server/privacy/ccpa"
+	"github.com/prebid/prebid-server/privacy/gdpr"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewBidmyadzSyncer(t *testing.T) {
+	syncURL := "https://test.com/c0f68227d14ed938c6c49f3967cbe9bc?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&ccpa={{.USPrivacy}}"
+	syncURLTemplate := template.Must(
+		template.New("sync-template").Parse(syncURL),
+	)
+	syncer := NewBidmyadzSyncer(syncURLTemplate)
+	syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{
+		GDPR: gdpr.Policy{
+			Signal:  "0",
+			Consent: "allGdpr",
+		},
+		CCPA: ccpa.Policy{
+			Consent: "1---",
+		},
+	})
+
+	assert.NoError(t, err)
+	assert.Equal(t, "https://test.com/c0f68227d14ed938c6c49f3967cbe9bc?gdpr=0&gdpr_consent=allGdpr&ccpa=1---", syncInfo.URL)
+	assert.Equal(t, "redirect", syncInfo.Type)
+	assert.Equal(t, false, syncInfo.SupportCORS)
+}
diff --git a/currency/aggregate_conversions_test.go b/currency/aggregate_conversions_test.go
index 35ca51a1..773a596c 100644
--- a/currency/aggregate_conversions_test.go
+++ b/currency/aggregate_conversions_test.go
@@ -65,7 +65,7 @@ func TestGroupedGetRate(t *testing.T) {
 			},
 		},
 		{
-			expectedError: ConversionRateNotFound{"GBP", "EUR"},
+			expectedError: ConversionNotFoundError{FromCur: "GBP", ToCur: "EUR"},
 			testCases: []aTest{
 				{"Valid three-digit currency codes, but conversion rate not found", "GBP", "EUR", 0},
 			},
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index deff0662..213af9a0 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -564,7 +564,7 @@ func TestMultiCurrencies(t *testing.T) {
 				{currency: "USD", price: 1.3 * 1.3050530256},
 			},
 			expectedBadCurrencyErrors: []error{
-				currency.ConversionRateNotFound{"JPY", "USD"},
+				currency.ConversionNotFoundError{FromCur: "JPY", ToCur: "USD"},
 			},
 			description: "Case 6 - Bidder respond with a mix of currencies and one unknown on all HTTP responses",
 		},
@@ -587,9 +587,9 @@ func TestMultiCurrencies(t *testing.T) {
 			},
 			expectedBids: []bid{},
 			expectedBadCurrencyErrors: []error{
-				currency.ConversionRateNotFound{"JPY", "USD"},
-				currency.ConversionRateNotFound{"BZD", "USD"},
-				currency.ConversionRateNotFound{"DKK", "USD"},
+				currency.ConversionNotFoundError{FromCur: "JPY", ToCur: "USD"},
+				currency.ConversionNotFoundError{FromCur: "BZD", ToCur: "USD"},
+				currency.ConversionNotFoundError{FromCur: "DKK", ToCur: "USD"},
 			},
 			description: "Case 7 - Bidder respond with currencies not having any rate on all HTTP responses",
 		},
@@ -720,9 +720,9 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) {
 			bidCurrency:       []string{"EUR", "EUR", "EUR"},
 			expectedBidsCount: 0,
 			expectedBadCurrencyErrors: []error{
-				currency.ConversionRateNotFound{"EUR", "USD"},
-				currency.ConversionRateNotFound{"EUR", "USD"},
-				currency.ConversionRateNotFound{"EUR", "USD"},
+				currency.ConversionNotFoundError{FromCur: "EUR", ToCur: "USD"},
+				currency.ConversionNotFoundError{FromCur: "EUR", ToCur: "USD"},
+				currency.ConversionNotFoundError{FromCur: "EUR", ToCur: "USD"},
 			},
 			description: "Case 2 - Bidder respond with the same currency (not default one) on all HTTP responses",
 		},
@@ -754,7 +754,7 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) {
 			bidCurrency:       []string{"EUR", "", "USD"},
 			expectedBidsCount: 2,
 			expectedBadCurrencyErrors: []error{
-				currency.ConversionRateNotFound{"EUR", "USD"},
+				currency.ConversionNotFoundError{FromCur: "EUR", ToCur: "USD"},
 			},
 			description: "Case 7 - Bidder responds with a mix of not set, non default currency and default currency in HTTP responses",
 		},
@@ -762,7 +762,7 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) {
 			bidCurrency:       []string{"GBP", "", "USD"},
 			expectedBidsCount: 2,
 			expectedBadCurrencyErrors: []error{
-				currency.ConversionRateNotFound{"GBP", "USD"},
+				currency.ConversionNotFoundError{FromCur: "GBP", ToCur: "USD"},
 			},
 			description: "Case 8 - Bidder responds with a mix of not set, non default currency and default currency in HTTP responses",
 		},
@@ -770,7 +770,7 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) {
 			bidCurrency:       []string{"GBP", "", ""},
 			expectedBidsCount: 2,
 			expectedBadCurrencyErrors: []error{
-				currency.ConversionRateNotFound{"GBP", "USD"},
+				currency.ConversionNotFoundError{FromCur: "GBP", ToCur: "USD"},
 			},
 			description: "Case 9 - Bidder responds with a mix of not set and empty currencies (default currency) in HTTP responses",
 		},
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 2d1306af..2a61a4e8 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -31,6 +31,7 @@ import (
 
 	"github.com/buger/jsonparser"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 	"github.com/yudai/gojsondiff"
 	"github.com/yudai/gojsondiff/formatter"
 )
@@ -539,7 +540,7 @@ func TestTwoBiddersDebugDisabledAndEnabled(t *testing.T) {
 
 func TestOverrideWithCustomCurrency(t *testing.T) {
 
-	mockCurrencyClient := &mockCurrencyRatesClient{
+	mockCurrencyClient := &fakeCurrencyRatesHttpClient{
 		responseBody: `{"dataAsOf":"2018-09-12","conversions":{"USD":{"MXN":10.00}}}`,
 	}
 	mockCurrencyConverter := currency.NewRateConverter(
@@ -694,6 +695,72 @@ func TestOverrideWithCustomCurrency(t *testing.T) {
 	}
 }
 
+func TestAdapterCurrency(t *testing.T) {
+	fakeCurrencyClient := &fakeCurrencyRatesHttpClient{
+		responseBody: `{"dataAsOf":"2018-09-12","conversions":{"USD":{"MXN":10.00}}}`,
+	}
+	currencyConverter := currency.NewRateConverter(
+		fakeCurrencyClient,
+		"currency.fake.com",
+		24*time.Hour,
+	)
+	currencyConverter.Run()
+
+	// Initialize Mock Bidder
+	// - Response purposefully causes PBS-Core to stop processing the request, since this test is only
+	//   interested in the call to MakeRequests and nothing after.
+	mockBidder := &mockBidder{}
+	mockBidder.On("MakeRequests", mock.Anything, mock.Anything).Return([]*adapters.RequestData(nil), []error(nil))
+
+	// Initialize Real Exchange
+	e := exchange{
+		cache:             &wellBehavedCache{},
+		me:                &metricsConf.DummyMetricsEngine{},
+		gDPR:              gdpr.AlwaysAllow{},
+		currencyConverter: currencyConverter,
+		categoriesFetcher: nilCategoryFetcher{},
+		bidIDGenerator:    &mockBidIDGenerator{false, false},
+		adapterMap: map[openrtb_ext.BidderName]adaptedBidder{
+			openrtb_ext.BidderName("foo"): adaptBidder(mockBidder, nil, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderName("foo"), nil),
+		},
+	}
+
+	// Define Bid Request
+	request := &openrtb2.BidRequest{
+		ID: "some-request-id",
+		Imp: []openrtb2.Imp{{
+			ID:     "some-impression-id",
+			Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}, {W: 300, H: 600}}},
+			Ext:    json.RawMessage(`{"foo": {"placementId": 1}}`),
+		}},
+		Site: &openrtb2.Site{
+			Page: "prebid.org",
+			Ext:  json.RawMessage(`{"amp":0}`),
+		},
+		Cur: []string{"USD"},
+		Ext: json.RawMessage(`{"prebid": {"currency": {"rates": {"USD": {"MXN": 20.00}}}}}`),
+	}
+
+	// Run Auction
+	auctionRequest := AuctionRequest{
+		BidRequest: request,
+		Account:    config.Account{},
+		UserSyncs:  &emptyUsersync{},
+	}
+	response, err := e.HoldAuction(context.Background(), auctionRequest, &DebugLog{})
+	assert.NoError(t, err)
+	assert.Equal(t, "some-request-id", response.ID, "Response ID")
+	assert.Empty(t, response.SeatBid, "Response Bids")
+	assert.Contains(t, string(response.Ext), `"errors":{"foo":[{"code":5,"message":"The adapter failed to generate any bid requests, but also failed to generate an error explaining why"}]}`, "Response Ext")
+
+	// Test Currency Converter Properly Passed To Adapter
+	if assert.NotNil(t, mockBidder.lastExtraRequestInfo, "Currency Conversion Argument") {
+		converted, err := mockBidder.lastExtraRequestInfo.ConvertCurrency(2.0, "USD", "MXN")
+		assert.NoError(t, err, "Currency Conversion Error")
+		assert.Equal(t, 40.0, converted, "Currency Conversion Response")
+	}
+}
+
 func TestGetAuctionCurrencyRates(t *testing.T) {
 
 	pbsRates := map[string]map[string]float64{
@@ -859,7 +926,7 @@ func TestGetAuctionCurrencyRates(t *testing.T) {
 		}
 
 		// Init mock currency conversion service
-		mockCurrencyClient := &mockCurrencyRatesClient{
+		mockCurrencyClient := &fakeCurrencyRatesHttpClient{
 			responseBody: `{"dataAsOf":"2018-09-12","conversions":` + string(jsonPbsRates) + `}`,
 		}
 		mockCurrencyConverter := currency.NewRateConverter(
@@ -3654,15 +3721,32 @@ func (nilCategoryFetcher) FetchCategories(ctx context.Context, primaryAdServer,
 	return "", nil
 }
 
-// mockCurrencyRatesClient is a simple http client mock returning a constant response body
-type mockCurrencyRatesClient struct {
+// fakeCurrencyRatesHttpClient is a simple http client mock returning a constant response body
+type fakeCurrencyRatesHttpClient struct {
 	responseBody string
 }
 
-func (m *mockCurrencyRatesClient) Do(req *http.Request) (*http.Response, error) {
+func (m *fakeCurrencyRatesHttpClient) Do(req *http.Request) (*http.Response, error) {
 	return &http.Response{
 		Status:     "200 OK",
 		StatusCode: http.StatusOK,
 		Body:       ioutil.NopCloser(strings.NewReader(m.responseBody)),
 	}, nil
 }
+
+type mockBidder struct {
+	mock.Mock
+	lastExtraRequestInfo *adapters.ExtraRequestInfo
+}
+
+func (m *mockBidder) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	m.lastExtraRequestInfo = reqInfo
+
+	args := m.Called(request, reqInfo)
+	return args.Get(0).([]*adapters.RequestData), args.Get(1).([]error)
+}
+
+func (m *mockBidder) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	args := m.Called(internalRequest, externalRequest, response)
+	return args.Get(0).(*adapters.BidderResponse), args.Get(1).([]error)
+}
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index 0cea346e..d86efea3 100644
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -37,6 +37,7 @@ func TestNewSyncerMap(t *testing.T) {
 			string(openrtb_ext.BidderBeachfront):       syncConfig,
 			string(openrtb_ext.BidderBeintoo):          syncConfig,
 			string(openrtb_ext.BidderBetween):          syncConfig,
+			string(openrtb_ext.BidderBidmyadz):         syncConfig,
 			string(openrtb_ext.BidderBmtm):             syncConfig,
 			string(openrtb_ext.BidderBrightroll):       syncConfig,
 			string(openrtb_ext.BidderColossus):         syncConfig,