github.com/Jeffail/benthos/v3@v3.65.0/lib/processor/avro_test.go (about) 1 package processor 2 3 import ( 4 "fmt" 5 "os" 6 "reflect" 7 "strconv" 8 "testing" 9 10 "github.com/Jeffail/benthos/v3/lib/log" 11 "github.com/Jeffail/benthos/v3/lib/message" 12 "github.com/Jeffail/benthos/v3/lib/metrics" 13 "github.com/Jeffail/benthos/v3/lib/types" 14 ) 15 16 func TestAvroBasic(t *testing.T) { 17 schema := `{ 18 "namespace": "foo.namespace.com", 19 "type": "record", 20 "name": "identity", 21 "fields": [ 22 { "name": "Name", "type": "string"}, 23 { "name": "Address", "type": ["null",{ 24 "namespace": "my.namespace.com", 25 "type": "record", 26 "name": "address", 27 "fields": [ 28 { "name": "City", "type": "string" }, 29 { "name": "State", "type": "string" } 30 ] 31 }],"default":null} 32 ] 33 }` 34 35 type testCase struct { 36 name string 37 operator string 38 encoding string 39 input []string 40 output []string 41 } 42 43 tests := []testCase{ 44 { 45 name: "textual to json 1", 46 operator: "to_json", 47 encoding: "textual", 48 input: []string{ 49 `{"Name":"foo","Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}}}`, 50 }, 51 output: []string{ 52 `{"Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}},"Name":"foo"}`, 53 }, 54 }, 55 { 56 name: "binary to json 1", 57 operator: "to_json", 58 encoding: "binary", 59 input: []string{ 60 "\x06foo\x02\x06foo\x06bar", 61 }, 62 output: []string{ 63 `{"Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}},"Name":"foo"}`, 64 }, 65 }, 66 /* 67 { 68 name: "single to json 1", 69 operator: "to_json", 70 encoding: "single", 71 input: []string{ 72 "\xc3\x01\x84>\xe0\xee\xbb\xf1Nj\x06foo\x02\x06foo\x06bar", 73 }, 74 output: []string{ 75 `{"Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}},"Name":"foo"}`, 76 }, 77 }, 78 */ 79 /* 80 // TODO: Unfortunately this serialisation is non-deterministic 81 { 82 name: "json to textual 1", 83 operator: "from_json", 84 encoding: "textual", 85 input: []string{ 86 `{"Name":"foo","Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}}}`, 87 }, 88 output: []string{ 89 `{"Name":"foo","Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}}}`, 90 }, 91 }, 92 */ 93 { 94 name: "json to binary 1", 95 operator: "from_json", 96 encoding: "binary", 97 input: []string{ 98 `{"Name":"foo","Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}}}`, 99 }, 100 output: []string{ 101 "\x06foo\x02\x06foo\x06bar", 102 }, 103 }, 104 /* 105 { 106 name: "json to single 1", 107 operator: "from_json", 108 encoding: "single", 109 input: []string{ 110 `{"Name":"foo","Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}}}`, 111 }, 112 output: []string{ 113 "\xc3\x01\x84>\xe0\xee\xbb\xf1Nj\x06foo\x02\x06foo\x06bar", 114 }, 115 }, 116 */ 117 } 118 119 for _, test := range tests { 120 t.Run(test.name, func(tt *testing.T) { 121 conf := NewConfig() 122 conf.Type = TypeAvro 123 conf.Avro.Operator = test.operator 124 conf.Avro.Encoding = test.encoding 125 conf.Avro.Schema = schema 126 127 proc, err := New(conf, nil, log.Noop(), metrics.Noop()) 128 if err != nil { 129 tt.Fatal(err) 130 } 131 132 input := message.New(nil) 133 for _, p := range test.input { 134 input.Append(message.NewPart([]byte(p))) 135 } 136 137 exp := make([][]byte, len(test.output)) 138 for i, p := range test.output { 139 exp[i] = []byte(p) 140 } 141 142 msgs, res := proc.ProcessMessage(input) 143 if res != nil { 144 tt.Fatal(res.Error()) 145 } 146 147 if len(msgs) != 1 { 148 tt.Fatalf("Expected one message, received: %v", len(msgs)) 149 } 150 if act := message.GetAllBytes(msgs[0]); !reflect.DeepEqual(act, exp) { 151 tt.Errorf("Unexpected output: %s != %s", exp, act) 152 tt.Logf("Part 0: %v", strconv.Quote(string(act[0]))) 153 } 154 msgs[0].Iter(func(i int, part types.Part) error { 155 if fail := part.Metadata().Get(FailFlagKey); len(fail) > 0 { 156 tt.Error(fail) 157 } 158 return nil 159 }) 160 }) 161 } 162 } 163 164 func TestAvroSchemaPath(t *testing.T) { 165 schema := `{ 166 "namespace": "foo.namespace.com", 167 "type": "record", 168 "name": "identity", 169 "fields": [ 170 { "name": "Name", "type": "string"}, 171 { "name": "Address", "type": ["null",{ 172 "namespace": "my.namespace.com", 173 "type": "record", 174 "name": "address", 175 "fields": [ 176 { "name": "City", "type": "string" }, 177 { "name": "State", "type": "string" } 178 ] 179 }],"default":null} 180 ] 181 }` 182 183 tmpSchemaFile, err := os.CreateTemp("", "benthos_avro_test") 184 if err != nil { 185 t.Fatal(err) 186 } 187 defer os.Remove(tmpSchemaFile.Name()) 188 189 // write schema definition to tmpfile 190 if _, err := tmpSchemaFile.WriteString(schema); err != nil { 191 t.Fatal(err) 192 } 193 194 type testCase struct { 195 name string 196 operator string 197 encoding string 198 input []string 199 output []string 200 } 201 202 tests := []testCase{ 203 { 204 name: "textual to json 1", 205 operator: "to_json", 206 encoding: "textual", 207 input: []string{ 208 `{"Name":"foo","Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}}}`, 209 }, 210 output: []string{ 211 `{"Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}},"Name":"foo"}`, 212 }, 213 }, 214 { 215 name: "binary to json 1", 216 operator: "to_json", 217 encoding: "binary", 218 input: []string{ 219 "\x06foo\x02\x06foo\x06bar", 220 }, 221 output: []string{ 222 `{"Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}},"Name":"foo"}`, 223 }, 224 }, 225 /* 226 { 227 name: "single to json 1", 228 operator: "to_json", 229 encoding: "single", 230 input: []string{ 231 "\xc3\x01\x84>\xe0\xee\xbb\xf1Nj\x06foo\x02\x06foo\x06bar", 232 }, 233 output: []string{ 234 `{"Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}},"Name":"foo"}`, 235 }, 236 }, 237 */ 238 /* 239 // TODO: Unfortunately this serialisation is non-deterministic 240 { 241 name: "json to textual 1", 242 operator: "from_json", 243 encoding: "textual", 244 input: []string{ 245 `{"Name":"foo","Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}}}`, 246 }, 247 output: []string{ 248 `{"Name":"foo","Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}}}`, 249 }, 250 }, 251 */ 252 { 253 name: "json to binary 1", 254 operator: "from_json", 255 encoding: "binary", 256 input: []string{ 257 `{"Name":"foo","Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}}}`, 258 }, 259 output: []string{ 260 "\x06foo\x02\x06foo\x06bar", 261 }, 262 }, 263 /* 264 { 265 name: "json to single 1", 266 operator: "from_json", 267 encoding: "single", 268 input: []string{ 269 `{"Name":"foo","Address":{"my.namespace.com.address":{"City":"foo","State":"bar"}}}`, 270 }, 271 output: []string{ 272 "\xc3\x01\x84>\xe0\xee\xbb\xf1Nj\x06foo\x02\x06foo\x06bar", 273 }, 274 }, 275 */ 276 } 277 278 for _, test := range tests { 279 t.Run(test.name, func(tt *testing.T) { 280 conf := NewConfig() 281 conf.Type = TypeAvro 282 conf.Avro.Operator = test.operator 283 conf.Avro.Encoding = test.encoding 284 conf.Avro.SchemaPath = fmt.Sprintf("file://%s", tmpSchemaFile.Name()) 285 286 proc, err := New(conf, nil, log.Noop(), metrics.Noop()) 287 if err != nil { 288 tt.Fatal(err) 289 } 290 291 input := message.New(nil) 292 for _, p := range test.input { 293 input.Append(message.NewPart([]byte(p))) 294 } 295 296 exp := make([][]byte, len(test.output)) 297 for i, p := range test.output { 298 exp[i] = []byte(p) 299 } 300 301 msgs, res := proc.ProcessMessage(input) 302 if res != nil { 303 tt.Fatal(res.Error()) 304 } 305 306 if len(msgs) != 1 { 307 tt.Fatalf("Expected one message, received: %v", len(msgs)) 308 } 309 if act := message.GetAllBytes(msgs[0]); !reflect.DeepEqual(act, exp) { 310 tt.Errorf("Unexpected output: %s != %s", exp, act) 311 tt.Logf("Part 0: %v", strconv.Quote(string(act[0]))) 312 } 313 msgs[0].Iter(func(i int, part types.Part) error { 314 if fail := part.Metadata().Get(FailFlagKey); len(fail) > 0 { 315 tt.Error(fail) 316 } 317 return nil 318 }) 319 }) 320 } 321 } 322 323 func TestAvroSchemaPathNotExist(t *testing.T) { 324 conf := NewConfig() 325 conf.Type = TypeAvro 326 conf.Avro.SchemaPath = "file://path_does_not_exist" 327 328 _, err := New(conf, nil, log.Noop(), metrics.Noop()) 329 if err == nil { 330 t.Error("expected error from loading non existant schema file") 331 } 332 }