Skip to content

Commit 2353687

Browse files
authored
feat: unexpose coderdtest.NewWithAPI (#2613)
* feat: unexpose coderdtest.NewWithAPI
1 parent 7dfec82 commit 2353687

18 files changed

+409
-241
lines changed

cli/create_test.go

+16-20
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package cli_test
22

33
import (
44
"context"
5-
"database/sql"
65
"fmt"
76
"os"
87
"testing"
@@ -13,7 +12,6 @@ import (
1312

1413
"github.com/coder/coder/cli/clitest"
1514
"github.com/coder/coder/coderd/coderdtest"
16-
"github.com/coder/coder/coderd/database"
1715
"github.com/coder/coder/codersdk"
1816
"github.com/coder/coder/provisioner/echo"
1917
"github.com/coder/coder/provisionersdk/proto"
@@ -255,44 +253,42 @@ func TestCreate(t *testing.T) {
255253

256254
t.Run("FailedDryRun", func(t *testing.T) {
257255
t.Parallel()
258-
client, api := coderdtest.NewWithAPI(t, &coderdtest.Options{IncludeProvisionerD: true})
256+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
259257
user := coderdtest.CreateFirstUser(t, client)
260258
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
261-
Parse: echo.ParseComplete,
259+
Parse: []*proto.Parse_Response{{
260+
Type: &proto.Parse_Response_Complete{
261+
Complete: &proto.Parse_Complete{
262+
ParameterSchemas: echo.ParameterSuccess,
263+
},
264+
},
265+
}},
262266
ProvisionDryRun: []*proto.Provision_Response{
263267
{
264268
Type: &proto.Provision_Response_Complete{
265-
Complete: &proto.Provision_Complete{
266-
Error: "test error",
267-
},
269+
Complete: &proto.Provision_Complete{},
268270
},
269271
},
270272
},
271273
})
272274

275+
tempDir := t.TempDir()
276+
parameterFile, _ := os.CreateTemp(tempDir, "testParameterFile*.yaml")
277+
_, _ = parameterFile.WriteString(fmt.Sprintf("%s: %q", echo.ParameterExecKey, echo.ParameterError("fail")))
278+
273279
// The template import job should end up failed, but we need it to be
274280
// succeeded so the dry-run can begin.
275281
version = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
276-
require.Equal(t, codersdk.ProvisionerJobFailed, version.Job.Status, "job is not failed")
277-
err := api.Database.UpdateProvisionerJobWithCompleteByID(context.Background(), database.UpdateProvisionerJobWithCompleteByIDParams{
278-
ID: version.Job.ID,
279-
CompletedAt: sql.NullTime{
280-
Time: time.Now(),
281-
Valid: true,
282-
},
283-
UpdatedAt: time.Now(),
284-
Error: sql.NullString{},
285-
})
286-
require.NoError(t, err, "update provisioner job")
282+
require.Equal(t, codersdk.ProvisionerJobSucceeded, version.Job.Status, "job is not failed")
287283

288284
_ = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
289-
cmd, root := clitest.New(t, "create", "test")
285+
cmd, root := clitest.New(t, "create", "test", "--parameter-file", parameterFile.Name())
290286
clitest.SetupConfig(t, client, root)
291287
pty := ptytest.New(t)
292288
cmd.SetIn(pty.Input())
293289
cmd.SetOut(pty.Output())
294290

295-
err = cmd.Execute()
291+
err := cmd.Execute()
296292
require.Error(t, err)
297293
require.ErrorContains(t, err, "dry-run workspace")
298294
})

cmd/coder/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
coder

coderd/coderd.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,11 @@ func New(options *Options) *API {
287287

288288
r.Post("/authorization", api.checkPermissions)
289289

290-
r.Post("/keys", api.postAPIKey)
290+
r.Route("/keys", func(r chi.Router) {
291+
r.Post("/", api.postAPIKey)
292+
r.Get("/{keyid}", api.apiKey)
293+
})
294+
291295
r.Route("/organizations", func(r chi.Router) {
292296
r.Get("/", api.organizationsByUser)
293297
r.Get("/{organizationname}", api.organizationByUserAndName)

coderd/coderd_test.go

+108-6
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ package coderd_test
22

33
import (
44
"context"
5+
"crypto/x509"
6+
"database/sql"
57
"io"
8+
"net"
69
"net/http"
10+
"net/http/httptest"
11+
"net/url"
12+
"os"
713
"strconv"
814
"strings"
915
"testing"
@@ -14,10 +20,23 @@ import (
1420
"github.com/stretchr/testify/require"
1521
"go.uber.org/goleak"
1622
"golang.org/x/xerrors"
23+
"google.golang.org/api/idtoken"
24+
"google.golang.org/api/option"
25+
26+
"cdr.dev/slog"
27+
"cdr.dev/slog/sloggers/slogtest"
1728

1829
"github.com/coder/coder/buildinfo"
30+
"github.com/coder/coder/coderd"
31+
"github.com/coder/coder/coderd/autobuild/executor"
1932
"github.com/coder/coder/coderd/coderdtest"
33+
"github.com/coder/coder/coderd/database"
34+
"github.com/coder/coder/coderd/database/databasefake"
35+
"github.com/coder/coder/coderd/database/postgres"
36+
"github.com/coder/coder/coderd/gitsshkey"
2037
"github.com/coder/coder/coderd/rbac"
38+
"github.com/coder/coder/coderd/telemetry"
39+
"github.com/coder/coder/coderd/turnconn"
2140
"github.com/coder/coder/codersdk"
2241
"github.com/coder/coder/provisioner/echo"
2342
"github.com/coder/coder/provisionersdk/proto"
@@ -39,13 +58,96 @@ func TestBuildInfo(t *testing.T) {
3958
// TestAuthorizeAllEndpoints will check `authorize` is called on every endpoint registered.
4059
func TestAuthorizeAllEndpoints(t *testing.T) {
4160
t.Parallel()
42-
ctx := context.Background()
61+
var (
62+
ctx = context.Background()
63+
authorizer = &fakeAuthorizer{}
64+
)
4365

44-
authorizer := &fakeAuthorizer{}
45-
client, api := coderdtest.NewWithAPI(t, &coderdtest.Options{
46-
Authorizer: authorizer,
47-
IncludeProvisionerD: true,
48-
})
66+
// This function was taken from coderdtest.newWithAPI. It is intentionally
67+
// copied to avoid exposing the API to other tests in coderd. Tests should
68+
// not need a reference to coderd.API...this test is an exception.
69+
newClient := func(authorizer rbac.Authorizer) (*codersdk.Client, *coderd.API) {
70+
// This can be hotswapped for a live database instance.
71+
db := databasefake.New()
72+
pubsub := database.NewPubsubInMemory()
73+
if os.Getenv("DB") != "" {
74+
connectionURL, closePg, err := postgres.Open()
75+
require.NoError(t, err)
76+
t.Cleanup(closePg)
77+
sqlDB, err := sql.Open("postgres", connectionURL)
78+
require.NoError(t, err)
79+
t.Cleanup(func() {
80+
_ = sqlDB.Close()
81+
})
82+
err = database.MigrateUp(sqlDB)
83+
require.NoError(t, err)
84+
db = database.New(sqlDB)
85+
86+
pubsub, err = database.NewPubsub(context.Background(), sqlDB, connectionURL)
87+
require.NoError(t, err)
88+
t.Cleanup(func() {
89+
_ = pubsub.Close()
90+
})
91+
}
92+
93+
tickerCh := make(chan time.Time)
94+
t.Cleanup(func() { close(tickerCh) })
95+
96+
ctx, cancelFunc := context.WithCancel(context.Background())
97+
lifecycleExecutor := executor.New(
98+
ctx,
99+
db,
100+
slogtest.Make(t, nil).Named("autobuild.executor").Leveled(slog.LevelDebug),
101+
tickerCh,
102+
).WithStatsChannel(nil)
103+
lifecycleExecutor.Run()
104+
105+
srv := httptest.NewUnstartedServer(nil)
106+
srv.Config.BaseContext = func(_ net.Listener) context.Context {
107+
return ctx
108+
}
109+
srv.Start()
110+
serverURL, err := url.Parse(srv.URL)
111+
require.NoError(t, err)
112+
113+
turnServer, err := turnconn.New(nil)
114+
require.NoError(t, err)
115+
116+
validator, err := idtoken.NewValidator(ctx, option.WithoutAuthentication())
117+
require.NoError(t, err)
118+
119+
// We set the handler after server creation for the access URL.
120+
coderAPI := coderd.New(&coderd.Options{
121+
AgentConnectionUpdateFrequency: 150 * time.Millisecond,
122+
AccessURL: serverURL,
123+
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
124+
Database: db,
125+
Pubsub: pubsub,
126+
127+
AWSCertificates: nil,
128+
AzureCertificates: x509.VerifyOptions{},
129+
GithubOAuth2Config: nil,
130+
GoogleTokenValidator: validator,
131+
SSHKeygenAlgorithm: gitsshkey.AlgorithmEd25519,
132+
TURNServer: turnServer,
133+
APIRateLimit: 0,
134+
Authorizer: authorizer,
135+
Telemetry: telemetry.NewNoop(),
136+
})
137+
srv.Config.Handler = coderAPI.Handler
138+
139+
_ = coderdtest.NewProvisionerDaemon(t, coderAPI)
140+
t.Cleanup(func() {
141+
cancelFunc()
142+
_ = turnServer.Close()
143+
srv.Close()
144+
_ = coderAPI.Close()
145+
})
146+
147+
return codersdk.New(serverURL), coderAPI
148+
}
149+
150+
client, api := newClient(authorizer)
49151
admin := coderdtest.CreateFirstUser(t, client)
50152
// The provisioner will call to coderd and register itself. This is async,
51153
// so we wait for it to occur.

coderd/coderdtest/coderdtest.go

+32-5
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,31 @@ type Options struct {
7474

7575
// New constructs a codersdk client connected to an in-memory API instance.
7676
func New(t *testing.T, options *Options) *codersdk.Client {
77-
client, _ := NewWithAPI(t, options)
77+
client, _ := newWithCloser(t, options)
7878
return client
7979
}
8080

81-
// NewWithAPI constructs a codersdk client connected to the returned in-memory API instance.
82-
func NewWithAPI(t *testing.T, options *Options) (*codersdk.Client, *coderd.API) {
81+
// NewWithProvisionerCloser returns a client as well as a handle to close
82+
// the provisioner. This is a temporary function while work is done to
83+
// standardize how provisioners are registered with coderd. The option
84+
// to include a provisioner is set to true for convenience.
85+
func NewWithProvisionerCloser(t *testing.T, options *Options) (*codersdk.Client, io.Closer) {
86+
if options == nil {
87+
options = &Options{}
88+
}
89+
options.IncludeProvisionerD = true
90+
client, closer := newWithCloser(t, options)
91+
return client, closer
92+
}
93+
94+
// newWithCloser constructs a codersdk client connected to an in-memory API instance.
95+
// The returned closer closes a provisioner if it was provided
96+
// The API is intentionally not returned here because coderd tests should not
97+
// require a handle to the API. Do not expose the API or wrath shall descend
98+
// upon thee. Even the io.Closer that is exposed here shouldn't be exposed
99+
// and is a temporary measure while the API to register provisioners is ironed
100+
// out.
101+
func newWithCloser(t *testing.T, options *Options) (*codersdk.Client, io.Closer) {
83102
if options == nil {
84103
options = &Options{}
85104
}
@@ -169,17 +188,21 @@ func NewWithAPI(t *testing.T, options *Options) (*codersdk.Client, *coderd.API)
169188
Telemetry: telemetry.NewNoop(),
170189
})
171190
srv.Config.Handler = coderAPI.Handler
191+
192+
var provisionerCloser io.Closer = nopcloser{}
172193
if options.IncludeProvisionerD {
173-
_ = NewProvisionerDaemon(t, coderAPI)
194+
provisionerCloser = NewProvisionerDaemon(t, coderAPI)
174195
}
196+
175197
t.Cleanup(func() {
176198
cancelFunc()
177199
_ = turnServer.Close()
178200
srv.Close()
179201
_ = coderAPI.Close()
202+
_ = provisionerCloser.Close()
180203
})
181204

182-
return codersdk.New(serverURL), coderAPI
205+
return codersdk.New(serverURL), provisionerCloser
183206
}
184207

185208
// NewProvisionerDaemon launches a provisionerd instance configured to work
@@ -648,3 +671,7 @@ type roundTripper func(req *http.Request) (*http.Response, error)
648671
func (r roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
649672
return r(req)
650673
}
674+
675+
type nopcloser struct{}
676+
677+
func (nopcloser) Close() error { return nil }

coderd/coderdtest/coderdtest_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ func TestMain(m *testing.M) {
1414

1515
func TestNew(t *testing.T) {
1616
t.Parallel()
17-
client, coderAPI := coderdtest.NewWithAPI(t, nil)
17+
client := coderdtest.New(t, &coderdtest.Options{
18+
IncludeProvisionerD: true,
19+
})
1820
user := coderdtest.CreateFirstUser(t, client)
19-
closer := coderdtest.NewProvisionerDaemon(t, coderAPI)
2021
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
2122
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
2223
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
@@ -25,5 +26,4 @@ func TestNew(t *testing.T) {
2526
coderdtest.AwaitWorkspaceAgents(t, client, workspace.LatestBuild.ID)
2627
_, _ = coderdtest.NewGoogleInstanceIdentity(t, "example", false)
2728
_, _ = coderdtest.NewAWSInstanceIdentity(t, "an-instance")
28-
closer.Close()
2929
}

coderd/gitsshkey_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ func TestGitSSHKey(t *testing.T) {
7979
func TestAgentGitSSHKey(t *testing.T) {
8080
t.Parallel()
8181

82-
client, coderAPI := coderdtest.NewWithAPI(t, nil)
82+
client := coderdtest.New(t, &coderdtest.Options{
83+
IncludeProvisionerD: true,
84+
})
8385
user := coderdtest.CreateFirstUser(t, client)
84-
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderAPI)
8586
authToken := uuid.NewString()
8687
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
8788
Parse: echo.ParseComplete,
@@ -107,7 +108,6 @@ func TestAgentGitSSHKey(t *testing.T) {
107108
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
108109
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, project.ID)
109110
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
110-
daemonCloser.Close()
111111

112112
agentClient := codersdk.New(client.URL)
113113
agentClient.SessionToken = authToken

0 commit comments

Comments
 (0)