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}