waveterm/pkg/waveattach/output_test.go
dfbb e64d1feb0f feat(wsh): add attach command for read-only terminal observation
Adds `wsh attach` — a command that streams the live output of any Wave
Terminal block to a local terminal window without affecting the remote
session. Useful for monitoring long-running processes, CI jobs, or AI
coding agents from a separate window or SSH session.

Key capabilities:
- Interactive block selector (workspace → tab → block)
- Live PTY streaming via snapshot + WPS event subscription
- Viewport model: server PTY size is fixed; local terminal is a
  moveable window into the remote screen (Ctrl+Arrow to pan)
- Diff-based renderer that emits only changed cells per frame,
  with full SGR, wide-character, alt-screen, and cursor-style sync
- Debounced render loop (16 ms) coalesces rapid PTY bursts so that
  full-screen TUI repaints are always consumed before rendering
- Resync command (Ctrl-A s) rebuilds xterm-go state from a fresh
  snapshot when local state drifts from the remote

Bug fix included: EventRecv messages are now dispatched synchronously
in the WshRpc message loop (same pattern as StreamData/StreamDataAck)
so that back-to-back PTY events are always processed in arrival order.
Without this fix, concurrent goroutines race to write PTY chunks into
the terminal emulator, producing mixed-frame garbling.
2026-05-01 23:08:15 +08:00

37 lines
886 B
Go

// Copyright 2026, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package waveattach
import (
"bytes"
"testing"
"time"
)
func TestEventBuffer_ReplayAfterCutoff(t *testing.T) {
buf := makeEventBuffer()
t0 := time.Now()
_ = buf.write(t0, []byte("A"), nil)
_ = buf.write(t0.Add(10*time.Millisecond), []byte("B"), nil)
cutoff := t0.Add(20 * time.Millisecond)
_ = buf.write(cutoff.Add(time.Millisecond), []byte("C"), nil)
var out bytes.Buffer
buf.flush(cutoff, &out)
if got := out.String(); got != "C" {
t.Errorf("want %q, got %q", "C", got)
}
}
func TestEventBuffer_StreamModeAfterFlush(t *testing.T) {
buf := makeEventBuffer()
cutoff := time.Now()
buf.flush(cutoff, &bytes.Buffer{})
var out bytes.Buffer
buf.write(cutoff.Add(time.Second), []byte("hello"), &out)
if got := out.String(); got != "hello" {
t.Errorf("want %q, got %q", "hello", got)
}
}