blob: 5a7cfff39108d6e0df9ae4db0b68f73038a1c296 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 manifest_test
import (
"context"
"errors"
"fmt"
"io"
"strings"
"testing"
"testing/iotest"
)
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
import (
"github.com/apache/dubbo-kubernetes/pkg/bufman/pkg/manifest"
)
func TestNewMemoryBlob(t *testing.T) {
t.Parallel()
const content = "some file content"
digest := mustDigestShake256(t, []byte(content))
blob, err := manifest.NewMemoryBlob(
*digest,
[]byte(content),
manifest.MemoryBlobWithDigestValidation(),
)
require.NoError(t, err)
assert.True(t, blob.Digest().Equal(*digest))
file, err := blob.Open(context.Background())
require.NoError(t, err)
blobContent, err := io.ReadAll(file)
require.NoError(t, err)
assert.Equal(t, []byte(content), blobContent)
}
func TestInvalidMemoryBlob(t *testing.T) {
t.Parallel()
const content = "some file content"
digest := mustDigestShake256(t, []byte(content))
t.Run("NoValidateDigest", func(t *testing.T) {
t.Parallel()
_, err := manifest.NewMemoryBlob(*digest, []byte("different content"))
assert.NoError(t, err)
})
t.Run("ValidatingDigest", func(t *testing.T) {
t.Parallel()
_, err := manifest.NewMemoryBlob(
*digest,
[]byte("different content"),
manifest.MemoryBlobWithDigestValidation(),
)
assert.Error(t, err)
})
}
func TestNewBlobSet(t *testing.T) {
t.Parallel()
blobSet, err := manifest.NewBlobSet(
context.Background(),
newBlobsArray(t),
manifest.BlobSetWithContentValidation(),
)
require.NoError(t, err)
assert.NotNil(t, blobSet)
}
func TestNewBlobValidDuplicates(t *testing.T) {
t.Parallel()
blobs := newBlobsArray(t)
blobSet, err := manifest.NewBlobSet(
context.Background(),
append(blobs, blobs[0]), // send the first blob twice
manifest.BlobSetWithContentValidation(),
)
require.NoError(t, err)
assert.NotNil(t, blobSet)
}
func TestNewBlobSetWithNilBlobs(t *testing.T) {
t.Parallel()
blobsWithNil := append(newBlobsArray(t), nil)
t.Run("DefaultRejectNils", func(t *testing.T) {
t.Parallel()
_, err := manifest.NewBlobSet(
context.Background(),
blobsWithNil,
)
require.Error(t, err)
})
t.Run("AllowNils", func(t *testing.T) {
t.Parallel()
blobSet, err := manifest.NewBlobSet(
context.Background(),
blobsWithNil,
manifest.BlobSetWithSkipNilBlobs(),
)
require.NoError(t, err)
assert.NotNil(t, blobSet)
})
}
func TestNewBlobInvalidDuplicates(t *testing.T) {
t.Parallel()
blobs := newBlobsArray(t)
incorrectBlob, err := manifest.NewMemoryBlob(
*blobs[0].Digest(),
[]byte("not blobs[0] content"),
)
require.NoError(t, err)
require.NotNil(t, incorrectBlob)
_, err = manifest.NewBlobSet(
context.Background(),
append(blobs, incorrectBlob), // send first digest twice, with diff content
manifest.BlobSetWithContentValidation(),
)
require.Error(t, err)
}
func TestAllBlobs(t *testing.T) {
t.Parallel()
blobs := newBlobsArray(t)
set, err := manifest.NewBlobSet(context.Background(), blobs)
require.NoError(t, err)
retBlobs := set.Blobs()
assertBlobsAreEqual(t, blobs, retBlobs)
}
type mockBlob struct {
digest *manifest.Digest
content io.Reader
openErr bool
}
func (mb *mockBlob) Digest() *manifest.Digest { return mb.digest }
func (mb *mockBlob) Open(_ context.Context) (io.ReadCloser, error) {
if mb.openErr {
return nil, errors.New("open error")
}
return io.NopCloser(mb.content), nil
}
type errAtEndReader struct{ reader io.Reader }
func (e *errAtEndReader) Read(p []byte) (int, error) {
n, err := e.reader.Read(p)
if err == io.EOF {
return n, errors.New("test erroring at EOF")
}
return n, err
}
func TestBlobEqual(t *testing.T) {
t.Parallel()
testBlobEqual(
t,
"does equal",
strings.NewReader("foo"),
strings.NewReader("foo"),
true,
false,
)
testBlobEqual(
t,
"equal with differing length reads",
strings.NewReader("foo"),
iotest.OneByteReader(strings.NewReader("foo")),
true,
false,
)
testBlobEqual(
t,
"mismatched equal-length content",
strings.NewReader("foo"),
strings.NewReader("bar"),
false,
false,
)
testBlobEqual(
t,
"mismatched equal-length content data with error",
iotest.DataErrReader(strings.NewReader("foo")),
iotest.DataErrReader(strings.NewReader("bar")),
false,
false,
)
testBlobEqual(
t,
"mismatched longer left content",
strings.NewReader("foofoo"),
strings.NewReader("foo"),
false,
false,
)
testBlobEqual(
t,
"mismatched longer right content",
strings.NewReader("foo"),
strings.NewReader("foofoo"),
false,
false,
)
testBlobEqual(
t,
"fast error left read",
iotest.ErrReader(errors.New("testing error")),
strings.NewReader("foo"),
false,
true,
)
testBlobEqual(
t,
"fast error right read",
strings.NewReader("foo"),
iotest.ErrReader(errors.New("testing error")),
false,
true,
)
testBlobEqual(
t,
"late error left read",
&errAtEndReader{reader: strings.NewReader("foo")},
strings.NewReader("foo"),
false,
true,
)
testBlobEqual(
t,
"late error right read",
strings.NewReader("foo"),
&errAtEndReader{reader: strings.NewReader("foo")},
false,
true,
)
testBlobEqual(
t,
"middle error left read",
iotest.TimeoutReader(iotest.OneByteReader(strings.NewReader("foo"))),
strings.NewReader("foo"),
false,
true,
)
testBlobEqual(
t,
"middle error right read",
strings.NewReader("foo"),
iotest.TimeoutReader(iotest.OneByteReader(strings.NewReader("foo"))),
false,
true,
)
}
func TestBlobEqualDigestMismatch(t *testing.T) {
t.Parallel()
ctx := context.Background()
digester, err := manifest.NewDigester(manifest.DigestTypeShake256)
require.NoError(t, err)
const foo = "foo"
const bar = "bar"
aDigest, err := digester.Digest(strings.NewReader(foo))
require.NoError(t, err)
bDigest, err := digester.Digest(strings.NewReader(bar))
require.NoError(t, err)
aBlob := &mockBlob{
digest: aDigest,
content: strings.NewReader(foo),
}
bBlob := &mockBlob{
digest: bDigest,
content: strings.NewReader(""),
}
equal, err := manifest.BlobEqual(ctx, aBlob, bBlob)
assert.False(t, equal)
assert.NoError(t, err)
}
func TestBlobEqualOpenError(t *testing.T) {
t.Parallel()
ctx := context.Background()
aBlob := &mockBlob{
digest: &manifest.Digest{},
openErr: true,
}
bBlob := &mockBlob{
digest: &manifest.Digest{},
}
equal, err := manifest.BlobEqual(ctx, aBlob, bBlob)
assert.False(t, equal)
assert.Error(t, err)
aBlob = &mockBlob{
digest: &manifest.Digest{},
}
bBlob = &mockBlob{
digest: &manifest.Digest{},
openErr: true,
}
equal, err = manifest.BlobEqual(ctx, aBlob, bBlob)
assert.False(t, equal)
assert.Error(t, err)
}
func testBlobEqual(
t *testing.T,
desc string,
a, b io.Reader,
isEqual bool,
isError bool,
) {
t.Helper()
t.Run(desc, func(t *testing.T) {
t.Parallel()
ctx := context.Background()
digest := &manifest.Digest{} // Avoid digest equality test.
aBlob := &mockBlob{
digest: digest,
content: a,
}
bBlob := &mockBlob{
digest: digest,
content: b,
}
equal, err := manifest.BlobEqual(ctx, aBlob, bBlob)
assert.Equal(t, isEqual, equal)
if isError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
func newBlobsArray(t *testing.T) []manifest.Blob {
var blobs []manifest.Blob
for i := 0; i < 10; i++ {
content := fmt.Sprintf("some content %d", i)
digest := mustDigestShake256(t, []byte(content))
blob, err := manifest.NewMemoryBlob(
*digest, []byte(content),
manifest.MemoryBlobWithDigestValidation(),
)
require.NoError(t, err)
blobs = append(blobs, blob)
}
return blobs
}
// assertBlobsAreEqual makes sure all the blobs digests in the array are the
// same (assuming they're correctly built), ignoring order in the blobs arrays.
func assertBlobsAreEqual(t *testing.T, expectedBlobs []manifest.Blob, actualBlobs []manifest.Blob) {
expectedDigests := make(map[string]struct{}, len(expectedBlobs))
for _, expectedBlob := range expectedBlobs {
expectedDigests[expectedBlob.Digest().String()] = struct{}{}
}
actualDigests := make(map[string]struct{}, len(actualBlobs))
for _, actualBlob := range actualBlobs {
actualDigests[actualBlob.Digest().String()] = struct{}{}
}
assert.Equal(t, expectedDigests, actualDigests)
}