TestPublisherClaimExpiry tests the claim query to ensure that events are not claimed if they've recently been claimed by another publisher.
(t *testing.T)
| 274 | // TestPublisherClaimExpiry tests the claim query to ensure that events are not |
| 275 | // claimed if they've recently been claimed by another publisher. |
| 276 | func TestPublisherClaimExpiry(t *testing.T) { |
| 277 | t.Parallel() |
| 278 | ctx := testutil.Context(t, testutil.WaitLong) |
| 279 | log := slogtest.Make(t, nil) |
| 280 | db, _ := dbtestutil.NewDB(t) |
| 281 | clock := quartz.NewMock(t) |
| 282 | deploymentID, licenseJWT := configureDeployment(ctx, t, db) |
| 283 | now := time.Now() |
| 284 | |
| 285 | var calls int |
| 286 | ingestURL := fakeServer(t, tallymanHandler(t, deploymentID.String(), licenseJWT, func(req usagetypes.TallymanV1IngestRequest) any { |
| 287 | calls++ |
| 288 | return tallymanAcceptAllHandler(req) |
| 289 | })) |
| 290 | |
| 291 | inserter := usage.NewDBInserter( |
| 292 | usage.InserterWithClock(clock), |
| 293 | ) |
| 294 | |
| 295 | publisher := usage.NewTallymanPublisher(ctx, log, db, coderdenttest.Keys, |
| 296 | usage.PublisherWithClock(clock), |
| 297 | usage.PublisherWithIngestURL(ingestURL), |
| 298 | usage.PublisherWithInitialDelay(17*time.Minute), |
| 299 | ) |
| 300 | defer publisher.Close() |
| 301 | |
| 302 | // Create an event that was claimed 1h-18m ago. The ticker has a forced |
| 303 | // delay of 17m in this test. |
| 304 | clock.Set(now) |
| 305 | err := inserter.InsertDiscreteUsageEvent(ctx, db, usagetypes.DCManagedAgentsV1{ |
| 306 | Count: 1, |
| 307 | }) |
| 308 | require.NoError(t, err) |
| 309 | // Claim the event in the past. Claiming it this way via the database |
| 310 | // directly means it won't be marked as published or unclaimed. |
| 311 | events, err := db.SelectUsageEventsForPublishing(ctx, now.Add(-42*time.Minute)) |
| 312 | require.NoError(t, err) |
| 313 | require.Len(t, events, 1) |
| 314 | |
| 315 | // Start the publisher with a trap. |
| 316 | tickerTrap := clock.Trap().NewTicker() |
| 317 | defer tickerTrap.Close() |
| 318 | startErr := make(chan error) |
| 319 | go func() { |
| 320 | err := publisher.Start() |
| 321 | testutil.RequireSend(ctx, t, startErr, err) |
| 322 | }() |
| 323 | tickerCall := tickerTrap.MustWait(ctx) |
| 324 | require.Equal(t, 17*time.Minute, tickerCall.Duration) |
| 325 | tickerCall.MustRelease(ctx) |
| 326 | require.NoError(t, testutil.RequireReceive(ctx, t, startErr)) |
| 327 | |
| 328 | // Set up a trap for the ticker.Reset call. |
| 329 | tickerResetTrap := clock.Trap().TickerReset() |
| 330 | defer tickerResetTrap.Close() |
| 331 | |
| 332 | // Advance the clock to the initial tick, which should trigger the first |
| 333 | // publish, then wait for the reset call. The duration will always be 17m |
nothing calls this directly
no test coverage detected