Skip to content

Commit 912b0f0

Browse files
committed
imapclient: add test for client disconnection
Extracted from emersion#718
1 parent 17771fb commit 912b0f0

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

imapclient/connection_test.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package imapclient_test
2+
3+
import (
4+
"io"
5+
"net"
6+
"testing"
7+
"time"
8+
9+
"github.com/emersion/go-imap/v2/imapclient"
10+
)
11+
12+
type pipeConn struct {
13+
io.Reader
14+
io.Writer
15+
closer io.Closer
16+
}
17+
18+
func (c pipeConn) Close() error {
19+
return c.closer.Close()
20+
}
21+
22+
func (c pipeConn) LocalAddr() net.Addr {
23+
return &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
24+
}
25+
26+
func (c pipeConn) RemoteAddr() net.Addr {
27+
return &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
28+
}
29+
30+
func (c pipeConn) SetDeadline(t time.Time) error {
31+
return nil
32+
}
33+
34+
func (c pipeConn) SetReadDeadline(t time.Time) error {
35+
return nil
36+
}
37+
38+
func (c pipeConn) SetWriteDeadline(t time.Time) error {
39+
return nil
40+
}
41+
42+
var _ net.Conn = pipeConn{}
43+
44+
// TestCommand_Wait_ConnectionFailure tests that Wait() returns an error instead
45+
// of hanging when the network connection drops unexpectedly.
46+
func TestCommand_Wait_ConnectionFailure(t *testing.T) {
47+
// Create a custom connection pair
48+
clientR, serverW := io.Pipe()
49+
serverR, clientW := io.Pipe()
50+
51+
clientConn := pipeConn{
52+
Reader: clientR,
53+
Writer: clientW,
54+
closer: clientW,
55+
}
56+
serverConn := pipeConn{
57+
Reader: serverR,
58+
Writer: serverW,
59+
closer: serverW,
60+
}
61+
62+
client := imapclient.New(clientConn, nil)
63+
defer client.Close()
64+
65+
// Hacky server which sends greeting then closes without responding to commands.
66+
go func() {
67+
serverW.Write([]byte("* OK IMAP server ready\r\n"))
68+
69+
buf := make([]byte, 1024)
70+
serverR.Read(buf)
71+
72+
time.Sleep(50 * time.Millisecond)
73+
serverConn.Close()
74+
}()
75+
76+
if err := client.WaitGreeting(); err != nil {
77+
t.Fatalf("WaitGreeting() = %v", err)
78+
}
79+
80+
noopCmd := client.Noop()
81+
82+
// Wait should return an error, not hang
83+
errCh := make(chan error, 1)
84+
go func() {
85+
errCh <- noopCmd.Wait()
86+
}()
87+
88+
select {
89+
case err := <-errCh:
90+
if err == nil {
91+
t.Error("Expected error after connection failure, got nil")
92+
} else {
93+
t.Logf("Wait() returned error as expected: %v", err)
94+
}
95+
case <-time.After(2 * time.Second):
96+
t.Fatal("Wait() hung after connection failure")
97+
}
98+
}
99+
100+
// TestMultipleCommands_ConnectionFailure tests that multiple pending commands
101+
// are properly unblocked when the connection drops.
102+
func TestMultipleCommands_ConnectionFailure(t *testing.T) {
103+
// Create a custom connection pair
104+
clientR, serverW := io.Pipe()
105+
serverR, clientW := io.Pipe()
106+
107+
clientConn := pipeConn{
108+
Reader: clientR,
109+
Writer: clientW,
110+
closer: clientW,
111+
}
112+
serverConn := pipeConn{
113+
Reader: serverR,
114+
Writer: serverW,
115+
closer: serverW,
116+
}
117+
118+
client := imapclient.New(clientConn, nil)
119+
defer client.Close()
120+
121+
// Hacky server which send greeting then closes without responding.
122+
go func() {
123+
serverW.Write([]byte("* OK IMAP server ready\r\n"))
124+
125+
buf := make([]byte, 4096)
126+
serverR.Read(buf)
127+
128+
time.Sleep(100 * time.Millisecond)
129+
serverConn.Close()
130+
}()
131+
132+
if err := client.WaitGreeting(); err != nil {
133+
t.Fatalf("WaitGreeting() = %v", err)
134+
}
135+
136+
cmd1 := client.Noop()
137+
cmd2 := client.Noop()
138+
cmd3 := client.Noop()
139+
140+
done := make(chan struct{})
141+
go func() {
142+
cmd1.Wait()
143+
cmd2.Wait()
144+
cmd3.Wait()
145+
close(done)
146+
}()
147+
148+
select {
149+
case <-done:
150+
t.Log("All commands completed after connection failure")
151+
case <-time.After(5 * time.Second):
152+
t.Fatal("Commands hung after connection failure")
153+
}
154+
}

0 commit comments

Comments
 (0)