github.com/prebid/prebid-server@v0.275.0/endpoints/info/bidders_detail_test.go (about)

     1  package info
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"testing"
     9  
    10  	"github.com/julienschmidt/httprouter"
    11  	"github.com/prebid/prebid-server/config"
    12  	"github.com/prebid/prebid-server/openrtb_ext"
    13  	"github.com/stretchr/testify/assert"
    14  )
    15  
    16  func TestPrepareBiddersDetailResponse(t *testing.T) {
    17  	bidderAInfo := config.BidderInfo{Endpoint: "https://secureEndpoint.com", Disabled: false, Maintainer: &config.MaintainerInfo{Email: "bidderA"}}
    18  	bidderAResponse := []byte(`{"status":"ACTIVE","usesHttps":true,"maintainer":{"email":"bidderA"}}`)
    19  
    20  	bidderBInfo := config.BidderInfo{Endpoint: "http://unsecureEndpoint.com", Disabled: false, Maintainer: &config.MaintainerInfo{Email: "bidderB"}}
    21  	bidderBResponse := []byte(`{"status":"ACTIVE","usesHttps":false,"maintainer":{"email":"bidderB"}}`)
    22  
    23  	allResponseBidderA := bytes.Buffer{}
    24  	allResponseBidderA.WriteString(`{"a":`)
    25  	allResponseBidderA.Write(bidderAResponse)
    26  	allResponseBidderA.WriteString(`}`)
    27  
    28  	allResponseBidderAB := bytes.Buffer{}
    29  	allResponseBidderAB.WriteString(`{"a":`)
    30  	allResponseBidderAB.Write(bidderAResponse)
    31  	allResponseBidderAB.WriteString(`,"b":`)
    32  	allResponseBidderAB.Write(bidderBResponse)
    33  	allResponseBidderAB.WriteString(`}`)
    34  
    35  	var testCases = []struct {
    36  		description       string
    37  		givenBidders      config.BidderInfos
    38  		givenAliases      map[string]string
    39  		expectedResponses map[string][]byte
    40  		expectedError     string
    41  	}{
    42  		{
    43  			description:       "None",
    44  			givenBidders:      config.BidderInfos{},
    45  			givenAliases:      map[string]string{},
    46  			expectedResponses: map[string][]byte{"all": []byte(`{}`)},
    47  		},
    48  		{
    49  			description:       "One",
    50  			givenBidders:      config.BidderInfos{"a": bidderAInfo},
    51  			givenAliases:      map[string]string{},
    52  			expectedResponses: map[string][]byte{"a": bidderAResponse, "all": allResponseBidderA.Bytes()},
    53  		},
    54  		{
    55  			description:       "Many",
    56  			givenBidders:      config.BidderInfos{"a": bidderAInfo, "b": bidderBInfo},
    57  			givenAliases:      map[string]string{},
    58  			expectedResponses: map[string][]byte{"a": bidderAResponse, "b": bidderBResponse, "all": allResponseBidderAB.Bytes()},
    59  		},
    60  		{
    61  			description:   "Error - Map Details", // Returns error due to invalid alias.
    62  			givenBidders:  config.BidderInfos{"a": bidderAInfo},
    63  			givenAliases:  map[string]string{"zAlias": "z"},
    64  			expectedError: "base adapter z for alias zAlias not found",
    65  		},
    66  	}
    67  
    68  	for _, test := range testCases {
    69  		responses, err := prepareBiddersDetailResponse(test.givenBidders, test.givenAliases)
    70  
    71  		if test.expectedError == "" {
    72  			assert.Equal(t, test.expectedResponses, responses, test.description+":responses")
    73  			assert.NoError(t, err, test.expectedError, test.description+":err")
    74  		} else {
    75  			assert.Empty(t, responses, test.description+":responses")
    76  			assert.EqualError(t, err, test.expectedError, test.description+":err")
    77  		}
    78  	}
    79  }
    80  
    81  func TestMapDetails(t *testing.T) {
    82  	trueValue := true
    83  	falseValue := false
    84  
    85  	bidderAInfo := config.BidderInfo{Endpoint: "https://secureEndpoint.com", Disabled: false, Maintainer: &config.MaintainerInfo{Email: "bidderA"}}
    86  	bidderADetail := bidderDetail{Status: "ACTIVE", UsesHTTPS: &trueValue, Maintainer: &maintainer{Email: "bidderA"}}
    87  	aliasADetail := bidderDetail{Status: "ACTIVE", UsesHTTPS: &trueValue, Maintainer: &maintainer{Email: "bidderA"}, AliasOf: "a"}
    88  
    89  	bidderBInfo := config.BidderInfo{Endpoint: "http://unsecureEndpoint.com", Disabled: false, Maintainer: &config.MaintainerInfo{Email: "bidderB"}}
    90  	bidderBDetail := bidderDetail{Status: "ACTIVE", UsesHTTPS: &falseValue, Maintainer: &maintainer{Email: "bidderB"}}
    91  	aliasBDetail := bidderDetail{Status: "ACTIVE", UsesHTTPS: &falseValue, Maintainer: &maintainer{Email: "bidderB"}, AliasOf: "b"}
    92  
    93  	var testCases = []struct {
    94  		description     string
    95  		givenBidders    config.BidderInfos
    96  		givenAliases    map[string]string
    97  		expectedDetails map[string]bidderDetail
    98  		expectedError   string
    99  	}{
   100  		{
   101  			description:     "None",
   102  			givenBidders:    config.BidderInfos{},
   103  			givenAliases:    map[string]string{},
   104  			expectedDetails: map[string]bidderDetail{},
   105  		},
   106  		{
   107  			description:     "One Core Bidder",
   108  			givenBidders:    config.BidderInfos{"a": bidderAInfo},
   109  			givenAliases:    map[string]string{},
   110  			expectedDetails: map[string]bidderDetail{"a": bidderADetail},
   111  		},
   112  		{
   113  			description:     "Many Core Bidders",
   114  			givenBidders:    config.BidderInfos{"a": bidderAInfo, "b": bidderBInfo},
   115  			givenAliases:    map[string]string{},
   116  			expectedDetails: map[string]bidderDetail{"a": bidderADetail, "b": bidderBDetail},
   117  		},
   118  		{
   119  			description:     "One Alias",
   120  			givenBidders:    config.BidderInfos{"a": bidderAInfo},
   121  			givenAliases:    map[string]string{"aAlias": "a"},
   122  			expectedDetails: map[string]bidderDetail{"a": bidderADetail, "aAlias": aliasADetail},
   123  		},
   124  		{
   125  			description:     "Many Aliases - Same Core Bidder",
   126  			givenBidders:    config.BidderInfos{"a": bidderAInfo},
   127  			givenAliases:    map[string]string{"aAlias1": "a", "aAlias2": "a"},
   128  			expectedDetails: map[string]bidderDetail{"a": bidderADetail, "aAlias1": aliasADetail, "aAlias2": aliasADetail},
   129  		},
   130  		{
   131  			description:     "Many Aliases - Different Core Bidders",
   132  			givenBidders:    config.BidderInfos{"a": bidderAInfo, "b": bidderBInfo},
   133  			givenAliases:    map[string]string{"aAlias": "a", "bAlias": "b"},
   134  			expectedDetails: map[string]bidderDetail{"a": bidderADetail, "b": bidderBDetail, "aAlias": aliasADetail, "bAlias": aliasBDetail},
   135  		},
   136  		{
   137  			description:   "Error - Alias Without Core Bidder",
   138  			givenBidders:  config.BidderInfos{"a": bidderAInfo},
   139  			givenAliases:  map[string]string{"zAlias": "z"},
   140  			expectedError: "base adapter z for alias zAlias not found",
   141  		},
   142  	}
   143  
   144  	for _, test := range testCases {
   145  		details, err := mapDetails(test.givenBidders, test.givenAliases)
   146  
   147  		if test.expectedError == "" {
   148  			assert.Equal(t, test.expectedDetails, details, test.description+":details")
   149  			assert.NoError(t, err, test.expectedError, test.description+":err")
   150  		} else {
   151  			assert.Empty(t, details, test.description+":details")
   152  			assert.EqualError(t, err, test.expectedError, test.description+":err")
   153  		}
   154  	}
   155  }
   156  
   157  func TestMarshalDetailsResponse(t *testing.T) {
   158  	// Verifies omitempty is working correctly for bidderDetail, maintainer, capabilities, and aliasOf.
   159  	bidderDetailA := bidderDetail{Status: "ACTIVE", Maintainer: &maintainer{Email: "bidderA"}}
   160  	bidderDetailAResponse := []byte(`{"status":"ACTIVE","maintainer":{"email":"bidderA"}}`)
   161  
   162  	// Verifies omitempty is working correctly for capabilities.app / capabilities.site.
   163  	bidderDetailB := bidderDetail{Status: "ACTIVE", Maintainer: &maintainer{Email: "bidderB"}, Capabilities: &capabilities{App: &platform{MediaTypes: []string{"banner"}}}}
   164  	bidderDetailBResponse := []byte(`{"status":"ACTIVE","maintainer":{"email":"bidderB"},"capabilities":{"app":{"mediaTypes":["banner"]}}}`)
   165  
   166  	var testCases = []struct {
   167  		description      string
   168  		givenDetails     map[string]bidderDetail
   169  		expectedResponse map[string][]byte
   170  	}{
   171  		{
   172  			description:      "None",
   173  			givenDetails:     map[string]bidderDetail{},
   174  			expectedResponse: map[string][]byte{},
   175  		},
   176  		{
   177  			description:      "One",
   178  			givenDetails:     map[string]bidderDetail{"a": bidderDetailA},
   179  			expectedResponse: map[string][]byte{"a": bidderDetailAResponse},
   180  		},
   181  		{
   182  			description:      "Many",
   183  			givenDetails:     map[string]bidderDetail{"a": bidderDetailA, "b": bidderDetailB},
   184  			expectedResponse: map[string][]byte{"a": bidderDetailAResponse, "b": bidderDetailBResponse},
   185  		},
   186  	}
   187  
   188  	for _, test := range testCases {
   189  		response, err := marshalDetailsResponse(test.givenDetails)
   190  
   191  		assert.NoError(t, err, test.description+":err")
   192  		assert.Equal(t, test.expectedResponse, response, test.description+":response")
   193  	}
   194  }
   195  
   196  func TestMarshalAllResponse(t *testing.T) {
   197  	responses := map[string][]byte{
   198  		"a": []byte(`{"Status":"ACTIVE"}`),
   199  		"b": []byte(`{"Status":"DISABLED"}`),
   200  	}
   201  
   202  	result, err := marshalAllResponse(responses)
   203  
   204  	assert.NoError(t, err)
   205  	assert.Equal(t, []byte(`{"a":{"Status":"ACTIVE"},"b":{"Status":"DISABLED"}}`), result)
   206  }
   207  
   208  func TestMapDetailFromConfig(t *testing.T) {
   209  	trueValue := true
   210  	falseValue := false
   211  
   212  	var testCases = []struct {
   213  		description     string
   214  		givenBidderInfo config.BidderInfo
   215  		expected        bidderDetail
   216  	}{
   217  		{
   218  			description: "Enabled - All Values Present",
   219  			givenBidderInfo: config.BidderInfo{
   220  				Endpoint: "http://anyEndpoint",
   221  				Disabled: false,
   222  				Maintainer: &config.MaintainerInfo{
   223  					Email: "foo@bar.com",
   224  				},
   225  				Capabilities: &config.CapabilitiesInfo{
   226  					App:  &config.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}},
   227  					Site: &config.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeVideo}},
   228  				},
   229  			},
   230  			expected: bidderDetail{
   231  				Status:    "ACTIVE",
   232  				UsesHTTPS: &falseValue,
   233  				Maintainer: &maintainer{
   234  					Email: "foo@bar.com",
   235  				},
   236  				Capabilities: &capabilities{
   237  					App:  &platform{MediaTypes: []string{"banner"}},
   238  					Site: &platform{MediaTypes: []string{"video"}},
   239  				},
   240  				AliasOf: "",
   241  			},
   242  		},
   243  		{
   244  			description: "Disabled - All Values Present",
   245  			givenBidderInfo: config.BidderInfo{
   246  				Endpoint: "http://anyEndpoint",
   247  				Disabled: true,
   248  				Maintainer: &config.MaintainerInfo{
   249  					Email: "foo@bar.com",
   250  				},
   251  				Capabilities: &config.CapabilitiesInfo{
   252  					App:  &config.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}},
   253  					Site: &config.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeVideo}},
   254  				},
   255  			},
   256  			expected: bidderDetail{
   257  				Status:    "DISABLED",
   258  				UsesHTTPS: nil,
   259  				Maintainer: &maintainer{
   260  					Email: "foo@bar.com",
   261  				},
   262  				Capabilities: nil,
   263  				AliasOf:      "",
   264  			},
   265  		},
   266  		{
   267  			description: "Enabled - No Values Present",
   268  			givenBidderInfo: config.BidderInfo{
   269  				Endpoint: "http://amyEndpoint",
   270  				Disabled: false,
   271  			},
   272  			expected: bidderDetail{
   273  				Status:    "ACTIVE",
   274  				UsesHTTPS: &falseValue,
   275  			},
   276  		},
   277  		{
   278  			description: "Enabled - Protocol - HTTP",
   279  			givenBidderInfo: config.BidderInfo{
   280  				Endpoint: "http://amyEndpoint",
   281  				Disabled: false,
   282  			},
   283  			expected: bidderDetail{
   284  				Status:    "ACTIVE",
   285  				UsesHTTPS: &falseValue,
   286  			},
   287  		},
   288  		{
   289  			description: "Enabled - Protocol - HTTPS",
   290  			givenBidderInfo: config.BidderInfo{
   291  				Endpoint: "https://amyEndpoint",
   292  				Disabled: false,
   293  			},
   294  			expected: bidderDetail{
   295  				Status:    "ACTIVE",
   296  				UsesHTTPS: &trueValue,
   297  			},
   298  		},
   299  		{
   300  			description: "Enabled - Protocol - HTTPS - Case Insensitive",
   301  			givenBidderInfo: config.BidderInfo{
   302  				Disabled: false,
   303  				Endpoint: "https://amyEndpoint",
   304  			},
   305  			expected: bidderDetail{
   306  				Status:    "ACTIVE",
   307  				UsesHTTPS: &trueValue,
   308  			},
   309  		},
   310  		{
   311  			description: "Enabled - Protocol - Unknown",
   312  			givenBidderInfo: config.BidderInfo{
   313  				Endpoint: "endpointWithoutProtocol",
   314  				Disabled: false,
   315  			},
   316  			expected: bidderDetail{
   317  				Status:    "ACTIVE",
   318  				UsesHTTPS: &falseValue,
   319  			},
   320  		},
   321  	}
   322  
   323  	for _, test := range testCases {
   324  		result := mapDetailFromConfig(test.givenBidderInfo)
   325  		assert.Equal(t, test.expected, result, test.description)
   326  	}
   327  }
   328  
   329  func TestMapMediaTypes(t *testing.T) {
   330  	var testCases = []struct {
   331  		description string
   332  		mediaTypes  []openrtb_ext.BidType
   333  		expected    []string
   334  	}{
   335  		{
   336  			description: "Nil",
   337  			mediaTypes:  nil,
   338  			expected:    nil,
   339  		},
   340  		{
   341  			description: "None",
   342  			mediaTypes:  []openrtb_ext.BidType{},
   343  			expected:    []string{},
   344  		},
   345  		{
   346  			description: "One",
   347  			mediaTypes:  []openrtb_ext.BidType{openrtb_ext.BidTypeBanner},
   348  			expected:    []string{"banner"},
   349  		},
   350  		{
   351  			description: "Many",
   352  			mediaTypes:  []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo},
   353  			expected:    []string{"banner", "video"},
   354  		},
   355  	}
   356  
   357  	for _, test := range testCases {
   358  		result := mapMediaTypes(test.mediaTypes)
   359  		assert.ElementsMatch(t, test.expected, result, test.description)
   360  	}
   361  }
   362  
   363  func TestBiddersDetailHandler(t *testing.T) {
   364  	bidderAInfo := config.BidderInfo{Endpoint: "https://secureEndpoint.com", Disabled: false, Maintainer: &config.MaintainerInfo{Email: "bidderA"}}
   365  	bidderAResponse := []byte(`{"status":"ACTIVE","usesHttps":true,"maintainer":{"email":"bidderA"}}`)
   366  	aliasAResponse := []byte(`{"status":"ACTIVE","usesHttps":true,"maintainer":{"email":"bidderA"},"aliasOf":"a"}`)
   367  
   368  	bidderBInfo := config.BidderInfo{Endpoint: "http://unsecureEndpoint.com", Disabled: false, Maintainer: &config.MaintainerInfo{Email: "bidderB"}}
   369  	bidderBResponse := []byte(`{"status":"ACTIVE","usesHttps":false,"maintainer":{"email":"bidderB"}}`)
   370  
   371  	allResponse := bytes.Buffer{}
   372  	allResponse.WriteString(`{"a":`)
   373  	allResponse.Write(bidderAResponse)
   374  	allResponse.WriteString(`,"aAlias":`)
   375  	allResponse.Write(aliasAResponse)
   376  	allResponse.WriteString(`,"b":`)
   377  	allResponse.Write(bidderBResponse)
   378  	allResponse.WriteString(`}`)
   379  
   380  	bidders := config.BidderInfos{"a": bidderAInfo, "b": bidderBInfo}
   381  	aliases := map[string]string{"aAlias": "a"}
   382  
   383  	handler := NewBiddersDetailEndpoint(bidders, aliases)
   384  
   385  	var testCases = []struct {
   386  		description      string
   387  		givenBidder      string
   388  		expectedStatus   int
   389  		expectedHeaders  http.Header
   390  		expectedResponse []byte
   391  	}{
   392  		{
   393  			description:      "Bidder A",
   394  			givenBidder:      "a",
   395  			expectedStatus:   http.StatusOK,
   396  			expectedHeaders:  http.Header{"Content-Type": []string{"application/json"}},
   397  			expectedResponse: bidderAResponse,
   398  		},
   399  		{
   400  			description:      "Bidder B",
   401  			givenBidder:      "b",
   402  			expectedStatus:   http.StatusOK,
   403  			expectedHeaders:  http.Header{"Content-Type": []string{"application/json"}},
   404  			expectedResponse: bidderBResponse,
   405  		},
   406  		{
   407  			description:      "Bidder A Alias",
   408  			givenBidder:      "aAlias",
   409  			expectedStatus:   http.StatusOK,
   410  			expectedHeaders:  http.Header{"Content-Type": []string{"application/json"}},
   411  			expectedResponse: aliasAResponse,
   412  		},
   413  		{
   414  			description:      "All Bidders",
   415  			givenBidder:      "all",
   416  			expectedStatus:   http.StatusOK,
   417  			expectedHeaders:  http.Header{"Content-Type": []string{"application/json"}},
   418  			expectedResponse: allResponse.Bytes(),
   419  		},
   420  		{
   421  			description:      "All Bidders - Wrong Case",
   422  			givenBidder:      "ALL",
   423  			expectedStatus:   http.StatusNotFound,
   424  			expectedHeaders:  http.Header{},
   425  			expectedResponse: []byte{},
   426  		},
   427  		{
   428  			description:      "Invalid Bidder",
   429  			givenBidder:      "doesntExist",
   430  			expectedStatus:   http.StatusNotFound,
   431  			expectedHeaders:  http.Header{},
   432  			expectedResponse: []byte{},
   433  		},
   434  	}
   435  
   436  	for _, test := range testCases {
   437  		responseRecorder := httptest.NewRecorder()
   438  		handler(responseRecorder, nil, httprouter.Params{{"bidderName", test.givenBidder}})
   439  
   440  		result := responseRecorder.Result()
   441  		assert.Equal(t, result.StatusCode, test.expectedStatus, test.description+":statuscode")
   442  
   443  		resultBody, _ := io.ReadAll(result.Body)
   444  		assert.Equal(t, test.expectedResponse, resultBody, test.description+":body")
   445  
   446  		resultHeaders := result.Header
   447  		assert.Equal(t, test.expectedHeaders, resultHeaders, test.description+":headers")
   448  	}
   449  }