cli/ping_test.go
Rama main · réplica de lectura del repositorio original.
package cli_test
import (
"bytes"
"context"
"encoding/json"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/agent/agenttest"
"github.com/coder/coder/v2/cli"
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd/coderdtest"
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/testutil"
"github.com/coder/coder/v2/testutil/expecter"
)
func TestPing_AgentSuggestions(t *testing.T) {
t.Parallel()
client, workspace, _ := setupWorkspaceForAgent(t, func(agents []*sdkproto.Agent) []*sdkproto.Agent {
agents[0].Name = "coder"
return append(agents, &sdkproto.Agent{
Id: uuid.NewString(),
Name: "dev",
Auth: &sdkproto.Agent_Token{Token: uuid.NewString()},
})
})
qualifiedName := "myuser/" + workspace.Name
for _, tc := range []struct {
name string
input string
want string
json bool
}{
{name: "Name", input: workspace.Name, want: workspace.Name},
{name: "OwnerAndName", input: qualifiedName, want: qualifiedName},
{name: "UUID", input: workspace.ID.String(), want: qualifiedName},
{name: "JSON", input: workspace.Name, want: workspace.Name, json: true},
} {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
args := []string{"ping", tc.input}
if tc.json {
args = append(args, "--num", "1", "--output", "json")
}
inv, root := clitest.New(t, args...)
clitest.SetupConfig(t, client, root)
var stdout bytes.Buffer
inv.Stdout = &stdout
err := inv.WithContext(testutil.Context(t, testutil.WaitLong)).Run()
require.ErrorContains(t, err, "multiple agents found")
require.ErrorContains(t, err, "Try running:")
assert.Contains(t, err.Error(), "$ coder ping "+tc.want+".coder")
assert.Contains(t, err.Error(), "$ coder ping "+tc.want+".dev")
assert.NotContains(t, err.Error(), "$ ssh ")
assert.Empty(t, stdout.String())
})
}
}
func TestPing(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
t.Parallel()
client, workspace, agentToken := setupWorkspaceForAgent(t)
inv, root := clitest.New(t, "ping", workspace.Name)
clitest.SetupConfig(t, client, root)
stdout := expecter.NewAttachedToInvocation(t, inv)
_ = agenttest.New(t, client.URL, agentToken)
_ = coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
cmdDone := tGo(t, func() {
err := inv.WithContext(ctx).Run()
assert.NoError(t, err)
})
stdout.ExpectMatch(ctx, "pong from "+workspace.Name)
cancel()
<-cmdDone
})
t.Run("1Ping", func(t *testing.T) {
t.Parallel()
client, workspace, agentToken := setupWorkspaceForAgent(t)
inv, root := clitest.New(t, "ping", "-n", "1", workspace.Name)
clitest.SetupConfig(t, client, root)
stdout := expecter.NewAttachedToInvocation(t, inv)
_ = agenttest.New(t, client.URL, agentToken)
_ = coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
cmdDone := tGo(t, func() {
err := inv.WithContext(ctx).Run()
assert.NoError(t, err)
})
stdout.ExpectMatch(ctx, "pong from "+workspace.Name)
// The default table output prints the summary after the last ping.
stdout.ExpectRegexMatch(ctx, `(?i)successful`)
cancel()
<-cmdDone
})
t.Run("1PingWithTime", func(t *testing.T) {
t.Parallel()
tests := []struct {
name string
utc bool
}{
{name: "LocalTime"}, // --time renders the pong response time.
{name: "UTC", utc: true}, // --utc implies --time, so we expect it to also contain the pong time.
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
client, workspace, agentToken := setupWorkspaceForAgent(t)
args := []string{"ping", "-n", "1", workspace.Name, "--time"}
if tc.utc {
args = append(args, "--utc")
}
inv, root := clitest.New(t, args...)
clitest.SetupConfig(t, client, root)
stdout := expecter.NewAttachedToInvocation(t, inv)
_ = agenttest.New(t, client.URL, agentToken)
_ = coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
cmdDone := tGo(t, func() {
err := inv.WithContext(ctx).Run()
assert.NoError(t, err)
})
// RFC3339 is the format used to render the pong times.
rfc3339 := `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?`
// Validate that dates are rendered as specified.
if tc.utc {
rfc3339 += `Z`
} else {
rfc3339 += `(?:Z|[+-]\d{2}:\d{2})`
}
stdout.ExpectRegexMatch(ctx, `\[`+rfc3339+`\] pong from `+workspace.Name)
cancel()
<-cmdDone
})
}
})
t.Run("JSON", func(t *testing.T) {
t.Parallel()
client, workspace, agentToken := setupWorkspaceForAgent(t)
inv, root := clitest.New(t, "ping", "-n", "1", "-o", "json", workspace.Name)
clitest.SetupConfig(t, client, root)
out := bytes.NewBuffer(nil)
inv.Stdout = out
_ = agenttest.New(t, client.URL, agentToken)
_ = coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
err := inv.WithContext(ctx).Run()
require.NoError(t, err)
// stdout must contain nothing but the JSON document; diagnostic
// and per-attempt lines are redirected to stderr in JSON mode.
var result cli.PingJSONOutput
require.NoError(t, json.Unmarshal(out.Bytes(), &result))
assert.Equal(t, workspace.Name, result.Workspace)
require.Len(t, result.Pongs, 1)
assert.Equal(t, 1, result.Pongs[0].Sequence)
assert.Empty(t, result.Pongs[0].Error)
require.NotNil(t, result.Pongs[0].LatencyMS)
require.NotNil(t, result.Pongs[0].Direct)
assert.Equal(t, 1, result.Summary.Total)
assert.Equal(t, 1, result.Summary.Successful)
require.NotNil(t, result.Summary.MinMS)
require.NotNil(t, result.Summary.AvgMS)
require.NotNil(t, result.Summary.MaxMS)
})
t.Run("JSONRequiresNum", func(t *testing.T) {
t.Parallel()
for _, args := range [][]string{
{"ping", "-o", "json", "my-workspace"},
{"ping", "-n", "0", "-o", "json", "my-workspace"},
} {
inv, _ := clitest.New(t, args...)
out := bytes.NewBuffer(nil)
inv.Stdout = out
err := inv.Run()
require.ErrorContains(t, err, "--output json requires --num")
assert.Empty(t, out.String())
}
})
}