1
0
Fork 0
golang-github-meilisearch-m.../encoding_test.go
Daniel Baumann 5d4914ed7f
Adding upstream version 0.31.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-18 21:42:39 +02:00

380 lines
11 KiB
Go

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()
})
}