mirror of
https://github.com/coder/coder.git
synced 2026-06-03 13:08:25 +00:00
f41275eb39
Relates to https://github.com/coder/internal/issues/711 This PR implements a project discovery mechanism that searches for any dev container projects and makes them visible in the UI so that they can be started. To make the wording on the site more clear, "Rebuild" has been changed to "Start" when there is no container associated with a known dev container configuration. I've also made it so that site will show the dev container config path when there is no other name available. ### Design decisions Just want to ensure my explanation for a few design decisions are noted down: - We only search for dev container configurations inside git repositories - We only search for these git repositories if they're at the top level or a direct child of the agent directory. This limited approach is to reduce the amount of files we ultimately walk when trying to find these projects. It makes sense to limit it to only the agent directory, although I'm open to expanding how deep we search.
674 lines
18 KiB
Go
674 lines
18 KiB
Go
package cli_test
|
|
|
|
import (
|
|
"context"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/coder/coder/v2/agent"
|
|
"github.com/coder/coder/v2/agent/agentcontainers"
|
|
"github.com/coder/coder/v2/agent/agentcontainers/watcher"
|
|
"github.com/coder/coder/v2/agent/agenttest"
|
|
"github.com/coder/coder/v2/cli/clitest"
|
|
"github.com/coder/coder/v2/coderd/coderdtest"
|
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
"github.com/coder/coder/v2/provisionersdk/proto"
|
|
"github.com/coder/coder/v2/pty/ptytest"
|
|
"github.com/coder/coder/v2/testutil"
|
|
)
|
|
|
|
func TestOpenVSCode(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
agentName := "agent1"
|
|
agentDir, err := filepath.Abs(filepath.FromSlash("/tmp"))
|
|
require.NoError(t, err)
|
|
client, workspace, agentToken := setupWorkspaceForAgent(t, func(agents []*proto.Agent) []*proto.Agent {
|
|
agents[0].Directory = agentDir
|
|
agents[0].Name = agentName
|
|
agents[0].OperatingSystem = runtime.GOOS
|
|
return agents
|
|
})
|
|
|
|
_ = agenttest.New(t, client.URL, agentToken)
|
|
_ = coderdtest.NewWorkspaceAgentWaiter(t, client, workspace.ID).Wait()
|
|
|
|
insideWorkspaceEnv := map[string]string{
|
|
"CODER": "true",
|
|
"CODER_WORKSPACE_NAME": workspace.Name,
|
|
"CODER_WORKSPACE_AGENT_NAME": agentName,
|
|
}
|
|
|
|
wd, err := os.Getwd()
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
args []string
|
|
env map[string]string
|
|
wantDir string
|
|
wantToken bool
|
|
wantError bool
|
|
}{
|
|
{
|
|
name: "no args",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "nonexistent workspace",
|
|
args: []string{"--test.open-error", workspace.Name + "bad"},
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "ok",
|
|
args: []string{"--test.open-error", workspace.Name},
|
|
wantDir: agentDir,
|
|
},
|
|
{
|
|
name: "ok relative path",
|
|
args: []string{"--test.open-error", workspace.Name, "my/relative/path"},
|
|
wantDir: filepath.Join(agentDir, filepath.FromSlash("my/relative/path")),
|
|
wantError: false,
|
|
},
|
|
{
|
|
name: "ok with absolute path",
|
|
args: []string{"--test.open-error", workspace.Name, agentDir},
|
|
wantDir: agentDir,
|
|
},
|
|
{
|
|
name: "ok with token",
|
|
args: []string{"--test.open-error", workspace.Name, "--generate-token"},
|
|
wantDir: agentDir,
|
|
wantToken: true,
|
|
},
|
|
// Inside workspace, does not require --test.open-error.
|
|
{
|
|
name: "ok inside workspace",
|
|
env: insideWorkspaceEnv,
|
|
args: []string{workspace.Name},
|
|
wantDir: agentDir,
|
|
},
|
|
{
|
|
name: "ok inside workspace relative path",
|
|
env: insideWorkspaceEnv,
|
|
args: []string{workspace.Name, "foo"},
|
|
wantDir: filepath.Join(wd, "foo"),
|
|
},
|
|
{
|
|
name: "ok inside workspace token",
|
|
env: insideWorkspaceEnv,
|
|
args: []string{workspace.Name, "--generate-token"},
|
|
wantDir: agentDir,
|
|
wantToken: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
inv, root := clitest.New(t, append([]string{"open", "vscode"}, tt.args...)...)
|
|
clitest.SetupConfig(t, client, root)
|
|
pty := ptytest.New(t)
|
|
inv.Stdin = pty.Input()
|
|
inv.Stdout = pty.Output()
|
|
|
|
ctx := testutil.Context(t, testutil.WaitLong)
|
|
inv = inv.WithContext(ctx)
|
|
for k, v := range tt.env {
|
|
inv.Environ.Set(k, v)
|
|
}
|
|
|
|
w := clitest.StartWithWaiter(t, inv)
|
|
|
|
if tt.wantError {
|
|
w.RequireError()
|
|
return
|
|
}
|
|
|
|
me, err := client.User(ctx, codersdk.Me)
|
|
require.NoError(t, err)
|
|
|
|
line := pty.ReadLine(ctx)
|
|
u, err := url.ParseRequestURI(line)
|
|
require.NoError(t, err, "line: %q", line)
|
|
|
|
qp := u.Query()
|
|
assert.Equal(t, client.URL.String(), qp.Get("url"))
|
|
assert.Equal(t, me.Username, qp.Get("owner"))
|
|
assert.Equal(t, workspace.Name, qp.Get("workspace"))
|
|
assert.Equal(t, agentName, qp.Get("agent"))
|
|
if tt.wantDir != "" {
|
|
assert.Contains(t, qp.Get("folder"), tt.wantDir)
|
|
} else {
|
|
assert.Empty(t, qp.Get("folder"))
|
|
}
|
|
if tt.wantToken {
|
|
assert.NotEmpty(t, qp.Get("token"))
|
|
} else {
|
|
assert.Empty(t, qp.Get("token"))
|
|
}
|
|
|
|
w.RequireSuccess()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOpenVSCode_NoAgentDirectory(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
agentName := "agent1"
|
|
client, workspace, agentToken := setupWorkspaceForAgent(t, func(agents []*proto.Agent) []*proto.Agent {
|
|
agents[0].Name = agentName
|
|
agents[0].OperatingSystem = runtime.GOOS
|
|
return agents
|
|
})
|
|
|
|
_ = agenttest.New(t, client.URL, agentToken)
|
|
_ = coderdtest.NewWorkspaceAgentWaiter(t, client, workspace.ID).Wait()
|
|
|
|
insideWorkspaceEnv := map[string]string{
|
|
"CODER": "true",
|
|
"CODER_WORKSPACE_NAME": workspace.Name,
|
|
"CODER_WORKSPACE_AGENT_NAME": agentName,
|
|
}
|
|
|
|
wd, err := os.Getwd()
|
|
require.NoError(t, err)
|
|
|
|
absPath := "/home/coder"
|
|
if runtime.GOOS == "windows" {
|
|
absPath = "C:\\home\\coder"
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
args []string
|
|
env map[string]string
|
|
wantDir string
|
|
wantToken bool
|
|
wantError bool
|
|
}{
|
|
{
|
|
name: "ok",
|
|
args: []string{"--test.open-error", workspace.Name},
|
|
},
|
|
{
|
|
name: "no agent dir error relative path",
|
|
args: []string{"--test.open-error", workspace.Name, "my/relative/path"},
|
|
wantDir: filepath.FromSlash("my/relative/path"),
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "ok with absolute path",
|
|
args: []string{"--test.open-error", workspace.Name, absPath},
|
|
wantDir: absPath,
|
|
},
|
|
{
|
|
name: "ok with token",
|
|
args: []string{"--test.open-error", workspace.Name, "--generate-token"},
|
|
wantToken: true,
|
|
},
|
|
// Inside workspace, does not require --test.open-error.
|
|
{
|
|
name: "ok inside workspace",
|
|
env: insideWorkspaceEnv,
|
|
args: []string{workspace.Name},
|
|
},
|
|
{
|
|
name: "ok inside workspace relative path",
|
|
env: insideWorkspaceEnv,
|
|
args: []string{workspace.Name, "foo"},
|
|
wantDir: filepath.Join(wd, "foo"),
|
|
},
|
|
{
|
|
name: "ok inside workspace token",
|
|
env: insideWorkspaceEnv,
|
|
args: []string{workspace.Name, "--generate-token"},
|
|
wantToken: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
inv, root := clitest.New(t, append([]string{"open", "vscode"}, tt.args...)...)
|
|
clitest.SetupConfig(t, client, root)
|
|
pty := ptytest.New(t)
|
|
inv.Stdin = pty.Input()
|
|
inv.Stdout = pty.Output()
|
|
|
|
ctx := testutil.Context(t, testutil.WaitLong)
|
|
inv = inv.WithContext(ctx)
|
|
for k, v := range tt.env {
|
|
inv.Environ.Set(k, v)
|
|
}
|
|
|
|
w := clitest.StartWithWaiter(t, inv)
|
|
|
|
if tt.wantError {
|
|
w.RequireError()
|
|
return
|
|
}
|
|
|
|
me, err := client.User(ctx, codersdk.Me)
|
|
require.NoError(t, err)
|
|
|
|
line := pty.ReadLine(ctx)
|
|
u, err := url.ParseRequestURI(line)
|
|
require.NoError(t, err, "line: %q", line)
|
|
|
|
qp := u.Query()
|
|
assert.Equal(t, client.URL.String(), qp.Get("url"))
|
|
assert.Equal(t, me.Username, qp.Get("owner"))
|
|
assert.Equal(t, workspace.Name, qp.Get("workspace"))
|
|
assert.Equal(t, agentName, qp.Get("agent"))
|
|
if tt.wantDir != "" {
|
|
assert.Contains(t, qp.Get("folder"), tt.wantDir)
|
|
} else {
|
|
assert.Empty(t, qp.Get("folder"))
|
|
}
|
|
if tt.wantToken {
|
|
assert.NotEmpty(t, qp.Get("token"))
|
|
} else {
|
|
assert.Empty(t, qp.Get("token"))
|
|
}
|
|
|
|
w.RequireSuccess()
|
|
})
|
|
}
|
|
}
|
|
|
|
type fakeContainerCLI struct {
|
|
resp codersdk.WorkspaceAgentListContainersResponse
|
|
}
|
|
|
|
func (f *fakeContainerCLI) List(ctx context.Context) (codersdk.WorkspaceAgentListContainersResponse, error) {
|
|
return f.resp, nil
|
|
}
|
|
|
|
func (*fakeContainerCLI) DetectArchitecture(ctx context.Context, containerID string) (string, error) {
|
|
return runtime.GOARCH, nil
|
|
}
|
|
|
|
func (*fakeContainerCLI) Copy(ctx context.Context, containerID, src, dst string) error {
|
|
return nil
|
|
}
|
|
|
|
func (*fakeContainerCLI) ExecAs(ctx context.Context, containerID, user string, args ...string) ([]byte, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
type fakeDevcontainerCLI struct {
|
|
config agentcontainers.DevcontainerConfig
|
|
execAgent func(ctx context.Context, token string) error
|
|
}
|
|
|
|
func (f *fakeDevcontainerCLI) ReadConfig(ctx context.Context, workspaceFolder, configFile string, env []string, opts ...agentcontainers.DevcontainerCLIReadConfigOptions) (agentcontainers.DevcontainerConfig, error) {
|
|
return f.config, nil
|
|
}
|
|
|
|
func (f *fakeDevcontainerCLI) Exec(ctx context.Context, workspaceFolder, configFile string, name string, args []string, opts ...agentcontainers.DevcontainerCLIExecOptions) error {
|
|
var opt agentcontainers.DevcontainerCLIExecConfig
|
|
for _, o := range opts {
|
|
o(&opt)
|
|
}
|
|
var token string
|
|
for _, arg := range opt.Args {
|
|
if strings.HasPrefix(arg, "CODER_AGENT_TOKEN=") {
|
|
token = strings.TrimPrefix(arg, "CODER_AGENT_TOKEN=")
|
|
break
|
|
}
|
|
}
|
|
if token == "" {
|
|
return xerrors.New("no agent token provided in args")
|
|
}
|
|
if f.execAgent == nil {
|
|
return nil
|
|
}
|
|
return f.execAgent(ctx, token)
|
|
}
|
|
|
|
func (*fakeDevcontainerCLI) Up(ctx context.Context, workspaceFolder, configFile string, opts ...agentcontainers.DevcontainerCLIUpOptions) (string, error) {
|
|
return "", nil
|
|
}
|
|
|
|
func TestOpenVSCodeDevContainer(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if runtime.GOOS != "linux" {
|
|
t.Skip("DevContainers are only supported for agents on Linux")
|
|
}
|
|
|
|
parentAgentName := "agent1"
|
|
|
|
devcontainerID := uuid.New()
|
|
devcontainerName := "wilson"
|
|
workspaceFolder := "/home/coder/wilson"
|
|
configFile := path.Join(workspaceFolder, ".devcontainer", "devcontainer.json")
|
|
|
|
containerID := uuid.NewString()
|
|
containerName := testutil.GetRandomName(t)
|
|
containerFolder := "/workspaces/wilson"
|
|
|
|
client, workspace, agentToken := setupWorkspaceForAgent(t, func(agents []*proto.Agent) []*proto.Agent {
|
|
agents[0].Name = parentAgentName
|
|
agents[0].OperatingSystem = runtime.GOOS
|
|
return agents
|
|
})
|
|
|
|
fCCLI := &fakeContainerCLI{
|
|
resp: codersdk.WorkspaceAgentListContainersResponse{
|
|
Containers: []codersdk.WorkspaceAgentContainer{
|
|
{
|
|
ID: containerID,
|
|
CreatedAt: dbtime.Now(),
|
|
FriendlyName: containerName,
|
|
Image: "busybox:latest",
|
|
Labels: map[string]string{
|
|
agentcontainers.DevcontainerLocalFolderLabel: workspaceFolder,
|
|
agentcontainers.DevcontainerConfigFileLabel: configFile,
|
|
agentcontainers.DevcontainerIsTestRunLabel: "true",
|
|
"coder.test": t.Name(),
|
|
},
|
|
Running: true,
|
|
Status: "running",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
fDCCLI := &fakeDevcontainerCLI{
|
|
config: agentcontainers.DevcontainerConfig{
|
|
Workspace: agentcontainers.DevcontainerWorkspace{
|
|
WorkspaceFolder: containerFolder,
|
|
},
|
|
},
|
|
execAgent: func(ctx context.Context, token string) error {
|
|
t.Logf("Starting devcontainer subagent with token: %s", token)
|
|
_ = agenttest.New(t, client.URL, token)
|
|
<-ctx.Done()
|
|
return ctx.Err()
|
|
},
|
|
}
|
|
|
|
_ = agenttest.New(t, client.URL, agentToken, func(o *agent.Options) {
|
|
o.Devcontainers = true
|
|
o.DevcontainerAPIOptions = append(o.DevcontainerAPIOptions,
|
|
agentcontainers.WithProjectDiscovery(false),
|
|
agentcontainers.WithContainerCLI(fCCLI),
|
|
agentcontainers.WithDevcontainerCLI(fDCCLI),
|
|
agentcontainers.WithWatcher(watcher.NewNoop()),
|
|
agentcontainers.WithDevcontainers(
|
|
[]codersdk.WorkspaceAgentDevcontainer{{
|
|
ID: devcontainerID,
|
|
Name: devcontainerName,
|
|
WorkspaceFolder: workspaceFolder,
|
|
Status: codersdk.WorkspaceAgentDevcontainerStatusStopped,
|
|
}},
|
|
[]codersdk.WorkspaceAgentScript{{
|
|
ID: devcontainerID,
|
|
LogSourceID: uuid.New(),
|
|
}},
|
|
),
|
|
agentcontainers.WithContainerLabelIncludeFilter("coder.test", t.Name()),
|
|
)
|
|
})
|
|
coderdtest.NewWorkspaceAgentWaiter(t, client, workspace.ID).AgentNames([]string{parentAgentName, devcontainerName}).Wait()
|
|
|
|
insideWorkspaceEnv := map[string]string{
|
|
"CODER": "true",
|
|
"CODER_WORKSPACE_NAME": workspace.Name,
|
|
"CODER_WORKSPACE_AGENT_NAME": devcontainerName,
|
|
}
|
|
|
|
wd, err := os.Getwd()
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
env map[string]string
|
|
args []string
|
|
wantDir string
|
|
wantError bool
|
|
wantToken bool
|
|
}{
|
|
{
|
|
name: "nonexistent container",
|
|
args: []string{"--test.open-error", workspace.Name + "." + devcontainerName + "bad"},
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "ok",
|
|
args: []string{"--test.open-error", workspace.Name + "." + devcontainerName},
|
|
wantError: false,
|
|
},
|
|
{
|
|
name: "ok with absolute path",
|
|
args: []string{"--test.open-error", workspace.Name + "." + devcontainerName, containerFolder},
|
|
wantError: false,
|
|
},
|
|
{
|
|
name: "ok with relative path",
|
|
args: []string{"--test.open-error", workspace.Name + "." + devcontainerName, "my/relative/path"},
|
|
wantDir: path.Join(containerFolder, "my/relative/path"),
|
|
wantError: false,
|
|
},
|
|
{
|
|
name: "ok with token",
|
|
args: []string{"--test.open-error", workspace.Name + "." + devcontainerName, "--generate-token"},
|
|
wantError: false,
|
|
wantToken: true,
|
|
},
|
|
// Inside workspace, does not require --test.open-error
|
|
{
|
|
name: "ok inside workspace",
|
|
env: insideWorkspaceEnv,
|
|
args: []string{workspace.Name + "." + devcontainerName},
|
|
},
|
|
{
|
|
name: "ok inside workspace relative path",
|
|
env: insideWorkspaceEnv,
|
|
args: []string{workspace.Name + "." + devcontainerName, "foo"},
|
|
wantDir: filepath.Join(wd, "foo"),
|
|
},
|
|
{
|
|
name: "ok inside workspace token",
|
|
env: insideWorkspaceEnv,
|
|
args: []string{workspace.Name + "." + devcontainerName, "--generate-token"},
|
|
wantToken: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
inv, root := clitest.New(t, append([]string{"open", "vscode"}, tt.args...)...)
|
|
clitest.SetupConfig(t, client, root)
|
|
|
|
pty := ptytest.New(t)
|
|
inv.Stdin = pty.Input()
|
|
inv.Stdout = pty.Output()
|
|
|
|
ctx := testutil.Context(t, testutil.WaitLong)
|
|
inv = inv.WithContext(ctx)
|
|
|
|
for k, v := range tt.env {
|
|
inv.Environ.Set(k, v)
|
|
}
|
|
|
|
w := clitest.StartWithWaiter(t, inv)
|
|
|
|
if tt.wantError {
|
|
w.RequireError()
|
|
return
|
|
}
|
|
|
|
me, err := client.User(ctx, codersdk.Me)
|
|
require.NoError(t, err)
|
|
|
|
line := pty.ReadLine(ctx)
|
|
u, err := url.ParseRequestURI(line)
|
|
require.NoError(t, err, "line: %q", line)
|
|
|
|
qp := u.Query()
|
|
assert.Equal(t, client.URL.String(), qp.Get("url"))
|
|
assert.Equal(t, me.Username, qp.Get("owner"))
|
|
assert.Equal(t, workspace.Name, qp.Get("workspace"))
|
|
assert.Equal(t, parentAgentName, qp.Get("agent"))
|
|
assert.Equal(t, containerName, qp.Get("devContainerName"))
|
|
assert.Equal(t, workspaceFolder, qp.Get("localWorkspaceFolder"))
|
|
assert.Equal(t, configFile, qp.Get("localConfigFile"))
|
|
|
|
if tt.wantDir != "" {
|
|
assert.Equal(t, tt.wantDir, qp.Get("devContainerFolder"))
|
|
} else {
|
|
assert.Equal(t, containerFolder, qp.Get("devContainerFolder"))
|
|
}
|
|
|
|
if tt.wantToken {
|
|
assert.NotEmpty(t, qp.Get("token"))
|
|
} else {
|
|
assert.Empty(t, qp.Get("token"))
|
|
}
|
|
|
|
w.RequireSuccess()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOpenApp(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("OK", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client, ws, _ := setupWorkspaceForAgent(t, func(agents []*proto.Agent) []*proto.Agent {
|
|
agents[0].Apps = []*proto.App{
|
|
{
|
|
Slug: "app1",
|
|
Url: "https://example.com/app1",
|
|
},
|
|
}
|
|
return agents
|
|
})
|
|
|
|
inv, root := clitest.New(t, "open", "app", ws.Name, "app1", "--test.open-error")
|
|
clitest.SetupConfig(t, client, root)
|
|
pty := ptytest.New(t)
|
|
inv.Stdin = pty.Input()
|
|
inv.Stdout = pty.Output()
|
|
|
|
w := clitest.StartWithWaiter(t, inv)
|
|
w.RequireError()
|
|
w.RequireContains("test.open-error")
|
|
})
|
|
|
|
t.Run("OnlyWorkspaceName", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client, ws, _ := setupWorkspaceForAgent(t)
|
|
inv, root := clitest.New(t, "open", "app", ws.Name)
|
|
clitest.SetupConfig(t, client, root)
|
|
var sb strings.Builder
|
|
inv.Stdout = &sb
|
|
inv.Stderr = &sb
|
|
|
|
w := clitest.StartWithWaiter(t, inv)
|
|
w.RequireSuccess()
|
|
|
|
require.Contains(t, sb.String(), "Available apps in")
|
|
})
|
|
|
|
t.Run("WorkspaceNotFound", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client, _, _ := setupWorkspaceForAgent(t)
|
|
inv, root := clitest.New(t, "open", "app", "not-a-workspace", "app1")
|
|
clitest.SetupConfig(t, client, root)
|
|
pty := ptytest.New(t)
|
|
inv.Stdin = pty.Input()
|
|
inv.Stdout = pty.Output()
|
|
w := clitest.StartWithWaiter(t, inv)
|
|
w.RequireError()
|
|
w.RequireContains("Resource not found or you do not have access to this resource")
|
|
})
|
|
|
|
t.Run("AppNotFound", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client, ws, _ := setupWorkspaceForAgent(t)
|
|
|
|
inv, root := clitest.New(t, "open", "app", ws.Name, "app1")
|
|
clitest.SetupConfig(t, client, root)
|
|
pty := ptytest.New(t)
|
|
inv.Stdin = pty.Input()
|
|
inv.Stdout = pty.Output()
|
|
|
|
w := clitest.StartWithWaiter(t, inv)
|
|
w.RequireError()
|
|
w.RequireContains("app not found")
|
|
})
|
|
|
|
t.Run("RegionNotFound", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client, ws, _ := setupWorkspaceForAgent(t, func(agents []*proto.Agent) []*proto.Agent {
|
|
agents[0].Apps = []*proto.App{
|
|
{
|
|
Slug: "app1",
|
|
Url: "https://example.com/app1",
|
|
},
|
|
}
|
|
return agents
|
|
})
|
|
|
|
inv, root := clitest.New(t, "open", "app", ws.Name, "app1", "--region", "bad-region")
|
|
clitest.SetupConfig(t, client, root)
|
|
pty := ptytest.New(t)
|
|
inv.Stdin = pty.Input()
|
|
inv.Stdout = pty.Output()
|
|
|
|
w := clitest.StartWithWaiter(t, inv)
|
|
w.RequireError()
|
|
w.RequireContains("region not found")
|
|
})
|
|
|
|
t.Run("ExternalAppSessionToken", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client, ws, _ := setupWorkspaceForAgent(t, func(agents []*proto.Agent) []*proto.Agent {
|
|
agents[0].Apps = []*proto.App{
|
|
{
|
|
Slug: "app1",
|
|
Url: "https://example.com/app1?token=$SESSION_TOKEN",
|
|
External: true,
|
|
},
|
|
}
|
|
return agents
|
|
})
|
|
inv, root := clitest.New(t, "open", "app", ws.Name, "app1", "--test.open-error")
|
|
clitest.SetupConfig(t, client, root)
|
|
pty := ptytest.New(t)
|
|
inv.Stdin = pty.Input()
|
|
inv.Stdout = pty.Output()
|
|
|
|
w := clitest.StartWithWaiter(t, inv)
|
|
w.RequireError()
|
|
w.RequireContains("test.open-error")
|
|
w.RequireContains(client.SessionToken())
|
|
})
|
|
}
|