test(agent): remove race in TestAgent_Session_TTY_QuietLogin/Hushlogin (#25865)

The subtest previously called session.Shell(), wrote "exit 0" through a
client-side PTY, and then waited indefinitely on session.Wait(). Under
the race detector the byte stream occasionally arrived at the agent
before the remote shell was in its read loop and was silently discarded;
the shell never exited, session.Wait() blocked until the go-test
watchdog kicked in and killed the test binary.

The agent writes the message of the day announcement banner
synchronously in agentssh.startPTYSession before forking the user shell.
The subtest now repeatedly sends "exit 0" and the writes/waiting on
session.Wait are time bound.

Also fixes a pre-existing test bug where the empty bytes intended to
create ~/.hushlogin were written to the MOTD path. The previous test
passed only because the MOTD file ended up empty, not because the
hushlogin code path was exercised. With the file now placed at the
correct path, the assertion genuinely validates isQuietLogin.

Generated with assistance from Coder Agents.
This commit is contained in:
Zach
2026-06-04 13:52:51 -06:00
committed by GitHub
parent b95697a370
commit 45475b803e
+30 -17
View File
@@ -983,22 +983,23 @@ func TestAgent_Session_TTY_QuietLogin(t *testing.T) {
}
wantNotMOTD := "Welcome to your Coder workspace!"
wantMaybeServiceBanner := "Service banner text goes here"
wantServiceBanner := "Service banner text goes here"
u, err := user.Current()
require.NoError(t, err, "get current user")
name := filepath.Join(u.HomeDir, "motd")
motdPath := filepath.Join(u.HomeDir, "motd")
hushloginPath := filepath.Join(u.HomeDir, ".hushlogin")
// Neither banner nor MOTD should show if not a login shell.
t.Run("NotLogin", func(t *testing.T) {
session := setupSSHSession(t, agentsdk.Manifest{
MOTDFile: name,
MOTDFile: motdPath,
}, codersdk.ServiceBannerConfig{
Enabled: true,
Message: wantMaybeServiceBanner,
Message: wantServiceBanner,
}, func(fs afero.Fs) {
err := afero.WriteFile(fs, name, []byte(wantNotMOTD), 0o600)
err := afero.WriteFile(fs, motdPath, []byte(wantNotMOTD), 0o600)
require.NoError(t, err, "write motd file")
})
err = session.RequestPty("xterm", 128, 128, ssh.TerminalModes{})
@@ -1011,41 +1012,53 @@ func TestAgent_Session_TTY_QuietLogin(t *testing.T) {
require.Contains(t, string(output), wantEcho, "should show echo")
require.NotContains(t, string(output), wantNotMOTD, "should not show motd")
require.NotContains(t, string(output), wantMaybeServiceBanner, "should not show service banner")
require.NotContains(t, string(output), wantServiceBanner, "should not show service banner")
})
// Only the MOTD should be silenced when hushlogin is present.
t.Run("Hushlogin", func(t *testing.T) {
session := setupSSHSession(t, agentsdk.Manifest{
MOTDFile: name,
MOTDFile: motdPath,
}, codersdk.ServiceBannerConfig{
Enabled: true,
Message: wantMaybeServiceBanner,
Message: wantServiceBanner,
}, func(fs afero.Fs) {
err := afero.WriteFile(fs, name, []byte(wantNotMOTD), 0o600)
err := afero.WriteFile(fs, motdPath, []byte(wantNotMOTD), 0o600)
require.NoError(t, err, "write motd file")
// Create hushlogin to silence motd.
err = afero.WriteFile(fs, name, []byte{}, 0o600)
// Place an empty .hushlogin in the user's home so the agent's
// isQuietLogin lookup succeeds and showMOTD is skipped.
err = afero.WriteFile(fs, hushloginPath, []byte{}, 0o600)
require.NoError(t, err, "write hushlogin file")
})
err = session.RequestPty("xterm", 128, 128, ssh.TerminalModes{})
require.NoError(t, err)
stdout := testutil.NewWaitBuffer()
ptty := ptytest.New(t)
var stdout bytes.Buffer
session.Stdout = &stdout
session.Stdout = stdout
session.Stderr = ptty.Output()
session.Stdin = ptty.Input()
err = session.Shell()
stdin, err := session.StdinPipe()
require.NoError(t, err)
require.NoError(t, session.Shell())
ctx := testutil.Context(t, testutil.WaitShort)
context.AfterFunc(ctx, func() { _ = session.Close() })
testutil.Go(t, func() {
for {
if _, err := stdin.Write([]byte("exit 0\n")); err != nil {
return
}
time.Sleep(testutil.IntervalFast)
}
})
ptty.WriteLine("exit 0")
err = session.Wait()
require.NoError(t, err)
require.Contains(t, stdout.String(), wantServiceBanner, "should show service banner")
require.NotContains(t, stdout.String(), wantNotMOTD, "should not show motd")
require.Contains(t, stdout.String(), wantMaybeServiceBanner, "should show service banner")
})
}