Adding upstream version 0.31.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
091495b2f3
commit
5d4914ed7f
61 changed files with 30627 additions and 0 deletions
380
encoding_test.go
Normal file
380
encoding_test.go
Normal file
|
@ -0,0 +1,380 @@
|
|||
package meilisearch
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/andybalholm/brotli"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type mockData struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
type errorWriter struct{}
|
||||
|
||||
func (e *errorWriter) Write(p []byte) (int, error) {
|
||||
return 0, errors.New("write error")
|
||||
}
|
||||
|
||||
type errorReader struct{}
|
||||
|
||||
func (e *errorReader) Read(p []byte) (int, error) {
|
||||
return 0, errors.New("read error")
|
||||
}
|
||||
|
||||
func Test_Encode_ErrorOnNewWriter(t *testing.T) {
|
||||
g := &gzipEncoder{
|
||||
gzWriterPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &gzipWriter{
|
||||
writer: nil,
|
||||
err: errors.New("new writer error"),
|
||||
}
|
||||
},
|
||||
},
|
||||
bufferPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
d := &flateEncoder{
|
||||
flWriterPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &flateWriter{
|
||||
writer: nil,
|
||||
err: errors.New("new writer error"),
|
||||
}
|
||||
},
|
||||
},
|
||||
bufferPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := g.Encode(bytes.NewReader([]byte("test")))
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = d.Encode(bytes.NewReader([]byte("test")))
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func Test_Encode_ErrorInCopyZeroAlloc(t *testing.T) {
|
||||
g := &gzipEncoder{
|
||||
gzWriterPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
w, _ := gzip.NewWriterLevel(io.Discard, gzip.DefaultCompression)
|
||||
return &gzipWriter{
|
||||
writer: w,
|
||||
err: nil,
|
||||
}
|
||||
},
|
||||
},
|
||||
bufferPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
d := &flateEncoder{
|
||||
flWriterPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
w, err := zlib.NewWriterLevel(io.Discard, flate.DefaultCompression)
|
||||
return &flateWriter{
|
||||
writer: w,
|
||||
err: err,
|
||||
}
|
||||
},
|
||||
},
|
||||
bufferPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
b := &brotliEncoder{
|
||||
brWriterPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return brotli.NewWriterLevel(io.Discard, brotli.DefaultCompression)
|
||||
},
|
||||
},
|
||||
bufferPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := g.Encode(&errorReader{})
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = d.Encode(&errorReader{})
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = b.Encode(&errorReader{})
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func Test_InvalidContentType(t *testing.T) {
|
||||
enc := newEncoding("invalid", DefaultCompression)
|
||||
require.Nil(t, enc)
|
||||
}
|
||||
|
||||
func TestGzipEncoder(t *testing.T) {
|
||||
encoder := newEncoding(GzipEncoding, DefaultCompression)
|
||||
assert.NotNil(t, encoder, "gzip encoder should not be nil")
|
||||
|
||||
original := &mockData{Name: "John Doe", Age: 30}
|
||||
|
||||
originalJSON, err := json.Marshal(original)
|
||||
assert.NoError(t, err, "marshalling original data should not produce an error")
|
||||
|
||||
readCloser := io.NopCloser(bytes.NewReader(originalJSON))
|
||||
|
||||
encodedData, err := encoder.Encode(readCloser)
|
||||
assert.NoError(t, err, "encoding should not produce an error")
|
||||
assert.NotNil(t, encodedData, "encoded data should not be nil")
|
||||
|
||||
var decoded mockData
|
||||
err = encoder.Decode(encodedData.Bytes(), &decoded)
|
||||
assert.NoError(t, err, "decoding should not produce an error")
|
||||
assert.Equal(t, original, &decoded, "decoded data should match the original")
|
||||
|
||||
var invalidType int
|
||||
err = encoder.Decode(encodedData.Bytes(), &invalidType)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestDeflateEncoder(t *testing.T) {
|
||||
encoder := newEncoding(DeflateEncoding, DefaultCompression)
|
||||
assert.NotNil(t, encoder, "deflate encoder should not be nil")
|
||||
|
||||
original := &mockData{Name: "Jane Doe", Age: 25}
|
||||
|
||||
originalJSON, err := json.Marshal(original)
|
||||
assert.NoError(t, err, "marshalling original data should not produce an error")
|
||||
|
||||
readCloser := io.NopCloser(bytes.NewReader(originalJSON))
|
||||
|
||||
encodedData, err := encoder.Encode(readCloser)
|
||||
assert.NoError(t, err, "encoding should not produce an error")
|
||||
assert.NotNil(t, encodedData, "encoded data should not be nil")
|
||||
|
||||
var decoded mockData
|
||||
err = encoder.Decode(encodedData.Bytes(), &decoded)
|
||||
assert.NoError(t, err, "decoding should not produce an error")
|
||||
assert.Equal(t, original, &decoded, "decoded data should match the original")
|
||||
|
||||
var invalidType int
|
||||
err = encoder.Decode(encodedData.Bytes(), &invalidType)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestBrotliEncoder(t *testing.T) {
|
||||
encoder := newEncoding(BrotliEncoding, DefaultCompression)
|
||||
assert.NotNil(t, encoder, "brotli encoder should not be nil")
|
||||
|
||||
original := &mockData{Name: "Jane Doe", Age: 25}
|
||||
|
||||
originalJSON, err := json.Marshal(original)
|
||||
assert.NoError(t, err, "marshalling original data should not produce an error")
|
||||
|
||||
readCloser := io.NopCloser(bytes.NewReader(originalJSON))
|
||||
|
||||
encodedData, err := encoder.Encode(readCloser)
|
||||
assert.NoError(t, err, "encoding should not produce an error")
|
||||
assert.NotNil(t, encodedData, "encoded data should not be nil")
|
||||
|
||||
var decoded mockData
|
||||
err = encoder.Decode(encodedData.Bytes(), &decoded)
|
||||
assert.NoError(t, err, "decoding should not produce an error")
|
||||
assert.Equal(t, original, &decoded, "decoded data should match the original")
|
||||
|
||||
var invalidType int
|
||||
err = encoder.Decode(encodedData.Bytes(), &invalidType)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGzipEncoder_EmptyData(t *testing.T) {
|
||||
encoder := newEncoding(GzipEncoding, DefaultCompression)
|
||||
assert.NotNil(t, encoder, "gzip encoder should not be nil")
|
||||
|
||||
original := &mockData{}
|
||||
|
||||
originalJSON, err := json.Marshal(original)
|
||||
assert.NoError(t, err, "marshalling original data should not produce an error")
|
||||
|
||||
readCloser := io.NopCloser(bytes.NewReader(originalJSON))
|
||||
|
||||
encodedData, err := encoder.Encode(readCloser)
|
||||
assert.NoError(t, err, "encoding should not produce an error")
|
||||
assert.NotNil(t, encodedData, "encoded data should not be nil")
|
||||
|
||||
var decoded mockData
|
||||
err = encoder.Decode(encodedData.Bytes(), &decoded)
|
||||
assert.NoError(t, err, "decoding should not produce an error")
|
||||
assert.Equal(t, original, &decoded, "decoded data should match the original")
|
||||
}
|
||||
|
||||
func TestDeflateEncoder_EmptyData(t *testing.T) {
|
||||
encoder := newEncoding(DeflateEncoding, DefaultCompression)
|
||||
assert.NotNil(t, encoder, "deflate encoder should not be nil")
|
||||
|
||||
original := &mockData{}
|
||||
|
||||
originalJSON, err := json.Marshal(original)
|
||||
assert.NoError(t, err, "marshalling original data should not produce an error")
|
||||
|
||||
readCloser := io.NopCloser(bytes.NewReader(originalJSON))
|
||||
|
||||
encodedData, err := encoder.Encode(readCloser)
|
||||
assert.NoError(t, err, "encoding should not produce an error")
|
||||
assert.NotNil(t, encodedData, "encoded data should not be nil")
|
||||
|
||||
var decoded mockData
|
||||
err = encoder.Decode(encodedData.Bytes(), &decoded)
|
||||
assert.NoError(t, err, "decoding should not produce an error")
|
||||
assert.Equal(t, original, &decoded, "decoded data should match the original")
|
||||
}
|
||||
|
||||
func TestBrotliEncoder_EmptyData(t *testing.T) {
|
||||
encoder := newEncoding(BrotliEncoding, DefaultCompression)
|
||||
assert.NotNil(t, encoder, "brotli encoder should not be nil")
|
||||
|
||||
original := &mockData{}
|
||||
|
||||
originalJSON, err := json.Marshal(original)
|
||||
assert.NoError(t, err, "marshalling original data should not produce an error")
|
||||
|
||||
readCloser := io.NopCloser(bytes.NewReader(originalJSON))
|
||||
|
||||
encodedData, err := encoder.Encode(readCloser)
|
||||
assert.NoError(t, err, "encoding should not produce an error")
|
||||
assert.NotNil(t, encodedData, "encoded data should not be nil")
|
||||
|
||||
var decoded mockData
|
||||
err = encoder.Decode(encodedData.Bytes(), &decoded)
|
||||
assert.NoError(t, err, "decoding should not produce an error")
|
||||
assert.Equal(t, original, &decoded, "decoded data should match the original")
|
||||
}
|
||||
|
||||
func TestGzipEncoder_InvalidData(t *testing.T) {
|
||||
encoder := newEncoding(GzipEncoding, DefaultCompression)
|
||||
assert.NotNil(t, encoder, "gzip encoder should not be nil")
|
||||
|
||||
var decoded mockData
|
||||
err := encoder.Decode([]byte("invalid data"), &decoded)
|
||||
assert.Error(t, err, "decoding invalid data should produce an error")
|
||||
}
|
||||
|
||||
func TestDeflateEncoder_InvalidData(t *testing.T) {
|
||||
encoder := newEncoding(DeflateEncoding, DefaultCompression)
|
||||
assert.NotNil(t, encoder, "deflate encoder should not be nil")
|
||||
|
||||
var decoded mockData
|
||||
err := encoder.Decode([]byte("invalid data"), &decoded)
|
||||
assert.Error(t, err, "decoding invalid data should produce an error")
|
||||
}
|
||||
|
||||
func TestBrotliEncoder_InvalidData(t *testing.T) {
|
||||
encoder := newEncoding(BrotliEncoding, DefaultCompression)
|
||||
assert.NotNil(t, encoder, "brotli encoder should not be nil")
|
||||
|
||||
var decoded mockData
|
||||
err := encoder.Decode([]byte("invalid data"), &decoded)
|
||||
assert.Error(t, err, "decoding invalid data should produce an error")
|
||||
}
|
||||
|
||||
func TestCopyZeroAlloc(t *testing.T) {
|
||||
t.Run("RegularCopy", func(t *testing.T) {
|
||||
src := strings.NewReader("hello world")
|
||||
dst := &bytes.Buffer{}
|
||||
|
||||
n, err := copyZeroAlloc(dst, src)
|
||||
assert.NoError(t, err, "copy should not produce an error")
|
||||
assert.Equal(t, int64(11), n, "copy length should be 11")
|
||||
assert.Equal(t, "hello world", dst.String(), "destination should contain the copied data")
|
||||
})
|
||||
|
||||
t.Run("EmptySource", func(t *testing.T) {
|
||||
src := strings.NewReader("")
|
||||
dst := &bytes.Buffer{}
|
||||
|
||||
n, err := copyZeroAlloc(dst, src)
|
||||
assert.NoError(t, err, "copy should not produce an error")
|
||||
assert.Equal(t, int64(0), n, "copy length should be 0")
|
||||
assert.Equal(t, "", dst.String(), "destination should be empty")
|
||||
})
|
||||
|
||||
t.Run("LargeDataCopy", func(t *testing.T) {
|
||||
data := strings.Repeat("a", 10000)
|
||||
src := strings.NewReader(data)
|
||||
dst := &bytes.Buffer{}
|
||||
|
||||
n, err := copyZeroAlloc(dst, src)
|
||||
assert.NoError(t, err, "copy should not produce an error")
|
||||
assert.Equal(t, int64(len(data)), n, "copy length should match the source data length")
|
||||
assert.Equal(t, data, dst.String(), "destination should contain the copied data")
|
||||
})
|
||||
|
||||
t.Run("ErrorOnWrite", func(t *testing.T) {
|
||||
src := strings.NewReader("hello world")
|
||||
dst := &errorWriter{}
|
||||
|
||||
n, err := copyZeroAlloc(dst, src)
|
||||
assert.Error(t, err, "copy should produce an error")
|
||||
assert.Equal(t, int64(0), n, "copy length should be 0 due to the error")
|
||||
assert.Equal(t, "write error", err.Error(), "error should match expected error")
|
||||
})
|
||||
|
||||
t.Run("ErrorOnRead", func(t *testing.T) {
|
||||
src := &errorReader{}
|
||||
dst := &bytes.Buffer{}
|
||||
|
||||
n, err := copyZeroAlloc(dst, src)
|
||||
assert.Error(t, err, "copy should produce an error")
|
||||
assert.Equal(t, int64(0), n, "copy length should be 0 due to the error")
|
||||
assert.Equal(t, "read error", err.Error(), "error should match expected error")
|
||||
})
|
||||
|
||||
t.Run("ConcurrentAccess", func(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
data := "concurrent data"
|
||||
var mu sync.Mutex
|
||||
dst := &bytes.Buffer{}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
src := strings.NewReader(data) // each goroutine gets its own reader
|
||||
buf := &bytes.Buffer{} // each goroutine uses a separate buffer
|
||||
_, _ = copyZeroAlloc(buf, src)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
dst.Write(buf.Bytes()) // safely combine results
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
mu.Lock()
|
||||
assert.Equal(t, strings.Repeat(data, 10), dst.String(), "destination should contain the copied data")
|
||||
mu.Unlock()
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue