nandi/oripublic Fork 0
main
Commits
Clone
git clone https://git.rickub.com/nandi/ori.git
git clone ssh://git@rickub.com/nandi/ori.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

forked from bots-garden/ori

terminal_test.go · 124 lines · 3.4 KBGo Blame HistoryRaw
✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme 76d62ac k33g yesterday1package terminal_test
2
3import (
4 "context"
5 "net/http/httptest"
6 "os"
7 "path/filepath"
8 "strings"
9 "testing"
10 "time"
11
12 "github.com/coder/websocket"
13 "github.com/coder/websocket/wsjson"
14
15 "rickub.com/bots-garden/ori/internal/terminal"
16)
17
18// dialTerminal starts the service on a test server, with plain sh for
19// determinism, and connects a WebSocket client to it.
20func dialTerminal(t *testing.T, cwd string) (*websocket.Conn, context.Context) {
21 t.Helper()
22 service := terminal.New(cwd, nil)
23 service.Shell = []string{"/bin/sh"}
24
25 server := httptest.NewServer(service.WebSocketHandler())
26 t.Cleanup(server.Close)
27
28 ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
29 t.Cleanup(cancel)
30
31 conn, _, err := websocket.Dial(ctx, "ws"+strings.TrimPrefix(server.URL, "http"), nil)
32 if err != nil {
33 t.Fatalf("websocket dial failed: %v", err)
34 }
35 t.Cleanup(func() { _ = conn.CloseNow() })
36 return conn, ctx
37}
38
39// readOutputUntil accumulates binary frames until the marker shows up.
40func readOutputUntil(t *testing.T, ctx context.Context, conn *websocket.Conn, marker string) string {
41 t.Helper()
42 var output strings.Builder
43 for !strings.Contains(output.String(), marker) {
44 kind, data, err := conn.Read(ctx)
45 if err != nil {
46 t.Fatalf("read failed while waiting for %q; got so far: %q (%v)", marker, output.String(), err)
47 }
48 if kind == websocket.MessageBinary {
49 output.Write(data)
50 }
51 }
52 return output.String()
53}
54
55func send(t *testing.T, ctx context.Context, conn *websocket.Conn, msg map[string]any) {
56 t.Helper()
57 if err := wsjson.Write(ctx, conn, msg); err != nil {
58 t.Fatalf("websocket write failed: %v", err)
59 }
60}
61
62func TestShellRunsCommandsAndStreamsOutput(t *testing.T) {
63 conn, ctx := dialTerminal(t, t.TempDir())
64
65 send(t, ctx, conn, map[string]any{"type": "input", "data": "echo ori-$((20+22))\n"})
66 output := readOutputUntil(t, ctx, conn, "ori-42")
67 if output == "" {
68 t.Fatal("no terminal output received")
69 }
70}
71
72func TestShellStartsInTheConfiguredDirectory(t *testing.T) {
73 cwd := t.TempDir()
74 resolved, err := filepath.EvalSymlinks(cwd)
75 if err != nil {
76 t.Fatal(err)
77 }
78 conn, ctx := dialTerminal(t, cwd)
79
80 send(t, ctx, conn, map[string]any{"type": "input", "data": "pwd\n"})
81 readOutputUntil(t, ctx, conn, resolved)
82}
83
84func TestResizeIsAcceptedAndSessionKeepsWorking(t *testing.T) {
85 conn, ctx := dialTerminal(t, t.TempDir())
86
87 send(t, ctx, conn, map[string]any{"type": "resize", "cols": 120, "rows": 40})
88 send(t, ctx, conn, map[string]any{"type": "input", "data": "stty size\n"})
89 readOutputUntil(t, ctx, conn, "40 120")
90}
91
92func TestMalformedFramesDoNotKillTheSession(t *testing.T) {
93 conn, ctx := dialTerminal(t, t.TempDir())
94
95 if err := conn.Write(ctx, websocket.MessageText, []byte("{not json")); err != nil {
96 t.Fatalf("write failed: %v", err)
97 }
98 send(t, ctx, conn, map[string]any{"type": "input", "data": "echo still-alive\n"})
99 readOutputUntil(t, ctx, conn, "still-alive")
100}
101
102func TestShellExitClosesTheSocket(t *testing.T) {
103 conn, ctx := dialTerminal(t, t.TempDir())
104
105 send(t, ctx, conn, map[string]any{"type": "input", "data": "exit\n"})
106
107 deadline := time.Now().Add(10 * time.Second)
108 for {
109 if _, _, err := conn.Read(ctx); err != nil {
110 return // closed, as expected
111 }
112 if time.Now().After(deadline) {
113 t.Fatal("socket still open long after the shell exited")
114 }
115 }
116}
117
118func TestMain(m *testing.M) {
119 if _, err := os.Stat("/bin/sh"); err != nil {
120 // No shell, no terminal tests (should not happen on linux).
121 os.Exit(0)
122 }
123 os.Exit(m.Run())
124}