package stario import ( "bufio" "context" "errors" "io" "strings" "testing" "time" ) func installTerminalCheckStub(t *testing.T, ok bool) { t.Helper() prev := terminalCheckFunc terminalCheckFunc = func(fd int) bool { return ok } t.Cleanup(func() { terminalCheckFunc = prev }) } func TestIsTerminalFDUsesStub(t *testing.T) { installTerminalCheckStub(t, true) if !IsTerminalFD(1) { t.Fatal("expected terminal check to return true") } } func TestReadPasswordContextRejectsNonTTY(t *testing.T) { installTerminalCheckStub(t, false) _, err := ReadPasswordContext(context.Background(), "pwd: ", "") if !errors.Is(err, ErrTerminalNotTTY) { t.Fatalf("expected ErrTerminalNotTTY, got %v", err) } } func TestReadPasswordContextReadsValue(t *testing.T) { installTerminalCheckStub(t, true) installRawInputStub(t, "secret\n", nil) got, err := ReadPasswordContext(context.Background(), "pwd: ", "") if err != nil { t.Fatalf("ReadPasswordContext returned error: %v", err) } if got != "secret" { t.Fatalf("unexpected password: %q", got) } } func TestReadPasswordContextCanceledWhileWaiting(t *testing.T) { installTerminalCheckStub(t, true) prevFactory := rawTerminalSessionFactory pipeReader, pipeWriter := io.Pipe() rawTerminalSessionFactory = func(hint string, printNewline bool) (*rawTerminalSession, error) { return &rawTerminalSession{ input: pipeReader, reader: bufio.NewReader(pipeReader), redrawHint: strings.TrimSpace(hint), }, nil } t.Cleanup(func() { rawTerminalSessionFactory = prevFactory _ = pipeWriter.Close() _ = pipeReader.Close() }) ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond) defer cancel() _, err := ReadPasswordContext(ctx, "pwd: ", "") if !errors.Is(err, context.DeadlineExceeded) { t.Fatalf("expected deadline exceeded, got %v", err) } }