Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 19 additions & 47 deletions pkg/tty/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,65 +8,37 @@ import (

"github.com/github/gh-aw/pkg/tty"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestSpec_PublicAPI_IsStdinTerminal validates the documented behavior of IsStdinTerminal
// as described by the tty package README.md specification.
// Spec: "Returns true if stdin (os.Stdin) is connected to a terminal."
func TestSpec_PublicAPI_IsStdinTerminal(t *testing.T) {
result1 := tty.IsStdinTerminal()
result2 := tty.IsStdinTerminal()
// Spec (Design Notes): "Terminal detection is evaluated at call time, not cached."
// In the same unmodified context, consecutive calls must return the same result.
assert.Equal(t, result1, result2, "consecutive calls in the same context must return the same result")
testNonTerminalStream(t, &os.Stdin, tty.IsStdinTerminal)
}

func TestIsStdinTerminalReturnsFalseForPipe(t *testing.T) {
oldStdin := os.Stdin
r, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.Stdin = oldStdin })
t.Cleanup(func() { r.Close() })
t.Cleanup(func() { w.Close() })
os.Stdin = r

assert.False(t, tty.IsStdinTerminal())
}

// TestSpec_PublicAPI_IsStdoutTerminal validates the documented behavior of IsStdoutTerminal
// as described in the tty package README.md specification.
// Spec: "Returns true if stdout (os.Stdout) is connected to a terminal."
// TestSpec_PublicAPI_IsStdoutTerminal validates the documented behavior of IsStdoutTerminal.
func TestSpec_PublicAPI_IsStdoutTerminal(t *testing.T) {
result1 := tty.IsStdoutTerminal()
result2 := tty.IsStdoutTerminal()
// Spec (Design Notes): "Terminal detection is evaluated at call time, not cached."
// In the same unmodified context, consecutive calls must return the same result.
assert.Equal(t, result1, result2, "consecutive calls in the same context must return the same result")
testNonTerminalStream(t, &os.Stdout, tty.IsStdoutTerminal)
}

// TestSpec_PublicAPI_IsStderrTerminal validates the documented behavior of IsStderrTerminal
// as described in the tty package README.md specification.
// Spec: "Returns true if stderr (os.Stderr) is connected to a terminal."
// TestSpec_PublicAPI_IsStderrTerminal validates the documented behavior of IsStderrTerminal.
func TestSpec_PublicAPI_IsStderrTerminal(t *testing.T) {
result1 := tty.IsStderrTerminal()
result2 := tty.IsStderrTerminal()
// Spec (Design Notes): "Terminal detection is evaluated at call time, not cached."
// In the same unmodified context, consecutive calls must return the same result.
assert.Equal(t, result1, result2, "consecutive calls in the same context must return the same result")
testNonTerminalStream(t, &os.Stderr, tty.IsStderrTerminal)
}

// TestSpec_DesignDecision_NonTerminalReturnsFalse validates that when standard streams are not
// connected to a terminal (e.g., CI pipelines, piped test execution), the functions return false.
// Spec: "The WASM stub (tty_wasm.go) always returns false so that components built for the
// browser never attempt to use ANSI escape codes."
// By the same contract, non-TTY environments return false.
func TestSpec_DesignDecision_NonTerminalReturnsFalse(t *testing.T) {
if tty.IsStdinTerminal() || tty.IsStdoutTerminal() || tty.IsStderrTerminal() {
t.Skip("skipping: running in an interactive terminal — non-TTY assertion not applicable here")
}
assert.False(t, tty.IsStdinTerminal(), "IsStdinTerminal must return false when stdin is not a terminal")
assert.False(t, tty.IsStdoutTerminal(), "IsStdoutTerminal must return false when stdout is not a terminal")
assert.False(t, tty.IsStderrTerminal(), "IsStderrTerminal must return false when stderr is not a terminal")
func testNonTerminalStream(t *testing.T, stream **os.File, detect func() bool) {
t.Helper()
original := *stream
reader, writer, err := os.Pipe()
require.NoError(t, err, "creating a non-terminal pipe should succeed")
t.Cleanup(func() {
*stream = original
require.NoError(t, reader.Close(), "closing the pipe reader should succeed")
require.NoError(t, writer.Close(), "closing the pipe writer should succeed")
})

*stream = reader
assert.False(t, detect(), "a standard stream redirected to a pipe must not be reported as a terminal")
}
Loading