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  }