X-Git-Url: http://git.giacomolli.uk/onechat/blobdiff_plain/6d1d9d9a7cf90b25a2e10af1c5bc56faee7e494c..153f3f9b4b09d521412eacca30eddeed395c3f41:/daemon_test.go?ds=inline diff --git a/daemon_test.go b/daemon_test.go index d1f1bc5..3d84faa 100644 --- a/daemon_test.go +++ b/daemon_test.go @@ -1,17 +1,13 @@ package main import ( - "bytes" "context" "io" - "net" "net/http" "os" "path/filepath" "strings" - "sync" "testing" - "time" "github.com/stretchr/testify/require" ) @@ -28,13 +24,13 @@ func TestClientsSeeTheTurnAsItHappens(t *testing.T) { } socket := startTestDaemon(t, server) - first := startTestClient(t, socket) - second := startTestClient(t, socket) + first := terminalStartTestClient(t, socket) + second := terminalStartTestClient(t, socket) first.WaitFor(t, "[connected to OneChat]") second.WaitFor(t, "[connected to OneChat]") first.Type("Run echo hi.") - for _, client := range []*testClient{first, second} { + for _, client := range []*TerminalTestClient{first, second} { client.WaitFor(t, "[done]") require.Equal(t, "[connected to OneChat]\n[working]\n[tool] shell {\"command\":\"echo hi\"}\n[result] hi\nIt said hi.\n[done]\n", client.Output()) } @@ -59,44 +55,17 @@ func TestStopInterruptsTheTurn(t *testing.T) { } socket := startTestDaemon(t, server) - client := startTestClient(t, socket) + client := terminalStartTestClient(t, socket) client.Type("a long task") <-arrived - late := startTestClient(t, socket) + late := terminalStartTestClient(t, socket) late.WaitFor(t, "[connected to OneChat]\n[working]") client.Type(stopCommand) client.WaitFor(t, "[error] interrupted\n[done]") } -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 <- chatSession(t.Context(), conn, in, io.Discard) - }() - - require.NoError(t, (<-accepted).Close()) - require.ErrorIs(t, <-done, errDaemonClosed) - require.NoError(t, listener.Close()) -} - func TestListenSocketReplacesAnOldSocketFile(t *testing.T) { path := filepath.Join(t.TempDir(), socketFileName) require.NoError(t, os.WriteFile(path, nil, 0o600)) @@ -142,100 +111,6 @@ func TestOneProcessHoldsTheTokens(t *testing.T) { require.NoError(t, lock.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, eventText(tc.event)) - }) - } -} - -// testClient is a terminal client of a test daemon, with a pipe as its input. -type testClient struct { - input *io.PipeWriter - done chan error - out *syncBuffer -} - -// Type writes one line to the input of the client. -func (c *testClient) Type(line string) { - _, err := io.WriteString(c.input, line+"\n") - if err != nil { - panic(err) - } -} - -// Output returns what the client showed. -func (c *testClient) Output() string { - return c.out.String() -} - -// WaitFor waits until the client showed text. -func (c *testClient) 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 *testClient) Close() error { - err := c.input.Close() - if err != nil { - return err - } - - return <-c.done -} - -// syncBuffer is a buffer that the test and the client use at once. -type syncBuffer struct { - mutex sync.Mutex - buffer bytes.Buffer -} - -func (b *syncBuffer) Write(p []byte) (int, error) { - b.mutex.Lock() - defer b.mutex.Unlock() - - return b.buffer.Write(p) -} - -func (b *syncBuffer) String() string { - b.mutex.Lock() - defer b.mutex.Unlock() - - return b.buffer.String() -} - // startTestDaemon serves a new agent on a socket in a temporary directory and // returns the socket path. The commands of the agent run in a temporary // directory. @@ -279,28 +154,3 @@ func startTestDaemonWithBot(t *testing.T, server *ModelFake, bot *TelegramBot) s return socket } - -// startTestClient connects a client to the daemon at socket. -func startTestClient(t *testing.T, socket string) *testClient { - t.Helper() - - conn, err := net.Dial("unix", socket) - require.NoError(t, err) - - reader, writer := io.Pipe() - client := &testClient{ - input: writer, - done: make(chan error, 1), - out: &syncBuffer{}, - } - - go func() { - client.done <- chatSession(t.Context(), conn, reader, client.out) - }() - - t.Cleanup(func() { - _ = writer.Close() - }) - - return client -}