termtap/internal/app/integration_session_test.go
Hayden Hargreaves 002773e77f test: AI generated all of these tests
Just for the MVP of course. Need to validate the idea.
2026-04-23 19:47:04 -07:00

110 lines
3.3 KiB
Go

package app
import (
"net/http"
"net/http/httptest"
"net/url"
"testing"
"time"
"termtap.dev/internal/model"
)
// NOTE: Run with -race; this validates cross-component concurrency.
func TestSessionIntegration_LifecycleAndRequestEvents(t *testing.T) {
addr := freeTCPAddr(t)
s, err := StartSession(model.Command{Name: "sh", Args: []string{"-c", "sleep 5"}}, addr)
if err != nil {
t.Fatalf("StartSession() error = %v", err)
}
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
}))
t.Cleanup(upstream.Close)
startupEvents := collectUntilTypes(t, s.Events, []model.EventType{
model.EventTypeProxyStarting,
model.EventTypeProcessStarting,
model.EventTypeProcessStarted,
}, 3*time.Second)
proxyURL, err := url.Parse(s.proxy.Url)
if err != nil {
t.Fatalf("url.Parse(proxy) error = %v", err)
}
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
Timeout: 3 * time.Second,
}
resp, err := client.Get(upstream.URL + "/session")
if err != nil {
t.Fatalf("proxy request error = %v", err)
}
_ = resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("status = %d, want %d", resp.StatusCode, http.StatusOK)
}
requestEvents := collectUntilTypes(t, s.Events, []model.EventType{
model.EventTypeRequestStarted,
model.EventTypeRequestFinished,
}, 3*time.Second)
s.Stop()
select {
case <-s.proc.Done:
case <-time.After(4 * time.Second):
t.Fatal("timeout waiting for process stop")
}
shutdownEvents := collectUntilTypes(t, s.Events, []model.EventType{
model.EventTypeProxyStopped,
model.EventTypeProcessSignaled,
model.EventTypeProcessExited,
}, 4*time.Second)
if !isBefore(startupEvents, model.EventTypeProcessStarting, model.EventTypeProcessStarted) {
t.Fatalf("expected %s before %s in startup events: %#v", model.EventTypeProcessStarting, model.EventTypeProcessStarted, startupEvents)
}
if !isBefore(requestEvents, model.EventTypeRequestStarted, model.EventTypeRequestFinished) {
t.Fatalf("expected %s before %s in request events: %#v", model.EventTypeRequestStarted, model.EventTypeRequestFinished, requestEvents)
}
if !isBefore(shutdownEvents, model.EventTypeProcessSignaled, model.EventTypeProcessExited) {
t.Fatalf("expected %s before %s in shutdown events: %#v", model.EventTypeProcessSignaled, model.EventTypeProcessExited, shutdownEvents)
}
}
func TestSessionIntegration_RestartProcessEmitsLifecycleEvents(t *testing.T) {
addr := freeTCPAddr(t)
s, err := StartSession(model.Command{Name: "sh", Args: []string{"-c", "sleep 5"}}, addr)
if err != nil {
t.Fatalf("StartSession() error = %v", err)
}
t.Cleanup(func() { s.Stop() })
_ = collectUntilTypes(t, s.Events, []model.EventType{
model.EventTypeProcessStarted,
model.EventTypeProxyStarting,
}, 3*time.Second)
if err := s.RestartProcess(); err != nil {
t.Fatalf("RestartProcess() error = %v", err)
}
events := collectUntilTypes(t, s.Events, []model.EventType{
model.EventTypeProcessRestarting,
model.EventTypeProcessSignaled,
model.EventTypeProcessExited,
model.EventTypeProcessStarting,
model.EventTypeProcessStarted,
}, 4*time.Second)
if !isBefore(events, model.EventTypeProcessRestarting, model.EventTypeProcessStarted) {
t.Fatalf("expected restarting before process started, got %#v", events)
}
}