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

Lines changed: 16 additions & 20 deletions
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
coder

coderd/coderd.go

Lines changed: 5 additions & 1 deletion
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

Lines changed: 108 additions & 6 deletions
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

Lines changed: 32 additions & 5 deletions
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

Lines changed: 3 additions & 3 deletions
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

Lines changed: 3 additions & 3 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy