Poll TTY echo mode instead of sleeping in password tests

Replace the fixed-duration sleep with a polling loop that checks the
actual TTY echo flag before sending password input. This eliminates the
race condition where huh has not yet disabled echo mode, which caused
flaky test failures in slow environments.

Follow-up to #13304.
This commit is contained in:
Pavel Dostál 2026-04-28 19:50:35 +02:00 committed by William Martin
parent 9b457e8aa2
commit c48bc1a7d1
3 changed files with 35 additions and 8 deletions

View file

@ -5,6 +5,7 @@ package prompter_test
import (
"fmt"
"io"
"os"
"slices"
"strings"
"testing"
@ -17,6 +18,7 @@ import (
"github.com/hinshun/vt10x"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
)
// The following tests are broadly testing the accessible prompter, and NOT asserting
@ -34,8 +36,6 @@ import (
// but doesn't mandate that prompts always look exactly the same.
func TestAccessiblePrompter(t *testing.T) {
beforePasswordSendTimeout := 100 * time.Millisecond
t.Run("Select", func(t *testing.T) {
console := newTestVirtualTerminal(t)
p := newTestAccessiblePrompter(t, console)
@ -505,8 +505,8 @@ func TestAccessiblePrompter(t *testing.T) {
_, err := console.ExpectString("Enter password")
require.NoError(t, err)
// Wait to ensure huh has time to set the echo mode
time.Sleep(beforePasswordSendTimeout)
// Wait until huh has disabled echo mode on the TTY
waitForEchoDisabled(t, console.Tty(), 5*time.Second)
// Enter a number
_, err = console.SendLine(dummyPassword)
@ -596,8 +596,8 @@ func TestAccessiblePrompter(t *testing.T) {
_, err := console.ExpectString("Paste your authentication token:")
require.NoError(t, err)
// Wait to ensure huh has time to set the echo mode
time.Sleep(beforePasswordSendTimeout)
// Wait until huh has disabled echo mode on the TTY
waitForEchoDisabled(t, console.Tty(), 5*time.Second)
// Enter some dummy auth token
_, err = console.SendLine(dummyAuthToken)
@ -641,8 +641,8 @@ func TestAccessiblePrompter(t *testing.T) {
_, err = console.ExpectString("Paste your authentication token:")
require.NoError(t, err)
// Wait to ensure huh has time to set the echo mode
time.Sleep(beforePasswordSendTimeout)
// Wait until huh has disabled echo mode on the TTY
waitForEchoDisabled(t, console.Tty(), 5*time.Second)
// Now enter some dummy auth token to return control back to the test
_, err = console.SendLine(dummyAuthTokenForAfterFailure)
@ -956,3 +956,20 @@ func testCloser(t *testing.T, closer io.Closer) {
t.Errorf("Close failed: %s", err)
}
}
// waitForEchoDisabled polls the TTY until echo mode is disabled or the
// timeout is reached. This is used in password and auth token tests to
// ensure that huh has configured the terminal before we send input.
func waitForEchoDisabled(t *testing.T, tty *os.File, timeout time.Duration) {
t.Helper()
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
termios, err := unix.IoctlGetTermios(int(tty.Fd()), ioctlGetTermios)
require.NoError(t, err)
if termios.Lflag&unix.ECHO == 0 {
return
}
time.Sleep(time.Millisecond)
}
t.Fatal("timed out waiting for echo mode to be disabled")
}

View file

@ -0,0 +1,5 @@
package prompter_test
import "golang.org/x/sys/unix"
const ioctlGetTermios = unix.TIOCGETA

View file

@ -0,0 +1,5 @@
package prompter_test
import "golang.org/x/sys/unix"
const ioctlGetTermios = unix.TCGETS