PNG  IHDR pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F@8N ' p @8N@8}' p '#@8N@8N pQ9p!i~}|6-ӪG` VP.@*j>[ K^<֐Z]@8N'KQ<Q(`s" 'hgpKB`R@Dqj '  'P$a ( `D$Na L?u80e J,K˷NI'0eݷ(NI'؀ 2ipIIKp`:O'`ʤxB8Ѥx Ѥx $ $P6 :vRNb 'p,>NB 'P]-->P T+*^h& p '‰a ‰ (ĵt#u33;Nt̵'ޯ; [3W ~]0KH1q@8]O2]3*̧7# *p>us p _6]/}-4|t'|Smx= DoʾM×M_8!)6lq':l7!|4} '\ne t!=hnLn (~Dn\+‰_4k)0e@OhZ`F `.m1} 'vp{F`ON7Srx 'D˸nV`><;yMx!IS钦OM)Ե٥x 'DSD6bS8!" ODz#R >S8!7ّxEh0m$MIPHi$IvS8IN$I p$O8I,sk&I)$IN$Hi$I^Ah.p$MIN$IR8I·N "IF9Ah0m$MIN$IR8IN$I 3jIU;kO$ɳN$+ q.x* tEXtComment

Viewing File: /opt/go/pkg/mod/github.com/go-openapi/runtime@v0.28.0/csv_test.go

// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package runtime

import (
	"bytes"
	"encoding/csv"
	"errors"
	"io"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const (
	csvFixture = `name,country,age
John,US,19
Mike,US,20
`
	badCSVFixture = `name,country,age
John,US,19
Mike,US
`
	commentedCSVFixture = `# heading line
name,country,age
#John's record
John,US,19
#Mike's record
Mike,US,20
`
)

var testCSVRecords = [][]string{
	{"name", "country", "age"},
	{"John", "US", "19"},
	{"Mike", "US", "20"},
}

func TestCSVConsumer(t *testing.T) {
	consumer := CSVConsumer()

	t.Run("can consume as a *csv.Writer", func(t *testing.T) {
		reader := bytes.NewBufferString(csvFixture)
		var buf bytes.Buffer
		dest := csv.NewWriter(&buf)

		err := consumer.Consume(reader, dest)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, buf.String())
	})

	t.Run("can consume as a CSVReader", func(t *testing.T) {
		reader := bytes.NewBufferString(csvFixture)
		var dest csvRecordsWriter

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)
		assertCSVRecords(t, dest.records)
	})

	t.Run("can consume as a Writer", func(t *testing.T) {
		reader := bytes.NewBufferString(csvFixture)
		var dest closingWriter

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, dest.b.String())
	})

	t.Run("can consume as a ReaderFrom", func(t *testing.T) {
		reader := bytes.NewBufferString(csvFixture)
		var dest readerFromDummy

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, dest.b.String())
	})

	t.Run("can consume as a BinaryUnmarshaler", func(t *testing.T) {
		reader := bytes.NewBufferString(csvFixture)
		var dest binaryUnmarshalDummy

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, dest.str)
	})

	t.Run("can consume as a *[][]string", func(t *testing.T) {
		reader := bytes.NewBufferString(csvFixture)
		dest := [][]string{}

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)
		assertCSVRecords(t, dest)
	})

	t.Run("can consume as an alias to *[][]string", func(t *testing.T) {
		reader := bytes.NewBufferString(csvFixture)
		type records [][]string
		var dest records

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)
		assertCSVRecords(t, dest)
	})

	t.Run("can consume as a *[]byte", func(t *testing.T) {
		reader := bytes.NewBufferString(csvFixture)
		var dest []byte

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, string(dest))
	})

	t.Run("can consume as an alias to *[]byte", func(t *testing.T) {
		reader := bytes.NewBufferString(csvFixture)
		type buffer []byte
		var dest buffer

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, string(dest))
	})

	t.Run("can consume as a *string", func(t *testing.T) {
		reader := bytes.NewBufferString(csvFixture)
		var dest string

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, dest)
	})

	t.Run("can consume as an alias to *string", func(t *testing.T) {
		reader := bytes.NewBufferString(csvFixture)
		type buffer string
		var dest buffer

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, string(dest))
	})

	t.Run("can consume from an empty reader", func(t *testing.T) {
		reader := &csvEmptyReader{}
		var dest bytes.Buffer

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)
		assert.Empty(t, dest.String())
	})

	t.Run("error cases", func(t *testing.T) {
		t.Run("nil data is never accepted", func(t *testing.T) {
			var rdr bytes.Buffer

			require.Error(t, consumer.Consume(&rdr, nil))
		})

		t.Run("nil readers should also never be acccepted", func(t *testing.T) {
			var buf bytes.Buffer

			err := consumer.Consume(nil, &buf)
			require.Error(t, err)
		})

		t.Run("data must be a pointer", func(t *testing.T) {
			var rdr bytes.Buffer
			var dest []byte

			err := consumer.Consume(&rdr, dest)
			require.Error(t, err)
		})

		t.Run("unsupported type", func(t *testing.T) {
			var rdr bytes.Buffer
			var dest struct{}

			err := consumer.Consume(&rdr, &dest)
			require.Error(t, err)
		})

		t.Run("should propagate CSV error (buffered)", func(t *testing.T) {
			reader := bytes.NewBufferString(badCSVFixture)
			var dest []byte

			err := consumer.Consume(reader, &dest)
			require.Error(t, err)
			require.EqualError(t, err, "record on line 3: wrong number of fields")
		})

		t.Run("should propagate CSV error (buffered, string)", func(t *testing.T) {
			reader := bytes.NewBufferString(badCSVFixture)
			var dest string

			err := consumer.Consume(reader, &dest)
			require.Error(t, err)
			require.EqualError(t, err, "record on line 3: wrong number of fields")
		})

		t.Run("should propagate CSV error (buffered, ReaderFrom)", func(t *testing.T) {
			reader := bytes.NewBufferString(badCSVFixture)
			var dest readerFromDummy

			err := consumer.Consume(reader, &dest)
			require.Error(t, err)
			require.EqualError(t, err, "record on line 3: wrong number of fields")
		})

		t.Run("should propagate CSV error (buffered, BinaryUnmarshaler)", func(t *testing.T) {
			reader := bytes.NewBufferString(badCSVFixture)
			var dest binaryUnmarshalDummy

			err := consumer.Consume(reader, &dest)
			require.Error(t, err)
			require.EqualError(t, err, "record on line 3: wrong number of fields")
		})

		t.Run("should propagate CSV error (streaming)", func(t *testing.T) {
			reader := bytes.NewBufferString(badCSVFixture)
			var dest bytes.Buffer

			err := consumer.Consume(reader, &dest)
			require.Error(t, err)
			require.EqualError(t, err, "record on line 3: wrong number of fields")
		})

		t.Run("should propagate CSV error (streaming, write error)", func(t *testing.T) {
			reader := bytes.NewBufferString(csvFixture)
			var buf bytes.Buffer
			dest := csvWriterDummy{err: errors.New("test error"), Writer: csv.NewWriter(&buf)}

			err := consumer.Consume(reader, &dest)
			require.Error(t, err)
			require.EqualError(t, err, "test error")
		})

		t.Run("should propagate ReaderFrom error", func(t *testing.T) {
			reader := bytes.NewBufferString(csvFixture)
			dest := readerFromDummy{err: errors.New("test error")}

			err := consumer.Consume(reader, &dest)
			require.Error(t, err)
			require.EqualError(t, err, "test error")
		})

		t.Run("should propagate BinaryUnmarshaler error", func(t *testing.T) {
			reader := bytes.NewBufferString(csvFixture)
			dest := binaryUnmarshalDummy{err: errors.New("test error")}

			err := consumer.Consume(reader, &dest)
			require.Error(t, err)
			require.EqualError(t, err, "test error")
		})
	})
}

func TestCSVConsumerWithOptions(t *testing.T) {
	semiColonFixture := strings.ReplaceAll(csvFixture, ",", ";")

	t.Run("with CSV reader Comma", func(t *testing.T) {
		consumer := CSVConsumer(WithCSVReaderOpts(csv.Reader{Comma: ';', FieldsPerRecord: 3}))

		t.Run("should not read comma-separated input", func(t *testing.T) {
			reader := bytes.NewBufferString(csvFixture)
			var dest bytes.Buffer

			err := consumer.Consume(reader, &dest)
			require.Error(t, err)
			require.EqualError(t, err, "record on line 1: wrong number of fields")
		})

		t.Run("should read semicolon-separated input and convert it to colon-separated", func(t *testing.T) {
			reader := bytes.NewBufferString(semiColonFixture)
			var dest bytes.Buffer

			err := consumer.Consume(reader, &dest)
			require.NoError(t, err)
			assert.Equal(t, csvFixture, dest.String())
		})
	})

	t.Run("with CSV reader Comment", func(t *testing.T) {
		consumer := CSVConsumer(WithCSVReaderOpts(csv.Reader{Comment: '#'}))

		t.Run("should read input and skip commented lines", func(t *testing.T) {
			reader := bytes.NewBufferString(commentedCSVFixture)
			var dest [][]string

			err := consumer.Consume(reader, &dest)
			require.NoError(t, err)
			assertCSVRecords(t, dest)
		})
	})

	t.Run("with CSV writer Comma", func(t *testing.T) {
		consumer := CSVConsumer(WithCSVWriterOpts(csv.Writer{Comma: ';'}))

		t.Run("should read comma-separated input and convert it to semicolon-separated", func(t *testing.T) {
			reader := bytes.NewBufferString(csvFixture)
			var dest bytes.Buffer

			err := consumer.Consume(reader, &dest)
			require.NoError(t, err)
			assert.Equal(t, semiColonFixture, dest.String())
		})
	})

	t.Run("with SkipLines (streaming)", func(t *testing.T) {
		consumer := CSVConsumer(WithCSVSkipLines(1))
		reader := bytes.NewBufferString(csvFixture)
		var dest [][]string

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)

		expected := testCSVRecords[1:]
		assert.Equalf(t, expected, dest, "expected output to skip header")
	})

	t.Run("with SkipLines (buffered)", func(t *testing.T) {
		consumer := CSVConsumer(WithCSVSkipLines(1))
		reader := bytes.NewBufferString(csvFixture)
		var dest []byte

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)

		r := csv.NewReader(bytes.NewReader(dest))
		consumed, err := r.ReadAll()
		require.NoError(t, err)
		expected := testCSVRecords[1:]
		assert.Equalf(t, expected, consumed, "expected output to skip header")
	})

	t.Run("should detect errors on skipped lines (streaming)", func(t *testing.T) {
		consumer := CSVConsumer(WithCSVSkipLines(1))
		reader := bytes.NewBufferString(strings.ReplaceAll(csvFixture, ",age", `,"age`))
		var dest [][]string

		err := consumer.Consume(reader, &dest)
		require.Error(t, err)
		require.ErrorContains(t, err, "record on line 1; parse error")
	})

	t.Run("should detect errors on skipped lines (buffered)", func(t *testing.T) {
		consumer := CSVConsumer(WithCSVSkipLines(1))
		reader := bytes.NewBufferString(strings.ReplaceAll(csvFixture, ",age", `,"age`))
		var dest []byte

		err := consumer.Consume(reader, &dest)
		require.Error(t, err)
		require.ErrorContains(t, err, "record on line 1; parse error")
	})

	t.Run("with SkipLines greater than the total number of lines (streaming)", func(t *testing.T) {
		consumer := CSVConsumer(WithCSVSkipLines(4))
		reader := bytes.NewBufferString(csvFixture)
		var dest [][]string

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)

		assert.Empty(t, dest)
	})

	t.Run("with SkipLines greater than the total number of lines (buffered)", func(t *testing.T) {
		consumer := CSVConsumer(WithCSVSkipLines(4))
		reader := bytes.NewBufferString(csvFixture)
		var dest []byte

		err := consumer.Consume(reader, &dest)
		require.NoError(t, err)

		assert.Empty(t, dest)
	})

	t.Run("with CloseStream", func(t *testing.T) {
		t.Run("wants to close stream", func(t *testing.T) {
			closingConsumer := CSVConsumer(WithCSVClosesStream())
			var dest bytes.Buffer
			r := &closingReader{b: bytes.NewBufferString(csvFixture)}

			require.NoError(t, closingConsumer.Consume(r, &dest))
			assert.Equal(t, csvFixture, dest.String())
			assert.EqualValues(t, 1, r.calledClose)
		})

		t.Run("don't want to close stream", func(t *testing.T) {
			nonClosingConsumer := CSVConsumer()
			var dest bytes.Buffer
			r := &closingReader{b: bytes.NewBufferString(csvFixture)}

			require.NoError(t, nonClosingConsumer.Consume(r, &dest))
			assert.Equal(t, csvFixture, dest.String())
			assert.EqualValues(t, 0, r.calledClose)
		})
	})
}

func TestCSVProducer(t *testing.T) {
	producer := CSVProducer()

	t.Run("can produce CSV from *csv.Reader", func(t *testing.T) {
		writer := new(bytes.Buffer)
		buf := bytes.NewBufferString(csvFixture)
		data := csv.NewReader(buf)

		err := producer.Produce(writer, data)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, writer.String())
	})

	t.Run("can produce CSV from CSVReader", func(t *testing.T) {
		writer := new(bytes.Buffer)
		data := &csvRecordsWriter{
			records: testCSVRecords,
		}

		err := producer.Produce(writer, data)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, writer.String())
	})

	t.Run("can produce CSV from Reader", func(t *testing.T) {
		writer := new(bytes.Buffer)
		data := bytes.NewReader([]byte(csvFixture))

		err := producer.Produce(writer, data)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, writer.String())
	})

	t.Run("can produce CSV from WriterTo", func(t *testing.T) {
		writer := new(bytes.Buffer)
		buf := bytes.NewBufferString(csvFixture)
		data := &writerToDummy{
			b: *buf,
		}

		err := producer.Produce(writer, data)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, writer.String())
	})

	t.Run("can produce CSV from BinaryMarshaler", func(t *testing.T) {
		writer := new(bytes.Buffer)
		data := &binaryMarshalDummy{str: csvFixture}

		err := producer.Produce(writer, data)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, writer.String())
	})

	t.Run("can produce CSV from [][]string", func(t *testing.T) {
		writer := new(bytes.Buffer)
		data := testCSVRecords

		err := producer.Produce(writer, data)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, writer.String())
	})

	t.Run("can produce CSV from alias to [][]string", func(t *testing.T) {
		writer := new(bytes.Buffer)
		type records [][]string
		data := records(testCSVRecords)

		err := producer.Produce(writer, data)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, writer.String())
	})

	t.Run("can produce CSV from []byte", func(t *testing.T) {
		writer := httptest.NewRecorder()
		data := []byte(csvFixture)

		err := producer.Produce(writer, data)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, writer.Body.String())
	})

	t.Run("can produce CSV from alias to []byte", func(t *testing.T) {
		writer := httptest.NewRecorder()
		type buffer []byte
		data := buffer(csvFixture)

		err := producer.Produce(writer, data)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, writer.Body.String())
	})

	t.Run("can produce CSV from string", func(t *testing.T) {
		writer := httptest.NewRecorder()
		data := csvFixture

		err := producer.Produce(writer, data)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, writer.Body.String())
	})

	t.Run("can produce CSV from alias to string", func(t *testing.T) {
		writer := httptest.NewRecorder()
		type buffer string
		data := buffer(csvFixture)

		err := producer.Produce(writer, data)
		require.NoError(t, err)
		assert.Equal(t, csvFixture, writer.Body.String())
	})

	t.Run("always close data reader whenever possible", func(t *testing.T) {
		nonClosingProducer := CSVProducer()
		r := &closingWriter{}
		data := &closingReader{b: bytes.NewBufferString(csvFixture)}

		require.NoError(t, nonClosingProducer.Produce(r, data))
		assert.Equal(t, csvFixture, r.String())
		assert.EqualValuesf(t, 0, r.calledClose, "expected the input reader NOT to be closed")
		assert.EqualValuesf(t, 1, data.calledClose, "expected the data reader to be closed")
	})

	t.Run("error cases", func(t *testing.T) {
		t.Run("unsupported type", func(t *testing.T) {
			writer := httptest.NewRecorder()
			var data struct{}

			err := producer.Produce(writer, data)
			require.Error(t, err)
		})

		t.Run("data cannot be nil", func(t *testing.T) {
			writer := httptest.NewRecorder()

			err := producer.Produce(writer, nil)
			require.Error(t, err)
		})

		t.Run("writer cannot be nil", func(t *testing.T) {
			data := []byte(csvFixture)

			err := producer.Produce(nil, data)
			require.Error(t, err)
		})

		t.Run("should propagate error from BinaryMarshaler", func(t *testing.T) {
			var rdr bytes.Buffer
			data := new(binaryMarshalDummy)

			err := producer.Produce(&rdr, data)
			require.Error(t, err)
			require.ErrorContains(t, err, "no text set")
		})
	})
}

func TestCSVProducerWithOptions(t *testing.T) {
	t.Run("with CloseStream", func(t *testing.T) {
		t.Run("wants to close stream", func(t *testing.T) {
			closingProducer := CSVProducer(WithCSVClosesStream())
			r := &closingWriter{}
			data := bytes.NewBufferString(csvFixture)

			require.NoError(t, closingProducer.Produce(r, data))
			assert.Equal(t, csvFixture, r.String())
			assert.EqualValues(t, 1, r.calledClose)
		})

		t.Run("don't want to close stream", func(t *testing.T) {
			nonClosingProducer := CSVProducer()
			r := &closingWriter{}
			data := bytes.NewBufferString(csvFixture)

			require.NoError(t, nonClosingProducer.Produce(r, data))
			assert.Equal(t, csvFixture, r.String())
			assert.EqualValues(t, 0, r.calledClose)
		})
	})
}

func assertCSVRecords(t testing.TB, dest [][]string) {
	assert.Len(t, dest, 3)
	for i, record := range dest {
		assert.Equal(t, testCSVRecords[i], record)
	}
}

type csvEmptyReader struct{}

func (r *csvEmptyReader) Read(_ []byte) (int, error) {
	return 0, io.EOF
}

type readerFromDummy struct {
	err error
	b   bytes.Buffer
}

func (r *readerFromDummy) ReadFrom(rdr io.Reader) (int64, error) {
	if r.err != nil {
		return 0, r.err
	}

	return r.b.ReadFrom(rdr)
}

type writerToDummy struct {
	b bytes.Buffer
}

func (w *writerToDummy) WriteTo(writer io.Writer) (int64, error) {
	return w.b.WriteTo(writer)
}

type csvWriterDummy struct {
	err error
	*csv.Writer
}

func (w *csvWriterDummy) Write(record []string) error {
	if w.err != nil {
		return w.err
	}

	return w.Writer.Write(record)
}

func (w *csvWriterDummy) Error() error {
	if w.err != nil {
		return w.err
	}

	return w.Writer.Error()
}
Back to Directory=ceiIENDB`