Skip to content

Commit e41b7d2

Browse files
pieternclaude
andauthored
Use nowMilli() in testserver to prevent flaky timestamp collisions (#4693)
## Summary - Replace `time.Now().UnixMilli()` with `nowMilli()` in testserver fake implementations (pipelines, catalogs, external locations, registered models) to guarantee strictly increasing millisecond timestamps - Add a `ruleguard` lint rule to prevent `time.Now().UnixMilli()` in `libs/testserver/` (except `fake_workspace.go` where the helper is defined) - Fix `CreatedAt`/`UpdatedAt` in create handlers to use the same timestamp value, matching real API behavior Fixes flaky test `TestAccept/bundle/resource_deps/pipelines_recreate/DATABRICKS_BUNDLE_ENGINE=direct` where a pipeline's `last_modified` and a job's `created_time` could collide to the same millisecond, causing `[UNIX_TIME_MILLIS][0]` vs `[UNIX_TIME_MILLIS][1]` index mismatch in test output. Example failure: https://github.com/databricks/cli/actions/runs/22710298364/job/65846540329 ## Test plan - [x] `TestAccept/bundle/resource_deps/pipelines_recreate` passes consistently (verified with `-count=3`) - [x] `make lintfull` passes with 0 issues - [x] `make test` passes (template test failures are pre-existing and unrelated) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b50013c commit e41b7d2

File tree

5 files changed

+24
-14
lines changed

5 files changed

+24
-14
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package gorules
2+
3+
import "github.com/quasilyte/go-ruleguard/dsl"
4+
5+
// NoTimeNowUnixMilliInTestServer forbids direct time.Now().UnixMilli() calls in libs/testserver.
6+
// Use nowMilli() instead to guarantee unique, strictly increasing timestamps.
7+
// Integer millisecond timestamps get indexed replacements in test output (e.g. [UNIX_TIME_MILLIS][0])
8+
// and collisions between resources cause flaky tests.
9+
func NoTimeNowUnixMilliInTestServer(m dsl.Matcher) {
10+
m.Match(`time.Now().UnixMilli()`).
11+
Where(m.File().PkgPath.Matches(`.*/libs/testserver`) &&
12+
!m.File().Name.Matches(`fake_workspace\.go$`)).
13+
Report(`Use nowMilli() instead of time.Now().UnixMilli() in testserver to ensure unique timestamps`)
14+
}

libs/testserver/catalogs.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"encoding/json"
55
"fmt"
66
"net/http"
7-
"time"
87

98
"github.com/databricks/databricks-sdk-go/service/catalog"
109
)
@@ -29,14 +28,14 @@ func (s *FakeWorkspace) CatalogsCreate(req Request) Response {
2928
Options: createRequest.Options,
3029
Properties: createRequest.Properties,
3130
FullName: createRequest.Name,
32-
CreatedAt: time.Now().UnixMilli(),
31+
CreatedAt: nowMilli(),
3332
CreatedBy: s.CurrentUser().UserName,
34-
UpdatedAt: time.Now().UnixMilli(),
3533
UpdatedBy: s.CurrentUser().UserName,
3634
MetastoreId: nextUUID(),
3735
Owner: s.CurrentUser().UserName,
3836
CatalogType: catalog.CatalogTypeManagedCatalog,
3937
}
38+
catalogInfo.UpdatedAt = catalogInfo.CreatedAt
4039

4140
s.Catalogs[createRequest.Name] = catalogInfo
4241
return Response{
@@ -79,7 +78,7 @@ func (s *FakeWorkspace) CatalogsUpdate(req Request, name string) Response {
7978
name = updateRequest.NewName
8079
}
8180

82-
existing.UpdatedAt = time.Now().UnixMilli()
81+
existing.UpdatedAt = nowMilli()
8382
existing.UpdatedBy = s.CurrentUser().UserName
8483

8584
s.Catalogs[name] = existing

libs/testserver/external_locations.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"encoding/json"
55
"fmt"
66
"net/http"
7-
"time"
87

98
"github.com/databricks/databricks-sdk-go/service/catalog"
109
)
@@ -37,13 +36,13 @@ func (s *FakeWorkspace) ExternalLocationsCreate(req Request) Response {
3736
Fallback: createRequest.Fallback,
3837
EncryptionDetails: createRequest.EncryptionDetails,
3938
FileEventQueue: createRequest.FileEventQueue,
40-
CreatedAt: time.Now().UnixMilli(),
39+
CreatedAt: nowMilli(),
4140
CreatedBy: s.CurrentUser().UserName,
42-
UpdatedAt: time.Now().UnixMilli(),
4341
UpdatedBy: s.CurrentUser().UserName,
4442
MetastoreId: nextUUID(),
4543
Owner: s.CurrentUser().UserName,
4644
}
45+
locationInfo.UpdatedAt = locationInfo.CreatedAt
4746

4847
s.ExternalLocations[createRequest.Name] = locationInfo
4948
return Response{
@@ -95,7 +94,7 @@ func (s *FakeWorkspace) ExternalLocationsUpdate(req Request, name string) Respon
9594
name = updateRequest.NewName
9695
}
9796

98-
existing.UpdatedAt = time.Now().UnixMilli()
97+
existing.UpdatedAt = nowMilli()
9998
existing.UpdatedBy = s.CurrentUser().UserName
10099

101100
s.ExternalLocations[name] = existing

libs/testserver/pipelines.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package testserver
33
import (
44
"encoding/json"
55
"fmt"
6-
"time"
76

87
"github.com/databricks/databricks-sdk-go/service/pipelines"
98
)
@@ -41,7 +40,7 @@ func (s *FakeWorkspace) PipelineCreate(req Request) Response {
4140
pipelineId := nextUUID()
4241
r.PipelineId = pipelineId
4342
r.CreatorUserName = "tester@databricks.com"
44-
r.LastModified = time.Now().UnixMilli()
43+
r.LastModified = nowMilli()
4544
r.Name = r.Spec.Name
4645
r.RunAsUserName = "tester@databricks.com"
4746
r.State = "IDLE"

libs/testserver/registered_models.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"encoding/json"
55
"fmt"
66
"net/http"
7-
"time"
87

98
"github.com/databricks/databricks-sdk-go/service/catalog"
109
)
@@ -30,13 +29,13 @@ func (s *FakeWorkspace) RegisteredModelsCreate(req Request) Response {
3029
SchemaName: createRequest.SchemaName,
3130
StorageLocation: createRequest.StorageLocation,
3231
FullName: fullName,
33-
CreatedAt: time.Now().UnixMilli(),
32+
CreatedAt: nowMilli(),
3433
CreatedBy: s.CurrentUser().UserName,
35-
UpdatedAt: time.Now().UnixMilli(),
3634
UpdatedBy: s.CurrentUser().UserName,
3735
MetastoreId: nextUUID(),
3836
Owner: s.CurrentUser().UserName,
3937
}
38+
registeredModel.UpdatedAt = registeredModel.CreatedAt
4039

4140
s.RegisteredModels[fullName] = registeredModel
4241
return Response{
@@ -78,7 +77,7 @@ func (s *FakeWorkspace) RegisteredModelsUpdate(req Request, fullName string) Res
7877
fullName = existing.CatalogName + "." + existing.SchemaName + "." + updateRequest.NewName
7978
}
8079

81-
existing.UpdatedAt = time.Now().UnixMilli()
80+
existing.UpdatedAt = nowMilli()
8281
s.RegisteredModels[fullName] = existing
8382
return Response{
8483
Body: existing,

0 commit comments

Comments
 (0)