From cbaea49c02ba393ecddfc256809714a61d730e7f Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Tue, 2 Jun 2026 10:50:27 -0400 Subject: [PATCH] test: batch 04 of refactoring CLI tests not to use PTY (#25937) Part of [coder/internal#1400](https://github.com/coder/internal/issues/1400) Batch of refactored CLI tests to avoid creating PTYs. --- cli/templateversions_test.go | 12 +- cli/update_test.go | 366 +++++++++++++++++------------------ cli/user_delete_test.go | 22 +-- cli/usercreate_test.go | 18 +- cli/userlist_test.go | 13 +- cli/vscodessh_test.go | 2 - 6 files changed, 210 insertions(+), 223 deletions(-) diff --git a/cli/templateversions_test.go b/cli/templateversions_test.go index 8ad9b573c6..732055f633 100644 --- a/cli/templateversions_test.go +++ b/cli/templateversions_test.go @@ -12,13 +12,15 @@ import ( "github.com/coder/coder/v2/cli/clitest" "github.com/coder/coder/v2/coderd/coderdtest" "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 TestTemplateVersions(t *testing.T) { t.Parallel() t.Run("ListVersions", func(t *testing.T) { t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) owner := coderdtest.CreateFirstUser(t, client) member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) @@ -29,7 +31,7 @@ func TestTemplateVersions(t *testing.T) { inv, root := clitest.New(t, "templates", "versions", "list", template.Name) clitest.SetupConfig(t, member, root) - pty := ptytest.New(t).Attach(inv) + stdout := expecter.NewAttachedToInvocation(t, inv) errC := make(chan error) go func() { @@ -38,9 +40,9 @@ func TestTemplateVersions(t *testing.T) { require.NoError(t, <-errC) - pty.ExpectMatch(version.Name) - pty.ExpectMatch(version.CreatedBy.Username) - pty.ExpectMatch("Active") + stdout.ExpectMatchContext(ctx, version.Name) + stdout.ExpectMatchContext(ctx, version.CreatedBy.Username) + stdout.ExpectMatchContext(ctx, "Active") }) t.Run("ListVersionsJSON", func(t *testing.T) { diff --git a/cli/update_test.go b/cli/update_test.go index b2cd202fe1..f5287bbdf5 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -15,8 +15,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 TestUpdate(t *testing.T) { @@ -230,6 +230,7 @@ func TestUpdateWithRichParameters(t *testing.T) { t.Run("ImmutableCannotBeCustomized", func(t *testing.T) { t.Parallel() + logger := testutil.Logger(t) client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) owner := coderdtest.CreateFirstUser(t, client) member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) @@ -255,7 +256,9 @@ func TestUpdateWithRichParameters(t *testing.T) { 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) + ctx := testutil.Context(t, testutil.WaitMedium) go func() { defer close(doneChan) err := inv.Run() @@ -270,9 +273,9 @@ func TestUpdateWithRichParameters(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 @@ -281,6 +284,7 @@ func TestUpdateWithRichParameters(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) @@ -308,7 +312,9 @@ func TestUpdateWithRichParameters(t *testing.T) { 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) + ctx := testutil.Context(t, testutil.WaitMedium) go func() { defer close(doneChan) err := inv.Run() @@ -322,9 +328,9 @@ func TestUpdateWithRichParameters(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 @@ -369,14 +375,15 @@ func TestUpdateWithRichParameters(t *testing.T) { clitest.SetupConfig(t, member, root) doneChan := make(chan struct{}) - pty := ptytest.New(t).Attach(inv) + stdout := expecter.NewAttachedToInvocation(t, inv) + ctx := testutil.Context(t, testutil.WaitMedium) go func() { defer close(doneChan) err := inv.Run() assert.NoError(t, err) }() - pty.ExpectMatch("Planning workspace") + stdout.ExpectMatchContext(ctx, "Planning workspace") <-doneChan // Verify if ephemeral parameter is set @@ -423,6 +430,7 @@ func TestUpdateValidateRichParameters(t *testing.T) { t.Run("ValidateString", func(t *testing.T) { t.Parallel() + logger := testutil.Logger(t) client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) owner := coderdtest.CreateFirstUser(t, client) member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) @@ -446,28 +454,30 @@ func TestUpdateValidateRichParameters(t *testing.T) { inv = inv.WithContext(ctx) 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) }() - pty.ExpectMatch(stringParameterName) - pty.ExpectMatch("> Enter a value: ") - pty.WriteLine("$$") - pty.ExpectMatch("does not match") - pty.ExpectMatch("> Enter a value: ") - pty.WriteLine("ABC") - pty.ExpectMatch("does not match") - pty.ExpectMatch("> Enter a value: ") - pty.WriteLine("abc") + stdout.ExpectMatchContext(ctx, stringParameterName) + stdout.ExpectMatchContext(ctx, "> Enter a value: ") + stdin.WriteLine("$$") + stdout.ExpectMatchContext(ctx, "does not match") + stdout.ExpectMatchContext(ctx, "> Enter a value: ") + stdin.WriteLine("ABC") + stdout.ExpectMatchContext(ctx, "does not match") + stdout.ExpectMatchContext(ctx, "> Enter a value: ") + stdin.WriteLine("abc") _ = testutil.TryReceive(ctx, t, doneChan) }) t.Run("ValidateNumber", func(t *testing.T) { t.Parallel() + logger := testutil.Logger(t) client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) owner := coderdtest.CreateFirstUser(t, client) member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) @@ -492,28 +502,30 @@ func TestUpdateValidateRichParameters(t *testing.T) { inv.WithContext(ctx) 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) }() - pty.ExpectMatch(numberParameterName) - pty.ExpectMatch("> Enter a value: ") - pty.WriteLine("12") - pty.ExpectMatch("is more than the maximum") - pty.ExpectMatch("> Enter a value: ") - pty.WriteLine("notanumber") - pty.ExpectMatch("is not a number") - pty.ExpectMatch("> Enter a value: ") - pty.WriteLine("8") + stdout.ExpectMatchContext(ctx, numberParameterName) + stdout.ExpectMatchContext(ctx, "> Enter a value: ") + stdin.WriteLine("12") + stdout.ExpectMatchContext(ctx, "is more than the maximum") + stdout.ExpectMatchContext(ctx, "> Enter a value: ") + stdin.WriteLine("notanumber") + stdout.ExpectMatchContext(ctx, "is not a number") + stdout.ExpectMatchContext(ctx, "> Enter a value: ") + stdin.WriteLine("8") _ = testutil.TryReceive(ctx, t, doneChan) }) t.Run("ValidateBool", func(t *testing.T) { t.Parallel() + logger := testutil.Logger(t) client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) owner := coderdtest.CreateFirstUser(t, client) member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) @@ -538,28 +550,30 @@ func TestUpdateValidateRichParameters(t *testing.T) { inv = inv.WithContext(ctx) 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) }() - pty.ExpectMatch(boolParameterName) - pty.ExpectMatch("> Enter a value: ") - pty.WriteLine("cat") - pty.ExpectMatch("boolean value can be either \"true\" or \"false\"") - pty.ExpectMatch("> Enter a value: ") - pty.WriteLine("dog") - pty.ExpectMatch("boolean value can be either \"true\" or \"false\"") - pty.ExpectMatch("> Enter a value: ") - pty.WriteLine("false") + stdout.ExpectMatchContext(ctx, boolParameterName) + stdout.ExpectMatchContext(ctx, "> Enter a value: ") + stdin.WriteLine("cat") + stdout.ExpectMatchContext(ctx, "boolean value can be either \"true\" or \"false\"") + stdout.ExpectMatchContext(ctx, "> Enter a value: ") + stdin.WriteLine("dog") + stdout.ExpectMatchContext(ctx, "boolean value can be either \"true\" or \"false\"") + stdout.ExpectMatchContext(ctx, "> Enter a value: ") + stdin.WriteLine("false") _ = testutil.TryReceive(ctx, t, doneChan) }) t.Run("RequiredParameterAdded", func(t *testing.T) { t.Parallel() + logger := testutil.Logger(t) client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) owner := coderdtest.CreateFirstUser(t, client) member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) @@ -605,7 +619,8 @@ func TestUpdateValidateRichParameters(t *testing.T) { inv.WithContext(ctx) 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() @@ -619,10 +634,10 @@ func TestUpdateValidateRichParameters(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) } } _ = testutil.TryReceive(ctx, t, doneChan) @@ -677,160 +692,122 @@ func TestUpdateValidateRichParameters(t *testing.T) { inv.WithContext(ctx) 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) }() - pty.ExpectMatch("Planning workspace...") + stdout.ExpectMatchContext(ctx, "Planning workspace...") _ = testutil.TryReceive(ctx, t, doneChan) }) - t.Run("ParameterOptionChanged", func(t *testing.T) { + t.Run("ParameterOption", func(t *testing.T) { t.Parallel() - // Create template and workspace - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - user := coderdtest.CreateFirstUser(t, client) - member, _ := coderdtest.CreateAnotherUser(t, client, user.OrganizationID) - - templateParameters := []*proto.RichParameter{ - {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ - {Name: "First option", Description: "This is first option", Value: "1st"}, - {Name: "Second option", Description: "This is second option", Value: "2nd"}, - {Name: "Third option", Description: "This is third option", Value: "3rd"}, - }}, + testCases := []struct { + name string + originalParameters []*proto.RichParameter + updatedParameters []*proto.RichParameter + }{ + { + name: "Changed", + originalParameters: []*proto.RichParameter{ + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "First option", Description: "This is first option", Value: "1st"}, + {Name: "Second option", Description: "This is second option", Value: "2nd"}, + {Name: "Third option", Description: "This is third option", Value: "3rd"}, + }}, + }, + updatedParameters: []*proto.RichParameter{ + // The order of rich parameter options must be maintained because `cliui.Select` automatically selects the first option during tests. + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "first_option", Description: "This is first option", Value: "1"}, + {Name: "second_option", Description: "This is second option", Value: "2"}, + {Name: "third_option", Description: "This is third option", Value: "3"}, + }}, + }, + }, + { + name: "Disappeared", + originalParameters: []*proto.RichParameter{ + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "First option", Description: "This is first option", Value: "1st"}, + {Name: "Second option", Description: "This is second option", Value: "2nd"}, + {Name: "Third option", Description: "This is third option", Value: "3rd"}, + }}, + }, + // Update template - 2nd option disappeared, 4th option added + updatedParameters: []*proto.RichParameter{ + // The order of rich parameter options must be maintained because `cliui.Select` automatically selects the first option during tests. + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "Third option", Description: "This is third option", Value: "3rd"}, + {Name: "First option", Description: "This is first option", Value: "1st"}, + {Name: "Fourth option", Description: "This is fourth option", Value: "4th"}, + }}, + }, + }, } - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(templateParameters)) - coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + logger := testutil.Logger(t) - // Create new workspace - inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd")) - clitest.SetupConfig(t, member, root) - err := inv.Run() - require.NoError(t, err) + // Create template and workspace + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + user := coderdtest.CreateFirstUser(t, client) + member, _ := coderdtest.CreateAnotherUser(t, client, user.OrganizationID) - // Update template - updatedTemplateParameters := []*proto.RichParameter{ - // The order of rich parameter options must be maintained because `cliui.Select` automatically selects the first option during tests. - {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ - {Name: "first_option", Description: "This is first option", Value: "1"}, - {Name: "second_option", Description: "This is second option", Value: "2"}, - {Name: "third_option", Description: "This is third option", Value: "3"}, - }}, + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(tc.originalParameters)) + coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + + // Create new workspace + inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd")) + clitest.SetupConfig(t, member, root) + err := inv.Run() + require.NoError(t, err) + + // Update template + updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(tc.updatedParameters), template.ID) + coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID) + err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ + ID: updatedVersion.ID, + }) + require.NoError(t, err) + + // Update the workspace + ctx := testutil.Context(t, testutil.WaitLong) + inv, root = clitest.New(t, "update", "my-workspace") + inv.WithContext(ctx) + clitest.SetupConfig(t, member, root) + doneChan := make(chan struct{}) + 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) + }() + + matches := []string{ + // `cliui.Select` will automatically pick the first option + "Planning workspace...", "", + } + for i := 0; i < len(matches); i += 2 { + match := matches[i] + value := matches[i+1] + stdout.ExpectMatchContext(ctx, match) + + if value != "" { + stdin.WriteLine(value) + } + } + + _ = testutil.TryReceive(ctx, t, doneChan) + }) } - - updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID) - coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID) - err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ - ID: updatedVersion.ID, - }) - require.NoError(t, err) - - // Update the workspace - ctx := testutil.Context(t, testutil.WaitLong) - inv, root = clitest.New(t, "update", "my-workspace") - inv.WithContext(ctx) - clitest.SetupConfig(t, member, root) - doneChan := make(chan struct{}) - pty := ptytest.New(t).Attach(inv) - go func() { - defer close(doneChan) - err := inv.Run() - assert.NoError(t, err) - }() - - matches := []string{ - // `cliui.Select` will automatically pick the first option - "Planning workspace...", "", - } - for i := 0; i < len(matches); i += 2 { - match := matches[i] - value := matches[i+1] - pty.ExpectMatch(match) - - if value != "" { - pty.WriteLine(value) - } - } - - _ = testutil.TryReceive(ctx, t, doneChan) - }) - - t.Run("ParameterOptionDisappeared", func(t *testing.T) { - t.Parallel() - - // Create template and workspace - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - owner := coderdtest.CreateFirstUser(t, client) - member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) - - templateParameters := []*proto.RichParameter{ - {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ - {Name: "First option", Description: "This is first option", Value: "1st"}, - {Name: "Second option", Description: "This is second option", Value: "2nd"}, - {Name: "Third option", Description: "This is third option", Value: "3rd"}, - }}, - } - version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, prepareEchoResponses(templateParameters)) - coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) - template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID) - - // Create new workspace - inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd")) - clitest.SetupConfig(t, member, root) - ptytest.New(t).Attach(inv) - err := inv.Run() - require.NoError(t, err) - - // Update template - 2nd option disappeared, 4th option added - updatedTemplateParameters := []*proto.RichParameter{ - // The order of rich parameter options must be maintained because `cliui.Select` automatically selects the first option during tests. - {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ - {Name: "Third option", Description: "This is third option", Value: "3rd"}, - {Name: "First option", Description: "This is first option", Value: "1st"}, - {Name: "Fourth option", Description: "This is fourth option", Value: "4th"}, - }}, - } - - updatedVersion := coderdtest.UpdateTemplateVersion(t, client, owner.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID) - coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID) - err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ - ID: updatedVersion.ID, - }) - require.NoError(t, err) - - // Update the workspace - ctx := testutil.Context(t, testutil.WaitLong) - inv, root = clitest.New(t, "update", "my-workspace") - inv.WithContext(ctx) - clitest.SetupConfig(t, member, root) - doneChan := make(chan struct{}) - pty := ptytest.New(t).Attach(inv) - go func() { - defer close(doneChan) - err := inv.Run() - assert.NoError(t, err) - }() - - matches := []string{ - // `cliui.Select` will automatically pick the first option - "Planning workspace...", "", - } - for i := 0; i < len(matches); i += 2 { - match := matches[i] - value := matches[i+1] - pty.ExpectMatch(match) - - if value != "" { - pty.WriteLine(value) - } - } - - _ = testutil.TryReceive(ctx, t, doneChan) }) t.Run("ParameterOptionFailsMonotonicValidation", func(t *testing.T) { @@ -859,7 +836,6 @@ func TestUpdateValidateRichParameters(t *testing.T) { // Create new workspace inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", numberParameterName, tempVal)) clitest.SetupConfig(t, member, root) - ptytest.New(t).Attach(inv) err := inv.Run() require.NoError(t, err) @@ -870,7 +846,7 @@ func TestUpdateValidateRichParameters(t *testing.T) { 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() @@ -886,7 +862,7 @@ func TestUpdateValidateRichParameters(t *testing.T) { } for i := 0; i < len(matches); i += 2 { match := matches[i] - pty.ExpectMatch(match) + stdout.ExpectMatchContext(ctx, match) } _ = testutil.TryReceive(ctx, t, doneChan) @@ -895,6 +871,7 @@ func TestUpdateValidateRichParameters(t *testing.T) { t.Run("ImmutableRequiredParameterExists_MutableRequiredParameterAdded", func(t *testing.T) { t.Parallel() + logger := testutil.Logger(t) // Create template and workspace client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) owner := coderdtest.CreateFirstUser(t, client) @@ -936,7 +913,8 @@ func TestUpdateValidateRichParameters(t *testing.T) { inv.WithContext(ctx) 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() @@ -950,10 +928,10 @@ func TestUpdateValidateRichParameters(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) } } @@ -963,6 +941,7 @@ func TestUpdateValidateRichParameters(t *testing.T) { t.Run("MutableRequiredParameterExists_ImmutableRequiredParameterAdded", func(t *testing.T) { t.Parallel() + logger := testutil.Logger(t) // Create template and workspace client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) owner := coderdtest.CreateFirstUser(t, client) @@ -1008,7 +987,8 @@ func TestUpdateValidateRichParameters(t *testing.T) { inv.WithContext(ctx) 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() @@ -1022,10 +1002,10 @@ func TestUpdateValidateRichParameters(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) } } @@ -1078,7 +1058,8 @@ func TestUpdateValidateRichParameters(t *testing.T) { "--parameter", fmt.Sprintf("%s=%s", immutableParameterName, "II")) clitest.SetupConfig(t, member, root) - pty := ptytest.New(t).Attach(inv) + stdout := expecter.NewAttachedToInvocation(t, inv) + ctx := testutil.Context(t, testutil.WaitLong) doneChan := make(chan struct{}) go func() { defer close(doneChan) @@ -1086,9 +1067,8 @@ func TestUpdateValidateRichParameters(t *testing.T) { assert.NoError(t, err) }() - pty.ExpectMatch("Planning workspace") + stdout.ExpectMatchContext(ctx, "Planning workspace") - ctx := testutil.Context(t, testutil.WaitLong) _ = testutil.TryReceive(ctx, t, doneChan) // Verify the immutable parameter was set correctly. diff --git a/cli/user_delete_test.go b/cli/user_delete_test.go index e07d1e850e..a835268021 100644 --- a/cli/user_delete_test.go +++ b/cli/user_delete_test.go @@ -1,7 +1,6 @@ package cli_test import ( - "context" "testing" "github.com/google/uuid" @@ -12,14 +11,15 @@ import ( "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/cryptorand" - "github.com/coder/coder/v2/pty/ptytest" + "github.com/coder/coder/v2/testutil" + "github.com/coder/coder/v2/testutil/expecter" ) func TestUserDelete(t *testing.T) { t.Parallel() t.Run("Username", func(t *testing.T) { t.Parallel() - ctx := context.Background() + ctx := testutil.Context(t, testutil.WaitMedium) client := coderdtest.New(t, nil) owner := coderdtest.CreateFirstUser(t, client) userAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleUserAdmin()) @@ -38,18 +38,18 @@ func TestUserDelete(t *testing.T) { inv, root := clitest.New(t, "users", "delete", "coolin") clitest.SetupConfig(t, userAdmin, 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("coolin") + stdout.ExpectMatchContext(ctx, "coolin") }) t.Run("UserID", func(t *testing.T) { t.Parallel() - ctx := context.Background() + ctx := testutil.Context(t, testutil.WaitMedium) client := coderdtest.New(t, nil) owner := coderdtest.CreateFirstUser(t, client) userAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleUserAdmin()) @@ -68,18 +68,18 @@ func TestUserDelete(t *testing.T) { inv, root := clitest.New(t, "users", "delete", user.ID.String()) clitest.SetupConfig(t, userAdmin, 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("coolin") + stdout.ExpectMatchContext(ctx, "coolin") }) t.Run("UserID", func(t *testing.T) { t.Parallel() - ctx := context.Background() + ctx := testutil.Context(t, testutil.WaitMedium) client := coderdtest.New(t, nil) owner := coderdtest.CreateFirstUser(t, client) userAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleUserAdmin()) @@ -98,13 +98,13 @@ func TestUserDelete(t *testing.T) { inv, root := clitest.New(t, "users", "delete", user.ID.String()) clitest.SetupConfig(t, userAdmin, 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("coolin") + stdout.ExpectMatchContext(ctx, "coolin") }) // TODO: reenable this test case. Fetching users without perms returns a diff --git a/cli/usercreate_test.go b/cli/usercreate_test.go index 2c8d69fe14..d06a76f8a9 100644 --- a/cli/usercreate_test.go +++ b/cli/usercreate_test.go @@ -9,21 +9,23 @@ import ( "github.com/coder/coder/v2/cli/clitest" "github.com/coder/coder/v2/coderd/coderdtest" "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 TestUserCreate(t *testing.T) { t.Parallel() t.Run("Prompts", func(t *testing.T) { t.Parallel() + logger := testutil.Logger(t) ctx := testutil.Context(t, testutil.WaitLong) client := coderdtest.New(t, nil) coderdtest.CreateFirstUser(t, client) inv, root := clitest.New(t, "users", "create") clitest.SetupConfig(t, client, 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() @@ -37,8 +39,8 @@ func TestUserCreate(t *testing.T) { for i := 0; i < len(matches); i += 2 { match := matches[i] value := matches[i+1] - pty.ExpectMatch(match) - pty.WriteLine(value) + stdout.ExpectMatchContext(ctx, match) + stdin.WriteLine(value) } _ = testutil.TryReceive(ctx, t, doneChan) created, err := client.User(ctx, matches[1]) @@ -50,13 +52,15 @@ func TestUserCreate(t *testing.T) { t.Run("PromptsNoName", func(t *testing.T) { t.Parallel() + logger := testutil.Logger(t) ctx := testutil.Context(t, testutil.WaitLong) client := coderdtest.New(t, nil) coderdtest.CreateFirstUser(t, client) inv, root := clitest.New(t, "users", "create") clitest.SetupConfig(t, client, 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() @@ -70,8 +74,8 @@ func TestUserCreate(t *testing.T) { for i := 0; i < len(matches); i += 2 { match := matches[i] value := matches[i+1] - pty.ExpectMatch(match) - pty.WriteLine(value) + stdout.ExpectMatchContext(ctx, match) + stdin.WriteLine(value) } _ = testutil.TryReceive(ctx, t, doneChan) created, err := client.User(ctx, matches[1]) diff --git a/cli/userlist_test.go b/cli/userlist_test.go index 2681f0d2a4..d595a580fa 100644 --- a/cli/userlist_test.go +++ b/cli/userlist_test.go @@ -15,25 +15,27 @@ import ( "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/rbac" "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 TestUserList(t *testing.T) { t.Parallel() t.Run("Table", func(t *testing.T) { t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) client := coderdtest.New(t, nil) owner := coderdtest.CreateFirstUser(t, client) userAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleUserAdmin()) inv, root := clitest.New(t, "users", "list") clitest.SetupConfig(t, userAdmin, 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("coder.com") + stdout.ExpectMatchContext(ctx, "coder.com") }) t.Run("JSON", func(t *testing.T) { t.Parallel() @@ -98,6 +100,7 @@ func TestUserShow(t *testing.T) { t.Run("Table", func(t *testing.T) { t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) client := coderdtest.New(t, nil) owner := coderdtest.CreateFirstUser(t, client) userAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleUserAdmin()) @@ -105,13 +108,13 @@ func TestUserShow(t *testing.T) { inv, root := clitest.New(t, "users", "show", otherUser.Username) clitest.SetupConfig(t, userAdmin, 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) }() - pty.ExpectMatch(otherUser.Email) + stdout.ExpectMatchContext(ctx, otherUser.Email) <-doneChan }) diff --git a/cli/vscodessh_test.go b/cli/vscodessh_test.go index 70037664c4..32afb52ca1 100644 --- a/cli/vscodessh_test.go +++ b/cli/vscodessh_test.go @@ -17,7 +17,6 @@ import ( "github.com/coder/coder/v2/coderd/database/dbfake" "github.com/coder/coder/v2/coderd/workspacestats/workspacestatstest" "github.com/coder/coder/v2/codersdk" - "github.com/coder/coder/v2/pty/ptytest" "github.com/coder/coder/v2/testutil" ) @@ -69,7 +68,6 @@ func TestVSCodeSSH(t *testing.T) { "--network-info-interval", "25ms", fmt.Sprintf("coder-vscode--%s--%s", user.Username, workspace.Name), ) - ptytest.New(t).Attach(inv) waiter := clitest.StartWithWaiter(t, inv.WithContext(ctx))