github.com/netdata/go.d.plugin@v0.58.1/pkg/logs/csv_test.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package logs 4 5 import ( 6 "strings" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 ) 12 13 var testCSVConfig = CSVConfig{ 14 Delimiter: " ", 15 Format: "$A %B", 16 } 17 18 func TestNewCSVParser(t *testing.T) { 19 tests := []struct { 20 name string 21 format string 22 wantErr bool 23 }{ 24 {name: "valid format", format: "$A $B"}, 25 {name: "empty format", wantErr: true}, 26 {name: "bad format: csv read error", format: "$A $B \"$C", wantErr: true}, 27 {name: "bad format: duplicate fields", format: "$A $A", wantErr: true}, 28 {name: "bad format: zero fields", format: "!A !B", wantErr: true}, 29 } 30 31 for _, tt := range tests { 32 t.Run(tt.name, func(t *testing.T) { 33 c := testCSVConfig 34 c.Format = tt.format 35 p, err := NewCSVParser(c, nil) 36 if tt.wantErr { 37 assert.Error(t, err) 38 assert.Nil(t, p) 39 } else { 40 assert.NoError(t, err) 41 assert.NotNil(t, p) 42 } 43 }) 44 } 45 } 46 47 func TestNewCSVFormat(t *testing.T) { 48 tests := []struct { 49 format string 50 wantFormat csvFormat 51 wantErr bool 52 }{ 53 {format: "$A $B", wantFormat: csvFormat{maxIndex: 1, fields: []csvField{{"$A", 0}, {"$B", 1}}}}, 54 {format: "$A $B !C $E", wantFormat: csvFormat{maxIndex: 3, fields: []csvField{{"$A", 0}, {"$B", 1}, {"$E", 3}}}}, 55 {format: "!A !B !C $E", wantFormat: csvFormat{maxIndex: 3, fields: []csvField{{"$E", 3}}}}, 56 {format: "$A $OFFSET $B", wantFormat: csvFormat{maxIndex: 3, fields: []csvField{{"$A", 0}, {"$B", 3}}}}, 57 {format: "$A $OFFSET $B $OFFSET !A", wantFormat: csvFormat{maxIndex: 3, fields: []csvField{{"$A", 0}, {"$B", 3}}}}, 58 {format: "$A $OFFSET $OFFSET $B", wantFormat: csvFormat{maxIndex: 5, fields: []csvField{{"$A", 0}, {"$B", 5}}}}, 59 {format: "$OFFSET $A $OFFSET $B", wantFormat: csvFormat{maxIndex: 5, fields: []csvField{{"$A", 2}, {"$B", 5}}}}, 60 {format: "$A \"$A", wantErr: true}, 61 {format: "$A $A", wantErr: true}, 62 {format: "!A !A", wantErr: true}, 63 {format: "", wantErr: true}, 64 } 65 66 for _, tt := range tests { 67 t.Run(tt.format, func(t *testing.T) { 68 c := testCSVConfig 69 c.Format = tt.format 70 c.CheckField = testCheckCSVFormatField 71 tt.wantFormat.raw = tt.format 72 73 f, err := newCSVFormat(c) 74 75 if tt.wantErr { 76 assert.Error(t, err) 77 assert.Nil(t, f) 78 } else { 79 assert.NoError(t, err) 80 assert.Equal(t, tt.wantFormat, *f) 81 } 82 }) 83 } 84 } 85 86 func TestCSVParser_ReadLine(t *testing.T) { 87 tests := []struct { 88 name string 89 row string 90 format string 91 wantErr bool 92 wantParseErr bool 93 }{ 94 {name: "match and no error", row: "1 2 3", format: `$A $B $C`}, 95 {name: "match but error on assigning", row: "1 2 3", format: `$A $B $ERR`, wantErr: true, wantParseErr: true}, 96 {name: "not match", row: "1 2 3", format: `$A $B $C $d`, wantErr: true, wantParseErr: true}, 97 {name: "error on reading csv.Err", row: "1 2\"3", format: `$A $B $C`, wantErr: true, wantParseErr: true}, 98 {name: "error on reading EOF", row: "", format: `$A $B $C`, wantErr: true}, 99 } 100 101 for _, tt := range tests { 102 t.Run(tt.name, func(t *testing.T) { 103 var line logLine 104 r := strings.NewReader(tt.row) 105 c := testCSVConfig 106 c.Format = tt.format 107 p, err := NewCSVParser(c, r) 108 require.NoError(t, err) 109 110 err = p.ReadLine(&line) 111 112 if tt.wantErr { 113 require.Error(t, err) 114 if tt.wantParseErr { 115 assert.True(t, IsParseError(err)) 116 } else { 117 assert.False(t, IsParseError(err)) 118 } 119 } else { 120 assert.NoError(t, err) 121 } 122 }) 123 } 124 } 125 126 func TestCSVParser_Parse(t *testing.T) { 127 tests := []struct { 128 name string 129 row string 130 format string 131 wantErr bool 132 }{ 133 {name: "match and no error", row: "1 2 3", format: `$A $B $C`}, 134 {name: "match but error on assigning", row: "1 2 3", format: `$A $B $ERR`, wantErr: true}, 135 {name: "not match", row: "1 2 3", format: `$A $B $C $d`, wantErr: true}, 136 {name: "error on reading csv.Err", row: "1 2\"3", format: `$A $B $C`, wantErr: true}, 137 } 138 139 for _, tt := range tests { 140 t.Run(tt.name, func(t *testing.T) { 141 var line logLine 142 r := strings.NewReader(tt.row) 143 c := testCSVConfig 144 c.Format = tt.format 145 p, err := NewCSVParser(c, r) 146 require.NoError(t, err) 147 148 err = p.ReadLine(&line) 149 150 if tt.wantErr { 151 require.Error(t, err) 152 assert.True(t, IsParseError(err)) 153 } else { 154 assert.NoError(t, err) 155 } 156 }) 157 } 158 159 } 160 161 func TestCSVParser_Info(t *testing.T) { 162 p, err := NewCSVParser(testCSVConfig, nil) 163 require.NoError(t, err) 164 assert.NotZero(t, p.Info()) 165 } 166 167 func testCheckCSVFormatField(name string) (newName string, offset int, valid bool) { 168 if len(name) < 2 || !strings.HasPrefix(name, "$") { 169 return "", 0, false 170 } 171 if name == "$OFFSET" { 172 return "", 1, false 173 } 174 return name, 0, true 175 }