fix(coderd): use dbtime.Now() instead of time.Now() in test assertions against DB timestamps (#22685)
`time.Now()` has nanosecond precision while Postgres timestamps are microsecond precision. When tests compare `time.Now()` against DB-sourced timestamps using `Before`/`After`/`WithinRange`/etc., there is a non-zero flake risk from the precision mismatch. This replaces `time.Now()` with `dbtime.Now()` (which rounds to microsecond precision) in all test assertions that compare against database timestamps. Follows from #22684. ## Changes (11 files) | File | Changes | |---|---| | `coderd/apikey_test.go` | 11 comparisons with `ExpiresAt` | | `coderd/users_test.go` | 2 comparisons with `ExpiresAt` | | `coderd/oauth2_test.go` | 1 comparison with `token.Expiry` | | `coderd/workspaces_test.go` | 2 comparisons with `DormantAt` | | `coderd/workspaceagents_test.go` | 3 comparisons with `ConnectedAt`/`DisconnectedAt` | | `coderd/workspaceapps/db_test.go` | 1 comparison with `token.Expiry` | | `coderd/provisionerdserver/provisionerdserver_test.go` | 1 comparison with `key.ExpiresAt` | | `enterprise/coderd/workspaces_test.go` | 1 comparison with `DormantAt` | | `enterprise/coderd/license/license_test.go` | 3 `NotBefore` values | | `enterprise/coderd/licenses_test.go` | 2 `NotBefore` values | | `enterprise/coderd/users_test.go` | 3 `Next()` comparisons | ## Not changed (intentionally) - `scaletest/placebo/run_test.go` — compares wall-clock elapsed time, not DB timestamps - `cli/server_test.go`, `coderd/jwtutils/jwt_test.go`, `enterprise/aibridgeproxyd/aibridgeproxyd_test.go` — TLS cert fields, not DB-stored - `coderd/azureidentity/azureidentity_test.go` — Azure cert expiry, not DB 🤖 Generated by Claude Opus 4.6 but reviewed manually.
This commit is contained in:
+11
-11
@@ -48,8 +48,8 @@ func TestTokenCRUD(t *testing.T) {
|
||||
require.EqualValues(t, len(keys), 1)
|
||||
require.Contains(t, res.Key, keys[0].ID)
|
||||
// expires_at should default to 30 days
|
||||
require.Greater(t, keys[0].ExpiresAt, time.Now().Add(time.Hour*24*6))
|
||||
require.Less(t, keys[0].ExpiresAt, time.Now().Add(time.Hour*24*8))
|
||||
require.Greater(t, keys[0].ExpiresAt, dbtime.Now().Add(time.Hour*24*6))
|
||||
require.Less(t, keys[0].ExpiresAt, dbtime.Now().Add(time.Hour*24*8))
|
||||
require.Equal(t, codersdk.APIKeyScopeAll, keys[0].Scope)
|
||||
require.Len(t, keys[0].AllowList, 1)
|
||||
require.Equal(t, "*:*", keys[0].AllowList[0].String())
|
||||
@@ -194,8 +194,8 @@ func TestUserSetTokenDuration(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
keys, err := client.Tokens(ctx, codersdk.Me, codersdk.TokensFilter{})
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, keys[0].ExpiresAt, time.Now().Add(time.Hour*6*24))
|
||||
require.Less(t, keys[0].ExpiresAt, time.Now().Add(time.Hour*8*24))
|
||||
require.Greater(t, keys[0].ExpiresAt, dbtime.Now().Add(time.Hour*6*24))
|
||||
require.Less(t, keys[0].ExpiresAt, dbtime.Now().Add(time.Hour*8*24))
|
||||
}
|
||||
|
||||
func TestDefaultTokenDuration(t *testing.T) {
|
||||
@@ -210,8 +210,8 @@ func TestDefaultTokenDuration(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
keys, err := client.Tokens(ctx, codersdk.Me, codersdk.TokensFilter{})
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, keys[0].ExpiresAt, time.Now().Add(time.Hour*24*6))
|
||||
require.Less(t, keys[0].ExpiresAt, time.Now().Add(time.Hour*24*8))
|
||||
require.Greater(t, keys[0].ExpiresAt, dbtime.Now().Add(time.Hour*24*6))
|
||||
require.Less(t, keys[0].ExpiresAt, dbtime.Now().Add(time.Hour*24*8))
|
||||
}
|
||||
|
||||
func TestTokenUserSetMaxLifetime(t *testing.T) {
|
||||
@@ -518,7 +518,7 @@ func TestExpireAPIKey(t *testing.T) {
|
||||
// Verify the token is not expired.
|
||||
key, err := adminClient.APIKeyByID(ctx, codersdk.Me, keyID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, key.ExpiresAt.After(time.Now()))
|
||||
require.True(t, key.ExpiresAt.After(dbtime.Now()))
|
||||
|
||||
auditor.ResetLogs()
|
||||
|
||||
@@ -529,7 +529,7 @@ func TestExpireAPIKey(t *testing.T) {
|
||||
// Verify the token is expired.
|
||||
key, err = adminClient.APIKeyByID(ctx, codersdk.Me, keyID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, key.ExpiresAt.Before(time.Now()))
|
||||
require.True(t, key.ExpiresAt.Before(dbtime.Now()))
|
||||
|
||||
// Verify audit log.
|
||||
als := auditor.AuditLogs()
|
||||
@@ -556,7 +556,7 @@ func TestExpireAPIKey(t *testing.T) {
|
||||
// Verify the token is expired.
|
||||
key, err := memberClient.APIKeyByID(ctx, codersdk.Me, keyID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, key.ExpiresAt.Before(time.Now()))
|
||||
require.True(t, key.ExpiresAt.Before(dbtime.Now()))
|
||||
})
|
||||
|
||||
t.Run("MemberCannotExpireOtherUsersToken", func(t *testing.T) {
|
||||
@@ -607,7 +607,7 @@ func TestExpireAPIKey(t *testing.T) {
|
||||
// Invariant: make sure it's actually expired
|
||||
key, err := adminClient.APIKeyByID(ctx, codersdk.Me, keyID)
|
||||
require.NoError(t, err)
|
||||
require.LessOrEqual(t, key.ExpiresAt, time.Now(), "key should be expired")
|
||||
require.LessOrEqual(t, key.ExpiresAt, dbtime.Now(), "key should be expired")
|
||||
|
||||
// Expire it again - should succeed (idempotent).
|
||||
err = adminClient.ExpireAPIKey(ctx, codersdk.Me, keyID)
|
||||
@@ -636,7 +636,7 @@ func TestExpireAPIKey(t *testing.T) {
|
||||
// Verify it's expired.
|
||||
key, err := adminClient.APIKeyByID(ctx, codersdk.Me, keyID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, key.ExpiresAt.Before(time.Now()))
|
||||
require.True(t, key.ExpiresAt.Before(dbtime.Now()))
|
||||
|
||||
// Delete the expired token - should succeed.
|
||||
err = adminClient.DeleteAPIKey(ctx, codersdk.Me, keyID)
|
||||
|
||||
@@ -516,7 +516,7 @@ func TestOAuth2ProviderTokenExchange(t *testing.T) {
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, token.AccessToken)
|
||||
require.True(t, time.Now().Before(token.Expiry))
|
||||
require.True(t, dbtime.Now().Before(token.Expiry))
|
||||
|
||||
// Check that the token works.
|
||||
newClient := codersdk.New(userClient.URL)
|
||||
|
||||
@@ -434,7 +434,7 @@ func TestAcquireJob(t *testing.T) {
|
||||
key, err := db.GetAPIKeyByID(ctx, toks[0])
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(dv.Sessions.MaximumTokenDuration.Value().Seconds()), key.LifetimeSeconds)
|
||||
require.WithinDuration(t, time.Now().Add(dv.Sessions.MaximumTokenDuration.Value()), key.ExpiresAt, time.Minute)
|
||||
require.WithinDuration(t, dbtime.Now().Add(dv.Sessions.MaximumTokenDuration.Value()), key.ExpiresAt, time.Minute)
|
||||
|
||||
wantedMetadata := &sdkproto.Metadata{
|
||||
CoderUrl: (&url.URL{}).String(),
|
||||
|
||||
@@ -302,8 +302,8 @@ func TestPostLogin(t *testing.T) {
|
||||
apiKey, err := client.APIKeyByID(ctx, owner.UserID.String(), split[0])
|
||||
require.NoError(t, err, "fetch api key")
|
||||
|
||||
require.True(t, apiKey.ExpiresAt.After(time.Now().Add(time.Hour*24*6)), "default tokens lasts more than 6 days")
|
||||
require.True(t, apiKey.ExpiresAt.Before(time.Now().Add(time.Hour*24*8)), "default tokens lasts less than 8 days")
|
||||
require.True(t, apiKey.ExpiresAt.After(dbtime.Now().Add(time.Hour*24*6)), "default tokens lasts more than 6 days")
|
||||
require.True(t, apiKey.ExpiresAt.Before(dbtime.Now().Add(time.Hour*24*8)), "default tokens lasts less than 8 days")
|
||||
require.Greater(t, apiKey.LifetimeSeconds, key.LifetimeSeconds, "token should have longer lifetime")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3041,13 +3041,13 @@ func TestUserTailnetTelemetry(t *testing.T) {
|
||||
telemetryConnection := snapshot.UserTailnetConnections[0]
|
||||
require.Equal(t, memberUser.ID.String(), telemetryConnection.UserID)
|
||||
require.GreaterOrEqual(t, telemetryConnection.ConnectedAt, predialTime)
|
||||
require.LessOrEqual(t, telemetryConnection.ConnectedAt, time.Now())
|
||||
require.LessOrEqual(t, telemetryConnection.ConnectedAt, dbtime.Now())
|
||||
require.NotEmpty(t, telemetryConnection.PeerID)
|
||||
requireEqualOrBothNil(t, telemetryConnection.DeviceID, tc.expected.DeviceID)
|
||||
requireEqualOrBothNil(t, telemetryConnection.DeviceOS, tc.expected.DeviceOS)
|
||||
requireEqualOrBothNil(t, telemetryConnection.CoderDesktopVersion, tc.expected.CoderDesktopVersion)
|
||||
|
||||
beforeDisconnectTime := time.Now()
|
||||
beforeDisconnectTime := dbtime.Now()
|
||||
err = wsConn.Close(websocket.StatusNormalClosure, "done")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -3060,7 +3060,7 @@ func TestUserTailnetTelemetry(t *testing.T) {
|
||||
require.Equal(t, telemetryConnection.PeerID, telemetryDisconnection.PeerID)
|
||||
require.NotNil(t, telemetryDisconnection.DisconnectedAt)
|
||||
require.GreaterOrEqual(t, *telemetryDisconnection.DisconnectedAt, beforeDisconnectTime)
|
||||
require.LessOrEqual(t, *telemetryDisconnection.DisconnectedAt, time.Now())
|
||||
require.LessOrEqual(t, *telemetryDisconnection.DisconnectedAt, dbtime.Now())
|
||||
requireEqualOrBothNil(t, telemetryConnection.DeviceID, tc.expected.DeviceID)
|
||||
requireEqualOrBothNil(t, telemetryConnection.DeviceOS, tc.expected.DeviceOS)
|
||||
requireEqualOrBothNil(t, telemetryConnection.CoderDesktopVersion, tc.expected.CoderDesktopVersion)
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/connectionlog"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
"github.com/coder/coder/v2/coderd/httpmw"
|
||||
"github.com/coder/coder/v2/coderd/jwtutils"
|
||||
"github.com/coder/coder/v2/coderd/tracing"
|
||||
@@ -310,7 +311,7 @@ func Test_ResolveRequest(t *testing.T) {
|
||||
CORSBehavior: codersdk.CORSBehaviorSimple,
|
||||
}, token)
|
||||
require.NotZero(t, token.Expiry)
|
||||
require.WithinDuration(t, time.Now().Add(workspaceapps.DefaultTokenExpiry), token.Expiry.Time(), time.Minute)
|
||||
require.WithinDuration(t, dbtime.Now().Add(workspaceapps.DefaultTokenExpiry), token.Expiry.Time(), time.Minute)
|
||||
|
||||
// Check that the token was set in the response and is valid.
|
||||
require.Len(t, w.Cookies(), 1)
|
||||
|
||||
@@ -4379,7 +4379,7 @@ func TestWorkspaceDormant(t *testing.T) {
|
||||
// The template doesn't have a time_til_dormant_autodelete set so this should be nil.
|
||||
require.Nil(t, workspace.DeletingAt)
|
||||
require.NotNil(t, workspace.DormantAt)
|
||||
require.WithinRange(t, *workspace.DormantAt, time.Now().Add(-time.Second*10), time.Now())
|
||||
require.WithinRange(t, *workspace.DormantAt, dbtime.Now().Add(-time.Second*10), dbtime.Now())
|
||||
require.Equal(t, lastUsedAt, workspace.LastUsedAt)
|
||||
|
||||
workspace = coderdtest.MustWorkspace(t, client, workspace.ID)
|
||||
@@ -4433,7 +4433,7 @@ func TestWorkspaceDormant(t *testing.T) {
|
||||
workspace, err = client.Workspace(ctx, workspace.ID)
|
||||
require.NoError(t, err, "fetch dormant workspace")
|
||||
if assert.NotNil(t, workspace.DormantAt, "workspace must be dormant") {
|
||||
require.WithinDuration(t, *workspace.DormantAt, time.Now(), 10*time.Second)
|
||||
require.WithinDuration(t, *workspace.DormantAt, dbtime.Now(), 10*time.Second)
|
||||
}
|
||||
// Starting a dormant workspace should 'wake' it.
|
||||
wb, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
||||
|
||||
@@ -749,7 +749,7 @@ func TestEntitlements(t *testing.T) {
|
||||
Features: license.Features{
|
||||
codersdk.FeatureHighAvailability: 1,
|
||||
},
|
||||
NotBefore: time.Now().Add(-time.Hour * 2),
|
||||
NotBefore: dbtime.Now().Add(-time.Hour * 2),
|
||||
GraceAt: time.Now().Add(-time.Hour),
|
||||
ExpiresAt: time.Now().Add(time.Hour),
|
||||
}),
|
||||
@@ -799,7 +799,7 @@ func TestEntitlements(t *testing.T) {
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
db.InsertLicense(context.Background(), database.InsertLicenseParams{
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
NotBefore: time.Now().Add(-time.Hour * 2),
|
||||
NotBefore: dbtime.Now().Add(-time.Hour * 2),
|
||||
GraceAt: time.Now().Add(-time.Hour),
|
||||
ExpiresAt: time.Now().Add(time.Hour),
|
||||
Features: license.Features{
|
||||
@@ -1430,7 +1430,7 @@ func TestUsageLimitFeatures(t *testing.T) {
|
||||
UUID: uuid.New(),
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
IssuedAt: time.Now().Add(-time.Minute * 2),
|
||||
NotBefore: time.Now().Add(-time.Minute * 2),
|
||||
NotBefore: dbtime.Now().Add(-time.Minute * 2),
|
||||
ExpiresAt: time.Now().Add(time.Hour * 2),
|
||||
Features: license.Features{
|
||||
codersdk.FeatureManagedAgentLimit: 100,
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/v2/enterprise/coderd/license"
|
||||
@@ -145,7 +146,7 @@ func TestPostLicense(t *testing.T) {
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
NotBefore: time.Now().Add(time.Hour),
|
||||
NotBefore: dbtime.Now().Add(time.Hour),
|
||||
GraceAt: time.Now().Add(2 * time.Hour),
|
||||
ExpiresAt: time.Now().Add(3 * time.Hour),
|
||||
})
|
||||
@@ -168,7 +169,7 @@ func TestPostLicense(t *testing.T) {
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
NotBefore: time.Now().Add(time.Hour),
|
||||
NotBefore: dbtime.Now().Add(time.Hour),
|
||||
GraceAt: time.Now().Add(2 * time.Hour),
|
||||
ExpiresAt: time.Now().Add(-time.Hour),
|
||||
})
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/coderd/schedule/cron"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
@@ -87,7 +88,7 @@ func TestUserQuietHours(t *testing.T) {
|
||||
require.False(t, sched1.UserSet)
|
||||
require.Equal(t, defaultScheduleParsed.TimeParsed().Format(TimeFormatHHMM), sched1.Time)
|
||||
require.Equal(t, defaultScheduleParsed.Location().String(), sched1.Timezone)
|
||||
require.WithinDuration(t, defaultScheduleParsed.Next(time.Now()), sched1.Next, 15*time.Second)
|
||||
require.WithinDuration(t, defaultScheduleParsed.Next(dbtime.Now()), sched1.Next, 15*time.Second)
|
||||
|
||||
// Set their quiet hours.
|
||||
customQuietHoursSchedule := "CRON_TZ=Australia/Sydney 0 0 * * *"
|
||||
@@ -110,7 +111,7 @@ func TestUserQuietHours(t *testing.T) {
|
||||
require.True(t, sched2.UserSet)
|
||||
require.Equal(t, customScheduleParsed.TimeParsed().Format(TimeFormatHHMM), sched2.Time)
|
||||
require.Equal(t, customScheduleParsed.Location().String(), sched2.Timezone)
|
||||
require.WithinDuration(t, customScheduleParsed.Next(time.Now()), sched2.Next, 15*time.Second)
|
||||
require.WithinDuration(t, customScheduleParsed.Next(dbtime.Now()), sched2.Next, 15*time.Second)
|
||||
|
||||
// Get quiet hours for a user that has them set.
|
||||
sched3, err := client.UserQuietHoursSchedule(ctx, user.ID.String())
|
||||
@@ -119,7 +120,7 @@ func TestUserQuietHours(t *testing.T) {
|
||||
require.True(t, sched3.UserSet)
|
||||
require.Equal(t, customScheduleParsed.TimeParsed().Format(TimeFormatHHMM), sched3.Time)
|
||||
require.Equal(t, customScheduleParsed.Location().String(), sched3.Timezone)
|
||||
require.WithinDuration(t, customScheduleParsed.Next(time.Now()), sched3.Next, 15*time.Second)
|
||||
require.WithinDuration(t, customScheduleParsed.Next(dbtime.Now()), sched3.Next, 15*time.Second)
|
||||
|
||||
// Try setting a garbage schedule.
|
||||
_, err = client.UpdateUserQuietHoursSchedule(ctx, user.ID.String(), codersdk.UpdateUserQuietHoursScheduleRequest{
|
||||
|
||||
@@ -4023,7 +4023,7 @@ func TestWorkspaceLock(t *testing.T) {
|
||||
require.NotNil(t, workspace.DeletingAt)
|
||||
require.NotNil(t, workspace.DormantAt)
|
||||
require.Equal(t, workspace.DormantAt.Add(dormantTTL), *workspace.DeletingAt)
|
||||
require.WithinRange(t, *workspace.DormantAt, time.Now().Add(-time.Second), time.Now())
|
||||
require.WithinRange(t, *workspace.DormantAt, dbtime.Now().Add(-time.Second), dbtime.Now())
|
||||
// Locking a workspace shouldn't update the last_used_at.
|
||||
require.Equal(t, lastUsedAt, workspace.LastUsedAt)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user