Skip to content

feat: unexpose coderdtest.NewWithAPI #2613

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

Merged
merged 11 commits into from
Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 16 additions & 20 deletions cli/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cli_test

import (
"context"
"database/sql"
"fmt"
"os"
"testing"
Expand All @@ -13,7 +12,6 @@ import (

"github.com/coder/coder/cli/clitest"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/provisioner/echo"
"github.com/coder/coder/provisionersdk/proto"
Expand Down Expand Up @@ -255,44 +253,42 @@ func TestCreate(t *testing.T) {

t.Run("FailedDryRun", func(t *testing.T) {
t.Parallel()
client, api := coderdtest.NewWithAPI(t, &coderdtest.Options{IncludeProvisionerD: true})
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
Parse: echo.ParseComplete,
Parse: []*proto.Parse_Response{{
Type: &proto.Parse_Response_Complete{
Complete: &proto.Parse_Complete{
ParameterSchemas: echo.ParameterSuccess,
},
},
}},
ProvisionDryRun: []*proto.Provision_Response{
{
Type: &proto.Provision_Response_Complete{
Complete: &proto.Provision_Complete{
Error: "test error",
},
Complete: &proto.Provision_Complete{},
},
},
},
})

tempDir := t.TempDir()
parameterFile, _ := os.CreateTemp(tempDir, "testParameterFile*.yaml")
_, _ = parameterFile.WriteString(fmt.Sprintf("%s: %q", echo.ParameterExecKey, echo.ParameterError("fail")))

// The template import job should end up failed, but we need it to be
// succeeded so the dry-run can begin.
version = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
require.Equal(t, codersdk.ProvisionerJobFailed, version.Job.Status, "job is not failed")
err := api.Database.UpdateProvisionerJobWithCompleteByID(context.Background(), database.UpdateProvisionerJobWithCompleteByIDParams{
ID: version.Job.ID,
CompletedAt: sql.NullTime{
Time: time.Now(),
Valid: true,
},
UpdatedAt: time.Now(),
Error: sql.NullString{},
})
require.NoError(t, err, "update provisioner job")
require.Equal(t, codersdk.ProvisionerJobSucceeded, version.Job.Status, "job is not failed")

_ = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
cmd, root := clitest.New(t, "create", "test")
cmd, root := clitest.New(t, "create", "test", "--parameter-file", parameterFile.Name())
clitest.SetupConfig(t, client, root)
pty := ptytest.New(t)
cmd.SetIn(pty.Input())
cmd.SetOut(pty.Output())

err = cmd.Execute()
err := cmd.Execute()
require.Error(t, err)
require.ErrorContains(t, err, "dry-run workspace")
})
Expand Down
1 change: 1 addition & 0 deletions cmd/coder/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
coder
6 changes: 5 additions & 1 deletion coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,11 @@ func New(options *Options) *API {

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

r.Post("/keys", api.postAPIKey)
r.Route("/keys", func(r chi.Router) {
r.Post("/", api.postAPIKey)
r.Get("/{keyid}", api.apiKey)
})

r.Route("/organizations", func(r chi.Router) {
r.Get("/", api.organizationsByUser)
r.Get("/{organizationname}", api.organizationByUserAndName)
Expand Down
114 changes: 108 additions & 6 deletions coderd/coderd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ package coderd_test

import (
"context"
"crypto/x509"
"database/sql"
"io"
"net"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strconv"
"strings"
"testing"
Expand All @@ -14,10 +20,23 @@ import (
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
"golang.org/x/xerrors"
"google.golang.org/api/idtoken"
"google.golang.org/api/option"

"cdr.dev/slog"
"cdr.dev/slog/sloggers/slogtest"

"github.com/coder/coder/buildinfo"
"github.com/coder/coder/coderd"
"github.com/coder/coder/coderd/autobuild/executor"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/databasefake"
"github.com/coder/coder/coderd/database/postgres"
"github.com/coder/coder/coderd/gitsshkey"
"github.com/coder/coder/coderd/rbac"
"github.com/coder/coder/coderd/telemetry"
"github.com/coder/coder/coderd/turnconn"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/provisioner/echo"
"github.com/coder/coder/provisionersdk/proto"
Expand All @@ -39,13 +58,96 @@ func TestBuildInfo(t *testing.T) {
// TestAuthorizeAllEndpoints will check `authorize` is called on every endpoint registered.
func TestAuthorizeAllEndpoints(t *testing.T) {
t.Parallel()
ctx := context.Background()
var (
ctx = context.Background()
authorizer = &fakeAuthorizer{}
)

authorizer := &fakeAuthorizer{}
client, api := coderdtest.NewWithAPI(t, &coderdtest.Options{
Authorizer: authorizer,
IncludeProvisionerD: true,
})
// This function was taken from coderdtest.newWithAPI. It is intentionally
// copied to avoid exposing the API to other tests in coderd. Tests should
// not need a reference to coderd.API...this test is an exception.
newClient := func(authorizer rbac.Authorizer) (*codersdk.Client, *coderd.API) {
// This can be hotswapped for a live database instance.
db := databasefake.New()
pubsub := database.NewPubsubInMemory()
if os.Getenv("DB") != "" {
connectionURL, closePg, err := postgres.Open()
require.NoError(t, err)
t.Cleanup(closePg)
sqlDB, err := sql.Open("postgres", connectionURL)
require.NoError(t, err)
t.Cleanup(func() {
_ = sqlDB.Close()
})
err = database.MigrateUp(sqlDB)
require.NoError(t, err)
db = database.New(sqlDB)

pubsub, err = database.NewPubsub(context.Background(), sqlDB, connectionURL)
require.NoError(t, err)
t.Cleanup(func() {
_ = pubsub.Close()
})
}

tickerCh := make(chan time.Time)
t.Cleanup(func() { close(tickerCh) })

ctx, cancelFunc := context.WithCancel(context.Background())
lifecycleExecutor := executor.New(
ctx,
db,
slogtest.Make(t, nil).Named("autobuild.executor").Leveled(slog.LevelDebug),
tickerCh,
).WithStatsChannel(nil)
lifecycleExecutor.Run()

srv := httptest.NewUnstartedServer(nil)
srv.Config.BaseContext = func(_ net.Listener) context.Context {
return ctx
}
srv.Start()
serverURL, err := url.Parse(srv.URL)
require.NoError(t, err)

turnServer, err := turnconn.New(nil)
require.NoError(t, err)

validator, err := idtoken.NewValidator(ctx, option.WithoutAuthentication())
require.NoError(t, err)

// We set the handler after server creation for the access URL.
coderAPI := coderd.New(&coderd.Options{
AgentConnectionUpdateFrequency: 150 * time.Millisecond,
AccessURL: serverURL,
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
Database: db,
Pubsub: pubsub,

AWSCertificates: nil,
AzureCertificates: x509.VerifyOptions{},
GithubOAuth2Config: nil,
GoogleTokenValidator: validator,
SSHKeygenAlgorithm: gitsshkey.AlgorithmEd25519,
TURNServer: turnServer,
APIRateLimit: 0,
Authorizer: authorizer,
Telemetry: telemetry.NewNoop(),
})
srv.Config.Handler = coderAPI.Handler

_ = coderdtest.NewProvisionerDaemon(t, coderAPI)
t.Cleanup(func() {
cancelFunc()
_ = turnServer.Close()
srv.Close()
_ = coderAPI.Close()
})

return codersdk.New(serverURL), coderAPI
}

client, api := newClient(authorizer)
admin := coderdtest.CreateFirstUser(t, client)
// The provisioner will call to coderd and register itself. This is async,
// so we wait for it to occur.
Expand Down
37 changes: 32 additions & 5 deletions coderd/coderdtest/coderdtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,31 @@ type Options struct {

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

// NewWithAPI constructs a codersdk client connected to the returned in-memory API instance.
func NewWithAPI(t *testing.T, options *Options) (*codersdk.Client, *coderd.API) {
// NewWithProvisionerCloser returns a client as well as a handle to close
// the provisioner. This is a temporary function while work is done to
// standardize how provisioners are registered with coderd. The option
// to include a provisioner is set to true for convenience.
func NewWithProvisionerCloser(t *testing.T, options *Options) (*codersdk.Client, io.Closer) {
if options == nil {
options = &Options{}
}
options.IncludeProvisionerD = true
client, closer := newWithCloser(t, options)
return client, closer
}

// newWithCloser constructs a codersdk client connected to an in-memory API instance.
// The returned closer closes a provisioner if it was provided
// The API is intentionally not returned here because coderd tests should not
// require a handle to the API. Do not expose the API or wrath shall descend
// upon thee. Even the io.Closer that is exposed here shouldn't be exposed
// and is a temporary measure while the API to register provisioners is ironed
// out.
func newWithCloser(t *testing.T, options *Options) (*codersdk.Client, io.Closer) {
if options == nil {
options = &Options{}
}
Expand Down Expand Up @@ -169,17 +188,21 @@ func NewWithAPI(t *testing.T, options *Options) (*codersdk.Client, *coderd.API)
Telemetry: telemetry.NewNoop(),
})
srv.Config.Handler = coderAPI.Handler

var provisionerCloser io.Closer = nopcloser{}
if options.IncludeProvisionerD {
_ = NewProvisionerDaemon(t, coderAPI)
provisionerCloser = NewProvisionerDaemon(t, coderAPI)
}

t.Cleanup(func() {
cancelFunc()
_ = turnServer.Close()
srv.Close()
_ = coderAPI.Close()
_ = provisionerCloser.Close()
})

return codersdk.New(serverURL), coderAPI
return codersdk.New(serverURL), provisionerCloser
}

// NewProvisionerDaemon launches a provisionerd instance configured to work
Expand Down Expand Up @@ -648,3 +671,7 @@ type roundTripper func(req *http.Request) (*http.Response, error)
func (r roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
return r(req)
}

type nopcloser struct{}

func (nopcloser) Close() error { return nil }
6 changes: 3 additions & 3 deletions coderd/coderdtest/coderdtest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ func TestMain(m *testing.M) {

func TestNew(t *testing.T) {
t.Parallel()
client, coderAPI := coderdtest.NewWithAPI(t, nil)
client := coderdtest.New(t, &coderdtest.Options{
IncludeProvisionerD: true,
})
user := coderdtest.CreateFirstUser(t, client)
closer := coderdtest.NewProvisionerDaemon(t, coderAPI)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
Expand All @@ -25,5 +26,4 @@ func TestNew(t *testing.T) {
coderdtest.AwaitWorkspaceAgents(t, client, workspace.LatestBuild.ID)
_, _ = coderdtest.NewGoogleInstanceIdentity(t, "example", false)
_, _ = coderdtest.NewAWSInstanceIdentity(t, "an-instance")
closer.Close()
}
6 changes: 3 additions & 3 deletions coderd/gitsshkey_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ func TestGitSSHKey(t *testing.T) {
func TestAgentGitSSHKey(t *testing.T) {
t.Parallel()

client, coderAPI := coderdtest.NewWithAPI(t, nil)
client := coderdtest.New(t, &coderdtest.Options{
IncludeProvisionerD: true,
})
user := coderdtest.CreateFirstUser(t, client)
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderAPI)
authToken := uuid.NewString()
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
Parse: echo.ParseComplete,
Expand All @@ -107,7 +108,6 @@ func TestAgentGitSSHKey(t *testing.T) {
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
daemonCloser.Close()

agentClient := codersdk.New(client.URL)
agentClient.SessionToken = authToken
Expand Down
Loading