conn_test.go
1package sync
2
3import (
4 "testing"
5
6 "github.com/coder/websocket"
7)
8
9func TestNewSyncConn(t *testing.T) {
10 opts := Options{
11 Endpoint: "ws://localhost:8080",
12 AuthToken: "test-token",
13 }
14
15 conn := NewSyncConn(nil, opts)
16
17 if conn.status != StatusDisconnected {
18 t.Fatalf("expected status Disconnected, got %d", conn.status)
19 }
20
21 if conn.opts.Endpoint != opts.Endpoint {
22 t.Fatalf("endpoint mismatch")
23 }
24
25 if conn.opts.AuthToken != opts.AuthToken {
26 t.Fatalf("auth token mismatch")
27 }
28}
29
30func TestSyncConnStatus(t *testing.T) {
31 conn := NewSyncConn(nil, Options{})
32
33 if conn.Status() != StatusDisconnected {
34 t.Fatalf("expected status Disconnected, got %d", conn.Status())
35 }
36}
37
38func TestSyncConnDone(t *testing.T) {
39 conn := NewSyncConn(nil, Options{})
40
41 done := conn.Done()
42 if done == nil {
43 t.Fatalf("expected done channel to be non-nil")
44 }
45}
46
47func TestClose(t *testing.T) {
48 conn := NewSyncConn(nil, Options{})
49
50 err := conn.Close()
51 if err != nil {
52 t.Fatalf("unexpected error: %v", err)
53 }
54
55 if conn.Status() != StatusDisconnected {
56 t.Fatalf("expected status Disconnected after Close, got %d", conn.Status())
57 }
58
59 err = conn.Close()
60 if err != nil {
61 t.Fatalf("second Close should not return error: %v", err)
62 }
63}
64
65func TestSyncConnEncodeMessage(t *testing.T) {
66 payload := []byte{0x01, 0x02, 0x03}
67 msg := encodeMessage(SyncStep1, payload)
68
69 if len(msg) < 2 {
70 t.Fatalf("encoded message too short")
71 }
72
73 msgType, n, err := readVarUint(msg)
74 if err != nil {
75 t.Fatalf("failed to read message type: %v", err)
76 }
77
78 if msgType != uint64(SyncStep1) {
79 t.Fatalf("message type mismatch: got %d, want %d", msgType, SyncStep1)
80 }
81
82 payloadLen, m, err := readVarUint(msg[n:])
83 if err != nil {
84 t.Fatalf("failed to read payload length: %v", err)
85 }
86
87 if payloadLen != uint64(len(payload)) {
88 t.Fatalf("payload length mismatch: got %d, want %d", payloadLen, len(payload))
89 }
90
91 rest := msg[n+m:]
92 if string(rest) != string(payload) {
93 t.Fatalf("payload mismatch")
94 }
95}
96
97func TestHandleMessageTooShort(t *testing.T) {
98 conn := NewSyncConn(nil, Options{})
99
100 err := conn.handleMessage(nil, []byte{})
101 if err == nil {
102 t.Fatalf("expected error for message too short")
103 }
104}
105
106func TestHandleMessageUnknownType(t *testing.T) {
107 conn := NewSyncConn(nil, Options{})
108
109 payload := []byte{0x01, 0x02, 0x03}
110 msg := encodeMessage(99, payload)
111
112 err := conn.handleMessage(nil, msg)
113 if err == nil {
114 t.Fatalf("expected error for unknown message type")
115 }
116}
117
118func TestHandleMessageInvalidMessageType(t *testing.T) {
119 // Valid varint but not a valid message type (unparseable)
120 msg := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
121
122 _, _, err := readVarUint(msg)
123 if err == nil {
124 t.Fatalf("expected error for overflow varint in message type")
125 }
126}
127
128func TestHandleSyncStep2(t *testing.T) {
129 appliedUpdate := false
130 mockDoc := &mockDoc{
131 writeTx: func(fn func(Transaction) error) error {
132 appliedUpdate = true
133 return nil
134 },
135 }
136
137 conn := NewSyncConn(mockDoc, Options{})
138 payload := []byte{0x01, 0x02, 0x03}
139
140 err := conn.handleSyncStep2(payload)
141 if err != nil {
142 t.Fatalf("handleSyncStep2 failed: %v", err)
143 }
144
145 if !appliedUpdate {
146 t.Error("expected update to be applied")
147 }
148}
149
150func TestHandleUpdateWithOnUpdateCallback(t *testing.T) {
151 updateCalled := false
152 mockDoc := &mockDoc{
153 writeTx: func(fn func(Transaction) error) error {
154 return nil
155 },
156 }
157
158 opts := Options{
159 OnUpdate: func(doc DocInterface) error {
160 updateCalled = true
161 return nil
162 },
163 }
164
165 conn := NewSyncConn(mockDoc, opts)
166 payload := []byte{0x01, 0x02, 0x03}
167
168 err := conn.handleUpdate(payload)
169 if err != nil {
170 t.Fatalf("handleUpdate failed: %v", err)
171 }
172
173 if !updateCalled {
174 t.Error("expected onUpdate callback to be called")
175 }
176}
177
178func TestHandleAwarenessWithCallback(t *testing.T) {
179 awarenessCalled := false
180 mockDoc := &mockDoc{}
181
182 conn := NewSyncConn(mockDoc, Options{})
183 conn.SetAwarenessUpdate(func([]byte) error {
184 awarenessCalled = true
185 return nil
186 })
187
188 payload := []byte{0x01, 0x02, 0x03}
189 err := conn.handleAwareness(payload)
190 if err != nil {
191 t.Fatalf("handleAwareness failed: %v", err)
192 }
193
194 if !awarenessCalled {
195 t.Error("expected awareness callback to be called")
196 }
197}
198
199func TestHandleAwarenessNoCallback(t *testing.T) {
200 mockDoc := &mockDoc{}
201
202 conn := NewSyncConn(mockDoc, Options{})
203 payload := []byte{0x01, 0x02, 0x03}
204
205 err := conn.handleAwareness(payload)
206 if err != nil {
207 t.Fatalf("handleAwareness without callback failed: %v", err)
208 }
209}
210
211func TestSyncConnSetDoc(t *testing.T) {
212 conn := NewSyncConn(nil, Options{})
213
214 newDoc := &mockDoc{}
215 conn.SetDoc(newDoc)
216
217 // Verify doc was set by checking internal doc field
218 // (the setter exists, we just can't easily verify without more internals)
219 if conn == nil {
220 t.Error("SetDoc should not panic")
221 }
222}
223
224func TestSyncConnInjectConn(t *testing.T) {
225 conn := NewSyncConn(nil, Options{})
226
227 // InjectConn should not panic
228 conn.InjectConn(nil)
229}
230
231func TestSyncConnSetAwarenessUpdate(t *testing.T) {
232 conn := NewSyncConn(nil, Options{})
233
234 fn := func([]byte) error { return nil }
235 conn.SetAwarenessUpdate(fn)
236}
237
238type mockConn struct{}
239
240func (m *mockConn) Read(ctx interface{}) (websocket.MessageType, interface{ Read([]byte) (int, error) }, error) {
241 return 0, nil, nil
242}
243
244func (m *mockConn) Write(ctx interface{}, typ websocket.MessageType, p []byte) error {
245 return nil
246}
247
248func (m *mockConn) Close(status websocket.StatusCode, reason string) error {
249 return nil
250}