VictoriaMetrics/lib/protoparser/csvimport/scanner_test.go
Aliaksandr Valialkin a892f22bf7
lib/protoparser/csvimport: properly parse the last empty column in CSV line
Do not ignore the last empty column in CSV line.
While at it, properly parse CSV columns in single quotes, e.g. `'foo,bar',baz` is parsed as two columns - `foo,bar` and `baz`

Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4048

See also https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4298
2023-05-12 17:00:56 -07:00

139 lines
2.9 KiB
Go

package csvimport
import (
"reflect"
"testing"
)
func TestReadQuotedFieldSuccess(t *testing.T) {
f := func(s, resultExpected, tailExpected string) {
t.Helper()
result, tail, err := readQuotedField(s)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if result != resultExpected {
t.Fatalf("unexpected result; got %q; want %q", result, resultExpected)
}
if tail != tailExpected {
t.Fatalf("unexpected tail; got %q; want %q", tail, tailExpected)
}
}
// double quotes
f(`""`, ``, ``)
f(`"",`, ``, `,`)
f(`"",foobar`, ``, `,foobar`)
f(`"","bc"`, ``, `,"bc"`)
f(`"a"`, `a`, ``)
f(`"a"bc`, `a`, `bc`)
f(`"foo`+"`',\n\t\r"+`bar"baz`, "foo`',\n\t\rbar", "baz")
// single quotes
f(`''`, ``, ``)
f(`'',`, ``, `,`)
f(`'',foobar`, ``, `,foobar`)
f(`'','bc'`, ``, `,'bc'`)
f(`'a'`, `a`, ``)
f(`'a'bc`, `a`, `bc`)
f(`'foo"`+"`,\n\t\r"+`bar'baz`, "foo\"`,\n\t\rbar", "baz")
// escaped double quotes
f(`" foo""bar"baz`, ` foo"bar`, `baz`)
f(`""""bar"baz`, `"`, `bar"baz`)
f(`"a,""b""'c",d,"e"`, `a,"b"'c`, `,d,"e"`)
// escaped single quotes
f(`' foo''bar'baz`, ` foo'bar`, `baz`)
f(`''''bar'baz`, `'`, `bar'baz`)
f(`'''bar'''baz`, `'bar'`, `baz`)
f(`'a,''b''"c',d,'e'`, `a,'b'"c`, `,d,'e'`)
}
func TestReadQuotedFieldFailure(t *testing.T) {
f := func(s string) {
t.Helper()
field, tail, err := readQuotedField(s)
if field != "" {
t.Fatalf("unexpected non-empty field returned: %q", field)
}
if tail != s {
t.Fatalf("unexpected tail returned; got %q; want %q", tail, s)
}
if err == nil {
t.Fatalf("expecting non-nil error")
}
}
f(`"`)
f(`'`)
f(`"foo""`)
f(`'foo''`)
f(`'foo''`)
}
func TestScannerSuccess(t *testing.T) {
f := func(s string, rowsExpected [][]string) {
t.Helper()
var sc scanner
sc.Init(s)
var rows [][]string
for sc.NextLine() {
var row []string
for sc.NextColumn() {
row = append(row, sc.Column)
}
rows = append(rows, row)
}
if sc.Error != nil {
t.Fatalf("unexpected error: %s", sc.Error)
}
if !reflect.DeepEqual(rows, rowsExpected) {
t.Fatalf("unexpected rows;\ngot\n%q\nwant\n%q", rows, rowsExpected)
}
}
f("", nil)
f("\n", nil)
f("\r\n\n\r", nil)
f("foo,bar\n\"aa,\"\"bb\",\"\"", [][]string{
{"foo", "bar"},
{`aa,"bb`, ``},
})
f(`fo"bar,baz'a,"bc""de",'g''e'`, [][]string{
{`fo"bar`, `baz'a`, `bc"de`, `g'e`},
})
f(`,`, [][]string{
{``, ``},
})
f(`foo`, [][]string{
{`foo`},
})
f(`foo,,`+"\r\n"+`,bar,`+"\n", [][]string{
{`foo`, ``, ``},
{``, `bar`, ``},
})
}
func TestScannerFailure(t *testing.T) {
f := func(s string) {
t.Helper()
var sc scanner
sc.Init(s)
for sc.NextLine() {
for sc.NextColumn() {
}
if sc.Error != nil {
if sc.NextColumn() {
t.Fatalf("unexpected NextColumn success after the error %v", sc.Error)
}
return
}
}
t.Fatalf("expecting at least a single error")
}
// Unclosed quote
f("foo\r\n\"bar,")
f(`"foo,"bar`)
f(`foo,"bar",""a`)
f(`foo,"bar","a""`)
}