package main import ( "io" "net" "path/filepath" "strings" "testing" "time" "github.com/stretchr/testify/require" ) func TestClientEndsWhenTheDaemonEnds(t *testing.T) { dir := t.TempDir() listener, err := listenSocket(filepath.Join(dir, socketFileName)) require.NoError(t, err) accepted := make(chan net.Conn) go func() { conn, err := listener.Accept() if err == nil { accepted <- conn } }() conn, err := net.Dial("unix", filepath.Join(dir, socketFileName)) require.NoError(t, err) in, _ := io.Pipe() done := make(chan error) go func() { done <- terminalSession(t.Context(), conn, in, io.Discard) }() require.NoError(t, (<-accepted).Close()) require.ErrorIs(t, <-done, errTerminalDaemonClosed) require.NoError(t, listener.Close()) } func TestEventText(t *testing.T) { testCases := []struct { name string event Event text string }{ { name: "reply as it is", event: Event{Kind: "reply", Text: "Done."}, text: "Done.", }, { name: "own user message not shown", event: Event{Kind: "user", Text: "typed", Source: terminalSource}, text: "", }, { name: "user message of another source marked", event: Event{Kind: "user", Text: "from the phone", Source: "telegram"}, text: "[telegram] from the phone", }, { name: "long result cut", event: Event{Kind: "echo", Text: strings.Repeat("line\n", 12)}, text: "[result] " + strings.Repeat("line\n", 9) + "line\n[2 more lines]", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { require.Equal(t, tc.text, terminalEventText(tc.event)) }) } } // TerminalTestClient is a terminal client of a test daemon, with a pipe as its // input. type TerminalTestClient struct { input *io.PipeWriter done chan error out *SyncBuffer } // Type writes one line to the input of the client. func (c *TerminalTestClient) Type(line string) { _, err := io.WriteString(c.input, line+"\n") if err != nil { panic(err) } } // Output returns what the client showed. func (c *TerminalTestClient) Output() string { return c.out.String() } // WaitFor waits until the client showed text. func (c *TerminalTestClient) WaitFor(t *testing.T, text string) { t.Helper() require.Eventually(t, func() bool { return strings.Contains(c.Output(), text) }, 10*time.Second, time.Millisecond, "output so far: %q", c.Output()) } // Close ends the input and waits for the client. func (c *TerminalTestClient) Close() error { err := c.input.Close() if err != nil { return err } return <-c.done } // terminalStartTestClient connects a client to the daemon at socket. func terminalStartTestClient(t *testing.T, socket string) *TerminalTestClient { t.Helper() conn, err := net.Dial("unix", socket) require.NoError(t, err) reader, writer := io.Pipe() client := &TerminalTestClient{ input: writer, done: make(chan error, 1), out: &SyncBuffer{}, } go func() { client.done <- terminalSession(t.Context(), conn, reader, client.out) }() t.Cleanup(func() { _ = writer.Close() }) return client }