encoding_test.go
1package sync
2
3import (
4 "bytes"
5 "testing"
6)
7
8func TestWriteVarUint(t *testing.T) {
9 tests := []struct {
10 name string
11 value uint64
12 expected []byte
13 }{
14 {"zero", 0, []byte{0x00}},
15 {"one", 1, []byte{0x01}},
16 {"127 (max single byte)", 127, []byte{0x7f}},
17 {"128 (two bytes)", 128, []byte{0x80, 0x01}},
18 {"300 (two bytes)", 300, []byte{0xac, 0x02}},
19 {"16383 (max two bytes)", 16383, []byte{0xff, 0x7f}},
20 {"16384 (three bytes)", 16384, []byte{0x80, 0x80, 0x01}},
21 {"max uint64", 0xffffffffffffffff, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}},
22 }
23
24 for _, tt := range tests {
25 t.Run(tt.name, func(t *testing.T) {
26 buf := make([]byte, 20)
27 n := writeVarUint(buf, tt.value)
28 if !bytes.Equal(buf[:n], tt.expected) {
29 t.Errorf("writeVarUint(%d) = %v, want %v", tt.value, buf[:n], tt.expected)
30 }
31 })
32 }
33}
34
35func TestReadVarUint(t *testing.T) {
36 tests := []struct {
37 name string
38 data []byte
39 expected uint64
40 wantErr bool
41 }{
42 {"zero", []byte{0x00}, 0, false},
43 {"one", []byte{0x01}, 1, false},
44 {"127", []byte{0x7f}, 127, false},
45 {"128", []byte{0x80, 0x01}, 128, false},
46 {"300", []byte{0xac, 0x02}, 300, false},
47 {"16383", []byte{0xff, 0x7f}, 16383, false},
48 {"16384", []byte{0x80, 0x80, 0x01}, 16384, false},
49 {"max uint64", []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}, 0xffffffffffffffff, false},
50 {"empty input", []byte{}, 0, true},
51 {"incomplete continuation", []byte{0x80}, 0, true},
52 }
53
54 for _, tt := range tests {
55 t.Run(tt.name, func(t *testing.T) {
56 val, n, err := readVarUint(tt.data)
57 if (err != nil) != tt.wantErr {
58 t.Errorf("readVarUint(%v) error = %v, wantErr %v", tt.data, err, tt.wantErr)
59 return
60 }
61 if !tt.wantErr && val != tt.expected {
62 t.Errorf("readVarUint(%v) = %d, want %d", tt.data, val, tt.expected)
63 }
64 if !tt.wantErr && n == 0 {
65 t.Error("readVarUint should return non-zero bytes read")
66 }
67 })
68 }
69}
70
71func TestEncodeVarByteArray(t *testing.T) {
72 tests := []struct {
73 name string
74 data []byte
75 expected []byte
76 }{
77 {"empty", []byte{}, []byte{0}},
78 {"single byte", []byte{0x01}, []byte{1, 0x01}},
79 {"multiple bytes", []byte{0x01, 0x02, 0x03}, []byte{3, 0x01, 0x02, 0x03}},
80 {"longer data", []byte(bytes.Repeat([]byte{0xab}, 100)), append([]byte{100}, bytes.Repeat([]byte{0xab}, 100)...)},
81 }
82
83 for _, tt := range tests {
84 t.Run(tt.name, func(t *testing.T) {
85 result := encodeVarByteArray(tt.data)
86 if !bytes.Equal(result, tt.expected) {
87 t.Errorf("encodeVarByteArray(%v) = %v, want %v", tt.data, result, tt.expected)
88 }
89 })
90 }
91}
92
93func TestEncodeMessage(t *testing.T) {
94 tests := []struct {
95 name string
96 msgType uint8
97 payload []byte
98 expected []byte
99 }{
100 {"SyncStep1 with empty payload", SyncStep1, []byte{}, []byte{0, 0}},
101 {"SyncStep2 with payload", SyncStep2, []byte{0x01, 0x02}, []byte{1, 2, 0x01, 0x02}},
102 {"Update with empty payload", Update, []byte{}, []byte{2, 0}},
103 {"Update with payload", Update, []byte{0xff, 0xff}, []byte{2, 2, 0xff, 0xff}},
104 {"large payload", SyncStep1, []byte(bytes.Repeat([]byte{0xaa}, 50)), func() []byte {
105 buf := []byte{0, 50}
106 return append(buf, bytes.Repeat([]byte{0xaa}, 50)...)
107 }()},
108 }
109
110 for _, tt := range tests {
111 t.Run(tt.name, func(t *testing.T) {
112 result := encodeMessage(tt.msgType, tt.payload)
113 if !bytes.Equal(result, tt.expected) {
114 t.Errorf("encodeMessage(%d, %v) = %v, want %v", tt.msgType, tt.payload, result, tt.expected)
115 }
116 })
117 }
118}
119
120func TestMessageTypeConstants(t *testing.T) {
121 if SyncStep1 != 0 {
122 t.Errorf("expected SyncStep1=0, got %d", SyncStep1)
123 }
124 if SyncStep2 != 1 {
125 t.Errorf("expected SyncStep2=1, got %d", SyncStep2)
126 }
127 if Update != 2 {
128 t.Errorf("expected Update=2, got %d", Update)
129 }
130}
131
132func TestWriteReadVarUintRoundTrip(t *testing.T) {
133 values := []uint64{0, 1, 127, 128, 300, 16383, 16384, 1000, 100000, 0xffffffff}
134 for _, v := range values {
135 buf := make([]byte, 20)
136 n := writeVarUint(buf, v)
137 val, read, err := readVarUint(buf[:n])
138 if err != nil {
139 t.Errorf("round trip failed for %d: %v", v, err)
140 }
141 if val != v {
142 t.Errorf("round trip: wrote %d, read %d", v, val)
143 }
144 if read != n {
145 t.Errorf("round trip: wrote %d bytes, read %d bytes", n, read)
146 }
147 }
148}
149
150func TestReadVarUintOverflow(t *testing.T) {
151 overflowData := make([]byte, 11)
152 for i := 0; i < 10; i++ {
153 overflowData[i] = 0x80
154 }
155 overflowData[10] = 0x01
156
157 _, _, err := readVarUint(overflowData)
158 if err == nil {
159 t.Error("expected error for varint overflow")
160 }
161}
162
163func TestAppendVarUint(t *testing.T) {
164 tests := []struct {
165 name string
166 value uint64
167 }{
168 {"zero", 0},
169 {"one", 1},
170 {"127", 127},
171 {"128", 128},
172 {"large", 0xFFFFFFFF},
173 }
174
175 for _, tt := range tests {
176 t.Run(tt.name, func(t *testing.T) {
177 result := appendVarUint(nil, tt.value)
178 val, n, err := readVarUint(result)
179 if err != nil {
180 t.Errorf("appendVarUint failed: %v", err)
181 }
182 if val != tt.value {
183 t.Errorf("appendVarUint(%d) = %d, want %d", tt.value, val, tt.value)
184 }
185 if n != len(result) {
186 t.Errorf("appendVarUint length mismatch: wrote %d, read %d", len(result), n)
187 }
188 })
189 }
190}
191
192func TestAppendVarByteArray(t *testing.T) {
193 tests := []struct {
194 name string
195 data []byte
196 }{
197 {"empty", []byte{}},
198 {"single byte", []byte{0x01}},
199 {"multiple bytes", []byte{0x01, 0x02, 0x03}},
200 }
201
202 for _, tt := range tests {
203 t.Run(tt.name, func(t *testing.T) {
204 result := appendVarByteArray(nil, tt.data)
205 if len(result) < 1 {
206 t.Error("encoded result too short")
207 }
208
209 length, n, err := readVarUint(result)
210 if err != nil {
211 t.Errorf("failed to read length: %v", err)
212 }
213
214 if length != uint64(len(tt.data)) {
215 t.Errorf("length mismatch: got %d, want %d", length, len(tt.data))
216 }
217
218 payload := result[n:]
219 if string(payload) != string(tt.data) {
220 t.Errorf("payload mismatch: got %v, want %v", payload, tt.data)
221 }
222 })
223 }
224}