Skip to content
Merged
Show file tree
Hide file tree
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
61 changes: 61 additions & 0 deletions src/pkg/tui/attach_errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package tui

import (
"errors"
"testing"
)

// The attach preflight communicates failures through two typed errors so the
// app (and these tests) can branch on cause without matching prose. Their
// Error/Unwrap contracts were previously uncovered: a reworded message or a
// dropped Unwrap would have shipped silently, breaking errors.Is chains and
// the operator-facing footer text.

func TestTmuxNotFoundErrorMessageAndUnwrap(t *testing.T) {
cause := errors.New("exec: \"tmux\": executable file not found in $PATH")
err := &tmuxNotFoundError{err: cause}

want := "tmux is not installed or is not available in PATH"
if got := err.Error(); got != want {
t.Errorf("Error() = %q, want %q", got, want)
}
if !errors.Is(err, cause) {
t.Error("errors.Is(err, cause) = false, want true — Unwrap must expose the LookPath error")
}

var typed *tmuxNotFoundError
if !errors.As(error(err), &typed) {
t.Error("errors.As failed to recover *tmuxNotFoundError")
}
}

func TestTmuxSessionMissingErrorMessageVariants(t *testing.T) {
cause := errors.New("exit status 1")

t.Run("without detail", func(t *testing.T) {
err := &tmuxSessionMissingError{session: "hive-scout", err: cause}
want := `tmux session "hive-scout" is unavailable`
if got := err.Error(); got != want {
t.Errorf("Error() = %q, want %q", got, want)
}
})

t.Run("with detail", func(t *testing.T) {
err := &tmuxSessionMissingError{
session: "hive-scout",
detail: "no server running on /tmp/tmux-1001/default",
err: cause,
}
want := `tmux session "hive-scout" is unavailable: no server running on /tmp/tmux-1001/default`
if got := err.Error(); got != want {
t.Errorf("Error() = %q, want %q", got, want)
}
})

t.Run("unwrap exposes tmux exit error", func(t *testing.T) {
err := &tmuxSessionMissingError{session: "hive-scout", err: cause}
if !errors.Is(err, cause) {
t.Error("errors.Is(err, cause) = false, want true — Unwrap must expose the has-session error")
}
})
}
26 changes: 26 additions & 0 deletions src/pkg/tui/client/kick_result_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package client

import "testing"

// KickResult.Queued is the seam callers use to distinguish "this call queued
// the prompt" from "an in-flight delivery absorbed it" (#5325 async kick).
// It was previously uncovered, so a typo against the wrong wire constant
// ("in-flight", or the lifecycle "resumed" mistake documented on Paused)
// would not have failed any test.
func TestKickResultQueued(t *testing.T) {
cases := []struct {
status string
want bool
}{
{"queued", true},
{"in-flight", false}, // deduplicated into an existing delivery
{"", false},
{"Queued", false}, // wire values are lowercase; case must not be folded
}
for _, tc := range cases {
r := KickResult{Status: tc.status, Agent: "scout"}
if got := r.Queued(); got != tc.want {
t.Errorf("KickResult{Status: %q}.Queued() = %v, want %v", tc.status, got, tc.want)
}
}
}
32 changes: 32 additions & 0 deletions src/pkg/tui/panes/stub_view_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package panes

import (
"strings"
"testing"
)

// TestBareStubViewRendersPlaceholder pins stub.View directly. Every shipped
// pane now overrides View, so this method is reachable only through the NEXT
// pane that embeds stub before its real content lands — exactly the moment
// nothing else would catch a regression in the shared fallback.
func TestBareStubViewRendersPlaceholder(t *testing.T) {
s := stub{title: "PENDING"}

const w, h = 30, 6
view := s.View(w, h)
for _, want := range []string{"PENDING", placeholder} {
if !strings.Contains(view, want) {
t.Fatalf("stub.View() missing %q:\n%s", want, view)
}
}
if lines := strings.Count(view, "\n") + 1; lines != h {
t.Fatalf("stub.View() renders %d lines, want exactly %d", lines, h)
}
if vw := visibleWidth(view); vw != w {
t.Fatalf("stub.View() widest line is %d cells, want exactly %d", vw, w)
}

if got := s.View(0, 0); got != "" {
t.Fatalf("stub.View(0,0) = %q, want empty", got)
}
}
Loading