test: batch 01 of refactoring CLI tests not to use PTY (#25871)

Part of https://github.com/coder/internal/issues/1400

Batch of refactored CLI tests to avoid creating PTYs.
This commit is contained in:
Spike Curtis
2026-05-29 20:12:52 +00:00
committed by GitHub
parent 8a47b7fa14
commit 3a727a9087
8 changed files with 240 additions and 238 deletions
+11 -6
View File
@@ -17,7 +17,8 @@ import (
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
"github.com/coder/coder/v2/testutil/expecter"
"github.com/coder/pretty"
)
@@ -29,6 +30,7 @@ func TestCurrentOrganization(t *testing.T) {
// 2. The user is connecting to an older Coder instance.
t.Run("no-default", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitMedium)
orgID := uuid.New()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -49,13 +51,13 @@ func TestCurrentOrganization(t *testing.T) {
client := codersdk.New(must(url.Parse(srv.URL)))
inv, root := clitest.New(t, "organizations", "show", "selected")
clitest.SetupConfig(t, client, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
errC := make(chan error)
go func() {
errC <- inv.Run()
}()
require.NoError(t, <-errC)
pty.ExpectMatch(orgID.String())
stdout.ExpectMatchContext(ctx, orgID.String())
})
}
@@ -140,6 +142,8 @@ func TestOrganizationDelete(t *testing.T) {
t.Run("Prompted", func(t *testing.T) {
t.Parallel()
logger := testutil.Logger(t)
ctx := testutil.Context(t, testutil.WaitMedium)
orgID := uuid.New()
var deleteCalled atomic.Bool
@@ -167,15 +171,16 @@ func TestOrganizationDelete(t *testing.T) {
client := codersdk.New(must(url.Parse(server.URL)))
inv, root := clitest.New(t, "organizations", "delete", "my-org")
clitest.SetupConfig(t, client, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
execDone := make(chan error)
go func() {
execDone <- inv.Run()
}()
pty.ExpectMatch(fmt.Sprintf("Delete organization %s?", pretty.Sprint(cliui.DefaultStyles.Code, "my-org")))
pty.WriteLine("yes")
stdout.ExpectMatchContext(ctx, fmt.Sprintf("Delete organization %s?", pretty.Sprint(cliui.DefaultStyles.Code, "my-org")))
stdin.WriteLine("yes")
require.NoError(t, <-execDone)
require.True(t, deleteCalled.Load(), "expected delete request")
+9 -19
View File
@@ -25,8 +25,8 @@ import (
"github.com/coder/coder/v2/coderd/database/dbfake"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
"github.com/coder/coder/v2/testutil/expecter"
)
func TestPortForward_None(t *testing.T) {
@@ -160,10 +160,7 @@ func TestPortForward(t *testing.T) {
// the "local" listener.
inv, root := clitest.New(t, "-v", "port-forward", workspace.Name, flag)
clitest.SetupConfig(t, member, root)
pty := ptytest.New(t)
inv.Stdin = pty.Input()
inv.Stdout = pty.Output()
inv.Stderr = pty.Output()
stdout := expecter.NewAttachedToInvocation(t, inv)
iNet := testutil.NewInProcNet()
inv.Net = iNet
@@ -175,7 +172,7 @@ func TestPortForward(t *testing.T) {
t.Logf("command complete; err=%s", err.Error())
errC <- err
}()
pty.ExpectMatchContext(ctx, "Ready!")
stdout.ExpectMatchContext(ctx, "Ready!")
// Open two connections simultaneously and test them out of
// sync.
@@ -216,10 +213,7 @@ func TestPortForward(t *testing.T) {
// the "local" listeners.
inv, root := clitest.New(t, "-v", "port-forward", workspace.Name, flag1, flag2)
clitest.SetupConfig(t, member, root)
pty := ptytest.New(t)
inv.Stdin = pty.Input()
inv.Stdout = pty.Output()
inv.Stderr = pty.Output()
stdout := expecter.NewAttachedToInvocation(t, inv)
iNet := testutil.NewInProcNet()
inv.Net = iNet
@@ -229,7 +223,7 @@ func TestPortForward(t *testing.T) {
go func() {
errC <- inv.WithContext(ctx).Run()
}()
pty.ExpectMatchContext(ctx, "Ready!")
stdout.ExpectMatchContext(ctx, "Ready!")
// Open a connection to both listener 1 and 2 simultaneously and
// then test them out of order.
@@ -277,8 +271,7 @@ func TestPortForward(t *testing.T) {
// the "local" listeners.
inv, root := clitest.New(t, append([]string{"-v", "port-forward", workspace.Name}, flags...)...)
clitest.SetupConfig(t, member, root)
pty := ptytest.New(t).Attach(inv)
inv.Stderr = pty.Output()
stdout := expecter.NewAttachedToInvocation(t, inv)
iNet := testutil.NewInProcNet()
inv.Net = iNet
@@ -288,7 +281,7 @@ func TestPortForward(t *testing.T) {
go func() {
errC <- inv.WithContext(ctx).Run()
}()
pty.ExpectMatchContext(ctx, "Ready!")
stdout.ExpectMatchContext(ctx, "Ready!")
// Open connections to all items in the "dial" array.
var (
@@ -338,10 +331,7 @@ func TestPortForward(t *testing.T) {
// the "local" listener.
inv, root := clitest.New(t, "-v", "port-forward", workspace.Name, flag)
clitest.SetupConfig(t, member, root)
pty := ptytest.New(t)
inv.Stdin = pty.Input()
inv.Stdout = pty.Output()
inv.Stderr = pty.Output()
stdout := expecter.NewAttachedToInvocation(t, inv)
iNet := testutil.NewInProcNet()
inv.Net = iNet
@@ -359,7 +349,7 @@ func TestPortForward(t *testing.T) {
t.Logf("command complete; err=%s", err.Error())
errC <- err
}()
pty.ExpectMatchContext(ctx, "Ready!")
stdout.ExpectMatchContext(ctx, "Ready!")
// Test IPv4 still works
dialCtx, dialCtxCancel := context.WithTimeout(ctx, testutil.WaitShort)
+7 -6
View File
@@ -8,12 +8,13 @@ import (
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
"github.com/coder/coder/v2/testutil/expecter"
)
func TestRename(t *testing.T) {
t.Parallel()
logger := testutil.Logger(t)
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, AllowWorkspaceRenames: true})
owner := coderdtest.CreateFirstUser(t, client)
@@ -30,13 +31,13 @@ func TestRename(t *testing.T) {
want := coderdtest.RandomUsername(t)
inv, root := clitest.New(t, "rename", workspace.Name, want, "--yes")
clitest.SetupConfig(t, member, root)
pty := ptytest.New(t)
pty.Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
clitest.Start(t, inv)
pty.ExpectMatch("confirm rename:")
pty.WriteLine(workspace.Name)
pty.ExpectMatch("renamed to")
stdout.ExpectMatchContext(ctx, "confirm rename:")
stdin.WriteLine(workspace.Name)
stdout.ExpectMatchContext(ctx, "renamed to")
ws, err := client.Workspace(ctx, workspace.ID)
assert.NoError(t, err)
+39 -42
View File
@@ -1,7 +1,6 @@
package cli_test
import (
"context"
"fmt"
"testing"
@@ -14,8 +13,8 @@ import (
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/provisioner/echo"
"github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
"github.com/coder/coder/v2/testutil/expecter"
)
func TestRestart(t *testing.T) {
@@ -49,15 +48,15 @@ func TestRestart(t *testing.T) {
inv, root := clitest.New(t, "restart", workspace.Name, "--yes")
clitest.SetupConfig(t, member, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
done := make(chan error, 1)
go func() {
done <- inv.WithContext(ctx).Run()
}()
pty.ExpectMatch("Stopping workspace")
pty.ExpectMatch("Starting workspace")
pty.ExpectMatch("workspace has been restarted")
stdout.ExpectMatchContext(ctx, "Stopping workspace")
stdout.ExpectMatchContext(ctx, "Starting workspace")
stdout.ExpectMatchContext(ctx, "workspace has been restarted")
err := <-done
require.NoError(t, err, "execute failed")
@@ -66,6 +65,7 @@ func TestRestart(t *testing.T) {
t.Run("PromptEphemeralParameters", func(t *testing.T) {
t.Parallel()
logger := testutil.Logger(t)
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
owner := coderdtest.CreateFirstUser(t, client)
member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
@@ -84,13 +84,15 @@ func TestRestart(t *testing.T) {
inv, root := clitest.New(t, "restart", workspace.Name, "--prompt-ephemeral-parameters")
clitest.SetupConfig(t, member, root)
doneChan := make(chan struct{})
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
go func() {
defer close(doneChan)
err := inv.Run()
assert.NoError(t, err)
}()
ctx := testutil.Context(t, testutil.WaitShort)
matches := []string{
ephemeralParameterDescription, ephemeralParameterValue,
"Restart workspace?", "yes",
@@ -101,18 +103,15 @@ func TestRestart(t *testing.T) {
for i := 0; i < len(matches); i += 2 {
match := matches[i]
value := matches[i+1]
pty.ExpectMatch(match)
stdout.ExpectMatchContext(ctx, match)
if value != "" {
pty.WriteLine(value)
stdin.WriteLine(value)
}
}
<-doneChan
// Verify if build option is set
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
defer cancel()
workspace, err := client.WorkspaceByOwnerAndName(ctx, memberUser.ID.String(), workspace.Name, codersdk.WorkspaceOptions{})
require.NoError(t, err)
actualParameters, err := client.WorkspaceBuildParameters(ctx, workspace.LatestBuild.ID)
@@ -126,6 +125,7 @@ func TestRestart(t *testing.T) {
t.Run("EphemeralParameterFlags", func(t *testing.T) {
t.Parallel()
logger := testutil.Logger(t)
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
owner := coderdtest.CreateFirstUser(t, client)
member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
@@ -143,13 +143,15 @@ func TestRestart(t *testing.T) {
"--ephemeral-parameter", fmt.Sprintf("%s=%s", ephemeralParameterName, ephemeralParameterValue))
clitest.SetupConfig(t, member, root)
doneChan := make(chan struct{})
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
go func() {
defer close(doneChan)
err := inv.Run()
assert.NoError(t, err)
}()
ctx := testutil.Context(t, testutil.WaitShort)
matches := []string{
"Restart workspace?", "yes",
"Stopping workspace", "",
@@ -159,18 +161,15 @@ func TestRestart(t *testing.T) {
for i := 0; i < len(matches); i += 2 {
match := matches[i]
value := matches[i+1]
pty.ExpectMatch(match)
stdout.ExpectMatchContext(ctx, match)
if value != "" {
pty.WriteLine(value)
stdin.WriteLine(value)
}
}
<-doneChan
// Verify if build option is set
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
defer cancel()
workspace, err := client.WorkspaceByOwnerAndName(ctx, memberUser.ID.String(), workspace.Name, codersdk.WorkspaceOptions{})
require.NoError(t, err)
actualParameters, err := client.WorkspaceBuildParameters(ctx, workspace.LatestBuild.ID)
@@ -184,6 +183,7 @@ func TestRestart(t *testing.T) {
t.Run("with deprecated build-options flag", func(t *testing.T) {
t.Parallel()
logger := testutil.Logger(t)
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
owner := coderdtest.CreateFirstUser(t, client)
member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
@@ -202,13 +202,15 @@ func TestRestart(t *testing.T) {
inv, root := clitest.New(t, "restart", workspace.Name, "--build-options")
clitest.SetupConfig(t, member, root)
doneChan := make(chan struct{})
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
go func() {
defer close(doneChan)
err := inv.Run()
assert.NoError(t, err)
}()
ctx := testutil.Context(t, testutil.WaitShort)
matches := []string{
ephemeralParameterDescription, ephemeralParameterValue,
"Restart workspace?", "yes",
@@ -219,18 +221,15 @@ func TestRestart(t *testing.T) {
for i := 0; i < len(matches); i += 2 {
match := matches[i]
value := matches[i+1]
pty.ExpectMatch(match)
stdout.ExpectMatchContext(ctx, match)
if value != "" {
pty.WriteLine(value)
stdin.WriteLine(value)
}
}
<-doneChan
// Verify if build option is set
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
defer cancel()
workspace, err := client.WorkspaceByOwnerAndName(ctx, memberUser.ID.String(), workspace.Name, codersdk.WorkspaceOptions{})
require.NoError(t, err)
actualParameters, err := client.WorkspaceBuildParameters(ctx, workspace.LatestBuild.ID)
@@ -244,6 +243,7 @@ func TestRestart(t *testing.T) {
t.Run("with deprecated build-option flag", func(t *testing.T) {
t.Parallel()
logger := testutil.Logger(t)
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
owner := coderdtest.CreateFirstUser(t, client)
member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
@@ -261,13 +261,15 @@ func TestRestart(t *testing.T) {
"--build-option", fmt.Sprintf("%s=%s", ephemeralParameterName, ephemeralParameterValue))
clitest.SetupConfig(t, member, root)
doneChan := make(chan struct{})
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
go func() {
defer close(doneChan)
err := inv.Run()
assert.NoError(t, err)
}()
ctx := testutil.Context(t, testutil.WaitShort)
matches := []string{
"Restart workspace?", "yes",
"Stopping workspace", "",
@@ -277,18 +279,15 @@ func TestRestart(t *testing.T) {
for i := 0; i < len(matches); i += 2 {
match := matches[i]
value := matches[i+1]
pty.ExpectMatch(match)
stdout.ExpectMatchContext(ctx, match)
if value != "" {
pty.WriteLine(value)
stdin.WriteLine(value)
}
}
<-doneChan
// Verify if build option is set
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
defer cancel()
workspace, err := client.WorkspaceByOwnerAndName(ctx, memberUser.ID.String(), workspace.Name, codersdk.WorkspaceOptions{})
require.NoError(t, err)
actualParameters, err := client.WorkspaceBuildParameters(ctx, workspace.LatestBuild.ID)
@@ -349,20 +348,18 @@ func TestRestartWithParameters(t *testing.T) {
inv, root := clitest.New(t, "restart", workspace.Name, "-y")
clitest.SetupConfig(t, member, root)
doneChan := make(chan struct{})
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
go func() {
defer close(doneChan)
err := inv.Run()
assert.NoError(t, err)
}()
ctx := testutil.Context(t, testutil.WaitShort)
pty.ExpectMatch("workspace has been restarted")
stdout.ExpectMatchContext(ctx, "workspace has been restarted")
<-doneChan
// Verify if immutable parameter is set
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
defer cancel()
workspace, err := client.WorkspaceByOwnerAndName(ctx, workspace.OwnerName, workspace.Name, codersdk.WorkspaceOptions{})
require.NoError(t, err)
actualParameters, err := client.WorkspaceBuildParameters(ctx, workspace.LatestBuild.ID)
@@ -376,6 +373,7 @@ func TestRestartWithParameters(t *testing.T) {
t.Run("AlwaysPrompt", func(t *testing.T) {
t.Parallel()
logger := testutil.Logger(t)
// Create the workspace
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
owner := coderdtest.CreateFirstUser(t, client)
@@ -396,24 +394,23 @@ func TestRestartWithParameters(t *testing.T) {
inv, root := clitest.New(t, "restart", workspace.Name, "-y", "--always-prompt")
clitest.SetupConfig(t, member, root)
doneChan := make(chan struct{})
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
go func() {
defer close(doneChan)
err := inv.Run()
assert.NoError(t, err)
}()
ctx := testutil.Context(t, testutil.WaitShort)
// We should be prompted for the parameters again.
newValue := "xyz"
pty.ExpectMatch(mutableParameterName)
pty.WriteLine(newValue)
pty.ExpectMatch("workspace has been restarted")
stdout.ExpectMatchContext(ctx, mutableParameterName)
stdin.WriteLine(newValue)
stdout.ExpectMatchContext(ctx, "workspace has been restarted")
<-doneChan
// Verify that the updated values are persisted.
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
defer cancel()
workspace, err := client.WorkspaceByOwnerAndName(ctx, workspace.OwnerName, workspace.Name, codersdk.WorkspaceOptions{})
require.NoError(t, err)
actualParameters, err := client.WorkspaceBuildParameters(ctx, workspace.LatestBuild.ID)
+73 -62
View File
@@ -19,8 +19,8 @@ import (
"github.com/coder/coder/v2/coderd/schedule/cron"
"github.com/coder/coder/v2/coderd/util/tz"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
"github.com/coder/coder/v2/testutil/expecter"
)
// setupTestSchedule creates 4 workspaces:
@@ -97,20 +97,21 @@ func TestScheduleShow(t *testing.T) {
inv, root := clitest.New(t, "schedule", "show")
//nolint:gocritic // Testing that owner user sees all
clitest.SetupConfig(t, ownerClient, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
ctx := testutil.Context(t, testutil.WaitShort)
require.NoError(t, inv.Run())
// Then: they should see their own workspaces.
// 1st workspace: a-owner-ws1 has both autostart and autostop enabled.
pty.ExpectMatch(ws[0].OwnerName + "/" + ws[0].Name)
pty.ExpectMatch(sched.Humanize())
pty.ExpectMatch(sched.Next(now).In(loc).Format(time.RFC3339))
pty.ExpectMatch("8h")
pty.ExpectMatch(ws[0].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, ws[0].OwnerName+"/"+ws[0].Name)
stdout.ExpectMatchContext(ctx, sched.Humanize())
stdout.ExpectMatchContext(ctx, sched.Next(now).In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, "8h")
stdout.ExpectMatchContext(ctx, ws[0].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
// 2nd workspace: b-owner-ws2 has only autostart enabled.
pty.ExpectMatch(ws[1].OwnerName + "/" + ws[1].Name)
pty.ExpectMatch(sched.Humanize())
pty.ExpectMatch(sched.Next(now).In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, ws[1].OwnerName+"/"+ws[1].Name)
stdout.ExpectMatchContext(ctx, sched.Humanize())
stdout.ExpectMatchContext(ctx, sched.Next(now).In(loc).Format(time.RFC3339))
})
t.Run("OwnerAll", func(t *testing.T) {
@@ -118,26 +119,27 @@ func TestScheduleShow(t *testing.T) {
inv, root := clitest.New(t, "schedule", "show", "--all")
//nolint:gocritic // Testing that owner user sees all
clitest.SetupConfig(t, ownerClient, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
ctx := testutil.Context(t, testutil.WaitShort)
require.NoError(t, inv.Run())
// Then: they should see all workspaces
// 1st workspace: a-owner-ws1 has both autostart and autostop enabled.
pty.ExpectMatch(ws[0].OwnerName + "/" + ws[0].Name)
pty.ExpectMatch(sched.Humanize())
pty.ExpectMatch(sched.Next(now).In(loc).Format(time.RFC3339))
pty.ExpectMatch("8h")
pty.ExpectMatch(ws[0].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, ws[0].OwnerName+"/"+ws[0].Name)
stdout.ExpectMatchContext(ctx, sched.Humanize())
stdout.ExpectMatchContext(ctx, sched.Next(now).In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, "8h")
stdout.ExpectMatchContext(ctx, ws[0].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
// 2nd workspace: b-owner-ws2 has only autostart enabled.
pty.ExpectMatch(ws[1].OwnerName + "/" + ws[1].Name)
pty.ExpectMatch(sched.Humanize())
pty.ExpectMatch(sched.Next(now).In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, ws[1].OwnerName+"/"+ws[1].Name)
stdout.ExpectMatchContext(ctx, sched.Humanize())
stdout.ExpectMatchContext(ctx, sched.Next(now).In(loc).Format(time.RFC3339))
// 3rd workspace: c-member-ws3 has only autostop enabled.
pty.ExpectMatch(ws[2].OwnerName + "/" + ws[2].Name)
pty.ExpectMatch("8h")
pty.ExpectMatch(ws[2].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, ws[2].OwnerName+"/"+ws[2].Name)
stdout.ExpectMatchContext(ctx, "8h")
stdout.ExpectMatchContext(ctx, ws[2].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
// 4th workspace: d-member-ws4 has neither autostart nor autostop enabled.
pty.ExpectMatch(ws[3].OwnerName + "/" + ws[3].Name)
stdout.ExpectMatchContext(ctx, ws[3].OwnerName+"/"+ws[3].Name)
})
t.Run("OwnerSearchByName", func(t *testing.T) {
@@ -145,14 +147,15 @@ func TestScheduleShow(t *testing.T) {
inv, root := clitest.New(t, "schedule", "show", "--search", "name:"+ws[1].Name)
//nolint:gocritic // Testing that owner user sees all
clitest.SetupConfig(t, ownerClient, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
ctx := testutil.Context(t, testutil.WaitShort)
require.NoError(t, inv.Run())
// Then: they should see workspaces matching that query
// 2nd workspace: b-owner-ws2 has only autostart enabled.
pty.ExpectMatch(ws[1].OwnerName + "/" + ws[1].Name)
pty.ExpectMatch(sched.Humanize())
pty.ExpectMatch(sched.Next(now).In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, ws[1].OwnerName+"/"+ws[1].Name)
stdout.ExpectMatchContext(ctx, sched.Humanize())
stdout.ExpectMatchContext(ctx, sched.Next(now).In(loc).Format(time.RFC3339))
})
t.Run("OwnerOneArg", func(t *testing.T) {
@@ -160,37 +163,39 @@ func TestScheduleShow(t *testing.T) {
inv, root := clitest.New(t, "schedule", "show", ws[2].OwnerName+"/"+ws[2].Name)
//nolint:gocritic // Testing that owner user sees all
clitest.SetupConfig(t, ownerClient, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
ctx := testutil.Context(t, testutil.WaitShort)
require.NoError(t, inv.Run())
// Then: they should see that workspace
// 3rd workspace: c-member-ws3 has only autostop enabled.
pty.ExpectMatch(ws[2].OwnerName + "/" + ws[2].Name)
pty.ExpectMatch("8h")
pty.ExpectMatch(ws[2].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, ws[2].OwnerName+"/"+ws[2].Name)
stdout.ExpectMatchContext(ctx, "8h")
stdout.ExpectMatchContext(ctx, ws[2].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
})
t.Run("MemberNoArgs", func(t *testing.T) {
// When: a member specifies no args
inv, root := clitest.New(t, "schedule", "show")
clitest.SetupConfig(t, memberClient, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
ctx := testutil.Context(t, testutil.WaitShort)
require.NoError(t, inv.Run())
// Then: they should see their own workspaces
// 1st workspace: c-member-ws3 has only autostop enabled.
pty.ExpectMatch(ws[2].OwnerName + "/" + ws[2].Name)
pty.ExpectMatch("8h")
pty.ExpectMatch(ws[2].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, ws[2].OwnerName+"/"+ws[2].Name)
stdout.ExpectMatchContext(ctx, "8h")
stdout.ExpectMatchContext(ctx, ws[2].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
// 2nd workspace: d-member-ws4 has neither autostart nor autostop enabled.
pty.ExpectMatch(ws[3].OwnerName + "/" + ws[3].Name)
stdout.ExpectMatchContext(ctx, ws[3].OwnerName+"/"+ws[3].Name)
})
t.Run("MemberAll", func(t *testing.T) {
// When: a member lists all workspaces
inv, root := clitest.New(t, "schedule", "show", "--all")
clitest.SetupConfig(t, memberClient, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
ctx := testutil.Context(t, testutil.WaitShort)
errC := make(chan error)
go func() {
@@ -200,11 +205,11 @@ func TestScheduleShow(t *testing.T) {
// Then: they should only see their own
// 1st workspace: c-member-ws3 has only autostop enabled.
pty.ExpectMatch(ws[2].OwnerName + "/" + ws[2].Name)
pty.ExpectMatch("8h")
pty.ExpectMatch(ws[2].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, ws[2].OwnerName+"/"+ws[2].Name)
stdout.ExpectMatchContext(ctx, "8h")
stdout.ExpectMatchContext(ctx, ws[2].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
// 2nd workspace: d-member-ws4 has neither autostart nor autostop enabled.
pty.ExpectMatch(ws[3].OwnerName + "/" + ws[3].Name)
stdout.ExpectMatchContext(ctx, ws[3].OwnerName+"/"+ws[3].Name)
})
t.Run("JSON", func(t *testing.T) {
@@ -276,13 +281,14 @@ func TestScheduleModify(t *testing.T) {
)
//nolint:gocritic // this workspace is not owned by the same user
clitest.SetupConfig(t, ownerClient, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
ctx := testutil.Context(t, testutil.WaitShort)
require.NoError(t, inv.Run())
// Then: the updated schedule should be shown
pty.ExpectMatch(ws[3].OwnerName + "/" + ws[3].Name)
pty.ExpectMatch(sched.Humanize())
pty.ExpectMatch(sched.Next(now).In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, ws[3].OwnerName+"/"+ws[3].Name)
stdout.ExpectMatchContext(ctx, sched.Humanize())
stdout.ExpectMatchContext(ctx, sched.Next(now).In(loc).Format(time.RFC3339))
})
t.Run("SetStop", func(t *testing.T) {
@@ -292,13 +298,14 @@ func TestScheduleModify(t *testing.T) {
)
//nolint:gocritic // this workspace is not owned by the same user
clitest.SetupConfig(t, ownerClient, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
ctx := testutil.Context(t, testutil.WaitShort)
require.NoError(t, inv.Run())
// Then: the updated schedule should be shown
pty.ExpectMatch(ws[2].OwnerName + "/" + ws[2].Name)
pty.ExpectMatch("8h30m")
pty.ExpectMatch(ws[2].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, ws[2].OwnerName+"/"+ws[2].Name)
stdout.ExpectMatchContext(ctx, "8h30m")
stdout.ExpectMatchContext(ctx, ws[2].LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339))
})
t.Run("UnsetStart", func(t *testing.T) {
@@ -308,11 +315,12 @@ func TestScheduleModify(t *testing.T) {
)
//nolint:gocritic // this workspace is owned by owner
clitest.SetupConfig(t, ownerClient, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
ctx := testutil.Context(t, testutil.WaitShort)
require.NoError(t, inv.Run())
// Then: the updated schedule should be shown
pty.ExpectMatch(ws[1].OwnerName + "/" + ws[1].Name)
stdout.ExpectMatchContext(ctx, ws[1].OwnerName+"/"+ws[1].Name)
})
t.Run("UnsetStop", func(t *testing.T) {
@@ -322,11 +330,12 @@ func TestScheduleModify(t *testing.T) {
)
//nolint:gocritic // this workspace is owned by owner
clitest.SetupConfig(t, ownerClient, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
ctx := testutil.Context(t, testutil.WaitShort)
require.NoError(t, inv.Run())
// Then: the updated schedule should be shown
pty.ExpectMatch(ws[0].OwnerName + "/" + ws[0].Name)
stdout.ExpectMatchContext(ctx, ws[0].OwnerName+"/"+ws[0].Name)
})
}
@@ -359,7 +368,8 @@ func TestScheduleOverride(t *testing.T) {
)
clitest.SetupConfig(t, ownerClient, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
ctx := testutil.Context(t, testutil.WaitShort)
require.NoError(t, inv.Run())
// Fetch the workspace to get the actual deadline set by the
@@ -376,11 +386,11 @@ func TestScheduleOverride(t *testing.T) {
expectedDeadline := updated.LatestBuild.Deadline.Time.In(loc).Format(time.RFC3339)
// Then: the updated schedule should be shown
pty.ExpectMatch(ws[0].OwnerName + "/" + ws[0].Name)
pty.ExpectMatch(sched.Humanize())
pty.ExpectMatch(sched.Next(now).In(loc).Format(time.RFC3339))
pty.ExpectMatch("8h")
pty.ExpectMatch(expectedDeadline)
stdout.ExpectMatchContext(ctx, ws[0].OwnerName+"/"+ws[0].Name)
stdout.ExpectMatchContext(ctx, sched.Humanize())
stdout.ExpectMatchContext(ctx, sched.Next(now).In(loc).Format(time.RFC3339))
stdout.ExpectMatchContext(ctx, "8h")
stdout.ExpectMatchContext(ctx, expectedDeadline)
})
}
}
@@ -422,13 +432,14 @@ func TestScheduleStart_TemplateAutostartRequirement(t *testing.T) {
"schedule", "start", workspace.Name, "9:30AM", "Mon-Fri",
)
clitest.SetupConfig(t, client, root)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
ctx := testutil.Context(t, testutil.WaitShort)
require.NoError(t, inv.Run())
// Then: warning should be shown
// In AGPL, this will show all days (enterprise feature defaults to all days allowed)
pty.ExpectMatch("Warning")
pty.ExpectMatch("may only autostart")
stdout.ExpectMatchContext(ctx, "Warning")
stdout.ExpectMatchContext(ctx, "may only autostart")
})
t.Run("NoWarningWhenManual", func(t *testing.T) {
+14 -10
View File
@@ -14,8 +14,8 @@ import (
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
"github.com/coder/coder/v2/testutil/expecter"
)
func TestSecretCreate(t *testing.T) {
@@ -501,6 +501,7 @@ func TestSecretDelete(t *testing.T) {
t.Run("Success", func(t *testing.T) {
t.Parallel()
logger := testutil.Logger(t)
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)
@@ -516,12 +517,13 @@ func TestSecretDelete(t *testing.T) {
ctx := testutil.Context(t, testutil.WaitMedium)
inv = inv.WithContext(ctx)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
waiter := clitest.StartWithWaiter(t, inv)
pty.ExpectMatchContext(ctx, "Delete secret")
pty.ExpectMatchContext(ctx, "service-token")
pty.WriteLine("yes")
pty.ExpectMatchContext(ctx, "Deleted secret")
stdout.ExpectMatchContext(ctx, "Delete secret")
stdout.ExpectMatchContext(ctx, "service-token")
stdin.WriteLine("yes")
stdout.ExpectMatchContext(ctx, "Deleted secret")
require.NoError(t, waiter.Wait())
@@ -566,6 +568,7 @@ func TestSecretDelete(t *testing.T) {
t.Run("NotFound", func(t *testing.T) {
t.Parallel()
logger := testutil.Logger(t)
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)
@@ -574,11 +577,12 @@ func TestSecretDelete(t *testing.T) {
ctx := testutil.Context(t, testutil.WaitMedium)
inv = inv.WithContext(ctx)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
waiter := clitest.StartWithWaiter(t, inv)
pty.ExpectMatchContext(ctx, "Delete secret")
pty.ExpectMatchContext(ctx, "missing-secret")
pty.WriteLine("yes")
stdout.ExpectMatchContext(ctx, "Delete secret")
stdout.ExpectMatchContext(ctx, "missing-secret")
stdin.WriteLine("yes")
err := waiter.Wait()
require.ErrorContains(t, err, `delete secret "missing-secret"`)
+25 -24
View File
@@ -21,6 +21,7 @@ import (
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
"github.com/coder/coder/v2/testutil/expecter"
)
//nolint:paralleltest, tparallel
@@ -128,19 +129,17 @@ func TestServerCreateAdminUser(t *testing.T) {
"--email", email,
"--password", password,
)
pty := ptytest.New(t)
inv.Stdout = pty.Output()
inv.Stderr = pty.Output()
stdout := expecter.NewAttachedToInvocation(t, inv)
clitest.Start(t, inv)
pty.ExpectMatchContext(ctx, "Creating user...")
pty.ExpectMatchContext(ctx, "Generating user SSH key...")
pty.ExpectMatchContext(ctx, fmt.Sprintf("Adding user to organization %q (%s) as admin...", org1Name, org1ID.String()))
pty.ExpectMatchContext(ctx, fmt.Sprintf("Adding user to organization %q (%s) as admin...", org2Name, org2ID.String()))
pty.ExpectMatchContext(ctx, "User created successfully.")
pty.ExpectMatchContext(ctx, username)
pty.ExpectMatchContext(ctx, email)
pty.ExpectMatchContext(ctx, "****")
stdout.ExpectMatchContext(ctx, "Creating user...")
stdout.ExpectMatchContext(ctx, "Generating user SSH key...")
stdout.ExpectMatchContext(ctx, fmt.Sprintf("Adding user to organization %q (%s) as admin...", org1Name, org1ID.String()))
stdout.ExpectMatchContext(ctx, fmt.Sprintf("Adding user to organization %q (%s) as admin...", org2Name, org2ID.String()))
stdout.ExpectMatchContext(ctx, "User created successfully.")
stdout.ExpectMatchContext(ctx, username)
stdout.ExpectMatchContext(ctx, email)
stdout.ExpectMatchContext(ctx, "****")
verifyUser(t, connectionURL, username, email, password)
})
@@ -184,6 +183,7 @@ func TestServerCreateAdminUser(t *testing.T) {
// Skip on non-Linux because it spawns a PostgreSQL instance.
t.SkipNow()
}
logger := testutil.Logger(t)
connectionURL, err := dbtestutil.Open(t)
require.NoError(t, err)
@@ -195,23 +195,24 @@ func TestServerCreateAdminUser(t *testing.T) {
"--postgres-url", connectionURL,
"--ssh-keygen-algorithm", "ed25519",
)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
clitest.Start(t, inv)
pty.ExpectMatchContext(ctx, "Username")
pty.WriteLine(username)
pty.ExpectMatchContext(ctx, "Email")
pty.WriteLine(email)
pty.ExpectMatchContext(ctx, "Password")
pty.WriteLine(password)
pty.ExpectMatchContext(ctx, "Confirm password")
pty.WriteLine(password)
stdout.ExpectMatchContext(ctx, "Username")
stdin.WriteLine(username)
stdout.ExpectMatchContext(ctx, "Email")
stdin.WriteLine(email)
stdout.ExpectMatchContext(ctx, "Password")
stdin.WriteLine(password)
stdout.ExpectMatchContext(ctx, "Confirm password")
stdin.WriteLine(password)
pty.ExpectMatchContext(ctx, "User created successfully.")
pty.ExpectMatchContext(ctx, username)
pty.ExpectMatchContext(ctx, email)
pty.ExpectMatchContext(ctx, "****")
stdout.ExpectMatchContext(ctx, "User created successfully.")
stdout.ExpectMatchContext(ctx, username)
stdout.ExpectMatchContext(ctx, email)
stdout.ExpectMatchContext(ctx, "****")
verifyUser(t, connectionURL, username, email, password)
})
+62 -69
View File
@@ -59,6 +59,7 @@ import (
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/tailnet/tailnettest"
"github.com/coder/coder/v2/testutil"
"github.com/coder/coder/v2/testutil/expecter"
"github.com/coder/serpent"
)
@@ -229,7 +230,7 @@ func TestServer(t *testing.T) {
"--access-url", "http://example.com",
"--ephemeral",
)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
// Embedded postgres takes a while to fire up.
const superDuperLong = testutil.WaitSuperLong * 3
@@ -240,7 +241,7 @@ func TestServer(t *testing.T) {
}()
matchCh1 := make(chan string, 1)
go func() {
matchCh1 <- pty.ExpectMatchContext(ctx, "Using an ephemeral deployment directory")
matchCh1 <- stdout.ExpectMatchContext(ctx, "Using an ephemeral deployment directory")
}()
select {
case err := <-errCh:
@@ -248,7 +249,7 @@ func TestServer(t *testing.T) {
case <-matchCh1:
// OK!
}
rootDirLine := pty.ReadLine(ctx)
rootDirLine := stdout.ReadLine(ctx)
rootDir := strings.TrimPrefix(rootDirLine, "Using an ephemeral deployment directory")
rootDir = strings.TrimSpace(rootDir)
rootDir = strings.TrimPrefix(rootDir, "(")
@@ -259,7 +260,7 @@ func TestServer(t *testing.T) {
matchCh2 := make(chan string, 1)
go func() {
// The "View the Web UI" log is a decent indicator that the server was successfully started.
matchCh2 <- pty.ExpectMatchContext(ctx, "View the Web UI")
matchCh2 <- stdout.ExpectMatchContext(ctx, "View the Web UI")
}()
select {
case err := <-errCh:
@@ -276,24 +277,23 @@ func TestServer(t *testing.T) {
t.Run("BuiltinPostgresURL", func(t *testing.T) {
t.Parallel()
root, _ := clitest.New(t, "server", "postgres-builtin-url")
pty := ptytest.New(t)
root.Stdout = pty.Output()
stdout := expecter.NewAttachedToInvocation(t, root)
ctx := testutil.Context(t, testutil.WaitShort)
err := root.Run()
require.NoError(t, err)
pty.ExpectMatch("psql")
stdout.ExpectMatchContext(ctx, "psql")
})
t.Run("BuiltinPostgresURLRaw", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitLong)
root, _ := clitest.New(t, "server", "postgres-builtin-url", "--raw-url")
pty := ptytest.New(t)
root.Stdout = pty.Output()
stdout := expecter.NewAttachedToInvocation(t, root)
err := root.WithContext(ctx).Run()
require.NoError(t, err)
got := pty.ReadLine(ctx)
got := stdout.ReadLine(ctx)
if !strings.HasPrefix(got, "postgres://") {
t.Fatalf("expected postgres URL to start with \"postgres://\", got %q", got)
}
@@ -506,6 +506,7 @@ func TestServer(t *testing.T) {
// reachable.
t.Run("LocalAccessURL", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
inv, cfg := clitest.New(t,
"server",
dbArg(t),
@@ -513,7 +514,7 @@ func TestServer(t *testing.T) {
"--access-url", "http://localhost:3000/",
"--cache-dir", t.TempDir(),
)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
// Since we end the test after seeing the log lines about the access url, we could cancel the test before
// our initial interactions with PostgreSQL are complete. So, ignore errors of that type for this test.
startIgnoringPostgresQueryCancel(t, inv)
@@ -521,9 +522,9 @@ func TestServer(t *testing.T) {
// Just wait for startup
_ = waitAccessURL(t, cfg)
pty.ExpectMatch("this may cause unexpected problems when creating workspaces")
pty.ExpectMatch("View the Web UI:")
pty.ExpectMatch("http://localhost:3000/")
stdout.ExpectMatchContext(ctx, "this may cause unexpected problems when creating workspaces")
stdout.ExpectMatchContext(ctx, "View the Web UI:")
stdout.ExpectMatchContext(ctx, "http://localhost:3000/")
})
// Validate that an https scheme is prepended to a remote access URL
@@ -531,6 +532,7 @@ func TestServer(t *testing.T) {
t.Run("RemoteAccessURL", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
inv, cfg := clitest.New(t,
"server",
dbArg(t),
@@ -538,7 +540,7 @@ func TestServer(t *testing.T) {
"--access-url", "https://foobarbaz.mydomain",
"--cache-dir", t.TempDir(),
)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
// Since we end the test after seeing the log lines about the access url, we could cancel the test before
// our initial interactions with PostgreSQL are complete. So, ignore errors of that type for this test.
@@ -547,13 +549,14 @@ func TestServer(t *testing.T) {
// Just wait for startup
_ = waitAccessURL(t, cfg)
pty.ExpectMatch("this may cause unexpected problems when creating workspaces")
pty.ExpectMatch("View the Web UI:")
pty.ExpectMatch("https://foobarbaz.mydomain")
stdout.ExpectMatchContext(ctx, "this may cause unexpected problems when creating workspaces")
stdout.ExpectMatchContext(ctx, "View the Web UI:")
stdout.ExpectMatchContext(ctx, "https://foobarbaz.mydomain")
})
t.Run("NoWarningWithRemoteAccessURL", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
inv, cfg := clitest.New(t,
"server",
dbArg(t),
@@ -561,7 +564,7 @@ func TestServer(t *testing.T) {
"--access-url", "https://google.com",
"--cache-dir", t.TempDir(),
)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
// Since we end the test after seeing the log lines about the access url, we could cancel the test before
// our initial interactions with PostgreSQL are complete. So, ignore errors of that type for this test.
startIgnoringPostgresQueryCancel(t, inv)
@@ -569,8 +572,8 @@ func TestServer(t *testing.T) {
// Just wait for startup
_ = waitAccessURL(t, cfg)
pty.ExpectMatch("View the Web UI:")
pty.ExpectMatch("https://google.com")
stdout.ExpectMatchContext(ctx, "View the Web UI:")
stdout.ExpectMatchContext(ctx, "https://google.com")
})
t.Run("NoSchemeAccessURL", func(t *testing.T) {
@@ -735,8 +738,6 @@ func TestServer(t *testing.T) {
"--tls-key-file", key2Path,
"--cache-dir", t.TempDir(),
)
pty := ptytest.New(t)
root.Stdout = pty.Output()
clitest.Start(t, root.WithContext(ctx))
accessURL := waitAccessURL(t, cfg)
@@ -814,18 +815,18 @@ func TestServer(t *testing.T) {
"--tls-key-file", keyPath,
"--cache-dir", t.TempDir(),
)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
clitest.Start(t, inv)
// We can't use waitAccessURL as it will only return the HTTP URL.
const httpLinePrefix = "Started HTTP listener at"
pty.ExpectMatch(httpLinePrefix)
httpLine := pty.ReadLine(ctx)
stdout.ExpectMatchContext(ctx, httpLinePrefix)
httpLine := stdout.ReadLine(ctx)
httpAddr := strings.TrimSpace(strings.TrimPrefix(httpLine, httpLinePrefix))
require.NotEmpty(t, httpAddr)
const tlsLinePrefix = "Started TLS/HTTPS listener at "
pty.ExpectMatch(tlsLinePrefix)
tlsLine := pty.ReadLine(ctx)
stdout.ExpectMatchContext(ctx, tlsLinePrefix)
tlsLine := stdout.ReadLine(ctx)
tlsAddr := strings.TrimSpace(strings.TrimPrefix(tlsLine, tlsLinePrefix))
require.NotEmpty(t, tlsAddr)
@@ -951,8 +952,7 @@ func TestServer(t *testing.T) {
}
inv, _ := clitest.New(t, flags...)
pty := ptytest.New(t)
pty.Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
clitest.Start(t, inv)
@@ -963,15 +963,15 @@ func TestServer(t *testing.T) {
// We can't use waitAccessURL as it will only return the HTTP URL.
if c.httpListener {
const httpLinePrefix = "Started HTTP listener at"
pty.ExpectMatch(httpLinePrefix)
httpLine := pty.ReadLine(ctx)
stdout.ExpectMatchContext(ctx, httpLinePrefix)
httpLine := stdout.ReadLine(ctx)
httpAddr = strings.TrimSpace(strings.TrimPrefix(httpLine, httpLinePrefix))
require.NotEmpty(t, httpAddr)
}
if c.tlsListener {
const tlsLinePrefix = "Started TLS/HTTPS listener at"
pty.ExpectMatch(tlsLinePrefix)
tlsLine := pty.ReadLine(ctx)
stdout.ExpectMatchContext(ctx, tlsLinePrefix)
tlsLine := stdout.ReadLine(ctx)
tlsAddr = strings.TrimSpace(strings.TrimPrefix(tlsLine, tlsLinePrefix))
require.NotEmpty(t, tlsAddr)
}
@@ -1041,6 +1041,7 @@ func TestServer(t *testing.T) {
t.Run("CanListenUnspecifiedv4", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
inv, _ := clitest.New(t,
"server",
dbArg(t),
@@ -1048,18 +1049,19 @@ func TestServer(t *testing.T) {
"--access-url", "http://example.com",
)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
// Since we end the test after seeing the log lines about the HTTP listener, we could cancel the test before
// our initial interactions with PostgreSQL are complete. So, ignore errors of that type for this test.
startIgnoringPostgresQueryCancel(t, inv)
pty.ExpectMatch("Started HTTP listener")
pty.ExpectMatch("http://0.0.0.0:")
stdout.ExpectMatchContext(ctx, "Started HTTP listener")
stdout.ExpectMatchContext(ctx, "http://0.0.0.0:")
})
t.Run("CanListenUnspecifiedv6", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
inv, _ := clitest.New(t,
"server",
dbArg(t),
@@ -1067,13 +1069,13 @@ func TestServer(t *testing.T) {
"--access-url", "http://example.com",
)
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
// Since we end the test after seeing the log lines about the HTTP listener, we could cancel the test before
// our initial interactions with PostgreSQL are complete. So, ignore errors of that type for this test.
startIgnoringPostgresQueryCancel(t, inv)
pty.ExpectMatch("Started HTTP listener at")
pty.ExpectMatch("http://[::]:")
stdout.ExpectMatchContext(ctx, "Started HTTP listener at")
stdout.ExpectMatchContext(ctx, "http://[::]:")
})
t.Run("NoAddress", func(t *testing.T) {
@@ -1128,12 +1130,10 @@ func TestServer(t *testing.T) {
"--access-url", "http://example.com",
"--cache-dir", t.TempDir(),
)
pty := ptytest.New(t)
inv.Stdout = pty.Output()
inv.Stderr = pty.Output()
stdout := expecter.NewAttachedToInvocation(t, inv)
clitest.Start(t, inv.WithContext(ctx))
pty.ExpectMatch("is deprecated")
stdout.ExpectMatchContext(ctx, "is deprecated")
accessURL := waitAccessURL(t, cfg)
require.Equal(t, "http", accessURL.Scheme)
@@ -1158,12 +1158,10 @@ func TestServer(t *testing.T) {
"--tls-key-file", keyPath,
"--cache-dir", t.TempDir(),
)
pty := ptytest.New(t)
root.Stdout = pty.Output()
root.Stderr = pty.Output()
stdout := expecter.NewAttachedToInvocation(t, root)
clitest.Start(t, root.WithContext(ctx))
pty.ExpectMatch("is deprecated")
stdout.ExpectMatchContext(ctx, "is deprecated")
accessURL := waitAccessURL(t, cfg)
require.Equal(t, "https", accessURL.Scheme)
@@ -1259,15 +1257,13 @@ func TestServer(t *testing.T) {
"--cache-dir", t.TempDir(),
)
pty := ptytest.New(t)
inv.Stdout = pty.Output()
inv.Stderr = pty.Output()
stdout := expecter.NewAttachedToInvocation(t, inv)
clitest.Start(t, inv)
// Wait until we see the prometheus address in the logs.
addrMatchExpr := `http server listening\s+addr=(\S+)\s+name=prometheus`
lineMatch := pty.ExpectRegexMatchContext(ctx, addrMatchExpr)
lineMatch := stdout.ExpectRegexMatchContext(ctx, addrMatchExpr)
promAddr := regexp.MustCompile(addrMatchExpr).FindStringSubmatch(lineMatch)[1]
testutil.Eventually(ctx, t, func(ctx context.Context) bool {
@@ -1322,15 +1318,13 @@ func TestServer(t *testing.T) {
"--cache-dir", t.TempDir(),
)
pty := ptytest.New(t)
inv.Stdout = pty.Output()
inv.Stderr = pty.Output()
stdout := expecter.NewAttachedToInvocation(t, inv)
clitest.Start(t, inv)
// Wait until we see the prometheus address in the logs.
addrMatchExpr := `http server listening\s+addr=(\S+)\s+name=prometheus`
lineMatch := pty.ExpectRegexMatchContext(ctx, addrMatchExpr)
lineMatch := stdout.ExpectRegexMatchContext(ctx, addrMatchExpr)
promAddr := regexp.MustCompile(addrMatchExpr).FindStringSubmatch(lineMatch)[1]
testutil.Eventually(ctx, t, func(ctx context.Context) bool {
@@ -1751,7 +1745,6 @@ func TestServer(t *testing.T) {
inv, cfg := clitest.New(t,
args...,
)
ptytest.New(t).Attach(inv)
inv = inv.WithContext(ctx)
w := clitest.StartWithWaiter(t, inv)
gotURL := waitAccessURL(t, cfg)
@@ -2019,15 +2012,15 @@ func TestServer_Logging_NoParallel(t *testing.T) {
"--provisioner-types=echo",
"--log-stackdriver", fi,
)
// Attach pty so we get debug output from the command if this test
// Attach expecter so we get debug output from the command if this test
// fails.
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
startIgnoringPostgresQueryCancel(t, inv.WithContext(ctx))
// Wait for server to listen on HTTP, this is a good
// starting point for expecting logs.
_ = pty.ExpectMatchContext(ctx, "Started HTTP listener at")
_ = stdout.ExpectMatchContext(ctx, "Started HTTP listener at")
loggingWaitFile(t, fi, testutil.WaitSuperLong)
})
@@ -2056,15 +2049,15 @@ func TestServer_Logging_NoParallel(t *testing.T) {
"--log-json", fi2,
"--log-stackdriver", fi3,
)
// Attach pty so we get debug output from the command if this test
// Attach expecter so we get debug output from the command if this test
// fails.
pty := ptytest.New(t).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
startIgnoringPostgresQueryCancel(t, inv)
// Wait for server to listen on HTTP, this is a good
// starting point for expecting logs.
_ = pty.ExpectMatchContext(ctx, "Started HTTP listener at")
_ = stdout.ExpectMatchContext(ctx, "Started HTTP listener at")
loggingWaitFile(t, fi1, testutil.WaitSuperLong)
loggingWaitFile(t, fi2, testutil.WaitSuperLong)
@@ -2258,7 +2251,7 @@ func TestServer_GracefulShutdown(t *testing.T) {
return ctx, stopFunc
})
serverErr := make(chan error, 1)
pty := ptytest.New(t).Attach(root)
stdout := expecter.NewAttachedToInvocation(t, root)
go func() {
serverErr <- root.WithContext(ctx).Run()
}()
@@ -2266,7 +2259,7 @@ func TestServer_GracefulShutdown(t *testing.T) {
// It's fair to assume `stopFunc` isn't nil here, because the server
// has started and access URL is propagated.
stopFunc()
pty.ExpectMatch("waiting for provisioner jobs to complete")
stdout.ExpectMatchContext(ctx, "waiting for provisioner jobs to complete")
err := <-serverErr
require.NoError(t, err)
}
@@ -2501,19 +2494,19 @@ func TestServer_TelemetryDisabled_FinalReport(t *testing.T) {
inv.Logger = inv.Logger.Named(opts.name)
errChan := make(chan error, 1)
pty := ptytest.New(t).Named(opts.name).Attach(inv)
stdout := expecter.NewAttachedToInvocation(t, inv)
go func() {
errChan <- inv.WithContext(ctx).Run()
// close the pty here so that we can start tearing down resources. This test creates multiple servers with
// associated ptys. There is a `t.Cleanup()` that does this, but it waits until the whole test is complete.
_ = pty.Close()
stdout.Close("invocation complete")
}()
if opts.waitForSnapshot {
pty.ExpectMatchContext(testutil.Context(t, testutil.WaitLong), "submitted snapshot")
stdout.ExpectMatchContext(testutil.Context(t, testutil.WaitLong), "submitted snapshot")
}
if opts.waitForTelemetryDisabledCheck {
pty.ExpectMatchContext(testutil.Context(t, testutil.WaitLong), "finished telemetry status check")
stdout.ExpectMatchContext(testutil.Context(t, testutil.WaitLong), "finished telemetry status check")
}
return errChan, cancelFunc
}