Files
coder/coderd/prometheusmetrics/prometheusmetrics_test.go

238 lines
6.1 KiB
Go

package prometheusmetrics_test
import (
"context"
"database/sql"
"testing"
"time"
"github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/databasefake"
"github.com/coder/coder/coderd/prometheusmetrics"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/testutil"
)
func TestActiveUsers(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
Name string
Database func() database.Store
Count int
}{{
Name: "None",
Database: func() database.Store {
return databasefake.New()
},
Count: 0,
}, {
Name: "One",
Database: func() database.Store {
db := databasefake.New()
_, _ = db.InsertAPIKey(context.Background(), database.InsertAPIKeyParams{
UserID: uuid.New(),
LastUsed: database.Now(),
Scope: database.APIKeyScopeAll,
})
return db
},
Count: 1,
}, {
Name: "OneWithExpired",
Database: func() database.Store {
db := databasefake.New()
_, _ = db.InsertAPIKey(context.Background(), database.InsertAPIKeyParams{
UserID: uuid.New(),
LastUsed: database.Now(),
Scope: database.APIKeyScopeAll,
})
// Because this API key hasn't been used in the past hour, this shouldn't
// add to the user count.
_, _ = db.InsertAPIKey(context.Background(), database.InsertAPIKeyParams{
UserID: uuid.New(),
LastUsed: database.Now().Add(-2 * time.Hour),
Scope: database.APIKeyScopeAll,
})
return db
},
Count: 1,
}, {
Name: "Multiple",
Database: func() database.Store {
db := databasefake.New()
_, _ = db.InsertAPIKey(context.Background(), database.InsertAPIKeyParams{
UserID: uuid.New(),
LastUsed: database.Now(),
Scope: database.APIKeyScopeAll,
})
_, _ = db.InsertAPIKey(context.Background(), database.InsertAPIKeyParams{
UserID: uuid.New(),
LastUsed: database.Now(),
Scope: database.APIKeyScopeAll,
})
return db
},
Count: 2,
}} {
tc := tc
t.Run(tc.Name, func(t *testing.T) {
t.Parallel()
registry := prometheus.NewRegistry()
cancel, err := prometheusmetrics.ActiveUsers(context.Background(), registry, tc.Database(), time.Millisecond)
require.NoError(t, err)
t.Cleanup(cancel)
require.Eventually(t, func() bool {
metrics, err := registry.Gather()
assert.NoError(t, err)
result := int(*metrics[0].Metric[0].Gauge.Value)
return result == tc.Count
}, testutil.WaitShort, testutil.IntervalFast)
})
}
}
func TestWorkspaces(t *testing.T) {
t.Parallel()
insertRunning := func(db database.Store) database.ProvisionerJob {
job, _ := db.InsertProvisionerJob(context.Background(), database.InsertProvisionerJobParams{
ID: uuid.New(),
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
Provisioner: database.ProvisionerTypeEcho,
})
_, _ = db.InsertWorkspaceBuild(context.Background(), database.InsertWorkspaceBuildParams{
ID: uuid.New(),
WorkspaceID: uuid.New(),
JobID: job.ID,
BuildNumber: 1,
})
// This marks the job as started.
_, _ = db.AcquireProvisionerJob(context.Background(), database.AcquireProvisionerJobParams{
StartedAt: sql.NullTime{
Time: database.Now(),
Valid: true,
},
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
})
return job
}
insertCanceled := func(db database.Store) {
job := insertRunning(db)
_ = db.UpdateProvisionerJobWithCancelByID(context.Background(), database.UpdateProvisionerJobWithCancelByIDParams{
ID: job.ID,
CanceledAt: sql.NullTime{
Time: database.Now(),
Valid: true,
},
})
_ = db.UpdateProvisionerJobWithCompleteByID(context.Background(), database.UpdateProvisionerJobWithCompleteByIDParams{
ID: job.ID,
CompletedAt: sql.NullTime{
Time: database.Now(),
Valid: true,
},
})
}
insertFailed := func(db database.Store) {
job := insertRunning(db)
_ = db.UpdateProvisionerJobWithCompleteByID(context.Background(), database.UpdateProvisionerJobWithCompleteByIDParams{
ID: job.ID,
CompletedAt: sql.NullTime{
Time: database.Now(),
Valid: true,
},
Error: sql.NullString{
String: "failed",
Valid: true,
},
})
}
insertSuccess := func(db database.Store) {
job := insertRunning(db)
_ = db.UpdateProvisionerJobWithCompleteByID(context.Background(), database.UpdateProvisionerJobWithCompleteByIDParams{
ID: job.ID,
CompletedAt: sql.NullTime{
Time: database.Now(),
Valid: true,
},
})
}
for _, tc := range []struct {
Name string
Database func() database.Store
Total int
Status map[codersdk.ProvisionerJobStatus]int
}{{
Name: "None",
Database: func() database.Store {
return databasefake.New()
},
Total: 0,
}, {
Name: "Multiple",
Database: func() database.Store {
db := databasefake.New()
insertCanceled(db)
insertFailed(db)
insertFailed(db)
insertSuccess(db)
insertSuccess(db)
insertSuccess(db)
insertRunning(db)
return db
},
Total: 7,
Status: map[codersdk.ProvisionerJobStatus]int{
codersdk.ProvisionerJobCanceled: 1,
codersdk.ProvisionerJobFailed: 2,
codersdk.ProvisionerJobSucceeded: 3,
codersdk.ProvisionerJobRunning: 1,
},
}} {
tc := tc
t.Run(tc.Name, func(t *testing.T) {
t.Parallel()
registry := prometheus.NewRegistry()
cancel, err := prometheusmetrics.Workspaces(context.Background(), registry, tc.Database(), time.Millisecond)
require.NoError(t, err)
t.Cleanup(cancel)
require.Eventually(t, func() bool {
metrics, err := registry.Gather()
assert.NoError(t, err)
if len(metrics) < 1 {
return false
}
sum := 0
for _, metric := range metrics[0].Metric {
count, ok := tc.Status[codersdk.ProvisionerJobStatus(metric.Label[0].GetValue())]
if metric.Gauge.GetValue() == 0 {
continue
}
if !ok {
t.Fail()
}
if metric.Gauge.GetValue() != float64(count) {
return false
}
sum += int(metric.Gauge.GetValue())
}
t.Logf("sum %d == total %d", sum, tc.Total)
return sum == tc.Total
}, testutil.WaitShort, testutil.IntervalFast)
})
}
}