Repository: prebid/prebid-server
Pull request #3229 (2023-10-17 17:05:48)

Adapter Name Case Insensitive: alternate bidder codes


Patch:
diff --git a/exchange/utils.go b/exchange/utils.go
index ea6b317d..8b4774a3 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -442,30 +442,32 @@ func buildRequestExtForBidder(bidder string, requestExt json.RawMessage, request
 }
 
 func buildRequestExtAlternateBidderCodes(bidder string, accABC *openrtb_ext.ExtAlternateBidderCodes, reqABC *openrtb_ext.ExtAlternateBidderCodes) *openrtb_ext.ExtAlternateBidderCodes {
-	if reqABC != nil {
-		alternateBidderCodes := &openrtb_ext.ExtAlternateBidderCodes{
-			Enabled: reqABC.Enabled,
-		}
-		if bidderCodes, ok := reqABC.Bidders[bidder]; ok {
-			alternateBidderCodes.Bidders = map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
-				bidder: bidderCodes,
-			}
-		}
-		return alternateBidderCodes
+
+	if altBidderCodes := copyExtAlternateBidderCodes(bidder, reqABC); altBidderCodes != nil {
+		return altBidderCodes
 	}
 
-	if accABC != nil {
+	if altBidderCodes := copyExtAlternateBidderCodes(bidder, accABC); altBidderCodes != nil {
+		return altBidderCodes
+	}
+
+	return nil
+}
+
+func copyExtAlternateBidderCodes(bidder string, altBidderCodes *openrtb_ext.ExtAlternateBidderCodes) *openrtb_ext.ExtAlternateBidderCodes {
+	if altBidderCodes != nil {
 		alternateBidderCodes := &openrtb_ext.ExtAlternateBidderCodes{
-			Enabled: accABC.Enabled,
+			Enabled: altBidderCodes.Enabled,
 		}
-		if bidderCodes, ok := accABC.Bidders[bidder]; ok {
+
+		if bidderCodes, ok := altBidderCodes.IsBidderInAlternateBidderCodes(bidder); ok {
 			alternateBidderCodes.Bidders = map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
 				bidder: bidderCodes,
 			}
 		}
+
 		return alternateBidderCodes
 	}
-
 	return nil
 }
 
diff --git a/openrtb_ext/alternatebiddercodes.go b/openrtb_ext/alternatebiddercodes.go
index 49291a3e..3dcb6c78 100644
--- a/openrtb_ext/alternatebiddercodes.go
+++ b/openrtb_ext/alternatebiddercodes.go
@@ -1,6 +1,9 @@
 package openrtb_ext
 
-import "fmt"
+import (
+	"fmt"
+	"strings"
+)
 
 // ExtAlternateBidderCodes defines list of alternate bidder codes allowed by adatpers. This overrides host level configs.
 type ExtAlternateBidderCodes struct {
@@ -14,7 +17,7 @@ type ExtAdapterAlternateBidderCodes struct {
 }
 
 func (bidderCodes *ExtAlternateBidderCodes) IsValidBidderCode(bidder, alternateBidder string) (bool, error) {
-	if alternateBidder == "" || bidder == alternateBidder {
+	if alternateBidder == "" || strings.EqualFold(bidder, alternateBidder) {
 		return true, nil
 	}
 
@@ -26,8 +29,8 @@ func (bidderCodes *ExtAlternateBidderCodes) IsValidBidderCode(bidder, alternateB
 		return false, alternateBidderNotDefinedError(bidder, alternateBidder)
 	}
 
-	adapterCfg, ok := bidderCodes.Bidders[bidder]
-	if !ok {
+	adapterCfg, found := bidderCodes.IsBidderInAlternateBidderCodes(bidder)
+	if !found {
 		return false, alternateBidderNotDefinedError(bidder, alternateBidder)
 	}
 
@@ -56,3 +59,23 @@ func alternateBidderDisabledError(bidder, alternateBidder string) error {
 func alternateBidderNotDefinedError(bidder, alternateBidder string) error {
 	return fmt.Errorf("alternateBidderCodes not defined for adapter %q, rejecting bids for %q", bidder, alternateBidder)
 }
+
+// IsBidderInAlternateBidderCodes tries to find bidder in the altBidderCodes.Bidders map in a case sensitive
+// manner first. If no match is found it'll try it in a case insensitive way in linear time
+func (bidderCodes *ExtAlternateBidderCodes) IsBidderInAlternateBidderCodes(bidder string) (ExtAdapterAlternateBidderCodes, bool) {
+	if len(bidder) > 0 && bidderCodes != nil && len(bidderCodes.Bidders) > 0 {
+		// try constant time exact match
+		if adapterCfg, found := bidderCodes.Bidders[bidder]; found {
+			return adapterCfg, true
+		}
+
+		// check if we can find with a case insensitive comparison
+		for bidderName, adapterCfg := range bidderCodes.Bidders {
+			if strings.EqualFold(bidder, bidderName) {
+				return adapterCfg, true
+			}
+		}
+	}
+
+	return ExtAdapterAlternateBidderCodes{}, false
+}


Test patch:
diff --git a/exchange/exchangetest/alternate-bidder-codes.json b/exchange/exchangetest/alternate-bidder-codes.json
new file mode 100644
index 00000000..26a7a05d
--- /dev/null
+++ b/exchange/exchangetest/alternate-bidder-codes.json
@@ -0,0 +1,272 @@
+{
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "site": {
+        "page": "test.somepage.com"
+      },
+      "imp": [
+        {
+          "id": "imp-id-1",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ]
+          },
+          "ext": {
+            "prebid": {
+              "bidder": {
+                "pubmatic": {
+                  "publisherId": 5890
+                },
+                "appnexus": {
+                  "placementId": 1
+                }
+              }
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {
+          "alternatebiddercodes": {
+            "enabled": true,
+            "bidders": {
+              "PUBmatic": {
+                "enabled": true,
+                "allowedbiddercodes": [
+                  "groupm"
+                ]
+              }
+            }
+          }
+        }
+      }
+    }
+  },
+  "outgoingRequests": {
+    "pubmatic": {
+      "expectRequest": {
+        "ortbRequest": {
+          "id": "some-request-id",
+          "site": {
+            "page": "test.somepage.com"
+          },
+          "imp": [
+            {
+              "id": "imp-id-1",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "publisherId": 5890
+                }
+              }
+            }
+          ],
+          "ext": {
+            "prebid": {
+              "alternatebiddercodes": {
+                "enabled": true,
+                "bidders": {
+                  "pubmatic": {
+                    "enabled": true,
+                    "allowedbiddercodes": [
+                      "groupm"
+                    ]
+                  }
+                }
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "pbsSeatBids": [
+          {
+            "pbsBids": [
+              {
+                "ortbBid": {
+                  "id": "pubmatic-bid-1",
+                  "impid": "imp-id-1",
+                  "price": 0.71
+                },
+                "bidType": "video",
+                "bidMeta": {
+                  "adaptercode": "pubmatic"
+                }
+              }
+            ],
+            "seat": "pubmatic"
+          },
+          {
+            "pbsBids": [
+              {
+                "ortbBid": {
+                  "id": "pubmatic-bid-2",
+                  "impid": "imp-id-1",
+                  "price": 0.51
+                },
+                "bidType": "video",
+                "bidMeta": {
+                  "adaptercode": "pubmatic"
+                }
+              }
+            ],
+            "seat": "groupm"
+          }
+        ]
+      }
+    },
+    "appnexus": {
+      "expectRequest": {
+        "ortbRequest": {
+          "id": "some-request-id",
+          "site": {
+            "page": "test.somepage.com"
+          },
+          "imp": [
+            {
+              "id": "imp-id-1",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": 1
+                }
+              }
+            }
+          ],
+          "ext": {
+            "prebid": {
+              "alternatebiddercodes": {
+                "enabled": true,
+                "bidders": null
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "pbsSeatBids": [
+          {
+            "pbsBids": [
+              {
+                "ortbBid": {
+                  "id": "appnexus-bid-1",
+                  "impid": "imp-id-1",
+                  "price": 0.3
+                },
+                "bidType": "banner",
+                "bidMeta": {
+                  "adaptercode": "appnexus"
+                }
+              }
+            ],
+            "seat": "appnexus"
+          },
+          {
+            "pbsBids": [
+              {
+                "ortbBid": {
+                  "id": "appnexus-bid-2",
+                  "impid": "imp-id-1",
+                  "price": 0.3
+                },
+                "bidType": "banner",
+                "bidMeta": {
+                  "adaptercode": "appnexus"
+                }
+              }
+            ],
+            "seat": "groupm"
+          }
+        ]
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "groupm",
+          "bid": [
+            {
+              "id": "pubmatic-bid-2",
+              "impid": "imp-id-1",
+              "price": 0.51,
+              "ext": {
+                "origbidcpm": 0.51,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "pubmatic"
+                  },
+                  "type": "video"
+                }
+              }
+            },
+            {
+              "id": "appnexus-bid-2",
+              "impid": "imp-id-1",
+              "price": 0.3,
+              "ext": {
+                "origbidcpm": 0.3,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "appnexus"
+                  },
+                  "type": "banner"
+                }
+              }
+            }
+          ]
+        },
+        {
+          "seat": "pubmatic",
+          "bid": [
+            {
+              "id": "pubmatic-bid-1",
+              "impid": "imp-id-1",
+              "price": 0.71,
+              "ext": {
+                "origbidcpm": 0.71,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "pubmatic"
+                  },
+                  "type": "video"
+                }
+              }
+            }
+          ]
+        },
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "appnexus-bid-1",
+              "impid": "imp-id-1",
+              "price": 0.3,
+              "ext": {
+                "origbidcpm": 0.3,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "appnexus"
+                  },
+                  "type": "banner"
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 3c28a43c..3a00c37c 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -5,10 +5,11 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/prebid/prebid-server/stored_responses"
 	"sort"
 	"testing"
 
+	"github.com/prebid/prebid-server/stored_responses"
+
 	gpplib "github.com/prebid/go-gpp"
 	"github.com/prebid/go-gpp/constants"
 	"github.com/prebid/openrtb/v19/openrtb2"
@@ -4756,6 +4757,224 @@ func TestApplyBidAdjustmentToFloor(t *testing.T) {
 	}
 }
 
+func TestBuildRequestExtAlternateBidderCodes(t *testing.T) {
+	type testInput struct {
+		bidderNameRaw string
+		accABC        *openrtb_ext.ExtAlternateBidderCodes
+		reqABC        *openrtb_ext.ExtAlternateBidderCodes
+	}
+	testCases := []struct {
+		desc     string
+		in       testInput
+		expected *openrtb_ext.ExtAlternateBidderCodes
+	}{
+		{
+			desc:     "No biddername, nil reqABC and accABC",
+			in:       testInput{},
+			expected: nil,
+		},
+		{
+			desc: "No biddername, non-nil reqABC",
+			in: testInput{
+				reqABC: &openrtb_ext.ExtAlternateBidderCodes{},
+			},
+			expected: &openrtb_ext.ExtAlternateBidderCodes{},
+		},
+		{
+			desc: "No biddername, non-nil accABC",
+			in: testInput{
+				accABC: &openrtb_ext.ExtAlternateBidderCodes{},
+			},
+			expected: &openrtb_ext.ExtAlternateBidderCodes{},
+		},
+		{
+			desc: "No biddername, non-nil reqABC nor accABC",
+			in: testInput{
+				reqABC: &openrtb_ext.ExtAlternateBidderCodes{},
+				accABC: &openrtb_ext.ExtAlternateBidderCodes{},
+			},
+			expected: &openrtb_ext.ExtAlternateBidderCodes{},
+		},
+		{
+			desc: "non-nil reqABC",
+			in: testInput{
+				bidderNameRaw: "pubmatic",
+				reqABC:        &openrtb_ext.ExtAlternateBidderCodes{},
+			},
+			expected: &openrtb_ext.ExtAlternateBidderCodes{},
+		},
+		{
+			desc: "non-nil accABC",
+			in: testInput{
+				bidderNameRaw: "pubmatic",
+				accABC:        &openrtb_ext.ExtAlternateBidderCodes{},
+			},
+			expected: &openrtb_ext.ExtAlternateBidderCodes{},
+		},
+		{
+			desc: "both reqABC and accABC enabled and bidder matches elements in accABC but reqABC comes first",
+			in: testInput{
+				bidderNameRaw: "PUBmatic",
+				reqABC: &openrtb_ext.ExtAlternateBidderCodes{
+					Enabled: true,
+					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
+						"appnexus": {
+							AllowedBidderCodes: []string{"pubCode1"},
+						},
+					},
+				},
+				accABC: &openrtb_ext.ExtAlternateBidderCodes{
+					Enabled: true,
+					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
+						"PubMatic": {
+							AllowedBidderCodes: []string{"pubCode2"},
+						},
+					},
+				},
+			},
+			expected: &openrtb_ext.ExtAlternateBidderCodes{Enabled: true},
+		},
+		{
+			desc: "both reqABC and accABC enabled and bidder matches elements in both but we prioritize reqABC",
+			in: testInput{
+				bidderNameRaw: "pubmatic",
+				reqABC: &openrtb_ext.ExtAlternateBidderCodes{
+					Enabled: true,
+					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
+						"PubMatic": {
+							AllowedBidderCodes: []string{"pubCode"},
+						},
+					},
+				},
+				accABC: &openrtb_ext.ExtAlternateBidderCodes{
+					Enabled: true,
+					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
+						"appnexus": {
+							AllowedBidderCodes: []string{"anxsCode"},
+						},
+					},
+				},
+			},
+			expected: &openrtb_ext.ExtAlternateBidderCodes{
+				Enabled: true,
+				Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
+					"pubmatic": {
+						AllowedBidderCodes: []string{"pubCode"},
+					},
+				},
+			},
+		},
+		{
+			desc: "nil reqABC non-nil accABC enabled and bidder matches elements in accABC",
+			in: testInput{
+				bidderNameRaw: "APPnexus",
+				accABC: &openrtb_ext.ExtAlternateBidderCodes{
+					Enabled: true,
+					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
+						"appnexus": {
+							AllowedBidderCodes: []string{"anxsCode"},
+						},
+					},
+				},
+			},
+			expected: &openrtb_ext.ExtAlternateBidderCodes{
+				Enabled: true,
+				Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
+					"APPnexus": {
+						AllowedBidderCodes: []string{"anxsCode"},
+					},
+				},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			alternateBidderCodes := buildRequestExtAlternateBidderCodes(tc.in.bidderNameRaw, tc.in.accABC, tc.in.reqABC)
+			assert.Equal(t, tc.expected, alternateBidderCodes)
+		})
+	}
+}
+
+func TestCopyExtAlternateBidderCodes(t *testing.T) {
+	type testInput struct {
+		bidder               string
+		alternateBidderCodes *openrtb_ext.ExtAlternateBidderCodes
+	}
+	testCases := []struct {
+		desc     string
+		in       testInput
+		expected *openrtb_ext.ExtAlternateBidderCodes
+	}{
+		{
+			desc:     "pass a nil alternateBidderCodes argument, expect nil output",
+			in:       testInput{},
+			expected: nil,
+		},
+		{
+			desc: "non-nil alternateBidderCodes argument but bidder doesn't match",
+			in: testInput{
+				alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{
+					Enabled: true,
+				},
+			},
+			expected: &openrtb_ext.ExtAlternateBidderCodes{
+				Enabled: true,
+			},
+		},
+		{
+			desc: "non-nil alternateBidderCodes argument bidder is identical to one element in map",
+			in: testInput{
+				bidder: "appnexus",
+				alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{
+					Enabled: true,
+					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
+						"appnexus": {
+							AllowedBidderCodes: []string{"adnxs"},
+						},
+					},
+				},
+			},
+			expected: &openrtb_ext.ExtAlternateBidderCodes{
+				Enabled: true,
+				Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
+					"appnexus": {
+						AllowedBidderCodes: []string{"adnxs"},
+					},
+				},
+			},
+		},
+		{
+			desc: "case insensitive match, keep bidder casing in output",
+			in: testInput{
+				bidder: "AppNexus",
+				alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{
+					Enabled: true,
+					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
+						"appnexus": {
+							AllowedBidderCodes: []string{"adnxs"},
+						},
+					},
+				},
+			},
+			expected: &openrtb_ext.ExtAlternateBidderCodes{
+				Enabled: true,
+				Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
+					"AppNexus": {
+						AllowedBidderCodes: []string{"adnxs"},
+					},
+				},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			alternateBidderCodes := copyExtAlternateBidderCodes(tc.in.bidder, tc.in.alternateBidderCodes)
+			assert.Equal(t, tc.expected, alternateBidderCodes)
+		})
+	}
+}
+
 func TestBuildBidResponseRequestBidderName(t *testing.T) {
 	bidderImpResponses := stored_responses.BidderImpsWithBidResponses{
 		openrtb_ext.BidderName("appnexus"): {"impId1": json.RawMessage(`{}`), "impId2": json.RawMessage(`{}`)},
diff --git a/openrtb_ext/alternatebiddercodes_test.go b/openrtb_ext/alternatebiddercodes_test.go
index 438aebad..f9229b13 100644
--- a/openrtb_ext/alternatebiddercodes_test.go
+++ b/openrtb_ext/alternatebiddercodes_test.go
@@ -35,6 +35,14 @@ func TestAlternateBidderCodes_IsValidBidderCode(t *testing.T) {
 			},
 			wantIsValid: true,
 		},
+		{
+			name: "alternateBidder and bidder are the same under Unicode case-folding (default non-extra bid case with seat's alternateBidder explicitly set)",
+			args: args{
+				bidder:          "pubmatic",
+				alternateBidder: "pubmatic",
+			},
+			wantIsValid: true,
+		},
 		{
 			name: "account.alternatebiddercodes config not defined (default, reject bid)",
 			args: args{
@@ -98,6 +106,20 @@ func TestAlternateBidderCodes_IsValidBidderCode(t *testing.T) {
 			},
 			wantIsValid: true,
 		},
+		{
+			name: "bidder is different in casing than the entry in account.alternatebiddercodes but they match because our case insensitive comparison",
+			args: args{
+				bidder:          "PUBmatic",
+				alternateBidder: "groupm",
+			},
+			fields: fields{
+				Enabled: true,
+				Bidders: map[string]ExtAdapterAlternateBidderCodes{
+					"pubmatic": {Enabled: true},
+				},
+			},
+			wantIsValid: true,
+		},
 		{
 			name: "allowedBidderCodes is *",
 			args: args{
@@ -178,3 +200,129 @@ func TestAlternateBidderCodes_IsValidBidderCode(t *testing.T) {
 		})
 	}
 }
+
+func TestIsBidderInAlternateBidderCodes(t *testing.T) {
+	type testInput struct {
+		bidder      string
+		bidderCodes *ExtAlternateBidderCodes
+	}
+	type testOutput struct {
+		adapterCfg ExtAdapterAlternateBidderCodes
+		found      bool
+	}
+	testCases := []struct {
+		desc     string
+		in       testInput
+		expected testOutput
+	}{
+		{
+			desc: "empty bidder",
+			in: testInput{
+				bidderCodes: &ExtAlternateBidderCodes{},
+			},
+			expected: testOutput{
+				adapterCfg: ExtAdapterAlternateBidderCodes{},
+				found:      false,
+			},
+		},
+		{
+			desc: "nil ExtAlternateBidderCodes",
+			in: testInput{
+				bidder:      "appnexus",
+				bidderCodes: nil,
+			},
+			expected: testOutput{
+				adapterCfg: ExtAdapterAlternateBidderCodes{},
+				found:      false,
+			},
+		},
+		{
+			desc: "nil ExtAlternateBidderCodes.Bidder map",
+			in: testInput{
+				bidder:      "appnexus",
+				bidderCodes: &ExtAlternateBidderCodes{},
+			},
+			expected: testOutput{
+				adapterCfg: ExtAdapterAlternateBidderCodes{},
+				found:      false,
+			},
+		},
+		{
+			desc: "nil ExtAlternateBidderCodes.Bidder map",
+			in: testInput{
+				bidder: "appnexus",
+				bidderCodes: &ExtAlternateBidderCodes{
+					Bidders: nil,
+				},
+			},
+			expected: testOutput{
+				adapterCfg: ExtAdapterAlternateBidderCodes{},
+				found:      false,
+			},
+		},
+		{
+			desc: "bidder arg identical to entry in Bidders map",
+			in: testInput{
+				bidder: "appnexus",
+				bidderCodes: &ExtAlternateBidderCodes{
+					Bidders: map[string]ExtAdapterAlternateBidderCodes{
+						"appnexus": {
+							Enabled:            true,
+							AllowedBidderCodes: []string{"abcCode"},
+						},
+					},
+				},
+			},
+			expected: testOutput{
+				adapterCfg: ExtAdapterAlternateBidderCodes{
+					Enabled:            true,
+					AllowedBidderCodes: []string{"abcCode"},
+				},
+				found: true,
+			},
+		},
+		{
+			desc: "bidder arg matches an entry in Bidders map with case insensitive comparisson",
+			in: testInput{
+				bidder: "appnexus",
+				bidderCodes: &ExtAlternateBidderCodes{
+					Bidders: map[string]ExtAdapterAlternateBidderCodes{
+						"AppNexus": {AllowedBidderCodes: []string{"adnxsCode"}},
+						"PubMatic": {AllowedBidderCodes: []string{"pubCode"}},
+						"Rubicon":  {AllowedBidderCodes: []string{"rCode"}},
+					},
+				},
+			},
+			expected: testOutput{
+				adapterCfg: ExtAdapterAlternateBidderCodes{
+					AllowedBidderCodes: []string{"adnxsCode"},
+				},
+				found: true,
+			},
+		},
+		{
+			desc: "bidder arg doesn't match any entry in map",
+			in: testInput{
+				bidder: "unknown",
+				bidderCodes: &ExtAlternateBidderCodes{
+					Bidders: map[string]ExtAdapterAlternateBidderCodes{
+						"AppNexus": {AllowedBidderCodes: []string{"adnxsCode"}},
+						"PubMatic": {AllowedBidderCodes: []string{"pubCode"}},
+						"Rubicon":  {AllowedBidderCodes: []string{"rCode"}},
+					},
+				},
+			},
+			expected: testOutput{
+				adapterCfg: ExtAdapterAlternateBidderCodes{},
+				found:      false,
+			},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			adapterCfg, found := tc.in.bidderCodes.IsBidderInAlternateBidderCodes(tc.in.bidder)
+			assert.Equal(t, tc.expected.adapterCfg, adapterCfg)
+			assert.Equal(t, tc.expected.found, found)
+		})
+	}
+}