@@ -307,6 +307,93 @@ func TestMetricsCollector(t *testing.T) {
307
307
}
308
308
}
309
309
310
+ func TestMetricsCollector_DuplicateTemplateNames (t * testing.T ) {
311
+ t .Parallel ()
312
+
313
+ if ! dbtestutil .WillUsePostgres () {
314
+ t .Skip ("this test requires postgres" )
315
+ }
316
+
317
+ type metricCheck struct {
318
+ name string
319
+ value * float64
320
+ isCounter bool
321
+ }
322
+
323
+ type testCase struct {
324
+ transition database.WorkspaceTransition
325
+ jobStatus database.ProvisionerJobStatus
326
+ initiatorID uuid.UUID
327
+ ownerID uuid.UUID
328
+ metrics []metricCheck
329
+ eligible bool
330
+ }
331
+
332
+ test := testCase {
333
+ transition : database .WorkspaceTransitionStart ,
334
+ jobStatus : database .ProvisionerJobStatusSucceeded ,
335
+ initiatorID : agplprebuilds .SystemUserID ,
336
+ ownerID : agplprebuilds .SystemUserID ,
337
+ metrics : []metricCheck {
338
+ {prebuilds .MetricCreatedCount , ptr .To (1.0 ), true },
339
+ {prebuilds .MetricClaimedCount , ptr .To (0.0 ), true },
340
+ {prebuilds .MetricFailedCount , ptr .To (0.0 ), true },
341
+ {prebuilds .MetricDesiredGauge , ptr .To (1.0 ), false },
342
+ {prebuilds .MetricRunningGauge , ptr .To (1.0 ), false },
343
+ {prebuilds .MetricEligibleGauge , ptr .To (1.0 ), false },
344
+ },
345
+ eligible : true ,
346
+ }
347
+
348
+ logger := slogtest .Make (t , & slogtest.Options {IgnoreErrors : true })
349
+ clock := quartz .NewMock (t )
350
+ db , pubsub := dbtestutil .NewDB (t )
351
+ reconciler := prebuilds .NewStoreReconciler (db , pubsub , codersdk.PrebuildsConfig {}, logger , quartz .NewMock (t ), prometheus .NewRegistry (), newNoopEnqueuer ())
352
+ ctx := testutil .Context (t , testutil .WaitLong )
353
+
354
+ createdUsers := []uuid.UUID {agplprebuilds .SystemUserID }
355
+ for _ , user := range []uuid.UUID {test .ownerID , test .initiatorID } {
356
+ if ! slices .Contains (createdUsers , user ) {
357
+ dbgen .User (t , db , database.User {
358
+ ID : user ,
359
+ })
360
+ createdUsers = append (createdUsers , user )
361
+ }
362
+ }
363
+
364
+ collector := prebuilds .NewMetricsCollector (db , logger , reconciler )
365
+ registry := prometheus .NewPedanticRegistry ()
366
+ registry .Register (collector )
367
+
368
+ defaultOrgName := "default-org"
369
+ defaultTemplateName := "default-template"
370
+ defaultPresetName := "default-preset"
371
+ defaultOrg := dbgen .Organization (t , db , database.Organization {
372
+ Name : defaultOrgName ,
373
+ })
374
+ setupTemplateWithDeps := func (templateDeleted bool ) {
375
+ template := setupTestDBTemplateWithinOrg (t , db , test .ownerID , true , defaultTemplateName , defaultOrg )
376
+ templateVersionID := setupTestDBTemplateVersion (ctx , t , clock , db , pubsub , defaultOrg .ID , test .ownerID , template .ID )
377
+ preset := setupTestDBPreset (t , db , templateVersionID , 1 , defaultPresetName )
378
+ workspace , _ := setupTestDBWorkspace (
379
+ t , clock , db , pubsub ,
380
+ test .transition , test .jobStatus , defaultOrg .ID , preset , template .ID , templateVersionID , test .initiatorID , test .ownerID ,
381
+ )
382
+ setupTestDBWorkspaceAgent (t , db , workspace .ID , test .eligible )
383
+ }
384
+ // Simulates creating and then deleting a template.
385
+ setupTemplateWithDeps (true )
386
+ // Simulates creating a template with the same org, template name, and preset name.
387
+ setupTemplateWithDeps (false )
388
+
389
+ // Force an update to the metrics state to allow the collector to collect fresh metrics.
390
+ // nolint:gocritic // Authz context needed to retrieve state.
391
+ require .NoError (t , collector .UpdateState (dbauthz .AsPrebuildsOrchestrator (ctx ), testutil .WaitLong ))
392
+
393
+ _ , err := registry .Gather ()
394
+ require .NoError (t , err )
395
+ }
396
+
310
397
func findMetric (metricsFamilies []* prometheus_client.MetricFamily , name string , labels map [string ]string ) * prometheus_client.Metric {
311
398
for _ , metricFamily := range metricsFamilies {
312
399
if metricFamily .GetName () != name {
0 commit comments