mirror of
https://github.com/coder/coder.git
synced 2025-07-06 15:41:45 +00:00
chore: Add test helpers to improve coverage (#166)
* chore: Rename ProjectHistory to ProjectVersion Version more accurately represents version storage. This forks from the WorkspaceHistory name, but I think it's easier to understand Workspace history. * Rename files * Standardize tests a bit more * Remove Server struct from coderdtest * Improve test coverage for workspace history * Fix linting errors * Fix coderd test leak * Fix coderd test leak * Improve workspace history logs * Standardize test structure for codersdk * Fix linting errors * Fix WebSocket compression * Update coderd/workspaces.go Co-authored-by: Bryan <bryan@coder.com> * Add test for listing project parameters * Cache npm dependencies with setup node * Remove windows npm cache key Co-authored-by: Bryan <bryan@coder.com>
This commit is contained in:
@ -86,7 +86,6 @@ func New(options *Options) http.Handler {
|
||||
r.Get("/", api.workspaces)
|
||||
r.Route("/{user}", func(r chi.Router) {
|
||||
r.Use(httpmw.ExtractUserParam(options.Database))
|
||||
r.Get("/", api.workspaces)
|
||||
r.Post("/", api.postWorkspaceByUser)
|
||||
r.Route("/{workspace}", func(r chi.Router) {
|
||||
r.Use(httpmw.ExtractWorkspaceParam(options.Database))
|
||||
|
11
coderd/coderd_test.go
Normal file
11
coderd/coderd_test.go
Normal file
@ -0,0 +1,11 @@
|
||||
package coderd_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m)
|
||||
}
|
@ -7,16 +7,18 @@ import (
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/moby/moby/pkg/namesgenerator"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
"github.com/coder/coder/coderd"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/cryptorand"
|
||||
"github.com/coder/coder/database"
|
||||
"github.com/coder/coder/database/databasefake"
|
||||
"github.com/coder/coder/database/postgres"
|
||||
@ -26,79 +28,9 @@ import (
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
)
|
||||
|
||||
// Server represents a test instance of coderd.
|
||||
// The database is intentionally omitted from
|
||||
// this struct to promote data being exposed via
|
||||
// the API.
|
||||
type Server struct {
|
||||
Client *codersdk.Client
|
||||
URL *url.URL
|
||||
}
|
||||
|
||||
// RandomInitialUser generates a random initial user and authenticates
|
||||
// it with the client on the Server struct.
|
||||
func (s *Server) RandomInitialUser(t *testing.T) coderd.CreateInitialUserRequest {
|
||||
username, err := cryptorand.String(12)
|
||||
require.NoError(t, err)
|
||||
password, err := cryptorand.String(12)
|
||||
require.NoError(t, err)
|
||||
organization, err := cryptorand.String(12)
|
||||
require.NoError(t, err)
|
||||
|
||||
req := coderd.CreateInitialUserRequest{
|
||||
Email: "testuser@coder.com",
|
||||
Username: username,
|
||||
Password: password,
|
||||
Organization: organization,
|
||||
}
|
||||
_, err = s.Client.CreateInitialUser(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
|
||||
login, err := s.Client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{
|
||||
Email: "testuser@coder.com",
|
||||
Password: password,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = s.Client.SetSessionToken(login.SessionToken)
|
||||
require.NoError(t, err)
|
||||
return req
|
||||
}
|
||||
|
||||
// AddProvisionerd launches a new provisionerd instance with the
|
||||
// test provisioner registered.
|
||||
func (s *Server) AddProvisionerd(t *testing.T) io.Closer {
|
||||
echoClient, echoServer := provisionersdk.TransportPipe()
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
t.Cleanup(func() {
|
||||
_ = echoClient.Close()
|
||||
_ = echoServer.Close()
|
||||
cancelFunc()
|
||||
})
|
||||
go func() {
|
||||
err := echo.Serve(ctx, &provisionersdk.ServeOptions{
|
||||
Listener: echoServer,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
closer := provisionerd.New(s.Client.ProvisionerDaemonClient, &provisionerd.Options{
|
||||
Logger: slogtest.Make(t, nil).Named("provisionerd").Leveled(slog.LevelDebug),
|
||||
PollInterval: 50 * time.Millisecond,
|
||||
UpdateInterval: 50 * time.Millisecond,
|
||||
Provisioners: provisionerd.Provisioners{
|
||||
string(database.ProvisionerTypeEcho): proto.NewDRPCProvisionerClient(provisionersdk.Conn(echoClient)),
|
||||
},
|
||||
WorkDirectory: t.TempDir(),
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
_ = closer.Close()
|
||||
})
|
||||
return closer
|
||||
}
|
||||
|
||||
// New constructs a new coderd test instance. This returned Server
|
||||
// should contain no side-effects.
|
||||
func New(t *testing.T) Server {
|
||||
func New(t *testing.T) *codersdk.Client {
|
||||
// This can be hotswapped for a live database instance.
|
||||
db := databasefake.New()
|
||||
pubsub := database.NewPubsubInMemory()
|
||||
@ -132,8 +64,123 @@ func New(t *testing.T) Server {
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
return Server{
|
||||
Client: codersdk.New(serverURL),
|
||||
URL: serverURL,
|
||||
}
|
||||
return codersdk.New(serverURL)
|
||||
}
|
||||
|
||||
// NewProvisionerDaemon launches a provisionerd instance configured to work
|
||||
// well with coderd testing. It registers the "echo" provisioner for
|
||||
// quick testing.
|
||||
func NewProvisionerDaemon(t *testing.T, client *codersdk.Client) io.Closer {
|
||||
echoClient, echoServer := provisionersdk.TransportPipe()
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
t.Cleanup(func() {
|
||||
_ = echoClient.Close()
|
||||
_ = echoServer.Close()
|
||||
cancelFunc()
|
||||
})
|
||||
go func() {
|
||||
err := echo.Serve(ctx, &provisionersdk.ServeOptions{
|
||||
Listener: echoServer,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
closer := provisionerd.New(client.ProvisionerDaemonClient, &provisionerd.Options{
|
||||
Logger: slogtest.Make(t, nil).Named("provisionerd").Leveled(slog.LevelDebug),
|
||||
PollInterval: 50 * time.Millisecond,
|
||||
UpdateInterval: 50 * time.Millisecond,
|
||||
Provisioners: provisionerd.Provisioners{
|
||||
string(database.ProvisionerTypeEcho): proto.NewDRPCProvisionerClient(provisionersdk.Conn(echoClient)),
|
||||
},
|
||||
WorkDirectory: t.TempDir(),
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
_ = closer.Close()
|
||||
})
|
||||
return closer
|
||||
}
|
||||
|
||||
// CreateInitialUser creates a user with preset credentials and authenticates
|
||||
// with the passed in codersdk client.
|
||||
func CreateInitialUser(t *testing.T, client *codersdk.Client) coderd.CreateInitialUserRequest {
|
||||
req := coderd.CreateInitialUserRequest{
|
||||
Email: "testuser@coder.com",
|
||||
Username: "testuser",
|
||||
Password: "testpass",
|
||||
Organization: "testorg",
|
||||
}
|
||||
_, err := client.CreateInitialUser(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
|
||||
login, err := client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{
|
||||
Email: req.Email,
|
||||
Password: req.Password,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = client.SetSessionToken(login.SessionToken)
|
||||
require.NoError(t, err)
|
||||
return req
|
||||
}
|
||||
|
||||
// CreateProject creates a project with the "echo" provisioner for
|
||||
// compatibility with testing. The name assigned is randomly generated.
|
||||
func CreateProject(t *testing.T, client *codersdk.Client, organization string) coderd.Project {
|
||||
project, err := client.CreateProject(context.Background(), organization, coderd.CreateProjectRequest{
|
||||
Name: randomUsername(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return project
|
||||
}
|
||||
|
||||
// CreateProjectVersion creates a project version for the "echo" provisioner
|
||||
// for compatibility with testing.
|
||||
func CreateProjectVersion(t *testing.T, client *codersdk.Client, organization, project string, responses *echo.Responses) coderd.ProjectVersion {
|
||||
data, err := echo.Tar(responses)
|
||||
require.NoError(t, err)
|
||||
version, err := client.CreateProjectVersion(context.Background(), organization, project, coderd.CreateProjectVersionRequest{
|
||||
StorageMethod: database.ProjectStorageMethodInlineArchive,
|
||||
StorageSource: data,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return version
|
||||
}
|
||||
|
||||
// AwaitProjectVersionImported awaits for the project import job to reach completed status.
|
||||
func AwaitProjectVersionImported(t *testing.T, client *codersdk.Client, organization, project, version string) coderd.ProjectVersion {
|
||||
var projectVersion coderd.ProjectVersion
|
||||
require.Eventually(t, func() bool {
|
||||
var err error
|
||||
projectVersion, err = client.ProjectVersion(context.Background(), organization, project, version)
|
||||
require.NoError(t, err)
|
||||
return projectVersion.Import.Status.Completed()
|
||||
}, 3*time.Second, 25*time.Millisecond)
|
||||
return projectVersion
|
||||
}
|
||||
|
||||
// CreateWorkspace creates a workspace for the user and project provided.
|
||||
// A random name is generated for it.
|
||||
func CreateWorkspace(t *testing.T, client *codersdk.Client, user string, projectID uuid.UUID) coderd.Workspace {
|
||||
workspace, err := client.CreateWorkspace(context.Background(), user, coderd.CreateWorkspaceRequest{
|
||||
ProjectID: projectID,
|
||||
Name: randomUsername(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return workspace
|
||||
}
|
||||
|
||||
// AwaitWorkspaceHistoryProvisioned awaits for the workspace provision job to reach completed status.
|
||||
func AwaitWorkspaceHistoryProvisioned(t *testing.T, client *codersdk.Client, user, workspace, history string) coderd.WorkspaceHistory {
|
||||
var workspaceHistory coderd.WorkspaceHistory
|
||||
require.Eventually(t, func() bool {
|
||||
var err error
|
||||
workspaceHistory, err = client.WorkspaceHistory(context.Background(), user, workspace, history)
|
||||
require.NoError(t, err)
|
||||
return workspaceHistory.Provision.Status.Completed()
|
||||
}, 3*time.Second, 25*time.Millisecond)
|
||||
return workspaceHistory
|
||||
}
|
||||
|
||||
func randomUsername() string {
|
||||
return strings.ReplaceAll(namesgenerator.GetRandomName(0), "_", "-")
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
package coderdtest_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"go.uber.org/goleak"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/coderd"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/database"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
@ -14,7 +19,18 @@ func TestMain(m *testing.M) {
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
_ = server.RandomInitialUser(t)
|
||||
_ = server.AddProvisionerd(t)
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
closer := coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
history, err := client.CreateWorkspaceHistory(context.Background(), "me", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
coderdtest.AwaitWorkspaceHistoryProvisioned(t, client, "me", workspace.Name, history.Name)
|
||||
closer.Close()
|
||||
}
|
||||
|
@ -49,6 +49,9 @@ func (api *api) projects(rw http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
return
|
||||
}
|
||||
if projects == nil {
|
||||
projects = []database.Project{}
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(rw, r, projects)
|
||||
}
|
||||
@ -66,6 +69,9 @@ func (api *api) projectsByOrganization(rw http.ResponseWriter, r *http.Request)
|
||||
})
|
||||
return
|
||||
}
|
||||
if projects == nil {
|
||||
projects = []database.Project{}
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(rw, r, projects)
|
||||
}
|
||||
@ -124,32 +130,6 @@ func (*api) projectByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(rw, r, project)
|
||||
}
|
||||
|
||||
// Returns all workspaces for a specific project.
|
||||
func (api *api) workspacesByProject(rw http.ResponseWriter, r *http.Request) {
|
||||
apiKey := httpmw.APIKey(r)
|
||||
project := httpmw.ProjectParam(r)
|
||||
workspaces, err := api.Database.GetWorkspacesByProjectAndUserID(r.Context(), database.GetWorkspacesByProjectAndUserIDParams{
|
||||
OwnerID: apiKey.UserID,
|
||||
ProjectID: project.ID,
|
||||
})
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get workspaces: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
apiWorkspaces := make([]Workspace, 0, len(workspaces))
|
||||
for _, workspace := range workspaces {
|
||||
apiWorkspaces = append(apiWorkspaces, convertWorkspace(workspace))
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(rw, r, apiWorkspaces)
|
||||
}
|
||||
|
||||
// Creates parameters for a project.
|
||||
// This should validate the calling user has permissions!
|
||||
func (api *api) postParametersByProject(rw http.ResponseWriter, r *http.Request) {
|
||||
|
@ -2,124 +2,109 @@ package coderd_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/coderd"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/database"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
)
|
||||
|
||||
func TestProjects(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "someproject",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("AlreadyExists", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "someproject",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "someproject",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("ListEmpty", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
_ = server.RandomInitialUser(t)
|
||||
projects, err := server.Client.Projects(context.Background(), "")
|
||||
client := coderdtest.New(t)
|
||||
_ = coderdtest.CreateInitialUser(t, client)
|
||||
projects, err := client.Projects(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, projects)
|
||||
require.Len(t, projects, 0)
|
||||
})
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "someproject",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// Ensure global query works.
|
||||
projects, err := server.Client.Projects(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, projects, 1)
|
||||
|
||||
// Ensure specified query works.
|
||||
projects, err = server.Client.Projects(context.Background(), user.Organization)
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_ = coderdtest.CreateProject(t, client, user.Organization)
|
||||
projects, err := client.Projects(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, projects, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestProjectsByOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("ListEmpty", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
|
||||
projects, err := server.Client.Projects(context.Background(), user.Organization)
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
projects, err := client.Projects(context.Background(), user.Organization)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, projects)
|
||||
require.Len(t, projects, 0)
|
||||
})
|
||||
|
||||
t.Run("Single", func(t *testing.T) {
|
||||
t.Run("List", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "someproject",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = server.Client.Project(context.Background(), user.Organization, project.Name)
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_ = coderdtest.CreateProject(t, client, user.Organization)
|
||||
projects, err := client.Projects(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, projects, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPostProjectsByOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_ = coderdtest.CreateProject(t, client, user.Organization)
|
||||
})
|
||||
|
||||
t.Run("Parameters", func(t *testing.T) {
|
||||
t.Run("AlreadyExists", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "someproject",
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
_, err := client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: project.Name,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = server.Client.ProjectParameters(context.Background(), user.Organization, project.Name)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
|
||||
})
|
||||
}
|
||||
|
||||
func TestProjectByOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("Get", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
_, err := client.Project(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("CreateParameter", func(t *testing.T) {
|
||||
func TestPostParametersByProject(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "someproject",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = server.Client.CreateProjectParameter(context.Background(), user.Organization, project.Name, coderd.CreateParameterValueRequest{
|
||||
Name: "hi",
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
_, err := client.CreateProjectParameter(context.Background(), user.Organization, project.Name, coderd.CreateParameterValueRequest{
|
||||
Name: "somename",
|
||||
SourceValue: "tomato",
|
||||
SourceScheme: database.ParameterSourceSchemeData,
|
||||
DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable,
|
||||
@ -127,40 +112,36 @@ func TestProjects(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("Import", func(t *testing.T) {
|
||||
func TestParametersByProject(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("ListEmpty", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_ = server.AddProvisionerd(t)
|
||||
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "someproject",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
params, err := client.ProjectParameters(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, params)
|
||||
})
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
_, err := client.CreateProjectParameter(context.Background(), user.Organization, project.Name, coderd.CreateParameterValueRequest{
|
||||
Name: "example",
|
||||
SourceValue: "source-value",
|
||||
SourceScheme: database.ParameterSourceSchemeData,
|
||||
DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable,
|
||||
DestinationValue: "destination-value",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
data, err := echo.Tar([]*proto.Parse_Response{{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
ParameterSchemas: []*proto.ParameterSchema{{
|
||||
Name: "example",
|
||||
}},
|
||||
},
|
||||
},
|
||||
}}, nil)
|
||||
require.NoError(t, err)
|
||||
version, err := server.Client.CreateProjectVersion(context.Background(), user.Organization, project.Name, coderd.CreateProjectVersionRequest{
|
||||
StorageMethod: database.ProjectStorageMethodInlineArchive,
|
||||
StorageSource: data,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Eventually(t, func() bool {
|
||||
projectVersion, err := server.Client.ProjectVersion(context.Background(), user.Organization, project.Name, version.Name)
|
||||
require.NoError(t, err)
|
||||
return projectVersion.Import.Status.Completed()
|
||||
}, 15*time.Second, 10*time.Millisecond)
|
||||
params, err := server.Client.ProjectVersionParameters(context.Background(), user.Organization, project.Name, version.Name)
|
||||
params, err := client.ProjectParameters(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, params)
|
||||
require.Len(t, params, 1)
|
||||
require.Equal(t, "example", params[0].Name)
|
||||
})
|
||||
}
|
||||
|
@ -110,19 +110,11 @@ func (api *api) postProjectVersionByOrganization(rw http.ResponseWriter, r *http
|
||||
return
|
||||
}
|
||||
|
||||
switch createProjectVersion.StorageMethod {
|
||||
case database.ProjectStorageMethodInlineArchive:
|
||||
tarReader := tar.NewReader(bytes.NewReader(createProjectVersion.StorageSource))
|
||||
_, err := tarReader.Next()
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
|
||||
Message: "the archive must be a tar",
|
||||
})
|
||||
return
|
||||
}
|
||||
default:
|
||||
tarReader := tar.NewReader(bytes.NewReader(createProjectVersion.StorageSource))
|
||||
_, err := tarReader.Next()
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
|
||||
Message: fmt.Sprintf("unsupported storage method %s", createProjectVersion.StorageMethod),
|
||||
Message: "the archive must be a tar",
|
||||
})
|
||||
return
|
||||
}
|
||||
@ -132,7 +124,7 @@ func (api *api) postProjectVersionByOrganization(rw http.ResponseWriter, r *http
|
||||
|
||||
var provisionerJob database.ProvisionerJob
|
||||
var projectVersion database.ProjectVersion
|
||||
err := api.Database.InTx(func(db database.Store) error {
|
||||
err = api.Database.InTx(func(db database.Store) error {
|
||||
projectVersionID := uuid.New()
|
||||
input, err := json.Marshal(projectImportJob{
|
||||
ProjectVersionID: projectVersionID,
|
||||
|
@ -1,103 +1,129 @@
|
||||
package coderd_test
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/coderd"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/database"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
)
|
||||
|
||||
func TestProjectVersion(t *testing.T) {
|
||||
func TestProjectVersionsByOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("NoHistory", func(t *testing.T) {
|
||||
t.Run("ListEmpty", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "someproject",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
versions, err := server.Client.ProjectVersions(context.Background(), user.Organization, project.Name)
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
versions, err := client.ProjectVersions(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, versions)
|
||||
require.Len(t, versions, 0)
|
||||
})
|
||||
|
||||
t.Run("CreateVersion", func(t *testing.T) {
|
||||
t.Run("List", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "someproject",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
data, err := echo.Tar([]*proto.Parse_Response{{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{},
|
||||
},
|
||||
}}, nil)
|
||||
require.NoError(t, err)
|
||||
version, err := server.Client.CreateProjectVersion(context.Background(), user.Organization, project.Name, coderd.CreateProjectVersionRequest{
|
||||
StorageMethod: database.ProjectStorageMethodInlineArchive,
|
||||
StorageSource: data,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
versions, err := server.Client.ProjectVersions(context.Background(), user.Organization, project.Name)
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
_ = coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
versions, err := client.ProjectVersions(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, versions, 1)
|
||||
})
|
||||
}
|
||||
|
||||
_, err = server.Client.ProjectVersion(context.Background(), user.Organization, project.Name, version.Name)
|
||||
require.NoError(t, err)
|
||||
func TestProjectVersionByOrganizationAndName(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("Get", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
require.Equal(t, version.Import.Status, coderd.ProvisionerJobStatusPending)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPostProjectVersionByOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
_ = coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
})
|
||||
|
||||
t.Run("CreateHistoryArchiveTooBig", func(t *testing.T) {
|
||||
t.Run("InvalidStorage", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "someproject",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
var buffer bytes.Buffer
|
||||
writer := tar.NewWriter(&buffer)
|
||||
err = writer.WriteHeader(&tar.Header{
|
||||
Name: "file",
|
||||
Size: 1 << 21,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = writer.Write(make([]byte, 1<<21))
|
||||
require.NoError(t, err)
|
||||
_, err = server.Client.CreateProjectVersion(context.Background(), user.Organization, project.Name, coderd.CreateProjectVersionRequest{
|
||||
StorageMethod: database.ProjectStorageMethodInlineArchive,
|
||||
StorageSource: buffer.Bytes(),
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("CreateHistoryInvalidArchive", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "someproject",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = server.Client.CreateProjectVersion(context.Background(), user.Organization, project.Name, coderd.CreateProjectVersionRequest{
|
||||
StorageMethod: database.ProjectStorageMethodInlineArchive,
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
_, err := client.CreateProjectVersion(context.Background(), user.Organization, project.Name, coderd.CreateProjectVersionRequest{
|
||||
StorageMethod: database.ProjectStorageMethod("invalid"),
|
||||
StorageSource: []byte{},
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestProjectVersionParametersByOrganizationAndName(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("NotImported", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
_, err := client.ProjectVersionParameters(context.Background(), user.Organization, project.Name, version.Name)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusPreconditionRequired, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("FailedImport", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_ = coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, &echo.Responses{
|
||||
Provision: []*proto.Provision_Response{{}},
|
||||
})
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
_, err := client.ProjectVersionParameters(context.Background(), user.Organization, project.Name, version.Name)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode())
|
||||
})
|
||||
t.Run("List", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_ = coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, &echo.Responses{
|
||||
Parse: []*proto.Parse_Response{{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
ParameterSchemas: []*proto.ParameterSchema{{
|
||||
Name: "example",
|
||||
}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
})
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
params, err := client.ProjectVersionParameters(context.Background(), user.Organization, project.Name, version.Name)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, params, 1)
|
||||
})
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
@ -35,7 +36,6 @@ func (api *api) provisionerDaemons(rw http.ResponseWriter, r *http.Request) {
|
||||
daemons, err := api.Database.GetProvisionerDaemons(r.Context())
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
daemons = []database.ProvisionerDaemon{}
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
@ -43,7 +43,9 @@ func (api *api) provisionerDaemons(rw http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if daemons == nil {
|
||||
daemons = []database.ProvisionerDaemon{}
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(rw, r, daemons)
|
||||
}
|
||||
@ -51,7 +53,7 @@ func (api *api) provisionerDaemons(rw http.ResponseWriter, r *http.Request) {
|
||||
// Serves the provisioner daemon protobuf API over a WebSocket.
|
||||
func (api *api) provisionerDaemonsServe(rw http.ResponseWriter, r *http.Request) {
|
||||
conn, err := websocket.Accept(rw, r, &websocket.AcceptOptions{
|
||||
// Need to disable compression to avoid a data-race
|
||||
// Need to disable compression to avoid a data-race.
|
||||
CompressionMode: websocket.CompressionDisabled,
|
||||
})
|
||||
if err != nil {
|
||||
@ -75,7 +77,9 @@ func (api *api) provisionerDaemonsServe(rw http.ResponseWriter, r *http.Request)
|
||||
// Multiplexes the incoming connection using yamux.
|
||||
// This allows multiple function calls to occur over
|
||||
// the same connection.
|
||||
session, err := yamux.Server(websocket.NetConn(r.Context(), conn, websocket.MessageBinary), nil)
|
||||
config := yamux.DefaultConfig()
|
||||
config.LogOutput = io.Discard
|
||||
session, err := yamux.Server(websocket.NetConn(r.Context(), conn, websocket.MessageBinary), config)
|
||||
if err != nil {
|
||||
_ = conn.Close(websocket.StatusInternalError, fmt.Sprintf("multiplex server: %s", err))
|
||||
return
|
||||
@ -221,25 +225,11 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty
|
||||
protoParameters = append(protoParameters, parameter.Proto)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
protoJob.Type = &proto.AcquiredJob_WorkspaceProvision_{
|
||||
WorkspaceProvision: &proto.AcquiredJob_WorkspaceProvision{
|
||||
WorkspaceHistoryId: workspaceHistory.ID.String(),
|
||||
WorkspaceName: workspace.Name,
|
||||
State: provisionerState,
|
||||
State: workspaceHistory.ProvisionerState,
|
||||
ParameterValues: protoParameters,
|
||||
},
|
||||
}
|
||||
@ -286,10 +276,10 @@ func (server *provisionerdServer) UpdateJob(stream proto.DRPCProvisionerDaemon_U
|
||||
return xerrors.Errorf("get job: %w", err)
|
||||
}
|
||||
if !job.WorkerID.Valid {
|
||||
return errors.New("job isn't running yet")
|
||||
return xerrors.New("job isn't running yet")
|
||||
}
|
||||
if job.WorkerID.UUID.String() != server.ID.String() {
|
||||
return errors.New("you don't own this job")
|
||||
return xerrors.New("you don't own this job")
|
||||
}
|
||||
|
||||
err = server.Database.UpdateProvisionerJobByID(stream.Context(), database.UpdateProvisionerJobByIDParams{
|
||||
|
@ -11,16 +11,18 @@ import (
|
||||
)
|
||||
|
||||
func TestProvisionerDaemons(t *testing.T) {
|
||||
// Tests for properly processing specific job
|
||||
// types should be placed in their respective
|
||||
// resource location.
|
||||
//
|
||||
// eg. project import is a project-related job
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Register", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
_ = server.AddProvisionerd(t)
|
||||
require.Eventually(t, func() bool {
|
||||
daemons, err := server.Client.ProvisionerDaemons(context.Background())
|
||||
require.NoError(t, err)
|
||||
return len(daemons) > 0
|
||||
}, time.Second, 10*time.Millisecond)
|
||||
})
|
||||
client := coderdtest.New(t)
|
||||
_ = coderdtest.NewProvisionerDaemon(t, client)
|
||||
require.Eventually(t, func() bool {
|
||||
daemons, err := client.ProvisionerDaemons(context.Background())
|
||||
require.NoError(t, err)
|
||||
return len(daemons) > 0
|
||||
}, time.Second, 25*time.Millisecond)
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ func convertProvisionerJob(provisionerJob database.ProvisionerJob) ProvisionerJo
|
||||
job.Status = ProvisionerJobStatusRunning
|
||||
}
|
||||
|
||||
if job.Error != "" {
|
||||
if !provisionerJob.CancelledAt.Valid && job.Error != "" {
|
||||
job.Status = ProvisionerJobStatusFailed
|
||||
}
|
||||
|
||||
|
@ -195,6 +195,10 @@ func (api *api) organizationsByUser(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
|
||||
organizations, err := api.Database.GetOrganizationsByUserID(r.Context(), user.ID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
organizations = []database.Organization{}
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get organizations: %s", err.Error()),
|
||||
|
@ -9,107 +9,143 @@ import (
|
||||
|
||||
"github.com/coder/coder/coderd"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/httpmw"
|
||||
)
|
||||
|
||||
func TestUsers(t *testing.T) {
|
||||
func TestPostUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Authenticated", func(t *testing.T) {
|
||||
t.Run("BadRequest", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
_ = server.RandomInitialUser(t)
|
||||
_, err := server.Client.User(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("CreateMultipleInitial", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
_ = server.RandomInitialUser(t)
|
||||
_, err := server.Client.CreateInitialUser(context.Background(), coderd.CreateInitialUserRequest{
|
||||
Email: "dummy@coder.com",
|
||||
Organization: "bananas",
|
||||
Username: "fake",
|
||||
Password: "password",
|
||||
})
|
||||
client := coderdtest.New(t)
|
||||
_, err := client.CreateInitialUser(context.Background(), coderd.CreateInitialUserRequest{})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("Login", func(t *testing.T) {
|
||||
t.Run("AlreadyExists", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_, err := server.Client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{
|
||||
client := coderdtest.New(t)
|
||||
_ = coderdtest.CreateInitialUser(t, client)
|
||||
_, err := client.CreateInitialUser(context.Background(), coderd.CreateInitialUserRequest{
|
||||
Email: "some@email.com",
|
||||
Username: "exampleuser",
|
||||
Password: "password",
|
||||
Organization: "someorg",
|
||||
})
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
_ = coderdtest.CreateInitialUser(t, client)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPostUsers(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("BadRequest", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
_, err := client.CreateInitialUser(context.Background(), coderd.CreateInitialUserRequest{})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("Conflicting", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_, err := client.CreateInitialUser(context.Background(), coderd.CreateInitialUserRequest{
|
||||
Email: user.Email,
|
||||
Username: user.Username,
|
||||
Password: "password",
|
||||
Organization: "someorg",
|
||||
})
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
_ = coderdtest.CreateInitialUser(t, client)
|
||||
_, err := client.CreateUser(context.Background(), coderd.CreateUserRequest{
|
||||
Email: "another@user.org",
|
||||
Username: "someone-else",
|
||||
Password: "testing",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserByName(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
_ = coderdtest.CreateInitialUser(t, client)
|
||||
_, err := client.User(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestOrganizationsByUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
_ = coderdtest.CreateInitialUser(t, client)
|
||||
orgs, err := client.UserOrganizations(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, orgs)
|
||||
require.Len(t, orgs, 1)
|
||||
}
|
||||
|
||||
func TestPostLogin(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("InvalidUser", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
_, err := client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{
|
||||
Email: "my@email.org",
|
||||
Password: "password",
|
||||
})
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("BadPassword", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_, err := client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{
|
||||
Email: user.Email,
|
||||
Password: "badpass",
|
||||
})
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_, err := client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{
|
||||
Email: user.Email,
|
||||
Password: user.Password,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("LoginInvalidUser", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
_, err := server.Client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{
|
||||
Email: "hello@io.io",
|
||||
Password: "wowie",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("LoginBadPassword", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_, err := server.Client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{
|
||||
Email: user.Email,
|
||||
Password: "bananas",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("ListOrganizations", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
_ = server.RandomInitialUser(t)
|
||||
orgs, err := server.Client.UserOrganizations(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, orgs, 1)
|
||||
})
|
||||
|
||||
t.Run("CreateUser", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
_ = server.RandomInitialUser(t)
|
||||
_, err := server.Client.CreateUser(context.Background(), coderd.CreateUserRequest{
|
||||
Email: "wow@ok.io",
|
||||
Username: "tomato",
|
||||
Password: "bananas",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("CreateUserConflict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_, err := server.Client.CreateUser(context.Background(), coderd.CreateUserRequest{
|
||||
Email: "wow@ok.io",
|
||||
Username: user.Username,
|
||||
Password: "bananas",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLogout(t *testing.T) {
|
||||
func TestPostLogout(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("LogoutShouldClearCookie", func(t *testing.T) {
|
||||
t.Run("ClearCookie", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := coderdtest.New(t)
|
||||
fullURL, err := server.URL.Parse("/api/v2/logout")
|
||||
client := coderdtest.New(t)
|
||||
fullURL, err := client.URL.Parse("/api/v2/logout")
|
||||
require.NoError(t, err, "Server URL should parse successfully")
|
||||
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, fullURL.String(), nil)
|
||||
|
@ -74,12 +74,12 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
|
||||
projectVersionJobStatus := convertProvisionerJob(projectVersionJob).Status
|
||||
switch projectVersionJobStatus {
|
||||
case ProvisionerJobStatusPending, ProvisionerJobStatusRunning:
|
||||
httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{
|
||||
httpapi.Write(rw, http.StatusNotAcceptable, httpapi.Response{
|
||||
Message: fmt.Sprintf("The provided project version is %s. Wait for it to complete importing!", projectVersionJobStatus),
|
||||
})
|
||||
return
|
||||
case ProvisionerJobStatusFailed:
|
||||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
|
||||
httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{
|
||||
Message: fmt.Sprintf("The provided project version %q has failed to import. You cannot create workspaces using it!", projectVersion.Name),
|
||||
})
|
||||
return
|
||||
@ -87,6 +87,7 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
|
||||
httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{
|
||||
Message: "The provided project version was canceled during import. You cannot create workspaces using it!",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
project, err := api.Database.GetProjectByID(r.Context(), projectVersion.ProjectID)
|
||||
@ -102,7 +103,7 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
|
||||
priorHistory, err := api.Database.GetWorkspaceHistoryByWorkspaceIDWithoutAfter(r.Context(), workspace.ID)
|
||||
if err == nil {
|
||||
priorJob, err := api.Database.GetProvisionerJobByID(r.Context(), priorHistory.ProvisionJobID)
|
||||
if err == nil && convertProvisionerJob(priorJob).Status.Completed() {
|
||||
if err == nil && !convertProvisionerJob(priorJob).Status.Completed() {
|
||||
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
|
||||
Message: "a workspace build is already active",
|
||||
})
|
||||
@ -113,8 +114,7 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
|
||||
UUID: priorHistory.ID,
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
if !errors.Is(err, sql.ErrNoRows) {
|
||||
} else if !errors.Is(err, sql.ErrNoRows) {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get prior workspace history: %s", err),
|
||||
})
|
||||
@ -168,8 +168,9 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
|
||||
if priorHistoryID.Valid {
|
||||
// Update the prior history entries "after" column.
|
||||
err = db.UpdateWorkspaceHistoryByID(r.Context(), database.UpdateWorkspaceHistoryByIDParams{
|
||||
ID: priorHistory.ID,
|
||||
UpdatedAt: database.Now(),
|
||||
ID: priorHistory.ID,
|
||||
ProvisionerState: priorHistory.ProvisionerState,
|
||||
UpdatedAt: database.Now(),
|
||||
AfterID: uuid.NullUUID{
|
||||
UUID: workspaceHistory.ID,
|
||||
Valid: true,
|
||||
@ -197,9 +198,10 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
|
||||
func (api *api) workspaceHistoryByUser(rw http.ResponseWriter, r *http.Request) {
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
|
||||
histories, err := api.Database.GetWorkspaceHistoryByWorkspaceID(r.Context(), workspace.ID)
|
||||
history, err := api.Database.GetWorkspaceHistoryByWorkspaceID(r.Context(), workspace.ID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
history = []database.WorkspaceHistory{}
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
@ -208,8 +210,8 @@ func (api *api) workspaceHistoryByUser(rw http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
apiHistory := make([]WorkspaceHistory, 0, len(histories))
|
||||
for _, history := range histories {
|
||||
apiHistory := make([]WorkspaceHistory, 0, len(history))
|
||||
for _, history := range history {
|
||||
job, err := api.Database.GetProvisionerJobByID(r.Context(), history.ProvisionJobID)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
|
@ -2,8 +2,8 @@ package coderd_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -13,141 +13,150 @@ import (
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/database"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
)
|
||||
|
||||
func TestWorkspaceHistory(t *testing.T) {
|
||||
func TestPostWorkspaceHistoryByUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
setupProjectAndWorkspace := func(t *testing.T, client *codersdk.Client, user coderd.CreateInitialUserRequest) (coderd.Project, coderd.Workspace) {
|
||||
project, err := client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "banana",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
workspace, err := client.CreateWorkspace(context.Background(), "", coderd.CreateWorkspaceRequest{
|
||||
Name: "example",
|
||||
ProjectID: project.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return project, workspace
|
||||
}
|
||||
|
||||
setupProjectVersion := func(t *testing.T, client *codersdk.Client, user coderd.CreateInitialUserRequest, project coderd.Project, data []byte) coderd.ProjectVersion {
|
||||
projectVersion, err := client.CreateProjectVersion(context.Background(), user.Organization, project.Name, coderd.CreateProjectVersionRequest{
|
||||
StorageMethod: database.ProjectStorageMethodInlineArchive,
|
||||
StorageSource: data,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Eventually(t, func() bool {
|
||||
version, err := client.ProjectVersion(context.Background(), user.Organization, project.Name, projectVersion.Name)
|
||||
require.NoError(t, err)
|
||||
t.Logf("Import status: %s\n", version.Import.Status)
|
||||
return version.Import.Status.Completed()
|
||||
}, 15*time.Second, 50*time.Millisecond)
|
||||
return projectVersion
|
||||
}
|
||||
|
||||
t.Run("AllHistory", func(t *testing.T) {
|
||||
t.Run("NoProjectVersion", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_ = server.AddProvisionerd(t)
|
||||
project, workspace := setupProjectAndWorkspace(t, server.Client, user)
|
||||
history, err := server.Client.ListWorkspaceHistory(context.Background(), "", workspace.Name)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, history, 0)
|
||||
data, err := echo.Tar(echo.ParseComplete, echo.ProvisionComplete)
|
||||
require.NoError(t, err)
|
||||
projectVersion := setupProjectVersion(t, server.Client, user, project, data)
|
||||
_, err = server.Client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: projectVersion.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
history, err = server.Client.ListWorkspaceHistory(context.Background(), "", workspace.Name)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, history, 1)
|
||||
})
|
||||
|
||||
t.Run("LatestHistory", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_ = server.AddProvisionerd(t)
|
||||
project, workspace := setupProjectAndWorkspace(t, server.Client, user)
|
||||
_, err := server.Client.WorkspaceHistory(context.Background(), "", workspace.Name, "")
|
||||
require.Error(t, err)
|
||||
data, err := echo.Tar(echo.ParseComplete, echo.ProvisionComplete)
|
||||
require.NoError(t, err)
|
||||
projectVersion := setupProjectVersion(t, server.Client, user, project, data)
|
||||
_, err = server.Client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: projectVersion.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = server.Client.WorkspaceHistory(context.Background(), "", workspace.Name, "")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("CreateHistory", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_ = server.AddProvisionerd(t)
|
||||
project, workspace := setupProjectAndWorkspace(t, server.Client, user)
|
||||
data, err := echo.Tar(echo.ParseComplete, echo.ProvisionComplete)
|
||||
require.NoError(t, err)
|
||||
projectVersion := setupProjectVersion(t, server.Client, user, project, data)
|
||||
_, err = server.Client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: projectVersion.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
var workspaceHistory coderd.WorkspaceHistory
|
||||
require.Eventually(t, func() bool {
|
||||
workspaceHistory, err = server.Client.WorkspaceHistory(context.Background(), "", workspace.Name, "")
|
||||
require.NoError(t, err)
|
||||
return workspaceHistory.Provision.Status.Completed()
|
||||
}, 15*time.Second, 50*time.Millisecond)
|
||||
require.Equal(t, "", workspaceHistory.Provision.Error)
|
||||
require.Equal(t, coderd.ProvisionerJobStatusSucceeded, workspaceHistory.Provision.Status)
|
||||
})
|
||||
|
||||
t.Run("CreateHistoryAlreadyInProgress", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_ = server.AddProvisionerd(t)
|
||||
project, workspace := setupProjectAndWorkspace(t, server.Client, user)
|
||||
data, err := echo.Tar(echo.ParseComplete, echo.ProvisionComplete)
|
||||
require.NoError(t, err)
|
||||
projectVersion := setupProjectVersion(t, server.Client, user, project, data)
|
||||
|
||||
_, err = server.Client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: projectVersion.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = server.Client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: projectVersion.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("CreateHistoryInvalidProjectVersion", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_ = server.AddProvisionerd(t)
|
||||
_, workspace := setupProjectAndWorkspace(t, server.Client, user)
|
||||
|
||||
_, err := server.Client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
_, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: uuid.New(),
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.Error(t, err)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("ProjectVersionFailedImport", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, &echo.Responses{
|
||||
Provision: []*proto.Provision_Response{{}},
|
||||
})
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
_, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.Error(t, err)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("AlreadyActive", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
_, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.Error(t, err)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("UpdatePriorAfterField", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
firstHistory, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
coderdtest.AwaitWorkspaceHistoryProvisioned(t, client, "me", workspace.Name, firstHistory.Name)
|
||||
secondHistory, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, firstHistory.ID.String(), secondHistory.BeforeID.String())
|
||||
|
||||
firstHistory, err = client.WorkspaceHistory(context.Background(), "", workspace.Name, firstHistory.Name)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, secondHistory.ID.String(), firstHistory.AfterID.String())
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkspaceHistoryByUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("ListEmpty", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
history, err := client.ListWorkspaceHistory(context.Background(), "me", workspace.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, history)
|
||||
require.Len(t, history, 0)
|
||||
})
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
_, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
history, err := client.ListWorkspaceHistory(context.Background(), "me", workspace.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, history)
|
||||
require.Len(t, history, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkspaceHistoryByName(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
history, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = client.WorkspaceHistory(context.Background(), "me", workspace.Name, history.Name)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
@ -87,7 +87,6 @@ func (api *api) workspaceHistoryLogsByName(rw http.ResponseWriter, r *http.Reque
|
||||
})
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
logs = []database.WorkspaceHistoryLog{}
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
@ -95,6 +94,9 @@ func (api *api) workspaceHistoryLogsByName(rw http.ResponseWriter, r *http.Reque
|
||||
})
|
||||
return
|
||||
}
|
||||
if logs == nil {
|
||||
logs = []database.WorkspaceHistoryLog{}
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(rw, r, logs)
|
||||
return
|
||||
@ -113,12 +115,8 @@ func (api *api) workspaceHistoryLogsByName(rw http.ResponseWriter, r *http.Reque
|
||||
select {
|
||||
case bufferedLogs <- log:
|
||||
default:
|
||||
// This is a case that shouldn't happen, but totally could.
|
||||
// There's no way to stream data from the database, so we'll
|
||||
// need to maintain some level of internal buffer.
|
||||
//
|
||||
// If this overflows users could miss logs when streaming.
|
||||
// We warn to make sure we know when it happens!
|
||||
// If this overflows users could miss logs streaming. This can happen
|
||||
// if a database request takes a long amount of time, and we get a lot of logs.
|
||||
api.Logger.Warn(r.Context(), "workspace history log overflowing channel")
|
||||
}
|
||||
}
|
||||
|
@ -9,90 +9,132 @@ import (
|
||||
|
||||
"github.com/coder/coder/coderd"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/database"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
)
|
||||
|
||||
func TestWorkspaceHistoryLogs(t *testing.T) {
|
||||
func TestWorkspaceHistoryLogsByName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
setupProjectAndWorkspace := func(t *testing.T, client *codersdk.Client, user coderd.CreateInitialUserRequest) (coderd.Project, coderd.Workspace) {
|
||||
project, err := client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "banana",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
workspace, err := client.CreateWorkspace(context.Background(), "", coderd.CreateWorkspaceRequest{
|
||||
Name: "example",
|
||||
ProjectID: project.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return project, workspace
|
||||
}
|
||||
|
||||
setupProjectVersion := func(t *testing.T, client *codersdk.Client, user coderd.CreateInitialUserRequest, project coderd.Project, data []byte) coderd.ProjectVersion {
|
||||
projectVersion, err := client.CreateProjectVersion(context.Background(), user.Organization, project.Name, coderd.CreateProjectVersionRequest{
|
||||
StorageMethod: database.ProjectStorageMethodInlineArchive,
|
||||
StorageSource: data,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Eventually(t, func() bool {
|
||||
hist, err := client.ProjectVersion(context.Background(), user.Organization, project.Name, projectVersion.Name)
|
||||
require.NoError(t, err)
|
||||
return hist.Import.Status.Completed()
|
||||
}, 15*time.Second, 50*time.Millisecond)
|
||||
return projectVersion
|
||||
}
|
||||
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_ = server.AddProvisionerd(t)
|
||||
project, workspace := setupProjectAndWorkspace(t, server.Client, user)
|
||||
data, err := echo.Tar(echo.ParseComplete, []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Log: &proto.Log{
|
||||
Output: "test",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
projectVersion := setupProjectVersion(t, server.Client, user, project, data)
|
||||
|
||||
workspaceHistory, err := server.Client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: projectVersion.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
now := database.Now()
|
||||
logChan, err := server.Client.FollowWorkspaceHistoryLogsAfter(context.Background(), "", workspace.Name, workspaceHistory.Name, now)
|
||||
require.NoError(t, err)
|
||||
|
||||
for {
|
||||
log, more := <-logChan
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
t.Logf("Output: %s", log.Output)
|
||||
}
|
||||
|
||||
t.Run("ReturnAll", func(t *testing.T) {
|
||||
t.Run("List", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := server.Client.WorkspaceHistoryLogs(context.Background(), "", workspace.Name, workspaceHistory.Name)
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
Provision: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "log-output",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}},
|
||||
})
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
history, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Successfully return empty logs before the job starts!
|
||||
logs, err := client.WorkspaceHistoryLogs(context.Background(), "", workspace.Name, history.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, logs)
|
||||
require.Len(t, logs, 0)
|
||||
|
||||
coderdtest.AwaitWorkspaceHistoryProvisioned(t, client, "", workspace.Name, history.Name)
|
||||
|
||||
// Return the log after completion!
|
||||
logs, err = client.WorkspaceHistoryLogs(context.Background(), "", workspace.Name, history.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, logs)
|
||||
require.Len(t, logs, 1)
|
||||
})
|
||||
|
||||
t.Run("Between", func(t *testing.T) {
|
||||
t.Run("StreamAfterComplete", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := server.Client.WorkspaceHistoryLogsBetween(context.Background(), "", workspace.Name, workspaceHistory.Name, time.Time{}, database.Now())
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
Provision: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "log-output",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}},
|
||||
})
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
before := time.Now().UTC()
|
||||
history, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
coderdtest.AwaitWorkspaceHistoryProvisioned(t, client, "", workspace.Name, history.Name)
|
||||
|
||||
logs, err := client.FollowWorkspaceHistoryLogsAfter(context.Background(), "", workspace.Name, history.Name, before)
|
||||
require.NoError(t, err)
|
||||
log := <-logs
|
||||
require.Equal(t, "log-output", log.Output)
|
||||
// Make sure the channel automatically closes!
|
||||
_, ok := <-logs
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
t.Run("StreamWhileRunning", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
Provision: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "log-output",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}},
|
||||
})
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
history, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionCreate,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
logs, err := client.FollowWorkspaceHistoryLogsAfter(context.Background(), "", workspace.Name, history.Name, time.Time{})
|
||||
require.NoError(t, err)
|
||||
log := <-logs
|
||||
require.Equal(t, "log-output", log.Output)
|
||||
// Make sure the channel automatically closes!
|
||||
_, ok := <-logs
|
||||
require.False(t, ok)
|
||||
})
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ func (api *api) postWorkspaceByUser(rw http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(rw, r, convertWorkspace(workspace))
|
||||
}
|
||||
|
||||
// Returns a single singleWorkspace.
|
||||
// Returns a single workspace.
|
||||
func (*api) workspaceByUser(rw http.ResponseWriter, r *http.Request) {
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
|
||||
@ -145,6 +145,32 @@ func (*api) workspaceByUser(rw http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(rw, r, convertWorkspace(workspace))
|
||||
}
|
||||
|
||||
// Returns all workspaces for a specific project.
|
||||
func (api *api) workspacesByProject(rw http.ResponseWriter, r *http.Request) {
|
||||
apiKey := httpmw.APIKey(r)
|
||||
project := httpmw.ProjectParam(r)
|
||||
workspaces, err := api.Database.GetWorkspacesByProjectAndUserID(r.Context(), database.GetWorkspacesByProjectAndUserIDParams{
|
||||
OwnerID: apiKey.UserID,
|
||||
ProjectID: project.ID,
|
||||
})
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get workspaces: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
apiWorkspaces := make([]Workspace, 0, len(workspaces))
|
||||
for _, workspace := range workspaces {
|
||||
apiWorkspaces = append(apiWorkspaces, convertWorkspace(workspace))
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(rw, r, apiWorkspaces)
|
||||
}
|
||||
|
||||
// Converts the internal workspace representation to a public external-facing model.
|
||||
func convertWorkspace(workspace database.Workspace) Workspace {
|
||||
return Workspace(workspace)
|
||||
|
@ -2,6 +2,7 @@ package coderd_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@ -10,143 +11,135 @@ import (
|
||||
"github.com/coder/coder/coderd"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/database"
|
||||
)
|
||||
|
||||
func TestWorkspaces(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("ListNone", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
_ = server.RandomInitialUser(t)
|
||||
workspaces, err := server.Client.WorkspacesByUser(context.Background(), "")
|
||||
client := coderdtest.New(t)
|
||||
_ = coderdtest.CreateInitialUser(t, client)
|
||||
workspaces, err := client.Workspaces(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, workspaces)
|
||||
require.Len(t, workspaces, 0)
|
||||
})
|
||||
|
||||
setupProjectAndWorkspace := func(t *testing.T, client *codersdk.Client, user coderd.CreateInitialUserRequest) (coderd.Project, coderd.Workspace) {
|
||||
project, err := client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "banana",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
workspace, err := client.CreateWorkspace(context.Background(), "", coderd.CreateWorkspaceRequest{
|
||||
Name: "example",
|
||||
ProjectID: project.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return project, workspace
|
||||
}
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_, _ = setupProjectAndWorkspace(t, server.Client, user)
|
||||
workspaces, err := server.Client.WorkspacesByUser(context.Background(), "")
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
_ = coderdtest.CreateWorkspace(t, client, "", project.ID)
|
||||
workspaces, err := client.Workspaces(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, workspaces, 1)
|
||||
})
|
||||
|
||||
t.Run("ListNoneForProject", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "banana",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
workspaces, err := server.Client.WorkspacesByProject(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, workspaces, 0)
|
||||
})
|
||||
|
||||
t.Run("ListForProject", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
project, _ := setupProjectAndWorkspace(t, server.Client, user)
|
||||
workspaces, err := server.Client.WorkspacesByProject(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, workspaces, 1)
|
||||
})
|
||||
|
||||
t.Run("CreateInvalidInput", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: "banana",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = server.Client.CreateWorkspace(context.Background(), "", coderd.CreateWorkspaceRequest{
|
||||
ProjectID: project.ID,
|
||||
Name: "$$$",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("CreateInvalidProject", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
_ = server.RandomInitialUser(t)
|
||||
_, err := server.Client.CreateWorkspace(context.Background(), "", coderd.CreateWorkspaceRequest{
|
||||
ProjectID: uuid.New(),
|
||||
Name: "moo",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("CreateNotInProjectOrganization", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
initial := server.RandomInitialUser(t)
|
||||
project, err := server.Client.CreateProject(context.Background(), initial.Organization, coderd.CreateProjectRequest{
|
||||
Name: "banana",
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = server.Client.CreateUser(context.Background(), coderd.CreateUserRequest{
|
||||
Email: "hello@ok.io",
|
||||
Username: "example",
|
||||
Password: "password",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
token, err := server.Client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{
|
||||
Email: "hello@ok.io",
|
||||
Password: "password",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = server.Client.SetSessionToken(token.SessionToken)
|
||||
require.NoError(t, err)
|
||||
_, err = server.Client.CreateWorkspace(context.Background(), "", coderd.CreateWorkspaceRequest{
|
||||
ProjectID: project.ID,
|
||||
Name: "moo",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("CreateAlreadyExists", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
project, workspace := setupProjectAndWorkspace(t, server.Client, user)
|
||||
_, err := server.Client.CreateWorkspace(context.Background(), "", coderd.CreateWorkspaceRequest{
|
||||
Name: workspace.Name,
|
||||
ProjectID: project.ID,
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("Single", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
server := coderdtest.New(t)
|
||||
user := server.RandomInitialUser(t)
|
||||
_, workspace := setupProjectAndWorkspace(t, server.Client, user)
|
||||
_, err := server.Client.Workspace(context.Background(), "", workspace.Name)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPostWorkspaceByUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("InvalidProject", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
_ = coderdtest.CreateInitialUser(t, client)
|
||||
_, err := client.CreateWorkspace(context.Background(), "", coderd.CreateWorkspaceRequest{
|
||||
ProjectID: uuid.New(),
|
||||
Name: "workspace",
|
||||
})
|
||||
require.Error(t, err)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("NoProjectAccess", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
|
||||
anotherUser := coderd.CreateUserRequest{
|
||||
Email: "another@user.org",
|
||||
Username: "someuser",
|
||||
Password: "somepass",
|
||||
}
|
||||
_, err := client.CreateUser(context.Background(), anotherUser)
|
||||
require.NoError(t, err)
|
||||
token, err := client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{
|
||||
Email: anotherUser.Email,
|
||||
Password: anotherUser.Password,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = client.SetSessionToken(token.SessionToken)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = client.CreateWorkspace(context.Background(), "", coderd.CreateWorkspaceRequest{
|
||||
ProjectID: project.ID,
|
||||
Name: "workspace",
|
||||
})
|
||||
require.Error(t, err)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("AlreadyExists", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
|
||||
_, err := client.CreateWorkspace(context.Background(), "", coderd.CreateWorkspaceRequest{
|
||||
ProjectID: project.ID,
|
||||
Name: workspace.Name,
|
||||
})
|
||||
require.Error(t, err)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
_ = coderdtest.CreateWorkspace(t, client, "", project.ID)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkspaceByUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
|
||||
_, err := client.Workspace(context.Background(), "", workspace.Name)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestWorkspacesByProject(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("ListEmpty", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
workspaces, err := client.WorkspacesByProject(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, workspaces)
|
||||
})
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
_ = coderdtest.CreateWorkspace(t, client, "", project.ID)
|
||||
workspaces, err := client.WorkspacesByProject(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, workspaces)
|
||||
require.Len(t, workspaces, 1)
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user