package csv2lp_test import ( "encoding/csv" "fmt" "io" "strings" "testing" "testing/iotest" "github.com/influxdata/influx-cli/v2/pkg/csv2lp" "github.com/stretchr/testify/require" ) // TestLineReader tests correctness of line reporting and reader implementation of LineReader func TestLineReader(t *testing.T) { type TestInput = struct { lines [4]string withDataErrorReader bool } tests := []TestInput{ { lines: [4]string{"a\n", "\n", "\n", "bcxy"}, withDataErrorReader: false, }, { lines: [4]string{"a\n", "\n", "\n", "bcxy"}, withDataErrorReader: true, }, { lines: [4]string{"a\n", "\n", "\n", "bcx\n"}, withDataErrorReader: false, }, { lines: [4]string{"a\n", "\n", "\n", "bcx\n"}, withDataErrorReader: true, }} for _, test := range tests { lines := test.lines input := strings.Join(lines[:], "") withDataErrorReader := test.withDataErrorReader t.Run(fmt.Sprintf("%s withDataErrorReader=%v", input, withDataErrorReader), func(t *testing.T) { var reader io.Reader = strings.NewReader(input) if withDataErrorReader { // ensures that the reader reports the last EOF error also with data reader = iotest.DataErrReader(reader) } lineReader := csv2lp.NewLineReaderSize(reader, 2) var buf []byte = make([]byte, 4) var err error var read int // patologic case: reading from empty buffer returns 0 without an error read, err = lineReader.Read(buf[0:0]) require.Equal(t, 0, read) require.Nil(t, err) require.Equal(t, 0, lineReader.LastLineNumber) // 1st line read, err = lineReader.Read(buf) require.Equal(t, []byte(lines[0]), buf[0:read]) require.Nil(t, err) require.Equal(t, 0, lineReader.LastLineNumber) // 2nd read, err = lineReader.Read(buf) require.Equal(t, []byte(lines[1]), buf[0:read]) require.Nil(t, err) require.Equal(t, 1, lineReader.LastLineNumber) // reading into empty does not change the game read, err = lineReader.Read(buf[0:0]) require.Equal(t, 0, read) require.Nil(t, err) require.Equal(t, 1, lineReader.LastLineNumber) // 3rd read, err = lineReader.Read(buf) require.Equal(t, []byte(lines[2]), buf[0:read]) require.Nil(t, err) require.Equal(t, 2, lineReader.LastLineNumber) // 4th line cannot be fully read, because buffer size is 2 read, err = lineReader.Read(buf) require.Equal(t, []byte(lines[3][:2]), buf[0:read]) require.Nil(t, err) require.Equal(t, 3, lineReader.LastLineNumber) read, err = lineReader.Read(buf) require.Equal(t, []byte(lines[3][2:]), buf[0:read]) require.Nil(t, err) require.Equal(t, 3, lineReader.LastLineNumber) // 5th line => error _, err = lineReader.Read(buf) require.NotNil(t, err) }) } } // TestLineReader_Read_BufferOverflow ensures calling Open into // a slice does not panic. Fixes https://github.com/influxdata/influxdb/issues/19586 func TestLineReader_Read_BufferOverflow(t *testing.T) { sr := strings.NewReader("foo\nbar") rd := csv2lp.NewLineReader(sr) buf := make([]byte, 2) n, err := rd.Read(buf) require.Equal(t, n, 2) require.NoError(t, err) } // TestLineReader_viaCsv tests correct line reporting when read through a CSV reader with various buffer sizes // to emulate multiple required reads with a small test data set func TestLineReader_viaCsv(t *testing.T) { type RowWithLine = struct { row []string lineNumber int } input := "a\n\nb\n\nc" expected := []RowWithLine{ {[]string{"a"}, 1}, {[]string{"b"}, 3}, {[]string{"c"}, 5}, } bufferSizes := []int{-1, 0, 2, 50} for _, bufferSize := range bufferSizes { t.Run(fmt.Sprintf("buffer size: %d", bufferSize), func(t *testing.T) { var lineReader *csv2lp.LineReader if bufferSize < 0 { lineReader = csv2lp.NewLineReader(strings.NewReader(input)) } else { lineReader = csv2lp.NewLineReaderSize(strings.NewReader(input), bufferSize) } lineReader.LineNumber = 1 // start with 1 csvReader := csv.NewReader(lineReader) results := make([]RowWithLine, 0, 3) for { row, _ := csvReader.Read() lineNumber := lineReader.LastLineNumber if row == nil { break } results = append(results, RowWithLine{row, lineNumber}) } require.Equal(t, expected, results) }) } }