]> git.giacomolli.uk - onechat/blob - terminal_test.go
Name and order the probe 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 := daemonListenSocket(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 }