github.com/prebid/prebid-server/v2@v2.18.0/analytics/agma/agma_module_test.go (about)

     1  package agma
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"sync"
     9  	"syscall"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/benbjohnson/clock"
    14  	"github.com/prebid/openrtb/v20/openrtb2"
    15  	"github.com/prebid/prebid-server/v2/analytics"
    16  	"github.com/prebid/prebid-server/v2/config"
    17  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/mock"
    20  )
    21  
    22  var agmaConsent = "CP6-v9RP6-v9RNlAAAENCZCAAICAAAAAAAAAIxQAQIxAAAAA.II7Nd_X__bX9n-_7_6ft0eY1f9_r37uQzDhfNs-8F3L_W_LwX32E7NF36tq4KmR4ku1bBIQNtHMnUDUmxaolVrzHsak2cpyNKJ_JkknsZe2dYGF9Pn9lD-YKZ7_5_9_f52T_9_9_-39z3_9f___dv_-__-vjf_599n_v9fV_78_Kf9______-____________8A"
    23  
    24  var mockValidAuctionObject = analytics.AuctionObject{
    25  	Status:    http.StatusOK,
    26  	StartTime: time.Date(2023, 2, 1, 0, 0, 0, 0, time.UTC),
    27  	RequestWrapper: &openrtb_ext.RequestWrapper{
    28  		BidRequest: &openrtb2.BidRequest{
    29  			ID: "some-id",
    30  			Site: &openrtb2.Site{
    31  				ID: "track-me-site",
    32  				Publisher: &openrtb2.Publisher{
    33  					ID: "track-me",
    34  				},
    35  			},
    36  			Device: &openrtb2.Device{
    37  				UA: "ua",
    38  			},
    39  			User: &openrtb2.User{
    40  				Ext: json.RawMessage(`{"consent": "` + agmaConsent + `"}`),
    41  			},
    42  		},
    43  	},
    44  }
    45  
    46  var mockValidVideoObject = analytics.VideoObject{
    47  	Status:    http.StatusOK,
    48  	StartTime: time.Date(2023, 2, 1, 0, 0, 0, 0, time.UTC),
    49  	RequestWrapper: &openrtb_ext.RequestWrapper{
    50  		BidRequest: &openrtb2.BidRequest{
    51  			ID: "some-id",
    52  			App: &openrtb2.App{
    53  				ID: "track-me-app",
    54  				Publisher: &openrtb2.Publisher{
    55  					ID: "track-me",
    56  				},
    57  			},
    58  			Device: &openrtb2.Device{
    59  				UA: "ua",
    60  			},
    61  			User: &openrtb2.User{
    62  				Ext: json.RawMessage(`{"consent": "` + agmaConsent + `"}`),
    63  			},
    64  		},
    65  	},
    66  }
    67  
    68  var mockValidAmpObject = analytics.AmpObject{
    69  	Status:    http.StatusOK,
    70  	StartTime: time.Date(2023, 2, 1, 0, 0, 0, 0, time.UTC),
    71  	RequestWrapper: &openrtb_ext.RequestWrapper{
    72  		BidRequest: &openrtb2.BidRequest{
    73  			ID: "some-id",
    74  			Site: &openrtb2.Site{
    75  				ID: "track-me-site",
    76  				Publisher: &openrtb2.Publisher{
    77  					ID: "track-me",
    78  				},
    79  			},
    80  			Device: &openrtb2.Device{
    81  				UA: "ua",
    82  			},
    83  			User: &openrtb2.User{
    84  				Ext: json.RawMessage(`{"consent": "` + agmaConsent + `"}`),
    85  			},
    86  		},
    87  	},
    88  }
    89  
    90  var mockValidAccounts = []config.AgmaAnalyticsAccount{
    91  	{
    92  		PublisherId: "track-me",
    93  		Code:        "abc",
    94  		SiteAppId:   "track-me-app",
    95  	},
    96  	{
    97  		PublisherId: "track-me",
    98  		Code:        "abcd",
    99  		SiteAppId:   "track-me-site",
   100  	},
   101  }
   102  
   103  type MockedSender struct {
   104  	mock.Mock
   105  }
   106  
   107  func (m *MockedSender) Send(payload []byte) error {
   108  	args := m.Called(payload)
   109  	return args.Error(0)
   110  }
   111  
   112  func TestConfigParsingError(t *testing.T) {
   113  	testCases := []struct {
   114  		name       string
   115  		config     config.AgmaAnalytics
   116  		shouldFail bool
   117  	}{
   118  		{
   119  			name: "Test with invalid/empty URL",
   120  			config: config.AgmaAnalytics{
   121  				Enabled: true,
   122  				Endpoint: config.AgmaAnalyticsHttpEndpoint{
   123  					Url:     "%%2815197306101420000%29",
   124  					Timeout: "1s",
   125  					Gzip:    false,
   126  				},
   127  			},
   128  			shouldFail: true,
   129  		},
   130  		{
   131  			name: "Test with invalid timout",
   132  			config: config.AgmaAnalytics{
   133  				Enabled: true,
   134  				Endpoint: config.AgmaAnalyticsHttpEndpoint{
   135  					Url:     "http://localhost:8000/event",
   136  					Timeout: "1x",
   137  					Gzip:    false,
   138  				},
   139  			},
   140  			shouldFail: true,
   141  		},
   142  		{
   143  			name: "Test with no accounts",
   144  			config: config.AgmaAnalytics{
   145  				Enabled: true,
   146  				Endpoint: config.AgmaAnalyticsHttpEndpoint{
   147  					Url:     "http://localhost:8000/event",
   148  					Timeout: "1s",
   149  					Gzip:    false,
   150  				},
   151  				Buffers: config.AgmaAnalyticsBuffer{
   152  					EventCount: 1,
   153  					BufferSize: "1Kb",
   154  					Timeout:    "1s",
   155  				},
   156  				Accounts: []config.AgmaAnalyticsAccount{},
   157  			},
   158  			shouldFail: true,
   159  		},
   160  	}
   161  	clockMock := clock.NewMock()
   162  	for _, tc := range testCases {
   163  		t.Run(tc.name, func(t *testing.T) {
   164  			_, err := NewModule(&http.Client{}, tc.config, clockMock)
   165  			if tc.shouldFail {
   166  				assert.Error(t, err)
   167  			} else {
   168  				assert.NoError(t, err)
   169  			}
   170  		})
   171  	}
   172  }
   173  
   174  func TestShouldTrackEvent(t *testing.T) {
   175  	cfg := config.AgmaAnalytics{
   176  		Enabled: true,
   177  		Endpoint: config.AgmaAnalyticsHttpEndpoint{
   178  			Url:     "http://localhost:8000/event",
   179  			Timeout: "5s",
   180  		},
   181  		Buffers: config.AgmaAnalyticsBuffer{
   182  			EventCount: 1,
   183  			BufferSize: "1Kb",
   184  			Timeout:    "1s",
   185  		},
   186  		Accounts: []config.AgmaAnalyticsAccount{
   187  			{
   188  				PublisherId: "track-me",
   189  				Code:        "abc",
   190  			},
   191  		},
   192  	}
   193  	mockedSender := new(MockedSender)
   194  	mockedSender.On("Send", mock.Anything).Return(nil)
   195  	clockMock := clock.NewMock()
   196  	logger, err := newAgmaLogger(cfg, mockedSender.Send, clockMock)
   197  	assert.NoError(t, err)
   198  
   199  	// no userExt
   200  	shouldTrack, code := logger.shouldTrackEvent(&openrtb_ext.RequestWrapper{
   201  		BidRequest: &openrtb2.BidRequest{
   202  			ID: "some-id",
   203  			App: &openrtb2.App{
   204  				ID: "com.app.test",
   205  				Publisher: &openrtb2.Publisher{
   206  					ID: "track-me-not",
   207  				},
   208  			},
   209  			User: &openrtb2.User{
   210  				Ext: json.RawMessage(`{"consent": "` + agmaConsent + `"}`),
   211  			},
   212  		},
   213  	})
   214  
   215  	assert.False(t, shouldTrack)
   216  	assert.Equal(t, "", code)
   217  
   218  	// no userExt
   219  	shouldTrack, code = logger.shouldTrackEvent(&openrtb_ext.RequestWrapper{
   220  		BidRequest: &openrtb2.BidRequest{
   221  			App: &openrtb2.App{
   222  				ID: "com.app.test",
   223  				Publisher: &openrtb2.Publisher{
   224  					ID: "track-me",
   225  				},
   226  			},
   227  		},
   228  	})
   229  
   230  	assert.False(t, shouldTrack)
   231  	assert.Equal(t, "", code)
   232  
   233  	// Constent: No agma
   234  	shouldTrack, code = logger.shouldTrackEvent(&openrtb_ext.RequestWrapper{
   235  		BidRequest: &openrtb2.BidRequest{
   236  			App: &openrtb2.App{
   237  				ID: "com.app.test",
   238  				Publisher: &openrtb2.Publisher{
   239  					ID: "track-me",
   240  				},
   241  			},
   242  			User: &openrtb2.User{
   243  				Ext: json.RawMessage(`{"consent": "CP4LywcP4LywcLRAAAENCZCAAAIAAAIAAAAAIxQAQIwgAAAA.II7Nd_X__bX9n-_7_6ft0eY1f9_r37uQzDhfNs-8F3L_W_LwX32E7NF36tq4KmR4ku1bBIQNtHMnUDUmxaolVrzHsak2cpyNKJ_JkknsZe2dYGF9Pn9lD-YKZ7_5_9_f52T_9_9_-39z3_9f___dv_-__-vjf_599n_v9fV_78_Kf9______-____________8A"}`),
   244  			},
   245  		},
   246  	})
   247  
   248  	assert.False(t, shouldTrack)
   249  	assert.Equal(t, "", code)
   250  
   251  	// Constent: No Purpose 9
   252  	shouldTrack, code = logger.shouldTrackEvent(&openrtb_ext.RequestWrapper{
   253  		BidRequest: &openrtb2.BidRequest{
   254  			App: &openrtb2.App{
   255  				ID: "com.app.test",
   256  				Publisher: &openrtb2.Publisher{
   257  					ID: "track-me",
   258  				},
   259  			},
   260  			User: &openrtb2.User{
   261  				Ext: json.RawMessage(`{"consent": "CP4LywcP4LywcLRAAAENCZCAAIAAAAAAAAAAIxQAQIxAAAAA.II7Nd_X__bX9n-_7_6ft0eY1f9_r37uQzDhfNs-8F3L_W_LwX32E7NF36tq4KmR4ku1bBIQNtHMnUDUmxaolVrzHsak2cpyNKJ_JkknsZe2dYGF9Pn9lD-YKZ7_5_9_f52T_9_9_-39z3_9f___dv_-__-vjf_599n_v9fV_78_Kf9______-____________8A"}`),
   262  			},
   263  		},
   264  	})
   265  
   266  	assert.False(t, shouldTrack)
   267  	assert.Equal(t, "", code)
   268  
   269  	// No valid sites / apps / empty publisher app
   270  	shouldTrack, code = logger.shouldTrackEvent(&openrtb_ext.RequestWrapper{
   271  		BidRequest: &openrtb2.BidRequest{
   272  			App: &openrtb2.App{
   273  				ID: "",
   274  				Publisher: &openrtb2.Publisher{
   275  					ID: "",
   276  				},
   277  			},
   278  			User: &openrtb2.User{
   279  				Ext: json.RawMessage(`{"consent": "` + agmaConsent + `"}`),
   280  			},
   281  		},
   282  	})
   283  
   284  	assert.False(t, shouldTrack)
   285  	assert.Equal(t, "", code)
   286  }
   287  
   288  func TestShouldTrackMultipleAccounts(t *testing.T) {
   289  	cfg := config.AgmaAnalytics{
   290  		Enabled: true,
   291  		Endpoint: config.AgmaAnalyticsHttpEndpoint{
   292  			Url:     "http://localhost:8000/event",
   293  			Timeout: "5s",
   294  		},
   295  		Buffers: config.AgmaAnalyticsBuffer{
   296  			EventCount: 1,
   297  			BufferSize: "1Kb",
   298  			Timeout:    "1s",
   299  		},
   300  		Accounts: []config.AgmaAnalyticsAccount{
   301  			{
   302  				PublisherId: "track-me-a",
   303  				Code:        "abc",
   304  			},
   305  			{
   306  				PublisherId: "track-me-b",
   307  				Code:        "123",
   308  			},
   309  		},
   310  	}
   311  	mockedSender := new(MockedSender)
   312  	mockedSender.On("Send", mock.Anything).Return(nil)
   313  	clockMock := clock.NewMock()
   314  	logger, err := newAgmaLogger(cfg, mockedSender.Send, clockMock)
   315  	assert.NoError(t, err)
   316  
   317  	shouldTrack, code := logger.shouldTrackEvent(&openrtb_ext.RequestWrapper{
   318  		BidRequest: &openrtb2.BidRequest{
   319  			ID: "some-id",
   320  			App: &openrtb2.App{
   321  				ID: "com.app.test",
   322  				Publisher: &openrtb2.Publisher{
   323  					ID: "track-me-a",
   324  				},
   325  			},
   326  			User: &openrtb2.User{
   327  				Ext: json.RawMessage(`{"consent": "` + agmaConsent + `"}`),
   328  			},
   329  		},
   330  	})
   331  
   332  	assert.True(t, shouldTrack)
   333  	assert.Equal(t, "abc", code)
   334  
   335  	shouldTrack, code = logger.shouldTrackEvent(&openrtb_ext.RequestWrapper{
   336  		BidRequest: &openrtb2.BidRequest{
   337  			ID: "some-id",
   338  			Site: &openrtb2.Site{
   339  				ID: "site-test",
   340  				Publisher: &openrtb2.Publisher{
   341  					ID: "track-me-b",
   342  				},
   343  			},
   344  			User: &openrtb2.User{
   345  				Ext: json.RawMessage(`{"consent": "` + agmaConsent + `"}`),
   346  			},
   347  		},
   348  	})
   349  
   350  	assert.True(t, shouldTrack)
   351  	assert.Equal(t, "123", code)
   352  }
   353  
   354  func TestShouldNotTrackLog(t *testing.T) {
   355  	testCases := []struct {
   356  		name   string
   357  		config config.AgmaAnalytics
   358  	}{
   359  		{
   360  			name: "Test with do-not-track PublisherId",
   361  			config: config.AgmaAnalytics{
   362  				Enabled: true,
   363  				Endpoint: config.AgmaAnalyticsHttpEndpoint{
   364  					Url:     "http://localhost:8000/event",
   365  					Timeout: "5s",
   366  				},
   367  				Buffers: config.AgmaAnalyticsBuffer{
   368  					EventCount: 1,
   369  					BufferSize: "1Kb",
   370  					Timeout:    "1s",
   371  				},
   372  				Accounts: []config.AgmaAnalyticsAccount{
   373  					{
   374  						PublisherId: "do-not-track",
   375  						Code:        "abc",
   376  					},
   377  				},
   378  			},
   379  		},
   380  		{
   381  			name: "Test with do-not-track PublisherId",
   382  			config: config.AgmaAnalytics{
   383  				Enabled: true,
   384  				Endpoint: config.AgmaAnalyticsHttpEndpoint{
   385  					Url:     "http://localhost:8000/event",
   386  					Timeout: "5s",
   387  				},
   388  				Buffers: config.AgmaAnalyticsBuffer{
   389  					EventCount: 1,
   390  					BufferSize: "1Kb",
   391  					Timeout:    "1s",
   392  				},
   393  				Accounts: []config.AgmaAnalyticsAccount{
   394  					{
   395  						PublisherId: "track-me",
   396  						Code:        "abc",
   397  						SiteAppId:   "do-not-track",
   398  					},
   399  				},
   400  			},
   401  		},
   402  	}
   403  	for _, tc := range testCases {
   404  		t.Run(tc.name, func(t *testing.T) {
   405  			mockedSender := new(MockedSender)
   406  			mockedSender.On("Send", mock.Anything).Return(nil)
   407  			clockMock := clock.NewMock()
   408  			logger, err := newAgmaLogger(tc.config, mockedSender.Send, clockMock)
   409  			assert.NoError(t, err)
   410  
   411  			go logger.start()
   412  			assert.Zero(t, logger.eventCount)
   413  
   414  			logger.LogAuctionObject(&mockValidAuctionObject)
   415  			logger.LogVideoObject(&mockValidVideoObject)
   416  			logger.LogAmpObject(&mockValidAmpObject)
   417  
   418  			clockMock.Add(2 * time.Minute)
   419  			mockedSender.AssertNumberOfCalls(t, "Send", 0)
   420  			assert.Zero(t, logger.eventCount)
   421  		})
   422  	}
   423  }
   424  
   425  func TestRaceAllEvents(t *testing.T) {
   426  	cfg := config.AgmaAnalytics{
   427  		Enabled: true,
   428  		Endpoint: config.AgmaAnalyticsHttpEndpoint{
   429  			Url:     "http://localhost:8000/event",
   430  			Timeout: "5s",
   431  		},
   432  		Buffers: config.AgmaAnalyticsBuffer{
   433  			EventCount: 10000,
   434  			BufferSize: "100Mb",
   435  			Timeout:    "5m",
   436  		},
   437  		Accounts: mockValidAccounts,
   438  	}
   439  	mockedSender := new(MockedSender)
   440  	mockedSender.On("Send", mock.Anything).Return(nil)
   441  	clockMock := clock.NewMock()
   442  	logger, err := newAgmaLogger(cfg, mockedSender.Send, clockMock)
   443  	assert.NoError(t, err)
   444  
   445  	go logger.start()
   446  
   447  	logger.LogAuctionObject(&mockValidAuctionObject)
   448  	logger.LogVideoObject(&mockValidVideoObject)
   449  	logger.LogAmpObject(&mockValidAmpObject)
   450  	clockMock.Add(10 * time.Millisecond)
   451  
   452  	logger.mux.RLock()
   453  	assert.Equal(t, int64(3), logger.eventCount)
   454  	logger.mux.RUnlock()
   455  }
   456  
   457  func TestFlushOnSigterm(t *testing.T) {
   458  	cfg := config.AgmaAnalytics{
   459  		Enabled: true,
   460  		Endpoint: config.AgmaAnalyticsHttpEndpoint{
   461  			Url:     "http://localhost:8000/event",
   462  			Timeout: "5s",
   463  		},
   464  		Buffers: config.AgmaAnalyticsBuffer{
   465  			EventCount: 10000,
   466  			BufferSize: "100Mb",
   467  			Timeout:    "5m",
   468  		},
   469  		Accounts: mockValidAccounts,
   470  	}
   471  	mockedSender := new(MockedSender)
   472  	mockedSender.On("Send", mock.Anything).Return(nil)
   473  	clockMock := clock.NewMock()
   474  	logger, err := newAgmaLogger(cfg, mockedSender.Send, clockMock)
   475  	assert.NoError(t, err)
   476  
   477  	done := make(chan struct{})
   478  	go func() {
   479  		logger.start()
   480  		close(done)
   481  	}()
   482  
   483  	logger.LogAuctionObject(&mockValidAuctionObject)
   484  	logger.LogVideoObject(&mockValidVideoObject)
   485  	logger.LogAmpObject(&mockValidAmpObject)
   486  
   487  	logger.sigTermCh <- syscall.SIGTERM
   488  	<-done
   489  
   490  	time.Sleep(100 * time.Millisecond)
   491  
   492  	mockedSender.AssertCalled(t, "Send", mock.Anything)
   493  }
   494  
   495  func TestRaceBufferCount(t *testing.T) {
   496  	cfg := config.AgmaAnalytics{
   497  		Enabled: true,
   498  		Endpoint: config.AgmaAnalyticsHttpEndpoint{
   499  			Url:     "http://localhost:8000/event",
   500  			Timeout: "5s",
   501  		},
   502  		Buffers: config.AgmaAnalyticsBuffer{
   503  			EventCount: 2,
   504  			BufferSize: "100Mb",
   505  			Timeout:    "5m",
   506  		},
   507  		Accounts: []config.AgmaAnalyticsAccount{
   508  			{
   509  				PublisherId: "track-me",
   510  				Code:        "abc",
   511  			},
   512  		},
   513  	}
   514  	mockedSender := new(MockedSender)
   515  	mockedSender.On("Send", mock.Anything).Return(nil)
   516  	clockMock := clock.NewMock()
   517  	logger, err := newAgmaLogger(cfg, mockedSender.Send, clockMock)
   518  	assert.NoError(t, err)
   519  
   520  	go logger.start()
   521  	assert.Zero(t, logger.eventCount)
   522  
   523  	// Test EventCount Buffer
   524  	logger.LogAuctionObject(&mockValidAuctionObject)
   525  
   526  	clockMock.Add(1 * time.Millisecond)
   527  
   528  	logger.mux.RLock()
   529  	assert.Equal(t, int64(1), logger.eventCount)
   530  	logger.mux.RUnlock()
   531  
   532  	assert.Equal(t, false, logger.isFull())
   533  
   534  	// add 1 more
   535  	logger.LogAuctionObject(&mockValidAuctionObject)
   536  	clockMock.Add(1 * time.Millisecond)
   537  
   538  	// should trigger send and flash the buffer
   539  	mockedSender.AssertCalled(t, "Send", mock.Anything)
   540  
   541  	logger.mux.RLock()
   542  	assert.Equal(t, int64(0), logger.eventCount)
   543  	logger.mux.RUnlock()
   544  }
   545  
   546  func TestBufferSize(t *testing.T) {
   547  	cfg := config.AgmaAnalytics{
   548  		Enabled: true,
   549  		Endpoint: config.AgmaAnalyticsHttpEndpoint{
   550  			Url:     "http://localhost:8000/event",
   551  			Timeout: "5s",
   552  		},
   553  		Buffers: config.AgmaAnalyticsBuffer{
   554  			EventCount: 1000,
   555  			BufferSize: "20Kb",
   556  			Timeout:    "5m",
   557  		},
   558  		Accounts: []config.AgmaAnalyticsAccount{
   559  			{
   560  				PublisherId: "track-me",
   561  				Code:        "abc",
   562  			},
   563  		},
   564  	}
   565  	mockedSender := new(MockedSender)
   566  	mockedSender.On("Send", mock.Anything).Return(nil)
   567  	clockMock := clock.NewMock()
   568  	logger, err := newAgmaLogger(cfg, mockedSender.Send, clockMock)
   569  	assert.NoError(t, err)
   570  
   571  	go logger.start()
   572  
   573  	for i := 0; i < 50; i++ {
   574  		logger.LogAuctionObject(&mockValidAuctionObject)
   575  	}
   576  	clockMock.Add(10 * time.Millisecond)
   577  	mockedSender.AssertCalled(t, "Send", mock.Anything)
   578  	mockedSender.AssertNumberOfCalls(t, "Send", 1)
   579  }
   580  
   581  func TestBufferTime(t *testing.T) {
   582  	cfg := config.AgmaAnalytics{
   583  		Enabled: true,
   584  		Endpoint: config.AgmaAnalyticsHttpEndpoint{
   585  			Url:     "http://localhost:8000/event",
   586  			Timeout: "5s",
   587  		},
   588  		Buffers: config.AgmaAnalyticsBuffer{
   589  			EventCount: 1000,
   590  			BufferSize: "100mb",
   591  			Timeout:    "5m",
   592  		},
   593  		Accounts: []config.AgmaAnalyticsAccount{
   594  			{
   595  				PublisherId: "track-me",
   596  				Code:        "abc",
   597  			},
   598  		},
   599  	}
   600  	mockedSender := new(MockedSender)
   601  	mockedSender.On("Send", mock.Anything).Return(nil)
   602  	clockMock := clock.NewMock()
   603  	logger, err := newAgmaLogger(cfg, mockedSender.Send, clockMock)
   604  	assert.NoError(t, err)
   605  
   606  	go logger.start()
   607  
   608  	for i := 0; i < 5; i++ {
   609  		logger.LogAuctionObject(&mockValidAuctionObject)
   610  	}
   611  	clockMock.Add(10 * time.Minute)
   612  	mockedSender.AssertCalled(t, "Send", mock.Anything)
   613  	mockedSender.AssertNumberOfCalls(t, "Send", 1)
   614  }
   615  
   616  func TestRaceEnd2End(t *testing.T) {
   617  	var mu sync.Mutex
   618  
   619  	requestBodyAsString := ""
   620  
   621  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   622  		// Check for reponse
   623  		requestBody, err := io.ReadAll(r.Body)
   624  		mu.Lock()
   625  		requestBodyAsString = string(requestBody)
   626  		mu.Unlock()
   627  		if err != nil {
   628  			http.Error(w, "Error reading request body", 500)
   629  			return
   630  		}
   631  
   632  		w.WriteHeader(http.StatusOK)
   633  	}))
   634  	cfg := config.AgmaAnalytics{
   635  		Enabled: true,
   636  		Endpoint: config.AgmaAnalyticsHttpEndpoint{
   637  			Url:     server.URL,
   638  			Timeout: "5s",
   639  		},
   640  		Buffers: config.AgmaAnalyticsBuffer{
   641  			EventCount: 2,
   642  			BufferSize: "100mb",
   643  			Timeout:    "5m",
   644  		},
   645  		Accounts: mockValidAccounts,
   646  	}
   647  
   648  	clockMock := clock.NewMock()
   649  	clockMock.Set(time.Date(2023, 2, 1, 0, 0, 0, 0, time.UTC))
   650  
   651  	logger, err := NewModule(&http.Client{}, cfg, clockMock)
   652  	assert.NoError(t, err)
   653  
   654  	logger.LogAmpObject(&mockValidAmpObject)
   655  	logger.LogAmpObject(&mockValidAmpObject)
   656  
   657  	time.Sleep(250 * time.Millisecond)
   658  
   659  	expected := "[{\"type\":\"amp\",\"id\":\"some-id\",\"code\":\"abcd\",\"site\":{\"id\":\"track-me-site\",\"publisher\":{\"id\":\"track-me\"}},\"device\":{\"ua\":\"ua\"},\"user\":{\"ext\":{\"consent\": \"" + agmaConsent + "\"}},\"created_at\":\"2023-02-01T00:00:00Z\"},{\"type\":\"amp\",\"id\":\"some-id\",\"code\":\"abcd\",\"site\":{\"id\":\"track-me-site\",\"publisher\":{\"id\":\"track-me\"}},\"device\":{\"ua\":\"ua\"},\"user\":{\"ext\":{\"consent\": \"" + agmaConsent + "\"}},\"created_at\":\"2023-02-01T00:00:00Z\"}]"
   660  
   661  	mu.Lock()
   662  	actual := requestBodyAsString
   663  	mu.Unlock()
   664  
   665  	assert.Equal(t, expected, actual)
   666  }