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 }