]> git.giacomolli.uk - onechat/blob - terminal_test.go
Name and order the terminal client code
[onechat] / terminal_test.go
1 package main
2
3 import (
4 "io"
5 "net"
6 "path/filepath"
7 "strings"
8 "testing"
9 "time"
10
11 "github.com/stretchr/testify/require"
12 )
13
14 func TestClientEndsWhenTheDaemonEnds(t *testing.T) {
15 dir := t.TempDir()
16 listener, err := listenSocket(filepath.Join(dir, socketFileName))
17 require.NoError(t, err)
18
19 accepted := make(chan net.Conn)
20 go func() {
21 conn, err := listener.Accept()
22 if err == nil {
23 accepted <- conn
24 }
25 }()
26
27 conn, err := net.Dial("unix", filepath.Join(dir, socketFileName))
28 require.NoError(t, err)
29
30 in, _ := io.Pipe()
31 done := make(chan error)
32 go func() {
33 done <- terminalSession(t.Context(), conn, in, io.Discard)
34 }()
35
36 require.NoError(t, (<-accepted).Close())
37 require.ErrorIs(t, <-done, errTerminalDaemonClosed)
38 require.NoError(t, listener.Close())
39 }
40
41 func TestEventText(t *testing.T) {
42 testCases := []struct {
43 name string
44 event Event
45 text string
46 }{
47 {
48 name: "reply as it is",
49 event: Event{Kind: "reply", Text: "Done."},
50 text: "Done.",
51 },
52 {
53 name: "own user message not shown",
54 event: Event{Kind: "user", Text: "typed", Source: terminalSource},
55 text: "",
56 },
57 {
58 name: "user message of another source marked",
59 event: Event{Kind: "user", Text: "from the phone", Source: "telegram"},
60 text: "[telegram] from the phone",
61 },
62 {
63 name: "long result cut",
64 event: Event{Kind: "echo", Text: strings.Repeat("line\n", 12)},
65 text: "[result] " + strings.Repeat("line\n", 9) + "line\n[2 more lines]",
66 },
67 }
68
69 for _, tc := range testCases {
70 t.Run(tc.name, func(t *testing.T) {
71 require.Equal(t, tc.text, terminalEventText(tc.event))
72 })
73 }
74 }
75
76 // TerminalTestClient is a terminal client of a test daemon, with a pipe as its
77 // input.
78 type TerminalTestClient struct {
79 input *io.PipeWriter
80 done chan error
81 out *SyncBuffer
82 }
83
84 // Type writes one line to the input of the client.
85 func (c *TerminalTestClient) Type(line string) {
86 _, err := io.WriteString(c.input, line+"\n")
87 if err != nil {
88 panic(err)
89 }
90 }
91
92 // Output returns what the client showed.
93 func (c *TerminalTestClient) Output() string {
94 return c.out.String()
95 }
96
97 // WaitFor waits until the client showed text.
98 func (c *TerminalTestClient) WaitFor(t *testing.T, text string) {
99 t.Helper()
100
101 require.Eventually(t, func() bool {
102 return strings.Contains(c.Output(), text)
103 }, 10*time.Second, time.Millisecond, "output so far: %q", c.Output())
104 }
105
106 // Close ends the input and waits for the client.
107 func (c *TerminalTestClient) Close() error {
108 err := c.input.Close()
109 if err != nil {
110 return err
111 }
112
113 return <-c.done
114 }
115
116 // terminalStartTestClient connects a client to the daemon at socket.
117 func terminalStartTestClient(t *testing.T, socket string) *TerminalTestClient {
118 t.Helper()
119
120 conn, err := net.Dial("unix", socket)
121 require.NoError(t, err)
122
123 reader, writer := io.Pipe()
124 client := &TerminalTestClient{
125 input: writer,
126 done: make(chan error, 1),
127 out: &SyncBuffer{},
128 }
129
130 go func() {
131 client.done <- terminalSession(t.Context(), conn, reader, client.out)
132 }()
133
134 t.Cleanup(func() {
135 _ = writer.Close()
136 })
137
138 return client
139 }