Repository navigation
chore(coderd/database/dbfake): add support for provisioner job timestamp control - #21944
Conversation
Add a Failed() method to WorkspaceBuildBuilder that sets the provisioner job to a failed state with the given error message and error code. This allows tests to create failed workspace builds using the builder pattern instead of manually creating provisioner jobs with dbgen. Migrate coderd/notifications/reports/generator_internal_test.go to use the new dbfake.WorkspaceBuild builder with: - Failed() method for failed builds - WithJobCompletedAt() method for timestamp control This simplifies test setup by consolidating workspace build and provisioner job creation into a single fluent builder call.
Migrate TestTemplateUpdateBuildDeadlines to use the new WorkspaceBuildBuilder timestamp control methods instead of manual AcquireProvisionerJob + UpdateProvisionerJobWithCompleteByID calls. This simplifies test setup by using: - dbfake.WorkspaceBuild() to create workspace, job, and build together - .WithJobCompletedAt(buildTime) to set the job completion timestamp - .Seed() to pass custom build parameters like ProvisionerState The second test (TestTemplateUpdateBuildDeadlinesSkip) was not migrated because it requires failed build states which the builder doesn't currently support.
…p control Replace custom withCreatedAt helper pattern with the standard dbfake.WorkspaceBuildBuilder.WithJobCreatedAt() method in TestExpiredPrebuildsMultipleActions. Changes: - Replace setupTestDBPrebuild call with dbfake.WorkspaceBuild() when timestamp control is needed for expiration testing - Remove prebuildOptions struct, prebuildOption type, and withCreatedAt helper function (no longer needed) - Remove opts parameter from setupTestDBPrebuild and setupTestDBWorkspace functions This standardizes timestamp control in tests to use the new builder methods available in dbfake.WorkspaceBuildBuilder.
… timestamp methods This commit migrates the workspace runtime audit tests to use the WorkspaceBuildBuilder's new timestamp control methods instead of manually creating provisioner jobs with explicit timestamps. Changes: - Add Failed() method to WorkspaceBuildBuilder for failed job status - Add ProvisionerJobStatusFailed case handling in builder's switch - Refactor createWorkspace() to use dbfake.WorkspaceBuild with WithJobCreatedAt/WithJobCompletedAt methods - Remove redundant 'canceled' field from workspaceBuildArgs struct - Remove unused 'database/sql' import The new approach simplifies test setup while preserving existing behavior. All tests continue to pass.
…spaceBuildBuilder Migrate the following tests to use the new dbfake.WorkspaceBuildBuilder timestamp control methods instead of manual dbgen.ProvisionerJob setup: - TestDetectorHungWorkspaceBuild - TestDetectorHungWorkspaceBuildNoOverrideState - TestDetectorHungWorkspaceBuildNoOverrideStateIfNoExistingBuild - TestDetectorPendingWorkspaceBuildNoOverrideStateIfNoExistingBuild - TestDetectorWorkspaceBuildForDormantWorkspace This significantly reduces boilerplate while making the test setup clearer. The builder handles all the necessary relationships automatically.
This method allows setting the UpdatedAt field separately from StartedAt when creating running jobs. This is essential for testing hung job detection where the detector checks UpdatedAt to determine if a job has been idle for too long. The AcquireProvisionerJob query sets UpdatedAt = StartedAt, so this method updates UpdatedAt after job acquisition to enable the necessary test scenario.
Address review feedback on WorkspaceBuildBuilder migration: 1. Use correct timestamps for hung job tests: the original tests had CreatedAt=tenMinAgo, StartedAt=tenMinAgo, UpdatedAt=sixMinAgo. The hung job detector checks UpdatedAt to determine if a job is hung, so these values must match the original test behavior. 2. Set DormantAt directly in workspace seed instead of using post-hoc UpdateWorkspaceDormantDeletingAt call. This simplifies the test and eliminates the need to refetch the workspace.
Remove ineffassign warning by eliminating unused workspace assignment and using buildResp.Build directly instead of creating an alias.
Address review feedback on the WorkspaceBuildBuilder timestamp migration: 1. Remove unnecessary comment in runtimeaudit_test.go (line 383) 2. Remove unnecessary comment in reconcile_test.go (lines 1833-1834) 3. Remove unnecessary alias in reconcile_test.go (line 1850) Use `resp.Workspace` directly instead of aliasing to `workspace`. 4. Simplify API by accepting variadic time.Time in status methods: - Starting(ts ...time.Time): timestamp used for StartedAt - Pending(ts ...time.Time): timestamp used for CreatedAt - Canceled(ts ...time.Time): timestamp used for CompletedAt - Failed(jobError, jobErrorCode string, ts ...time.Time): timestamp used for CompletedAt 5. Remove WithJobCreatedAt and WithJobStartedAt methods 6. Keep WithJobUpdatedAt for hung job tests where UpdatedAt != StartedAt 7. Keep WithJobCompletedAt for succeeded builds that need completion timestamps 8. Job CreatedAt now defaults to workspace CreatedAt when not explicitly set, which allows expiration tests to work without needing WithJobCreatedAt. All callers updated to use the simplified API: - Starting().WithJobStartedAt(t) → Starting(t) - Pending().WithJobCreatedAt(t) → Pending(t) - Canceled().WithJobCompletedAt(t) → Canceled(t) - Failed(...).WithJobCompletedAt(t) → Failed(..., t)
…ary comments - Rename WithJobUpdatedAt to JobUpdatedAt - Rename WithJobCompletedAt to JobCompletedAt - Remove unnecessary comments in doInTX()
- Add Succeeded(ts ...time.Time) for consistency with other status methods - Remove JobCompletedAt() as Succeeded() now handles this case - Fix Canceled() comment to mention both CanceledAt and CompletedAt
There was a problem hiding this comment.
Pull request overview
This PR extends the dbfake.WorkspaceBuildBuilder with methods for controlling provisioner job timestamps and statuses, reducing boilerplate when creating test workspaces with specific timing requirements. The changes simplify test code by replacing manual provisioner job creation and manipulation with a cleaner builder API.
Changes:
- Extended
WorkspaceBuildBuilderwith timestamp control methods (Starting(),Pending(),Canceled(),Succeeded(),Failed(),JobUpdatedAt()) - Migrated tests in 5 packages to use the new builder methods instead of manually creating provisioner jobs with
dbgen - Removed unused helper functions and options that are now obsolete with the new API
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| coderd/database/dbfake/dbfake.go | Core implementation adding timestamp and status control to WorkspaceBuildBuilder |
| coderd/jobreaper/detector_test.go | Migrated 5 test cases to use new builder methods for creating hung/pending jobs |
| coderd/notifications/reports/generator_internal_test.go | Replaced manual job creation with builder methods for failed/succeeded builds |
| enterprise/coderd/schedule/template_test.go | Simplified workspace build creation using new Succeeded() method |
| enterprise/coderd/prebuilds/reconcile_test.go | Updated prebuild creation to use builder API and removed obsolete helper options |
| scripts/workspace-runtime-audit/runtimeaudit_test.go | Refactored workspace build creation to use builder pattern with status methods |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <[email protected]>
mafredri
left a comment
There was a problem hiding this comment.
Looks like a solid improvement 👍🏻.
Although I'm wondering if we should just introduce:
func (b WorkspaceBuildBuilder) UpdateJob(job database.ProvisionerJob)
For more advanced use-cases.
Think:
b.
Failed(error, code).
UpdateJob(database.ProvisionerJob{CompletedAt: ts})Or a less-generic approach:
b.
Failed(error, code).
CompletedAt(ts)And respective methods for StartedAt/CreatedAt/UpdatedAt.
I'm down for that. Alternatively, WDYT about |
|
I think the alternative raises the question of when does the callback function run. 🤔 Do you have a use-case in mind where the cb method provides an edge? |
Yeah, good point. Your proposed API is more congruent with the existing usage of |
Replace variadic timestamps in WorkspaceBuildBuilder status methods (Starting, Pending, Canceled, Failed, Succeeded) and remove JobUpdatedAt method in favor of a single UpdateJob(database.ProvisionerJob) method. This provides a cleaner API for customizing provisioner job fields by allowing callers to specify any combination of job field overrides using the ProvisionerJob struct. Changes: - Remove variadic timestamp parameters from status methods - Remove JobUpdatedAt method - Add jobOverrides field to WorkspaceBuildBuilder struct - Add UpdateJob method that accepts database.ProvisionerJob - Add takeFirstTime helper function for time.Time zero-value handling - Update doInTX to apply non-zero overrides after job creation - Update all test callers to use the new API
This feels the easiest to implement.
I would assume at We can keep it as So the callbacks would be used above this line to make those insert params: coder/coderd/database/dbfake/dbfake.go Line 268 in afdb13e |
|
Part of the issue with allowing passing in an entire |
Remove the jobOverrides field and UpdateJob method from WorkspaceBuildBuilder. Add individual timestamp fields (jobCreatedAt, jobStartedAt, jobUpdatedAt, jobCompletedAt) and functional BuilderOption type with WithJob* option functions. Status methods (Starting, Pending, Canceled, Succeeded, Failed) now accept variadic BuilderOption to set job timestamps directly: .Succeeded(dbfake.WithJobCompletedAt(twentyMinAgo)) .Starting(dbfake.WithJobStartedAt(tenMinAgo), dbfake.WithJobUpdatedAt(sixMinAgo)) .Failed(error, code, dbfake.WithJobCompletedAt(now)) This eliminates the dual source-of-truth between jobStatus and jobOverrides.JobStatus that existed before.
Update the 4 remaining callers of the removed UpdateJob method to use the new functional options pattern (WithJobCompletedAt). Also removes unused 'database/sql' import from runtimeaudit_test.go.
Add WithJobError and WithJobErrorCode BuilderOption functions and change Failed() to only accept ...BuilderOption instead of explicit jobError and jobErrorCode string parameters. This makes the API more consistent with other builder methods where all customization is done via options. The default error message 'failed' is still used when no WithJobError option is provided.
good point |
Emyrk
left a comment
There was a problem hiding this comment.
This is the most straight forward 👍
Is there any value to setting the times without the status? The only thing I see is you need to do a .Pending/Succeeded/Etc to set the times.
not a big deal though
I can't think of a reason to do that, apart from if you're testing something really weird. |
mafredri
left a comment
There was a problem hiding this comment.
Nice work!
Just for completeness, I'm going to document one last idea that came to my mind. Do with it what you will 😄:
dbfake.JobBuilder().CreatedAt(ts).Error("foo").Do()| Transition: b.transition, | ||
| InitiatorID: s.usr.ID, | ||
| }). | ||
| Succeeded(dbfake.WithJobCompletedAt(b.at)) |
There was a problem hiding this comment.
This approach turned out pretty good IMO! 👍🏻
|
Filed coder/internal#1344 for the |
Motivation: reduce
dbgenboilerplate when creating tests that depend on specific pieces of provisioner job timing.Relates to #21922 / coder/internal#1259
Original API design:
Extends dbfake.WorkspaceBuildBuilder with timestamp and status control methods
Starting(), which sets the provisioner job start and update time.Pending(), which sets provisioner job create and update timeCanceled(), which sets provisioner job complete and cancel timeFailed()which sets a job to the failed state with a given error message / error code, and optionally allows setting job complete time.Succeeded()which allows setting the completed at time of the job.Updated after review feedback:
dbfake.BuilderOption func(*WorkspaceBuildBuilder)BuilderOptionmethods for setting various provisioner job related fields onWorkspaceBuildBuilder.coderd/jobreapercoderd/notifications/reportsenterprise/coderd/scheduleenterprise/coderd/prebuildsscripts/workspace-runtime-audit🤖 Created using Mux (Opus 4.5)