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