Skip to content

feat: add an endpoint to manually pause a coder task#21889

Merged
SasSwart merged 10 commits intomainfrom
jjs/internal-1261
Feb 9, 2026
Merged

feat: add an endpoint to manually pause a coder task#21889
SasSwart merged 10 commits intomainfrom
jjs/internal-1261

Conversation

@SasSwart
Copy link
Contributor

@SasSwart SasSwart commented Feb 3, 2026

Closes coder/internal#1261.

This pull request adds an endpoint to pause coder tasks by stopping the underlying workspace.

  • Instead of POST /api/v2/tasks/{user}/{task}/pause, the endpoint is currently experimental.

getWorkspaceByID: func(ctx context.Context, id uuid.UUID) (database.Workspace, error) {
if id == workspaceID && id != uuid.Nil {
return database.Workspace{}, sql.ErrNoRows
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the idea here is to provide 100% coverage? Even so, I think we could simplify and skip the wrapper entirely by using dbgen to insert the data in a way that produces the same or similar result, or skip cases that can't happen. (Same for the other uses too.)

For instance, our data model already enforces that when a WorkspaceID is non-null, the workspace must exist (FK).

Main motivator here is to keep the tests aligned with existing tests for endpoints.

Copy link
Contributor Author

@SasSwart SasSwart Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked into this. I've removed a few spots where we were using the wrapper with no benefit. That leaves it only in-use in two tests.

One of these tests is technically currently redundant because of the FK, as you stated. I see some value in keeping it simply because it defines behaviour defensively in case anything ever changes to make it possible. Nevertheless, I'm happy to remove it.

For the last remaining use of the wrapper in t.Run("Workspace lookup internal error",..., simulating this using a contrived db interaction would be less clear in my opinion than faking the behaviour this way.

What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is one of those things that it'd make sense to test for all endpoints if we're going to, rather than just one. I'd be OK with cutting it but won't enforce it.

Alternatively perhaps it could be a shared method like func (*API) getTaskWorkspace that can be used by all handlers so we only need to test it once. I'm not particularly fond of that suggestion, however.

(For context, this aitasks_test.go file already has many testing patterns, and I'm a bit concerned with each endpoint having bespoke testing arbitrarily enforcing some conditions while allowing others to go unchecked.)

expectedStatus: http.StatusAccepted,
},
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit conflicted about testing this (I don't think we have it for other endpoints). On the one hand, it's very illustrative. But on another, this should already be enforced by rbac/middleware that's in use.

At the very least, this should not be repeated per-endpoint, but rather testing all task endpoints for the same roles.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote this test to prove to myself that the following requirement is implicitly met: Authorization checks task ownership

You're right that it should already be enforced in another abstraction layer architecturally. But this test makes a domain level promise based on the product requirement.

I'm happy to rework this. We could just move it to the package where this is enforced. But then its no longer as clear that we have a test for this specific requirement on this specific endpoint.

What do you think?

Copy link
Member

@mafredri mafredri Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can understand how that might seem like a distinction that requires testing, however, this endpoint does not differ in authorization compared to other task endpoints. Essentially what that requirement asks for is the presence of the task id/name handling middleware which enforces auth.

One way to stress-test this addition is: Would you duplicate/repeat these tests for the resume endpoint? If yes, that's a signal that we're either going too granular or should generalize.

On (some) other endpoints we're testing auth by creating another user and making sure they don't have access. This is already duplication, but it's not taking roles to this level of granularity. To me it would make sense to take this level of granularity to all endpoints (one test/subtests, tests all) and get rid of the duplication. Or, to duplicate the more simplistic test. I'd prefer the former but up to you as either approach is OK for the PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One way to stress-test this addition is: Would you duplicate/repeat these tests for the resume endpoint? If yes, that's a signal that we're either going too granular or should generalize.

This is a great point. And in response I tend to fall on the "should generalize" side of things. Perhaps we can start by generalizing this test to work for both Pause and Resume in the next PR. And then after that generalize it further to all of tasks or beyond?

require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
})

t.Run("Task lookup forbidden", func(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This repeats the role test above?

Copy link
Contributor Author

@SasSwart SasSwart Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this test the task actually exists and we test that the reason it can't be found is authz. its part of testing the Authorization checks task ownership requirement.

The test above checks what happens when there is just simply no such task.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice to generalize this as well for all tasks endpoints tbh. Perhaps we should create a follow-up task to tests auth/roles for all endpoints and remove duplication?

@SasSwart
Copy link
Contributor Author

SasSwart commented Feb 6, 2026

@mafredri I'll work on generalizing the tests over in the next PR where I am already implementing the Task resume tests. It makes sense to do it there since we have both endpoints to generalize on. Is there anything else you'd like to address on this PR or can we move forward?

@SasSwart SasSwart requested a review from mafredri February 6, 2026 14:02
Copy link
Member

@mafredri mafredri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy with the suggestions for test refactoring/generalization in follow-up PR. Implementation looks solid, nice work. 👍🏻

getWorkspaceByID: func(ctx context.Context, id uuid.UUID) (database.Workspace, error) {
if id == workspaceID && id != uuid.Nil {
return database.Workspace{}, sql.ErrNoRows
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is one of those things that it'd make sense to test for all endpoints if we're going to, rather than just one. I'd be OK with cutting it but won't enforce it.

Alternatively perhaps it could be a shared method like func (*API) getTaskWorkspace that can be used by all handlers so we only need to test it once. I'm not particularly fond of that suggestion, however.

(For context, this aitasks_test.go file already has many testing patterns, and I'm a bit concerned with each endpoint having bespoke testing arbitrarily enforcing some conditions while allowing others to go unchecked.)

@SasSwart SasSwart merged commit e6fbf50 into main Feb 9, 2026
32 checks passed
@SasSwart SasSwart deleted the jjs/internal-1261 branch February 9, 2026 06:56
@github-actions github-actions bot locked and limited conversation to collaborators Feb 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tasks: Manual pause endpoint

2 participants