generate_avro_fixtures/
generate-avro-fixtures.rs

1use std::{fs::File, io::Write, path::PathBuf};
2use vector_common::Result;
3
4use apache_avro::{Decimal, Schema, types::Value};
5use serde::{Deserialize, Serialize};
6
7const FIXTURES_PATH: &str = "lib/codecs/tests/data/avro/generated";
8
9fn generate_avro_test_case_boolean() -> Result<()> {
10    let schema = r#"
11    {
12        "type": "record",
13        "name": "test",
14        "fields": [
15            {"name": "bool_field", "type": "boolean", "default": false}
16        ]
17    }
18    "#;
19    #[derive(Debug, Serialize, Deserialize, Clone)]
20    struct Test {
21        bool_field: bool,
22    }
23    let value = Test { bool_field: true };
24    generate_test_case(schema, value, "boolean")
25}
26
27fn generate_avro_test_case_int() -> Result<()> {
28    let schema = r#"
29    {
30        "type": "record",
31        "name": "test",
32        "fields": [
33            {"name": "int_field", "type": "int", "default": 0}
34        ]
35    }
36    "#;
37    #[derive(Debug, Serialize, Deserialize, Clone)]
38    struct Test {
39        int_field: i32,
40    }
41    let value = Test { int_field: 1234 };
42    generate_test_case(schema, value, "int")
43}
44
45fn generate_avro_test_case_long() -> Result<()> {
46    let schema = r#"
47    {
48        "type": "record",
49        "name": "test",
50        "fields": [
51            {"name": "long_field", "type": "long", "default": 0}
52        ]
53    }
54    "#;
55    #[derive(Debug, Serialize, Deserialize, Clone)]
56    struct Test {
57        long_field: i64,
58    }
59    let value = Test {
60        long_field: 42949672960i64,
61    };
62    generate_test_case(schema, value, "long")
63}
64
65fn generate_avro_test_case_float() -> Result<()> {
66    let schema = r#"
67    {
68        "type": "record",
69        "name": "test",
70        "fields": [
71            {"name": "float_field", "type": "float", "default": 0}
72        ]
73    }
74    "#;
75    #[derive(Debug, Serialize, Deserialize, Clone)]
76    struct Test {
77        float_field: f32,
78    }
79    let value = Test {
80        float_field: 123.456,
81    };
82    generate_test_case(schema, value, "float")
83}
84
85fn generate_avro_test_case_double() -> Result<()> {
86    let schema = r#"
87    {
88        "type": "record",
89        "name": "test",
90        "fields": [
91            {"name": "double_field", "type": "double", "default": 0}
92        ]
93    }
94    "#;
95    #[derive(Debug, Serialize, Deserialize, Clone)]
96    struct Test {
97        double_field: f64,
98    }
99    let value = Test {
100        double_field: 123.456f64,
101    };
102    generate_test_case(schema, value, "double")
103}
104
105fn generate_avro_test_case_bytes() -> Result<()> {
106    let schema = r#"
107    {
108        "type": "record",
109        "name": "test",
110        "fields": [
111            {"name": "bytes_field", "type": "bytes"}
112        ]
113    }
114    "#;
115    #[derive(Debug, Serialize, Deserialize, Clone)]
116    struct Test {
117        bytes_field: Vec<u8>,
118    }
119    let value = Test {
120        bytes_field: vec![1, 2, 3, 4, 5, 6, 6, 7],
121    };
122    generate_test_case(schema, value, "bytes")
123}
124
125fn generate_avro_test_case_string() -> Result<()> {
126    let schema = r#"
127    {
128        "type": "record",
129        "name": "test",
130        "fields": [
131            {"name": "string_field", "type": "string"}
132        ]
133    }
134    "#;
135    #[derive(Debug, Serialize, Deserialize, Clone)]
136    struct Test {
137        string_field: String,
138    }
139    let value = Test {
140        string_field: "hello world!".to_string(),
141    };
142    generate_test_case(schema, value, "string")
143}
144
145#[allow(unused)]
146fn generate_avro_test_case_fixed() -> Result<()> {
147    let schema = r#"
148    {
149        "type": "record",
150        "name": "test",
151        "fields": [
152            {"name": "fixed_field", "type":"fixed", "size": 16}
153        ]
154    }
155    "#;
156    let record = Value::Record(vec![(
157        "fixed_field".into(),
158        Value::Fixed(16, b"1019181716151413".to_vec()),
159    )]);
160    generate_test_case_from_value(schema, record, "fixed")
161}
162
163fn generate_avro_test_case_enum() -> Result<()> {
164    let schema = r#"
165    {
166        "type": "record",
167        "name": "test",
168        "fields": [
169            {"name": "enum_field", "type": "enum", "symbols" : ["Spades", "Hearts", "Diamonds", "Clubs"]}
170        ]
171    }
172    "#;
173    #[derive(Debug, Serialize, Deserialize, Clone)]
174    enum Value {
175        Spades,
176        Hearts,
177        Diamonds,
178        Clubs,
179    }
180    #[derive(Debug, Serialize, Deserialize, Clone)]
181    struct Test {
182        enum_field: Value,
183    }
184    let value = Test {
185        enum_field: Value::Hearts,
186    };
187    generate_test_case(schema, value, "enum")
188}
189
190fn generate_avro_test_case_union() -> Result<()> {
191    let schema = r#"
192    {
193        "type": "record",
194        "name": "test",
195        "fields": [
196            {"name": "union_field", "type": [
197                "string",
198                "int"
199                ]
200            }
201        ]
202    }
203    "#;
204    #[derive(Debug, Serialize, Deserialize, Clone)]
205    struct Test {
206        union_field: i32,
207    }
208    let value = Test {
209        union_field: 123456,
210    };
211    generate_test_case(schema, value, "union")
212}
213
214fn generate_avro_test_case_array() -> Result<()> {
215    let schema = r#"
216    {
217        "type": "record",
218        "name": "test",
219        "fields": [
220            {"name": "array_field", "type": "array", "items" : "string"}
221        ]
222    }
223    "#;
224    #[derive(Debug, Serialize, Deserialize, Clone)]
225    struct Test {
226        array_field: Vec<String>,
227    }
228    let value = Test {
229        array_field: vec![
230            "hello".to_string(),
231            "vector".to_string(),
232            "avro".to_string(),
233            "codec".to_string(),
234        ],
235    };
236    generate_test_case(schema, value, "array")
237}
238
239fn generate_avro_test_case_map() -> Result<()> {
240    let schema = r#"
241    {
242        "type": "record",
243        "name": "test",
244        "fields": [
245            {"name": "map_field", "type": "map", "values" : "long","default": {}}
246        ]
247    }
248    "#;
249    use std::collections::HashMap;
250    #[derive(Debug, Serialize, Deserialize, Clone)]
251    struct Test {
252        map_field: HashMap<String, i64>,
253    }
254    let mut scores = HashMap::new();
255    scores.insert(String::from("Blue"), 10i64);
256    let value = Test { map_field: scores };
257    generate_test_case(schema, value, "map")
258}
259
260fn generate_avro_test_case_record() -> Result<()> {
261    let schema = r#"
262    {
263        "type": "record",
264        "name": "test",
265        "fields": [
266            {"name": "name", "type": "string"},
267            {"name": "age", "type": "int"}
268        ]
269    }
270    "#;
271    #[derive(Debug, Serialize, Deserialize, Clone)]
272    struct Test {
273        name: String,
274        age: i32,
275    }
276    let value = Test {
277        name: "John".to_string(),
278        age: 23,
279    };
280    generate_test_case(schema, value, "record")
281}
282
283#[allow(unused)]
284fn generate_avro_test_case_date() -> Result<()> {
285    let schema = r#"
286    {
287        "type": "record",
288        "name": "test",
289        "fields": [
290            {"name": "date_field", "type": "int", "logicalType": "date"}
291        ]
292    }
293    "#;
294    #[derive(Debug, Serialize, Deserialize, Clone)]
295    struct Test {
296        date_field: i32,
297    }
298    let value = Test { date_field: 19646 };
299    generate_test_case(schema, value, "date")
300}
301
302#[allow(unused)]
303fn generate_avro_test_case_decimal_var() -> Result<()> {
304    let schema = r#"
305    {
306        "type": "record",
307        "name": "test",
308        "fields": [
309            {"name": "decimal_var_field", "type": "bytes", "logicalType": "decimal","precision": 10,"scale": 3}
310        ]
311    }
312    "#;
313
314    let record = Value::Record(vec![(
315        "decimal_var_field".into(),
316        Value::Decimal(Decimal::from([
317            249, 33, 74, 206, 142, 64, 190, 170, 17, 153,
318        ])),
319    )]);
320    generate_test_case_from_value(schema, record, "decimal_var")
321}
322
323#[allow(unused)]
324fn generate_avro_test_case_time_millis() -> Result<()> {
325    let schema = r#"
326    {
327        "type": "record",
328        "name": "test",
329        "fields": [
330            {"name": "time_millis_field", "type": "int", "logicalType": "time-millis"}
331        ]
332    }
333    "#;
334    #[derive(Debug, Serialize, Deserialize, Clone)]
335    struct Test {
336        time_millis_field: i32,
337    }
338    let value = Test {
339        time_millis_field: 59820123,
340    };
341    generate_test_case(schema, value, "time_millis")
342}
343
344fn generate_avro_test_case_time_micros() -> Result<()> {
345    let schema = r#"
346    {
347        "type": "record",
348        "name": "test",
349        "fields": [
350            {"name": "time_micros_field", "type": "long", "logicalType": "time-micros"}
351        ]
352    }
353    "#;
354    #[derive(Debug, Serialize, Deserialize, Clone)]
355    struct Test {
356        time_micros_field: i64,
357    }
358    let value: Test = Test {
359        time_micros_field: 59820123456i64,
360    };
361    generate_test_case(schema, value, "time_micros")
362}
363
364fn generate_avro_test_case_timestamp_millis() -> Result<()> {
365    let schema = r#"
366    {
367        "type": "record",
368        "name": "test",
369        "fields": [
370            {"name": "timestamp_millis_field", "type": "long", "logicalType": "timestamp-millis"}
371        ]
372    }
373    "#;
374    #[derive(Debug, Serialize, Deserialize, Clone)]
375    struct Test {
376        timestamp_millis_field: i64,
377    }
378    let value = Test {
379        timestamp_millis_field: 1697445291056i64,
380    };
381    generate_test_case(schema, value, "timestamp_millis")
382}
383
384fn generate_avro_test_case_timestamp_micros() -> Result<()> {
385    let schema = r#"
386    {
387        "type": "record",
388        "name": "test",
389        "fields": [
390            {"name": "timestamp_micros_field", "type": "long", "logicalType": "timestamp-micros"}
391        ]
392    }
393    "#;
394    #[derive(Debug, Serialize, Deserialize, Clone)]
395    struct Test {
396        timestamp_micros_field: i64,
397    }
398    let value = Test {
399        timestamp_micros_field: 1697445291056567i64,
400    };
401    generate_test_case(schema, value, "timestamp_micros")
402}
403
404fn generate_avro_test_case_local_timestamp_millis() -> Result<()> {
405    let schema = r#"
406    {
407        "type": "record",
408        "name": "test",
409        "fields": [
410            {"name": "local_timestamp_millis_field", "type": "long", "logicalType": "local-timestamp-millis"}
411        ]
412    }
413    "#;
414    #[derive(Debug, Serialize, Deserialize, Clone)]
415    struct Test {
416        local_timestamp_millis_field: i64,
417    }
418    let value = Test {
419        local_timestamp_millis_field: 1697445291056i64,
420    };
421    generate_test_case(schema, value, "local-timestamp_millis")
422}
423
424fn generate_avro_test_case_local_timestamp_micros() -> Result<()> {
425    let schema = r#"
426    {
427        "type": "record",
428        "name": "test",
429        "fields": [
430            {"name": "local_timestamp_micros_field", "type": "long", "logicalType": "local-timestamp-micros"}
431        ]
432    }
433    "#;
434    #[derive(Debug, Serialize, Deserialize, Clone)]
435    struct Test {
436        local_timestamp_micros_field: i64,
437    }
438    let value = Test {
439        local_timestamp_micros_field: 1697445291056567i64,
440    };
441    generate_test_case(schema, value, "local-timestamp_micros")
442}
443
444fn generate_avro_test_case_uuid() -> Result<()> {
445    let schema = r#"
446    {
447        "type": "record",
448        "name": "test",
449        "fields": [
450            {"name": "uuid_field", "type": "string",
451              "logicalType": "uuid"
452            }
453        ]
454    }
455    "#;
456    #[derive(Debug, Serialize, Deserialize, Clone)]
457    struct Test {
458        uuid_field: String,
459    }
460    let value = Test {
461        uuid_field: "550e8400-e29b-41d4-a716-446655440000".into(),
462    };
463    generate_test_case(schema, value, "uuid")
464}
465
466fn generate_test_case<S: Serialize>(schema: &str, value: S, filename: &str) -> Result<()> {
467    let value = apache_avro::to_value(value)?;
468    generate_test_case_from_value(schema, value, filename)
469}
470
471fn generate_test_case_from_value(schema: &str, value: Value, filename: &str) -> Result<()> {
472    let schema = Schema::parse_str(schema)?;
473
474    let value = value.resolve(&schema)?;
475    let bytes = apache_avro::to_avro_datum(&schema, value)?;
476
477    let mut schema_file = File::create(format!("{FIXTURES_PATH}/{filename}.avsc"))?;
478    let mut avro_file = File::create(format!("{FIXTURES_PATH}/{filename}.avro"))?;
479    schema_file.write_all(schema.canonical_form().as_bytes())?;
480    avro_file.write_all(&bytes)?;
481    Ok(())
482}
483
484fn main() -> Result<()> {
485    if !PathBuf::from(FIXTURES_PATH).is_dir() {
486        panic!("dir {FIXTURES_PATH} not exist\n");
487    }
488    generate_avro_test_case_array()?;
489    generate_avro_test_case_boolean()?;
490    generate_avro_test_case_bytes()?;
491    generate_avro_test_case_double()?;
492    generate_avro_test_case_enum()?;
493    generate_avro_test_case_float()?;
494    generate_avro_test_case_int()?;
495    generate_avro_test_case_long()?;
496    generate_avro_test_case_map()?;
497    generate_avro_test_case_record()?;
498    generate_avro_test_case_string()?;
499    generate_avro_test_case_time_micros()?;
500    generate_avro_test_case_timestamp_micros()?;
501    generate_avro_test_case_timestamp_millis()?;
502    generate_avro_test_case_local_timestamp_micros()?;
503    generate_avro_test_case_local_timestamp_millis()?;
504    generate_avro_test_case_union()?;
505    generate_avro_test_case_uuid()?;
506    Ok(())
507}