github.com/prebid/prebid-server/v2@v2.18.0/endpoints/events/event_test.go (about) 1 package events 2 3 import ( 4 "context" 5 "encoding/base64" 6 "encoding/json" 7 "errors" 8 "io" 9 "net/http" 10 "net/http/httptest" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/prebid/prebid-server/v2/analytics" 16 "github.com/prebid/prebid-server/v2/config" 17 "github.com/prebid/prebid-server/v2/errortypes" 18 "github.com/prebid/prebid-server/v2/metrics" 19 "github.com/prebid/prebid-server/v2/privacy" 20 "github.com/prebid/prebid-server/v2/stored_requests" 21 "github.com/stretchr/testify/assert" 22 ) 23 24 type eventsMockAnalyticsModule struct { 25 Fail bool 26 Error error 27 Invoked bool 28 } 29 30 func (e *eventsMockAnalyticsModule) LogAuctionObject(ao *analytics.AuctionObject, _ privacy.ActivityControl) { 31 if e.Fail { 32 panic(e.Error) 33 } 34 } 35 36 func (e *eventsMockAnalyticsModule) LogVideoObject(vo *analytics.VideoObject, _ privacy.ActivityControl) { 37 if e.Fail { 38 panic(e.Error) 39 } 40 } 41 42 func (e *eventsMockAnalyticsModule) LogCookieSyncObject(cso *analytics.CookieSyncObject) { 43 if e.Fail { 44 panic(e.Error) 45 } 46 } 47 48 func (e *eventsMockAnalyticsModule) LogSetUIDObject(so *analytics.SetUIDObject) { 49 if e.Fail { 50 panic(e.Error) 51 } 52 } 53 54 func (e *eventsMockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject, _ privacy.ActivityControl) { 55 if e.Fail { 56 panic(e.Error) 57 } 58 } 59 60 func (e *eventsMockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent, _ privacy.ActivityControl) { 61 if e.Fail { 62 panic(e.Error) 63 } 64 e.Invoked = true 65 } 66 67 var mockAccountData = map[string]json.RawMessage{ 68 "events_enabled": json.RawMessage(`{"events": {"enabled":true}}`), 69 "events_disabled": json.RawMessage(`{"events": {"enabled":false}}`), 70 "malformed_acct": json.RawMessage(`{"events": {"enabled":"invalid type"}}`), 71 "disabled_acct": json.RawMessage(`{"disabled": true}`), 72 } 73 74 type mockAccountsFetcher struct { 75 Fail bool 76 Error error 77 DurationMS int 78 } 79 80 func (maf mockAccountsFetcher) FetchAccount(ctx context.Context, defaultAccountJSON json.RawMessage, accountID string) (json.RawMessage, []error) { 81 if maf.DurationMS > 0 { 82 select { 83 case <-time.After(time.Duration(maf.DurationMS) * time.Millisecond): 84 break 85 case <-ctx.Done(): 86 return nil, []error{ctx.Err()} 87 } 88 } 89 90 if account, ok := mockAccountData[accountID]; ok { 91 return account, nil 92 } 93 94 if maf.Fail { 95 return nil, []error{maf.Error} 96 } 97 98 return nil, []error{stored_requests.NotFoundError{ID: accountID, DataType: "Account"}} 99 } 100 101 // Tests 102 103 func TestShouldReturnBadRequestWhenTypeIsMissing(t *testing.T) { 104 105 // mock AccountsFetcher 106 mockAccountsFetcher := &mockAccountsFetcher{} 107 108 // mock PBS Analytics Module 109 mockAnalyticsModule := &eventsMockAnalyticsModule{ 110 Fail: false, 111 } 112 113 // mock config 114 cfg := &config.Configuration{ 115 AccountDefaults: config.Account{}, 116 } 117 118 // prepare 119 reqData := "" 120 121 req := httptest.NewRequest("GET", "/event?b=test", strings.NewReader(reqData)) 122 recorder := httptest.NewRecorder() 123 124 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 125 126 // execute 127 e(recorder, req, nil) 128 129 d, err := io.ReadAll(recorder.Result().Body) 130 if err != nil { 131 t.Fatal(err) 132 } 133 134 // validate 135 assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 on request with missing type parameter") 136 assert.Equal(t, "invalid request: parameter 't' is required\n", string(d)) 137 } 138 139 func TestShouldReturnBadRequestWhenTypeIsInvalid(t *testing.T) { 140 141 // mock AccountsFetcher 142 mockAccounts := &mockAccountsFetcher{} 143 144 // mock PBS Analytics Module 145 mockAnalyticsModule := &eventsMockAnalyticsModule{ 146 Fail: false, 147 } 148 149 // mock config 150 cfg := &config.Configuration{ 151 AccountDefaults: config.Account{}, 152 } 153 154 // prepare 155 reqData := "" 156 157 req := httptest.NewRequest("GET", "/event?t=test&b=t", strings.NewReader(reqData)) 158 recorder := httptest.NewRecorder() 159 160 e := NewEventEndpoint(cfg, mockAccounts, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 161 162 // execute 163 e(recorder, req, nil) 164 165 d, err := io.ReadAll(recorder.Result().Body) 166 if err != nil { 167 t.Fatal(err) 168 } 169 170 // validate 171 assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 on request with invalid type parameter") 172 assert.Equal(t, "invalid request: unknown type: 'test'\n", string(d)) 173 } 174 175 func TestShouldReturnBadRequestWhenBidIdIsMissing(t *testing.T) { 176 177 // mock AccountsFetcher 178 mockAccountsFetcher := &mockAccountsFetcher{} 179 180 // mock PBS Analytics Module 181 mockAnalyticsModule := &eventsMockAnalyticsModule{ 182 Fail: false, 183 } 184 185 // mock config 186 cfg := &config.Configuration{ 187 AccountDefaults: config.Account{}, 188 } 189 190 // prepare 191 reqData := "" 192 193 req := httptest.NewRequest("GET", "/event?t=win", strings.NewReader(reqData)) 194 recorder := httptest.NewRecorder() 195 196 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 197 198 // execute 199 e(recorder, req, nil) 200 201 d, err := io.ReadAll(recorder.Result().Body) 202 if err != nil { 203 t.Fatal(err) 204 } 205 206 // validate 207 assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 on request with missing bidid parameter") 208 assert.Equal(t, "invalid request: parameter 'b' is required\n", string(d)) 209 } 210 211 func TestShouldReturnBadRequestWhenTimestampIsInvalid(t *testing.T) { 212 213 // mock AccountsFetcher 214 mockAccountsFetcher := &mockAccountsFetcher{} 215 216 // mock PBS Analytics Module 217 mockAnalyticsModule := &eventsMockAnalyticsModule{ 218 Fail: false, 219 } 220 221 // mock config 222 cfg := &config.Configuration{ 223 AccountDefaults: config.Account{}, 224 } 225 226 // prepare 227 reqData := "" 228 229 req := httptest.NewRequest("GET", "/event?t=win&b=test&ts=q", strings.NewReader(reqData)) 230 recorder := httptest.NewRecorder() 231 232 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 233 234 // execute 235 e(recorder, req, nil) 236 237 d, err := io.ReadAll(recorder.Result().Body) 238 if err != nil { 239 t.Fatal(err) 240 } 241 242 // validate 243 assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 on request with invalid timestamp parameter") 244 assert.Equal(t, "invalid request: invalid request: error parsing timestamp 'q'\n", string(d)) 245 } 246 247 func TestShouldReturnUnauthorizedWhenAccountIsMissing(t *testing.T) { 248 249 // mock AccountsFetcher 250 mockAccountsFetcher := &mockAccountsFetcher{} 251 252 // mock PBS Analytics Module 253 mockAnalyticsModule := &eventsMockAnalyticsModule{ 254 Fail: false, 255 } 256 257 // mock config 258 cfg := &config.Configuration{ 259 AccountDefaults: config.Account{}, 260 } 261 262 // prepare 263 reqData := "" 264 265 req := httptest.NewRequest("GET", "/event?t=win&b=test&ts=1234", strings.NewReader(reqData)) 266 recorder := httptest.NewRecorder() 267 268 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 269 270 // execute 271 e(recorder, req, nil) 272 273 d, err := io.ReadAll(recorder.Result().Body) 274 if err != nil { 275 t.Fatal(err) 276 } 277 278 // validate 279 assert.Equal(t, 401, recorder.Result().StatusCode, "Expected 401 on request with missing account id parameter") 280 assert.Equal(t, "Account 'a' is required query parameter and can't be empty", string(d)) 281 } 282 283 func TestShouldReturnBadRequestWhenFormatValueIsInvalid(t *testing.T) { 284 285 // mock AccountsFetcher 286 mockAccountsFetcher := &mockAccountsFetcher{} 287 288 // mock PBS Analytics Module 289 mockAnalyticsModule := &eventsMockAnalyticsModule{ 290 Fail: false, 291 } 292 293 // mock config 294 cfg := &config.Configuration{ 295 AccountDefaults: config.Account{}, 296 } 297 298 // prepare 299 reqData := "" 300 301 req := httptest.NewRequest("GET", "/event?t=win&b=test&ts=1234&f=q", strings.NewReader(reqData)) 302 recorder := httptest.NewRecorder() 303 304 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 305 306 // execute 307 e(recorder, req, nil) 308 309 d, err := io.ReadAll(recorder.Result().Body) 310 if err != nil { 311 t.Fatal(err) 312 } 313 314 // validate 315 assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 on request with invalid format parameter") 316 assert.Equal(t, "invalid request: unknown format: 'q'\n", string(d)) 317 } 318 319 func TestShouldReturnBadRequestWhenAnalyticsValueIsInvalid(t *testing.T) { 320 321 // mock AccountsFetcher 322 mockAccountsFetcher := &mockAccountsFetcher{} 323 324 // mock PBS Analytics Module 325 mockAnalyticsModule := &eventsMockAnalyticsModule{ 326 Fail: false, 327 } 328 329 // mock config 330 cfg := &config.Configuration{ 331 AccountDefaults: config.Account{}, 332 } 333 334 // prepare 335 reqData := "" 336 337 req := httptest.NewRequest("GET", "/event?t=win&b=test&ts=1234&f=b&x=4", strings.NewReader(reqData)) 338 recorder := httptest.NewRecorder() 339 340 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 341 342 // execute 343 e(recorder, req, nil) 344 345 d, err := io.ReadAll(recorder.Result().Body) 346 if err != nil { 347 t.Fatal(err) 348 } 349 350 // validate 351 assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 on request with invalid analytics parameter") 352 assert.Equal(t, "invalid request: unknown analytics: '4'\n", string(d)) 353 } 354 355 func TestShouldNotPassEventToAnalyticsReporterWhenAccountNotFoundAndDefaultIsFalse(t *testing.T) { 356 357 // mock AccountsFetcher 358 mockAccountsFetcher := &mockAccountsFetcher{ 359 Fail: true, 360 Error: stored_requests.NotFoundError{ID: "testacc"}, 361 } 362 363 // mock PBS Analytics Module 364 mockAnalyticsModule := &eventsMockAnalyticsModule{ 365 Fail: false, 366 } 367 368 // mock config 369 cfg := &config.Configuration{ 370 AccountDefaults: config.Account{}, 371 } 372 373 // prepare 374 reqData := "" 375 376 req := httptest.NewRequest("GET", "/event?t=win&b=test&ts=1234&f=b&x=1&a=testacc", strings.NewReader(reqData)) 377 recorder := httptest.NewRecorder() 378 379 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 380 381 // execute 382 e(recorder, req, nil) 383 d, err := io.ReadAll(recorder.Result().Body) 384 if err != nil { 385 t.Fatal(err) 386 } 387 388 // validate 389 assert.Equal(t, 401, recorder.Result().StatusCode, "Expected 401 on account not found") 390 assert.Equal(t, "Account 'testacc' doesn't support events", string(d)) 391 } 392 393 func TestShouldReturnBadRequestWhenIntegrationValueIsInvalid(t *testing.T) { 394 // mock AccountsFetcher 395 mockAccountsFetcher := &mockAccountsFetcher{} 396 397 // mock PBS Analytics Module 398 mockAnalyticsModule := &eventsMockAnalyticsModule{ 399 Fail: false, 400 } 401 402 // mock config 403 cfg := &config.Configuration{ 404 AccountDefaults: config.Account{}, 405 } 406 407 // prepare 408 reqData := "" 409 410 req := httptest.NewRequest("GET", "/event?t=win&b=bidId&f=b&ts=1000&x=1&a=accountId&bidder=bidder&int=Te$tIntegrationType", strings.NewReader(reqData)) 411 recorder := httptest.NewRecorder() 412 413 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 414 415 // execute 416 e(recorder, req, nil) 417 418 d, err := io.ReadAll(recorder.Result().Body) 419 if err != nil { 420 t.Fatal(err) 421 } 422 423 // validate 424 assert.Equal(t, 400, recorder.Result().StatusCode, "Expected 400 on request with invalid integration type parameter") 425 assert.Equal(t, "invalid request: integration type can only contain numbers, letters and these characters '-', '_'\n", string(d)) 426 } 427 428 func TestShouldNotPassEventToAnalyticsReporterWhenAccountEventNotEnabled(t *testing.T) { 429 430 // mock AccountsFetcher 431 mockAccountsFetcher := &mockAccountsFetcher{ 432 Fail: false, 433 } 434 435 // mock PBS Analytics Module 436 mockAnalyticsModule := &eventsMockAnalyticsModule{ 437 Fail: false, 438 } 439 440 // mock config 441 cfg := &config.Configuration{ 442 AccountDefaults: config.Account{}, 443 } 444 cfg.MarshalAccountDefaults() 445 446 // prepare 447 reqData := "" 448 449 req := httptest.NewRequest("GET", "/event?t=win&b=test&ts=1234&f=b&x=1&a=events_disabled", strings.NewReader(reqData)) 450 recorder := httptest.NewRecorder() 451 452 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 453 454 // execute 455 e(recorder, req, nil) 456 d, err := io.ReadAll(recorder.Result().Body) 457 if err != nil { 458 t.Fatal(err) 459 } 460 461 // validate 462 assert.Equal(t, 401, recorder.Result().StatusCode, "Expected 401 on account with events disabled") 463 assert.Equal(t, "Account 'events_disabled' doesn't support events", string(d)) 464 } 465 466 func TestShouldPassEventToAnalyticsReporterWhenAccountEventEnabled(t *testing.T) { 467 468 // mock AccountsFetcher 469 mockAccountsFetcher := &mockAccountsFetcher{ 470 Fail: false, 471 } 472 473 // mock PBS Analytics Module 474 mockAnalyticsModule := &eventsMockAnalyticsModule{ 475 Fail: false, 476 } 477 478 // mock config 479 cfg := &config.Configuration{ 480 AccountDefaults: config.Account{}, 481 } 482 cfg.MarshalAccountDefaults() 483 484 // prepare 485 reqData := "" 486 487 req := httptest.NewRequest("GET", "/event?t=win&b=test&ts=1234&f=b&x=1&a=events_enabled", strings.NewReader(reqData)) 488 recorder := httptest.NewRecorder() 489 490 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 491 492 // execute 493 e(recorder, req, nil) 494 495 // validate 496 assert.Equal(t, 204, recorder.Result().StatusCode, "Expected 204 when account has events enabled") 497 assert.Equal(t, true, mockAnalyticsModule.Invoked) 498 } 499 500 func TestShouldNotPassEventToAnalyticsReporterWhenAnalyticsValueIsZero(t *testing.T) { 501 502 // mock AccountsFetcher 503 mockAccountsFetcher := &mockAccountsFetcher{ 504 Fail: false, 505 } 506 507 // mock PBS Analytics Module 508 mockAnalyticsModule := &eventsMockAnalyticsModule{ 509 Fail: false, 510 } 511 512 // mock config 513 cfg := &config.Configuration{ 514 AccountDefaults: config.Account{}, 515 } 516 cfg.MarshalAccountDefaults() 517 518 // prepare 519 reqData := "" 520 521 req := httptest.NewRequest("GET", "/event?t=win&b=test&ts=1234&f=b&x=0&a=events_enabled", strings.NewReader(reqData)) 522 recorder := httptest.NewRecorder() 523 524 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 525 526 // execute 527 e(recorder, req, nil) 528 529 // validate 530 assert.Equal(t, 204, recorder.Result().StatusCode) 531 assert.Equal(t, true, mockAnalyticsModule.Invoked != true) 532 } 533 534 func TestShouldRespondWithPixelAndContentTypeWhenRequestFormatIsImage(t *testing.T) { 535 536 // mock AccountsFetcher 537 mockAccountsFetcher := &mockAccountsFetcher{ 538 Fail: false, 539 } 540 541 // mock PBS Analytics Module 542 mockAnalyticsModule := &eventsMockAnalyticsModule{ 543 Fail: false, 544 } 545 546 // mock config 547 cfg := &config.Configuration{ 548 AccountDefaults: config.Account{}, 549 } 550 cfg.MarshalAccountDefaults() 551 552 // prepare 553 reqData := "" 554 555 req := httptest.NewRequest("GET", "/event?t=win&b=test&ts=1234&f=i&x=1&a=events_enabled", strings.NewReader(reqData)) 556 recorder := httptest.NewRecorder() 557 558 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 559 560 // execute 561 e(recorder, req, nil) 562 563 d, err := io.ReadAll(recorder.Result().Body) 564 if err != nil { 565 t.Fatal(err) 566 } 567 568 // validate 569 assert.Equal(t, 200, recorder.Result().StatusCode, "Expected 200 with tracking pixel when format is imp") 570 assert.Equal(t, true, mockAnalyticsModule.Invoked) 571 assert.Equal(t, "image/png", recorder.Header().Get("Content-Type")) 572 assert.Equal(t, "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABHNCSVQICAgIfAhkiAAAAA1JREFUCJljYGBgYAAAAAUAAYehTtQAAAAASUVORK5CYII=", base64.URLEncoding.EncodeToString(d)) 573 } 574 575 func TestShouldRespondWithNoContentWhenRequestFormatIsNotDefined(t *testing.T) { 576 577 // mock AccountsFetcher 578 mockAccountsFetcher := &mockAccountsFetcher{ 579 Fail: false, 580 } 581 582 // mock PBS Analytics Module 583 mockAnalyticsModule := &eventsMockAnalyticsModule{ 584 Fail: false, 585 } 586 587 // mock config 588 cfg := &config.Configuration{ 589 AccountDefaults: config.Account{}, 590 } 591 cfg.MarshalAccountDefaults() 592 593 // prepare 594 reqData := "" 595 596 req := httptest.NewRequest("GET", "/event?t=imp&b=test&ts=1234&x=1&a=events_enabled", strings.NewReader(reqData)) 597 recorder := httptest.NewRecorder() 598 599 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 600 601 // execute 602 e(recorder, req, nil) 603 604 d, err := io.ReadAll(recorder.Result().Body) 605 if err != nil { 606 t.Fatal(err) 607 } 608 609 // validate 610 assert.Equal(t, 204, recorder.Result().StatusCode, "Expected 200 with empty response") 611 assert.Equal(t, true, mockAnalyticsModule.Invoked) 612 assert.Equal(t, "", recorder.Header().Get("Content-Type")) 613 assert.Equal(t, 0, len(d)) 614 } 615 616 func TestShouldParseEventCorrectly(t *testing.T) { 617 618 tests := map[string]struct { 619 req *http.Request 620 expected *analytics.EventRequest 621 }{ 622 "one": { 623 req: httptest.NewRequest("GET", "/event?t=win&b=bidId&f=b&ts=1000&x=1&a=accountId&bidder=bidder&int=intType", strings.NewReader("")), 624 expected: &analytics.EventRequest{ 625 Type: analytics.Win, 626 BidID: "bidId", 627 Timestamp: 1000, 628 Bidder: "bidder", 629 AccountID: "", 630 Format: analytics.Blank, 631 Analytics: analytics.Enabled, 632 Integration: "intType", 633 }, 634 }, 635 "two": { 636 req: httptest.NewRequest("GET", "/event?t=win&b=bidId&ts=0&a=accountId", strings.NewReader("")), 637 expected: &analytics.EventRequest{ 638 Type: analytics.Win, 639 BidID: "bidId", 640 Timestamp: 0, 641 Analytics: analytics.Enabled, 642 }, 643 }, 644 "three - vtype = start": { 645 req: httptest.NewRequest("GET", "/event?t=vast&vtype=start&b=bidId&ts=0&a=accountId", strings.NewReader("")), 646 expected: &analytics.EventRequest{ 647 Type: analytics.Vast, 648 VType: analytics.Start, 649 BidID: "bidId", 650 Timestamp: 0, 651 Analytics: analytics.Enabled, 652 }, 653 }, 654 "case insensitive bidder name": { 655 req: httptest.NewRequest("GET", "/event?t=win&b=bidId&f=b&ts=1000&x=1&a=accountId&bidder=RubiCon&int=intType", strings.NewReader("")), 656 expected: &analytics.EventRequest{ 657 Type: analytics.Win, 658 BidID: "bidId", 659 Timestamp: 1000, 660 Bidder: "rubicon", 661 AccountID: "", 662 Format: analytics.Blank, 663 Analytics: analytics.Enabled, 664 Integration: "intType", 665 }, 666 }, 667 } 668 669 for name, test := range tests { 670 t.Run(name, func(t *testing.T) { 671 672 // execute 673 er, errs := ParseEventRequest(test.req) 674 675 // validate 676 assert.Equal(t, 0, len(errs)) 677 assert.EqualValues(t, test.expected, er) 678 }) 679 } 680 } 681 682 func TestEventRequestToUrl(t *testing.T) { 683 externalUrl := "http://localhost:8000" 684 tests := map[string]struct { 685 er *analytics.EventRequest 686 want string 687 }{ 688 "one": { 689 er: &analytics.EventRequest{ 690 Type: analytics.Imp, 691 BidID: "bidid", 692 AccountID: "accountId", 693 Bidder: "bidder", 694 Timestamp: 1234567, 695 Format: analytics.Blank, 696 Analytics: analytics.Enabled, 697 }, 698 want: "http://localhost:8000/event?t=imp&b=bidid&a=accountId&bidder=bidder&f=b&ts=1234567&x=1", 699 }, 700 "two": { 701 er: &analytics.EventRequest{ 702 Type: analytics.Win, 703 BidID: "bidid", 704 AccountID: "accountId", 705 Bidder: "bidder", 706 Timestamp: 1234567, 707 Format: analytics.Image, 708 Analytics: analytics.Disabled, 709 }, 710 want: "http://localhost:8000/event?t=win&b=bidid&a=accountId&bidder=bidder&f=i&ts=1234567&x=0", 711 }, 712 "three": { 713 er: &analytics.EventRequest{ 714 Type: analytics.Win, 715 BidID: "bidid", 716 AccountID: "accountId", 717 Bidder: "bidder", 718 Timestamp: 1234567, 719 Format: analytics.Image, 720 Analytics: analytics.Disabled, 721 Integration: "integration", 722 }, 723 want: "http://localhost:8000/event?t=win&b=bidid&a=accountId&bidder=bidder&f=i&int=integration&ts=1234567&x=0", 724 }, 725 } 726 727 for name, test := range tests { 728 t.Run(name, func(t *testing.T) { 729 expected := EventRequestToUrl(externalUrl, test.er) 730 // validate 731 assert.Equal(t, test.want, expected) 732 }) 733 } 734 } 735 736 func TestReadIntegrationType(t *testing.T) { 737 testCases := []struct { 738 description string 739 givenHttpRequest *http.Request 740 expectedIntegrationType string 741 expectedError error 742 }{ 743 { 744 description: "Integration type in http request is valid, expect same integration time and no errors", 745 givenHttpRequest: httptest.NewRequest("GET", "/event?t=win&b=bidId&f=b&ts=1000&x=1&a=accountId&bidder=bidder&int=TestIntegrationType", strings.NewReader("")), 746 expectedIntegrationType: "TestIntegrationType", 747 expectedError: nil, 748 }, 749 { 750 description: "Integration type in http request is too long, expect too long error", 751 givenHttpRequest: httptest.NewRequest("GET", "/event?t=win&b=bidId&f=b&ts=1000&x=1&a=accountId&bidder=bidder&int=TestIntegrationTypeTooLongTestIntegrationTypeTooLongTestIntegrationType", strings.NewReader("")), 752 expectedError: errors.New("integration type length is too long"), 753 }, 754 { 755 description: "Integration type in http request contains invalid character, expect invalid character error", 756 givenHttpRequest: httptest.NewRequest("GET", "/event?t=win&b=bidId&f=b&ts=1000&x=1&a=accountId&bidder=bidder&int=Te$tIntegrationType", strings.NewReader("")), 757 expectedError: errors.New("integration type can only contain numbers, letters and these characters '-', '_'"), 758 }, 759 } 760 761 for _, test := range testCases { 762 testEventRequest := &analytics.EventRequest{} 763 err := readIntegrationType(testEventRequest, test.givenHttpRequest) 764 if test.expectedError != nil { 765 assert.Equal(t, test.expectedError, err, test.description) 766 } else { 767 assert.Empty(t, err, test.description) 768 assert.Equalf(t, test.expectedIntegrationType, testEventRequest.Integration, test.description) 769 } 770 } 771 } 772 773 func TestShouldReturnBadRequestWhenVTypeIsInvalid(t *testing.T) { 774 775 reqData := "" 776 777 tests := []struct { 778 description string 779 req *http.Request 780 expectedStatusCode int 781 expectedStatus string 782 }{ 783 { 784 description: "vtype parameter is missing", 785 req: httptest.NewRequest("GET", "/event?t=vast&b=bidID", strings.NewReader(reqData)), 786 expectedStatusCode: 400, 787 expectedStatus: "invalid request: parameter 'vtype' is required\n", 788 }, 789 { 790 description: "invalid vtype parameter", 791 req: httptest.NewRequest("GET", "/event?t=vast&vtype=abc&b=bidID", strings.NewReader(reqData)), 792 expectedStatusCode: 400, 793 expectedStatus: "invalid request: unknown vtype: 'abc'\n", 794 }, 795 { 796 description: "vtype is passed when event != vast", 797 req: httptest.NewRequest("GET", "/event?t=win&vtype=startc&b=bidID", strings.NewReader(reqData)), 798 expectedStatusCode: 400, 799 expectedStatus: "invalid request: parameter 'vtype' is only required for t=vast\n", 800 }, 801 } 802 803 for _, test := range tests { 804 mockAccountsFetcher := &mockAccountsFetcher{} 805 806 mockAnalyticsModule := &eventsMockAnalyticsModule{ 807 Fail: false, 808 } 809 810 cfg := &config.Configuration{ 811 AccountDefaults: config.Account{}, 812 } 813 814 recorder := httptest.NewRecorder() 815 816 e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule, &metrics.MetricsEngineMock{}) 817 e(recorder, test.req, nil) 818 819 d, err := io.ReadAll(recorder.Result().Body) 820 if err != nil { 821 t.Fatal(err) 822 } 823 824 assert.Equal(t, test.expectedStatusCode, recorder.Result().StatusCode, test.description) 825 assert.Equal(t, test.expectedStatus, string(d), test.description) 826 827 } 828 } 829 830 func TestReadVType(t *testing.T) { 831 type args struct { 832 er *analytics.EventRequest 833 req *http.Request 834 } 835 tests := []struct { 836 name string 837 args args 838 expectedError error 839 expectedVType analytics.VastType 840 }{ 841 { 842 name: "vtype = start", 843 args: args{ 844 er: &analytics.EventRequest{ 845 Type: analytics.Vast, 846 }, 847 req: httptest.NewRequest("GET", "/event?t=vast&vtype=start&b=bidId&ts=0&a=accountId", strings.NewReader("")), 848 }, 849 expectedError: nil, 850 expectedVType: analytics.Start, 851 }, 852 { 853 name: "vtype = firstQuartile", 854 args: args{ 855 er: &analytics.EventRequest{ 856 Type: analytics.Vast, 857 }, 858 req: httptest.NewRequest("GET", "/event?t=vast&vtype=firstQuartile&b=bidId&ts=0&a=accountId", strings.NewReader("")), 859 }, 860 expectedError: nil, 861 expectedVType: analytics.FirstQuartile, 862 }, 863 { 864 name: "vtype = midPoint", 865 args: args{ 866 er: &analytics.EventRequest{ 867 Type: analytics.Vast, 868 }, 869 req: httptest.NewRequest("GET", "/event?t=vast&vtype=midPoint&b=bidId&ts=0&a=accountId", strings.NewReader("")), 870 }, 871 expectedError: nil, 872 expectedVType: analytics.MidPoint, 873 }, 874 { 875 name: "vtype = thirdQuartile", 876 args: args{ 877 er: &analytics.EventRequest{ 878 Type: analytics.Vast, 879 }, 880 req: httptest.NewRequest("GET", "/event?t=vast&vtype=thirdQuartile&b=bidId&ts=0&a=accountId", strings.NewReader("")), 881 }, 882 expectedError: nil, 883 expectedVType: analytics.ThirdQuartile, 884 }, 885 { 886 name: "vtype = complete", 887 args: args{ 888 er: &analytics.EventRequest{ 889 Type: analytics.Vast, 890 }, 891 req: httptest.NewRequest("GET", "/event?t=vast&vtype=complete&b=bidId&ts=0&a=accountId", strings.NewReader("")), 892 }, 893 expectedError: nil, 894 expectedVType: analytics.Complete, 895 }, 896 { 897 name: "unknown vtype", 898 args: args{ 899 er: &analytics.EventRequest{ 900 Type: analytics.Vast, 901 }, 902 req: httptest.NewRequest("GET", "/event?t=vast&vtype=test&b=bidId&ts=0&a=accountId", strings.NewReader("")), 903 }, 904 expectedError: &errortypes.BadInput{Message: "unknown vtype: 'test'"}, 905 expectedVType: "", 906 }, 907 } 908 for _, tt := range tests { 909 t.Run(tt.name, func(t *testing.T) { 910 err := readVType(tt.args.er, tt.args.req) 911 assert.Equal(t, tt.expectedError, err, tt.name) 912 assert.Equal(t, tt.expectedVType, tt.args.er.VType, tt.name) 913 }) 914 } 915 }