awareness_test.go

  1package sync
  2
  3import (
  4	"testing"
  5	"time"
  6)
  7
  8func TestNewAwarenessClient(t *testing.T) {
  9	client := NewAwarenessClient(123, time.Minute, func() {})
 10
 11	if client == nil {
 12		t.Error("expected non-nil client")
 13	}
 14
 15	if client.clients == nil {
 16		t.Error("expected clients map to be initialized")
 17	}
 18
 19	if client.clientID != 123 {
 20		t.Errorf("expected clientID 123, got %d", client.clientID)
 21	}
 22}
 23
 24func TestAwarenessStateStruct(t *testing.T) {
 25	state := AwarenessState{
 26		ClientID: 123,
 27		Clock:    456,
 28		State:    map[string]interface{}{"key": "value"},
 29	}
 30
 31	if state.ClientID != 123 {
 32		t.Errorf("expected ClientID 123, got %d", state.ClientID)
 33	}
 34
 35	if state.Clock != 456 {
 36		t.Errorf("expected Clock 456, got %d", state.Clock)
 37	}
 38
 39	if state.State["key"] != "value" {
 40		t.Errorf("expected State[key] value, got %v", state.State["key"])
 41	}
 42}
 43
 44func TestAwarenessClientStates(t *testing.T) {
 45	client := NewAwarenessClient(1, time.Minute, func() {})
 46
 47	states := client.States()
 48	if len(states) != 0 {
 49		t.Errorf("expected empty states, got %d", len(states))
 50	}
 51
 52	client.mu.Lock()
 53	client.clients[1] = AwarenessState{
 54		ClientID: 1,
 55		Clock:    10,
 56		State:    map[string]interface{}{"name": "alice"},
 57	}
 58	client.clients[2] = AwarenessState{
 59		ClientID: 2,
 60		Clock:    20,
 61		State:    map[string]interface{}{"name": "bob"},
 62	}
 63	client.mu.Unlock()
 64
 65	states = client.States()
 66	if len(states) != 2 {
 67		t.Errorf("expected 2 states, got %d", len(states))
 68	}
 69}
 70
 71func TestAwarenessClientLastUpdate(t *testing.T) {
 72	client := NewAwarenessClient(1, time.Minute, func() {})
 73
 74	before := client.lastUpdate
 75
 76	time.Sleep(10 * time.Millisecond)
 77
 78	client.mu.Lock()
 79	client.lastUpdate = time.Now()
 80	client.mu.Unlock()
 81
 82	after := client.lastUpdate
 83
 84	if before.Equal(after) {
 85		t.Error("expected lastUpdate to be updated")
 86	}
 87}
 88
 89func TestAwarenessClientTimeoutNoOtherClients(t *testing.T) {
 90	disconnected := false
 91	client := NewAwarenessClient(1, time.Minute, func() {
 92		disconnected = true
 93	})
 94
 95	client.mu.Lock()
 96	client.clients[1] = AwarenessState{
 97		ClientID: 1,
 98		Clock:    10,
 99		State:    map[string]interface{}{"name": "self"},
100	}
101	client.mu.Unlock()
102
103	client.checkTimeout()
104
105	if disconnected {
106		t.Error("should not disconnect when only self client exists")
107	}
108}
109
110func TestAwarenessClientTimeoutWithOtherClients(t *testing.T) {
111	disconnected := false
112	client := NewAwarenessClient(1, time.Minute, func() {
113		disconnected = true
114	})
115
116	client.mu.Lock()
117	client.clients[1] = AwarenessState{
118		ClientID: 1,
119		Clock:    10,
120		State:    map[string]interface{}{"name": "self"},
121	}
122	client.clients[2] = AwarenessState{
123		ClientID: 2,
124		Clock:    20,
125		State:    map[string]interface{}{"name": "other"},
126	}
127	client.mu.Unlock()
128
129	client.checkTimeout()
130
131	if disconnected {
132		t.Error("should not disconnect when other clients exist")
133	}
134}
135
136func TestAwarenessHandleUpdateEmptyData(t *testing.T) {
137	client := NewAwarenessClient(1, time.Minute, func() {})
138
139	err := client.HandleUpdate([]byte{})
140	if err != nil {
141		t.Errorf("expected no error for empty data, got %v", err)
142	}
143}
144
145func TestAwarenessHandleUpdateTruncatedData(t *testing.T) {
146	client := NewAwarenessClient(1, time.Minute, func() {})
147
148	// Data with only message type (count=0), should work
149	err := client.HandleUpdate([]byte{0})
150	if err != nil {
151		t.Errorf("expected no error for zero count, got %v", err)
152	}
153
154	// Data with count=1 but no client ID
155	err = client.HandleUpdate([]byte{1})
156	if err != nil {
157		t.Errorf("expected no error for truncated data with count, got %v", err)
158	}
159}
160
161func TestAwarenessHandleUpdateWithState(t *testing.T) {
162	client := NewAwarenessClient(1, time.Minute, func() {})
163
164	// Build a valid awareness update with one client
165	stateJSON := []byte(`{"name":"alice"}`)
166	count := appendVarUint(nil, 1)
167	clientID := appendVarUint(nil, 1)
168	clock := appendVarUint(nil, 1)
169	data := append(append(append(count, clientID...), clock...), stateJSON...)
170
171	err := client.HandleUpdate(data)
172	if err != nil {
173		t.Errorf("failed to handle update: %v", err)
174	}
175
176	states := client.States()
177	if len(states) != 1 {
178		t.Errorf("expected 1 state, got %d", len(states))
179	}
180
181	if states[0].State["name"] != "alice" {
182		t.Errorf("expected state name 'alice', got %v", states[0].State["name"])
183	}
184}
185
186func TestAwarenessClientID(t *testing.T) {
187	client := NewAwarenessClient(123, time.Minute, func() {})
188
189	if client.ClientID() != 123 {
190		t.Errorf("expected clientID 123, got %d", client.ClientID())
191	}
192}
193
194func TestAwarenessHandleUpdateMultipleClients(t *testing.T) {
195	client := NewAwarenessClient(1, time.Minute, func() {})
196
197	// Note: The current implementation only processes the first client
198	// because it sets offset = len(data) after reading each state.
199	// This test verifies that at least one client is processed correctly.
200	state1 := []byte(`{"name":"alice"}`)
201
202	var data []byte
203	data = appendVarUint(data, 2) // count = 2, but only first will be read
204	data = appendVarUint(data, 1)
205	data = appendVarUint(data, 1)
206	data = append(data, state1...)
207
208	err := client.HandleUpdate(data)
209	if err != nil {
210		t.Errorf("failed to handle update: %v", err)
211	}
212
213	states := client.States()
214	if len(states) != 1 {
215		t.Errorf("expected 1 state (implementation only reads first), got %d", len(states))
216	}
217
218	if states[0].State["name"] != "alice" {
219		t.Errorf("expected state name 'alice', got %v", states[0].State["name"])
220	}
221}