github.com/segmentio/encoding@v0.4.0/proto/rewrite_test.go (about) 1 package proto 2 3 import ( 4 "reflect" 5 "testing" 6 ) 7 8 func TestRewrite(t *testing.T) { 9 type message struct { 10 A int 11 B float32 12 C float64 13 D string 14 M *message 15 } 16 17 tests := []struct { 18 scenario string 19 in message 20 out message 21 rw Rewriter 22 }{ 23 { 24 scenario: "identity", 25 in: message{A: 42, M: &message{A: 1}}, 26 out: message{A: 42, M: &message{A: 1}}, 27 rw: MessageRewriter(nil), 28 }, 29 30 { 31 scenario: "rewrite field 1", 32 in: message{A: 21}, 33 out: message{A: 42}, 34 rw: MessageRewriter{ 35 1: FieldNumber(1).Int(42), 36 }, 37 }, 38 39 { 40 scenario: "rewrite field 2", 41 in: message{A: 21, B: 0.125}, 42 out: message{A: 21, B: -1}, 43 rw: MessageRewriter{ 44 2: FieldNumber(2).Float32(-1), 45 }, 46 }, 47 48 { 49 scenario: "rewrite field 3", 50 in: message{A: 21, B: 0.125, C: 0.0}, 51 out: message{A: 21, B: 0.125, C: 1.0}, 52 rw: MessageRewriter{ 53 3: FieldNumber(3).Float64(+1), 54 }, 55 }, 56 57 { 58 scenario: "rewrite field 4", 59 in: message{A: 21, B: 0.125, C: 1.0, D: "A"}, 60 out: message{A: 21, B: 0.125, C: 1.0, D: "Hello World!"}, 61 rw: MessageRewriter{ 62 4: FieldNumber(4).String("Hello World!"), 63 }, 64 }, 65 } 66 67 for _, test := range tests { 68 t.Run(test.scenario, func(t *testing.T) { 69 b, err := Marshal(test.in) 70 if err != nil { 71 t.Fatal(err) 72 } 73 74 b, err = test.rw.Rewrite(nil, b) 75 if err != nil { 76 t.Fatal(err) 77 } 78 79 m := message{} 80 if err := Unmarshal(b, &m); err != nil { 81 t.Fatal(err) 82 } 83 84 if !reflect.DeepEqual(m, test.out) { 85 t.Errorf("messages mismatch:\nwant: %+v\ngot: %+v", test.out, m) 86 } 87 }) 88 } 89 } 90 91 func TestParseRewriteTemplate(t *testing.T) { 92 type submessage struct { 93 Question string `protobuf:"bytes,1,opt,name=question,proto3"` 94 Answer string `protobuf:"bytes,2,opt,name=answer,proto3"` 95 } 96 97 type message struct { 98 Field1 bool `protobuf:"varint,1,opt,name=field_1,proto3"` 99 100 Field2 int `protobuf:"varint,2,opt,name=field_2,proto3"` 101 Field3 int32 `protobuf:"varint,3,opt,name=field_3,proto3"` 102 Field4 int64 `protobuf:"varint,4,opt,name=field_4,proto3"` 103 104 Field5 uint `protobuf:"varint,5,opt,name=field_5,proto3"` 105 Field6 uint32 `protobuf:"varint,6,opt,name=field_6,proto3"` 106 Field7 uint64 `protobuf:"varint,7,opt,name=field_7,proto3"` 107 108 Field8 int32 `protobuf:"zigzag32,8,opt,name=field_8,proto3"` 109 Field9 int64 `protobuf:"zigzag64,9,opt,name=field_9,proto3"` 110 111 Field10 float32 `protobuf:"fixed32,10,opt,name=field_10,proto3"` 112 Field11 float64 `protobuf:"fixed64,11,opt,name=field_11,proto3"` 113 114 Field12 string `protobuf:"bytes,12,opt,name=field_12,proto3"` 115 Field13 []byte `protobuf:"bytes,13,opt,name=field_13,proto3"` 116 117 Zero1 bool `protobuf:"varint,21,opt,name=zero_1,proto3"` 118 Zero2 int `protobuf:"varint,22,opt,name=zero_2,proto3"` 119 Zero3 int32 `protobuf:"varint,23,opt,name=zero_3,proto3"` 120 Zero4 int64 `protobuf:"varint,24,opt,name=zero_4,proto3"` 121 Zero5 uint `protobuf:"varint,25,opt,name=zero_5,proto3"` 122 Zero6 uint32 `protobuf:"varint,26,opt,name=zero_6,proto3"` 123 Zero7 uint64 `protobuf:"varint,27,opt,name=zero_7,proto3"` 124 Zero8 float32 `protobuf:"fixed32,28,opt,name=zero_8,proto3"` 125 Zero9 float64 `protobuf:"fixed64,29,opt,name=zero_9,proto3"` 126 127 Subfield *submessage `protobuf:"bytes,99,opt,name=subfield,proto3"` 128 Submessages []submessage `protobuf:"bytes,100,rep,name=submessages,proto3"` 129 130 Mapping map[string]int `protobuf:"bytes,200,opt,name=mapping,proto3"` 131 } 132 133 original := &message{ 134 Field1: false, 135 136 Field2: -1, 137 Field3: -2, 138 Field4: -3, 139 140 Field5: 1, 141 Field6: 2, 142 Field7: 3, 143 144 Field8: -10, 145 Field9: -11, 146 147 Field10: 1.0, 148 Field11: 2.0, 149 150 Field12: "field 12", 151 Field13: nil, 152 153 Zero1: true, 154 Zero2: 102, 155 Zero3: 103, 156 Zero4: 104, 157 Zero5: 105, 158 Zero6: 106, 159 Zero7: 107, 160 Zero8: 0.108, 161 Zero9: 0.109, 162 163 Subfield: &submessage{ 164 Answer: "Good!", 165 }, 166 167 Submessages: []submessage{ 168 {Question: "Q1?", Answer: "A1"}, 169 {Question: "Q2?", Answer: "A2"}, 170 {Question: "Q3?", Answer: "A3"}, 171 }, 172 173 Mapping: map[string]int{ 174 "hello": 1, 175 "world": 2, 176 }, 177 } 178 179 expected := &message{ 180 Field1: true, 181 182 Field2: 2, 183 Field3: 3, 184 Field4: 4, 185 186 Field5: 10, 187 Field6: 11, 188 Field7: 12, 189 190 Field8: -21, 191 Field9: -42, 192 193 Field10: 0.25, 194 Field11: 0.5, 195 196 Field12: "Hello!", 197 Field13: []byte("World!"), 198 199 Subfield: &submessage{ 200 Question: "How are you?", 201 Answer: "Good!", 202 }, 203 204 Submessages: []submessage{ 205 {Question: "Q1?", Answer: "A1"}, 206 {Question: "Q2?", Answer: "A2"}, 207 {Question: "Q3?", Answer: "Hello World!"}, 208 }, 209 210 Mapping: map[string]int{ 211 "answer": 42, 212 }, 213 } 214 215 rw, err := ParseRewriteTemplate(TypeOf(reflect.TypeOf(original)), []byte(`{ 216 "field_1": true, 217 218 "field_2": 2, 219 "field_3": 3, 220 "field_4": 4, 221 222 "field_5": 10, 223 "field_6": 11, 224 "field_7": 12, 225 226 "field_8": -21, 227 "field_9": -42, 228 229 "field_10": 0.25, 230 "field_11": 0.5, 231 232 "field_12": "Hello!", 233 "field_13": "World!", 234 235 "zero_1": null, 236 "zero_2": null, 237 "zero_3": null, 238 "zero_4": null, 239 "zero_5": null, 240 "zero_6": null, 241 "zero_7": null, 242 "zero_8": null, 243 "zero_9": null, 244 245 "subfield": { 246 "question": "How are you?" 247 }, 248 249 "submessages": [ 250 { 251 "question": "Q1?", 252 "answer": "A1" 253 }, 254 { 255 "question": "Q2?", 256 "answer": "A2" 257 }, 258 { 259 "question": "Q3?", 260 "answer": "Hello World!" 261 } 262 ], 263 264 "mapping": { 265 "answer": 42 266 } 267 }`)) 268 if err != nil { 269 t.Fatal(err) 270 } 271 272 b1, err := Marshal(original) 273 if err != nil { 274 t.Fatal(err) 275 } 276 277 b2, err := rw.Rewrite(nil, b1) 278 if err != nil { 279 t.Fatal(err) 280 } 281 282 found := &message{} 283 if err := Unmarshal(b2, &found); err != nil { 284 t.Fatal(err) 285 } 286 287 if !reflect.DeepEqual(expected, found) { 288 t.Error("messages mismatch after rewrite") 289 t.Logf("want:\n%+v", expected) 290 t.Logf("got:\n%+v", found) 291 } 292 } 293 294 func BenchmarkRewrite(b *testing.B) { 295 type message struct { 296 A int 297 B float32 298 C float64 299 D string 300 } 301 302 in := message{A: 21, B: 0.125, D: "A"} 303 rw := MessageRewriter{ 304 1: FieldNumber(1).Int(42), 305 2: FieldNumber(2).Float32(-1), 306 3: FieldNumber(3).Float64(+1), 307 4: FieldNumber(4).String("Hello World!"), 308 } 309 310 p, err := Marshal(in) 311 if err != nil { 312 b.Fatal(err) 313 } 314 315 out := make([]byte, 0, 2*cap(p)) 316 317 for i := 0; i < b.N; i++ { 318 out, err = rw.Rewrite(out[:0], p) 319 } 320 } 321 322 func TestRewriteStructIdentity(t *testing.T) { 323 type Node struct { 324 Next []Node `protobuf:"bytes,1,rep,name=next,proto3" json:"next"` 325 Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name"` 326 Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type"` 327 On uint32 `protobuf:"varint,4,opt,name=on,proto3,enum=op.Status" json:"on,omitempty"` 328 Key string `protobuf:"bytes,5,opt,name=key,proto3" json:"key,omitempty"` 329 Seed uint64 `protobuf:"fixed64,6,opt,name=seed,proto3" json:"seed,omitempty"` 330 ScheduleAfter uint32 `protobuf:"varint,7,opt,name=schedule_after,json=scheduleAfter,proto3" json:"schedule_after,omitempty"` 331 ExpireAfter uint32 `protobuf:"varint,8,opt,name=expire_after,json=expireAfter,proto3" json:"expire_after,omitempty"` 332 BackoffCoefficient uint32 `protobuf:"varint,9,opt,name=backoff_coefficient,json=backoffCoefficient,proto3" json:"backoff_coefficient,omitempty"` 333 BackoffMinDelay uint32 `protobuf:"varint,10,opt,name=backoff_min_delay,json=backoffMinDelay,proto3" json:"backoff_min_delay,omitempty"` 334 BackoffMaxDelay uint32 `protobuf:"varint,11,opt,name=backoff_max_delay,json=backoffMaxDelay,proto3" json:"backoff_max_delay,omitempty"` 335 ExecutionTimeout uint32 `protobuf:"varint,12,opt,name=execution_timeout,json=executionTimeout,proto3" json:"execution_timeout,omitempty"` 336 NextLength uint64 `protobuf:"varint,13,opt,name=next_length,json=nextLength,proto3" json:"next_length,omitempty"` 337 BatchMaxBytes uint32 `protobuf:"varint,14,opt,name=batch_max_bytes,json=batchMaxBytes,proto3" json:"batch_max_bytes,omitempty"` 338 BatchMaxCount uint32 `protobuf:"varint,15,opt,name=batch_max_count,json=batchMaxCount,proto3" json:"batch_max_count,omitempty"` 339 BatchTimeout uint32 `protobuf:"varint,16,opt,name=batch_timeout,json=batchTimeout,proto3" json:"batch_timeout,omitempty"` 340 BatchKey [16]byte `protobuf:"bytes,17,opt,name=batch_key,json=batchKey,proto3,customtype=U128" json:"batch_key"` 341 } 342 343 type Header struct { 344 Flows string `protobuf:"bytes,1,opt,name=flows,proto3" json:"flows,omitempty"` 345 Root Node `protobuf:"bytes,2,opt,name=root,proto3" json:"root"` 346 TraceContext []byte `protobuf:"bytes,6,opt,name=trace_context,json=traceContext,proto3" json:"trace_context"` 347 ContentLength int64 `protobuf:"varint,7,opt,name=content_length,json=contentLength,proto3" json:"content_length,omitempty"` 348 ContentType string `protobuf:"bytes,8,opt,name=content_type,json=contentType,proto3" json:"content_type"` 349 } 350 351 b := []byte{ 352 0xa, 0xd, 0x66, 0x6c, 0x6f, 0x77, 0x2d, 0x42, 0x3a, 0x66, 0x6c, 0x6f, 0x77, 0x2d, 0x30, 0x12, 353 0x7a, 0xa, 0x30, 0x12, 0xc, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 354 0x64, 0x1a, 0x0, 0x20, 0x2, 0x31, 0x69, 0xf2, 0xc9, 0xd0, 0xc1, 0x2f, 0xe0, 0x80, 0x68, 0x65, 355 0x8a, 0x1, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 356 0x0, 0x0, 0x0, 0x12, 0x4, 0x74, 0x65, 0x73, 0x74, 0x1a, 0x4, 0x68, 0x74, 0x74, 0x70, 0x31, 357 0xa, 0x7f, 0xf5, 0xf8, 0x13, 0x1d, 0xfb, 0x17, 0x38, 0xc1, 0xd, 0x40, 0xff, 0xdb, 0x1, 0x48, 358 0xc6, 0xf, 0x50, 0xbd, 0x1b, 0x58, 0x9a, 0xbe, 0x2, 0x60, 0x88, 0x27, 0x68, 0x5d, 0x70, 0x80, 359 0x80, 0x4, 0x78, 0xa, 0x80, 0x1, 0xd0, 0xf, 0x8a, 0x1, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 360 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x1c, 0xa, 0x10, 0x2a, 0x43, 361 0x4c, 0xf3, 0x8c, 0x67, 0x48, 0x8f, 0xe, 0xca, 0xe8, 0x28, 0x96, 0x6c, 0x2b, 0xe4, 0x12, 362 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x91, 0x66, 0x42, 0x18, 0x61, 0x70, 363 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x2d, 364 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 365 } 366 367 m := Header{} 368 if err := Unmarshal(b, &m); err != nil { 369 t.Fatal(err) 370 } 371 372 r, err := ParseRewriteTemplate(TypeOf(reflect.TypeOf(m)), []byte(`{"root":{}}`)) 373 if err != nil { 374 t.Fatal(err) 375 } 376 377 c, err := r.Rewrite(nil, b) 378 if err != nil { 379 t.Fatal(err) 380 } 381 382 x := Header{} 383 if err := Unmarshal(c, &x); err != nil { 384 t.Fatal(err) 385 } 386 387 if !reflect.DeepEqual(m, x) { 388 t.Errorf("messages mismatch") 389 t.Logf("want: %+v", m) 390 t.Logf("got: %+v", x) 391 } 392 }