-
Notifications
You must be signed in to change notification settings - Fork 875
chore: Add test helpers to improve coverage #166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Version more accurately represents version storage. This forks from the WorkspaceHistory name, but I think it's easier to understand Workspace history.
Codecov Report
@@ Coverage Diff @@
## main #166 +/- ##
==========================================
+ Coverage 65.44% 67.45% +2.01%
==========================================
Files 106 106
Lines 5580 5636 +56
Branches 68 68
==========================================
+ Hits 3652 3802 +150
+ Misses 1572 1475 -97
- Partials 356 359 +3
Continue to review full report at Codecov.
|
a703482
to
b2d6337
Compare
b2d6337
to
57efae2
Compare
13ef836
to
16449d8
Compare
16449d8
to
0fb2a5a
Compare
}) | ||
client := coderdtest.New(t) | ||
_ = coderdtest.CreateInitialUser(t, client) | ||
projects, err := client.Projects(context.Background(), "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like how much simpler this is 🎉
if logs == nil { | ||
logs = []database.WorkspaceHistoryLog{} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this is the same issue I saw with projects? Sometimes, I'd get nil
for projects returned - even in the case where was no error (and had to do something similar): https://github.com/coder/coder/pull/140/files#r797267887
I didn't get a chance to dive deeper yet (I'll look at it next week) - but wondering if its the same issue here? And maybe a bug with our query or sqlc
integration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, it is! Not an issue with our sqlc
implementation, just a false assumption on my part! We now test for this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, good to know! I didn't realize that there could be a case where those functions could return nil
(I thought they'd always return an empty array) - but something I'll watch out for now.
// Use xerrors everywhere! It provides additional stacktrace info! | ||
//nolint:unused,deadcode,varnamelen | ||
func xerrors(m dsl.Matcher) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding this 👍
@@ -220,7 +224,7 @@ func (p *provisionerDaemon) runJob(ctx context.Context, job *proto.AcquiredJob) | |||
JobId: job.JobId, | |||
}) | |||
if err != nil { | |||
go p.cancelActiveJobf("send periodic update: %s", err) | |||
p.cancelActiveJobf("send periodic update: %s", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this intentional to remove all these? It might make sense to split this out to a separate change, if possible - just in case there is flakiness or a race condition introduced, it'll be easier to track down.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was intentional! The job was hitting a finished
state even though it was supposed to cancel itself.
We can separate it out if you feel strongly. It was a logical lapse when creating provisionerd for sure.
Co-authored-by: Bryan <bryan@coder.com>
provisionerState := []byte{} | ||
// If workspace history exists before this entry, use that state. | ||
// We can't use the before state everytime, because if a job fails | ||
// for some random reason, the workspace shouldn't be reset. | ||
// | ||
// Maybe we should make state global on a workspace? | ||
if workspaceHistory.BeforeID.Valid { | ||
beforeHistory, err := server.Database.GetWorkspaceHistoryByID(ctx, workspaceHistory.BeforeID.UUID) | ||
if err != nil { | ||
return nil, failJob(fmt.Sprintf("get workspace history: %s", err)) | ||
} | ||
provisionerState = beforeHistory.ProvisionerState | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this not needed, or is it handled elsewhere? Wasn't clear to me why this could be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not needed. This wasn't tested before, and wasn't actually necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some minor nits, but overall looks like a really nice improvement to our tests 🎉 Thanks for doing this cleanup @kylecarbs !
settings: | ||
ruleguard: | ||
failOn: all | ||
rules: rules.go |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggest using separate files for ruleguard like we do in the monorepo (and using a wildcard here), it helps keep each rule organized, and gives us a way to group related rules
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer for us to wait until this becomes more unruly.
return | ||
} | ||
|
||
err := os.MkdirAll(p.opts.WorkDirectory, 0700) | ||
if err != nil { | ||
go p.cancelActiveJobf("create work directory %q: %s", p.opts.WorkDirectory, err) | ||
p.cancelActiveJobf("create work directory %q: %s", p.opts.WorkDirectory, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice, doing this sync seems like it'll be easier to follow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed!
@@ -54,7 +55,8 @@ func New(clientDialer Dialer, opts *Options) io.Closer { | |||
closeCancel: ctxCancel, | |||
closed: make(chan struct{}), | |||
|
|||
jobRunning: make(chan struct{}), | |||
jobRunning: make(chan struct{}), | |||
jobCancelled: *atomic.NewBool(true), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how come we need to use this atomic class if we're already using locks to protect things?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Job cancel and completion need to be grouped separately. I tried to make them one before this, but it became more difficult to manage.
If two cancels come immediately after one another, it's not good to lock on a mutex and wait for the other to complete, because a job may cancel itself at the same time provisionerd is canceling it, and lock forever.
provisioner/echo/serve_test.go
Outdated
@@ -53,7 +53,7 @@ func TestEcho(t *testing.T) { | |||
}, | |||
}, | |||
}} | |||
data, err := echo.Tar(responses, nil) | |||
data, err := echo.Tar(&echo.Responses{responses, nil}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
personally I find this way of defining structs to be less readable, and prefer the variant that lists fields. this is also order-dependent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. I'll adjust this!
id uuid NOT NULL, | ||
created_at timestamp with time zone NOT NULL, | ||
project_version_id uuid NOT NULL, | ||
name character varying(64) NOT NULL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just curious, are there benefits to defining columns as varchar
vs text
? or is it just cosmetic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a nice restriction to prevent someone from making a 10MB name accidentally.
|
||
t.Run("CreateMultiple", func(t *testing.T) { | ||
func TestUserOrganizations(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do these functions handle context cancellation? might be nice to add tests to check that the server won't do anything if the context has been cancelled and/or that it can abandon in-progress requests and return early in that case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The t.Cleanup
on coderdtest.New
will close all connections, so shouldn't be possible.
I'm adding goleak now though so human error can't exist here. Good suggestion!
codersdk/client.go
Outdated
@@ -112,5 +113,9 @@ func (e *Error) StatusCode() int { | |||
} | |||
|
|||
func (e *Error) Error() string { | |||
return fmt.Sprintf("status code %d: %s", e.statusCode, e.Message) | |||
msg := fmt.Sprintf("status code %d: %s", e.statusCode, e.Message) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it doesn't really matter unless this is a hot path, but using strings.Builder
and Fprintf
to construct these will reduce allocations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestion regardless! Updated it.
} | ||
if err != nil { | ||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ | ||
Message: fmt.Sprintf("get workspace history: %s", err), | ||
}) | ||
return | ||
} | ||
if logs == nil { | ||
logs = []database.WorkspaceHistoryLog{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this is just to get the JSON to return an empty array instead of nil
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly!
return | ||
} | ||
default: | ||
tarReader := tar.NewReader(bytes.NewReader(createProjectVersion.StorageSource)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should there be a content negotiation check here? i.e. client should use a Content-Type: application/x-tar
on the upload, and server can return 415 Unsupported Media Type
- that would allow us to accept other content types in the future
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/415
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose the answer is maybe. Right now, the archive is sent as base64 encoded bytes in JSON. That's not ideal, and we'll probably swap it to a file-upload format like mentioned.
b900f18
to
479397d
Compare
95c711e
to
b900f18
Compare
ad31063
to
a8a0531
Compare
This removes the
Server
struct fromcoderdtest
. I don't believe we should ever need anything but an API client to test all functionality againstcoderd
. If we do, there's probably a bad abstraction in play.I've adopted a naming scheme of
Test<HTTP handler>
for each route. I'll be creating a custom linter for it inrules.go
.