1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
package protocol
import (
"strings"
"testing"
"github.com/mimecast/dtail/internal/testutil"
)
func TestProtocolConstants(t *testing.T) {
// Test that protocol version follows expected format
t.Run("protocol version format", func(t *testing.T) {
// Should be in format X.Y
parts := strings.Split(ProtocolCompat, ".")
if len(parts) != 2 {
t.Errorf("ProtocolCompat should be in X.Y format, got %q", ProtocolCompat)
}
})
// Test message delimiter uniqueness
t.Run("delimiter uniqueness", func(t *testing.T) {
// Note: CSVDelimiter and AggregateGroupKeyCombinator intentionally use the same delimiter
delimiters := map[string]string{
"MessageDelimiter": string(MessageDelimiter),
"FieldDelimiter": FieldDelimiter,
"AggregateKVDelimiter": AggregateKVDelimiter,
"AggregateDelimiter": AggregateDelimiter,
}
// Check that protocol delimiters are unique (excluding CSV/GroupKey which share ",")
seen := make(map[string]string)
for name, d := range delimiters {
if prevName, exists := seen[d]; exists {
t.Errorf("Delimiter %q used by both %s and %s", d, prevName, name)
}
seen[d] = name
}
// Verify CSV and GroupKey combinator are the same (by design)
testutil.AssertEqual(t, CSVDelimiter, AggregateGroupKeyCombinator)
})
// Test that delimiters are not empty
t.Run("non-empty delimiters", func(t *testing.T) {
if MessageDelimiter == 0 {
t.Error("MessageDelimiter should not be zero byte")
}
if FieldDelimiter == "" {
t.Error("FieldDelimiter should not be empty")
}
if CSVDelimiter == "" {
t.Error("CSVDelimiter should not be empty")
}
if AggregateKVDelimiter == "" {
t.Error("AggregateKVDelimiter should not be empty")
}
if AggregateDelimiter == "" {
t.Error("AggregateDelimiter should not be empty")
}
if AggregateGroupKeyCombinator == "" {
t.Error("AggregateGroupKeyCombinator should not be empty")
}
})
// Test expected values (for documentation and regression prevention)
t.Run("expected values", func(t *testing.T) {
testutil.AssertEqual(t, byte('¬'), MessageDelimiter)
testutil.AssertEqual(t, "|", FieldDelimiter)
testutil.AssertEqual(t, ",", CSVDelimiter)
testutil.AssertEqual(t, "≔", AggregateKVDelimiter)
testutil.AssertEqual(t, "∥", AggregateDelimiter)
testutil.AssertEqual(t, ",", AggregateGroupKeyCombinator)
testutil.AssertEqual(t, "4.1", ProtocolCompat)
})
// Test that special delimiters don't conflict with common characters
t.Run("delimiter safety", func(t *testing.T) {
// Common characters that shouldn't be used as delimiters
commonChars := []string{
" ", "\n", "\r", "\t", // Whitespace
"a", "e", "i", "o", "u", // Common letters
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", // Digits
".", ":", ";", "-", "_", "/", "\\", // Common punctuation in logs
}
delimiters := []string{
string(MessageDelimiter),
FieldDelimiter,
AggregateKVDelimiter,
AggregateDelimiter,
}
for _, delimiter := range delimiters {
for _, common := range commonChars {
if delimiter == common {
t.Errorf("Delimiter %q conflicts with common character", delimiter)
}
}
}
})
}
func TestDelimiterUsage(t *testing.T) {
// Test typical protocol message construction and parsing patterns
t.Run("message construction", func(t *testing.T) {
// Simulate building a protocol message
fields := []string{"HEALTH", "OK", "server1", "100"}
message := strings.Join(fields, FieldDelimiter)
// Should be able to reconstruct fields
parsed := strings.Split(message, FieldDelimiter)
if len(parsed) != len(fields) {
t.Errorf("Expected %d fields, got %d", len(fields), len(parsed))
}
for i, field := range fields {
testutil.AssertEqual(t, field, parsed[i])
}
})
t.Run("aggregate message construction", func(t *testing.T) {
// Simulate MapReduce aggregation message
key := "error"
value := "42"
kvPair := key + AggregateKVDelimiter + value
// Should be able to parse key-value
parts := strings.Split(kvPair, AggregateKVDelimiter)
if len(parts) != 2 {
t.Fatalf("Expected 2 parts in KV pair, got %d", len(parts))
}
testutil.AssertEqual(t, key, parts[0])
testutil.AssertEqual(t, value, parts[1])
})
t.Run("multiple messages", func(t *testing.T) {
// Simulate multiple messages in a stream
messages := []string{"MSG1", "MSG2", "MSG3"}
// Build stream with message delimiter between messages
var parts []string
for _, msg := range messages {
parts = append(parts, msg)
}
// Join with delimiter
delimiter := string(MessageDelimiter)
stream := strings.Join(parts, delimiter)
// Parse messages back
parsed := strings.Split(stream, delimiter)
if len(parsed) != len(messages) {
t.Errorf("Expected %d messages, got %d", len(messages), len(parsed))
}
for i, msg := range messages {
if i < len(parsed) {
testutil.AssertEqual(t, msg, parsed[i])
}
}
})
}
func TestCSVDelimiter(t *testing.T) {
// Test CSV parsing scenarios
t.Run("csv field parsing", func(t *testing.T) {
csvLine := "field1,field2,field3,field4"
fields := strings.Split(csvLine, CSVDelimiter)
if len(fields) != 4 {
t.Errorf("Expected 4 CSV fields, got %d", len(fields))
}
expected := []string{"field1", "field2", "field3", "field4"}
for i, field := range expected {
testutil.AssertEqual(t, field, fields[i])
}
})
}
func TestGroupKeyCombinator(t *testing.T) {
// Test group key combination for MapReduce
t.Run("combine group keys", func(t *testing.T) {
keys := []string{"host", "service", "level"}
combined := strings.Join(keys, AggregateGroupKeyCombinator)
// Should be able to split back
parsed := strings.Split(combined, AggregateGroupKeyCombinator)
if len(parsed) != len(keys) {
t.Errorf("Expected %d keys, got %d", len(keys), len(parsed))
}
for i, key := range keys {
testutil.AssertEqual(t, key, parsed[i])
}
})
}
|