package shell import ( "bytes" "io" "sync" "testing" "time" ) // nopCloser wraps a ReadWriter with a no-op Close. type nopCloser struct { io.ReadWriter } func (nopCloser) Close() error { return nil } func TestRecordingChannelPassthrough(t *testing.T) { var buf bytes.Buffer rc := NewRecordingChannel(nopCloser{&buf}) // Write through the recorder. msg := []byte("hello") n, err := rc.Write(msg) if err != nil { t.Fatalf("Write: %v", err) } if n != len(msg) { t.Errorf("Write n = %d, want %d", n, len(msg)) } // Read through the recorder. out := make([]byte, 16) n, err = rc.Read(out) if err != nil { t.Fatalf("Read: %v", err) } if string(out[:n]) != "hello" { t.Errorf("Read = %q, want %q", out[:n], "hello") } if err := rc.Close(); err != nil { t.Fatalf("Close: %v", err) } } func TestRecordingChannelMultiCallback(t *testing.T) { var buf bytes.Buffer rc := NewRecordingChannel(nopCloser{&buf}) type event struct { ts time.Time direction int data string } var mu sync.Mutex var events1, events2 []event rc.AddCallback(func(ts time.Time, direction int, data []byte) { mu.Lock() defer mu.Unlock() events1 = append(events1, event{ts, direction, string(data)}) }) rc.AddCallback(func(ts time.Time, direction int, data []byte) { mu.Lock() defer mu.Unlock() events2 = append(events2, event{ts, direction, string(data)}) }) // Write triggers both callbacks with direction=1. rc.Write([]byte("hello")) // Read triggers both callbacks with direction=0. out := make([]byte, 16) rc.Read(out) mu.Lock() defer mu.Unlock() if len(events1) != 2 { t.Fatalf("callback1 got %d events, want 2", len(events1)) } if len(events2) != 2 { t.Fatalf("callback2 got %d events, want 2", len(events2)) } // Write event should be direction=1. if events1[0].direction != 1 { t.Errorf("write direction = %d, want 1", events1[0].direction) } // Read event should be direction=0. if events1[1].direction != 0 { t.Errorf("read direction = %d, want 0", events1[1].direction) } // Both callbacks should get the same timestamp for a single operation. if events1[0].ts != events2[0].ts { t.Error("callbacks should receive the same timestamp") } } func TestRecordingChannelWithCallbackClearsExisting(t *testing.T) { var buf bytes.Buffer rc := NewRecordingChannel(nopCloser{&buf}) called1 := false called2 := false rc.AddCallback(func(_ time.Time, _ int, _ []byte) { called1 = true }) // WithCallback should clear existing and set new. rc.WithCallback(func(_ time.Time, _ int, _ []byte) { called2 = true }) rc.Write([]byte("x")) if called1 { t.Error("first callback should not be called after WithCallback") } if !called2 { t.Error("second callback should be called") } }