oras.land/oras-go/v2@v2.5.1-0.20240520045656-aef90e4d04c4/content/memory/memory_test.go (about)

     1  /*
     2  Copyright The ORAS Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package memory
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	_ "crypto/sha256"
    22  	"encoding/json"
    23  	"errors"
    24  	"fmt"
    25  	"io"
    26  	"reflect"
    27  	"strings"
    28  	"testing"
    29  
    30  	"github.com/opencontainers/go-digest"
    31  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    32  	"golang.org/x/sync/errgroup"
    33  	"oras.land/oras-go/v2"
    34  	"oras.land/oras-go/v2/content"
    35  	"oras.land/oras-go/v2/errdef"
    36  	"oras.land/oras-go/v2/internal/cas"
    37  	"oras.land/oras-go/v2/internal/resolver"
    38  	"oras.land/oras-go/v2/internal/spec"
    39  )
    40  
    41  func TestStoreInterface(t *testing.T) {
    42  	var store interface{} = &Store{}
    43  	if _, ok := store.(oras.Target); !ok {
    44  		t.Error("&Store{} does not conform oras.Target")
    45  	}
    46  	if _, ok := store.(content.PredecessorFinder); !ok {
    47  		t.Error("&Store{} does not conform content.PredecessorFinder")
    48  	}
    49  }
    50  
    51  func TestStoreSuccess(t *testing.T) {
    52  	content := []byte("hello world")
    53  	desc := ocispec.Descriptor{
    54  		MediaType: "test",
    55  		Digest:    digest.FromBytes(content),
    56  		Size:      int64(len(content)),
    57  	}
    58  	ref := "foobar"
    59  
    60  	s := New()
    61  	ctx := context.Background()
    62  
    63  	err := s.Push(ctx, desc, bytes.NewReader(content))
    64  	if err != nil {
    65  		t.Fatal("Store.Push() error =", err)
    66  	}
    67  
    68  	err = s.Tag(ctx, desc, ref)
    69  	if err != nil {
    70  		t.Fatal("Store.Tag() error =", err)
    71  	}
    72  
    73  	gotDesc, err := s.Resolve(ctx, ref)
    74  	if err != nil {
    75  		t.Fatal("Store.Resolve() error =", err)
    76  	}
    77  	if !reflect.DeepEqual(gotDesc, desc) {
    78  		t.Errorf("Store.Resolve() = %v, want %v", gotDesc, desc)
    79  	}
    80  	internalResolver := s.resolver.(*resolver.Memory)
    81  	if got := len(internalResolver.Map()); got != 1 {
    82  		t.Errorf("resolver.Map() = %v, want %v", got, 1)
    83  	}
    84  
    85  	exists, err := s.Exists(ctx, desc)
    86  	if err != nil {
    87  		t.Fatal("Store.Exists() error =", err)
    88  	}
    89  	if !exists {
    90  		t.Errorf("Store.Exists() = %v, want %v", exists, true)
    91  	}
    92  
    93  	rc, err := s.Fetch(ctx, desc)
    94  	if err != nil {
    95  		t.Fatal("Store.Fetch() error =", err)
    96  	}
    97  	got, err := io.ReadAll(rc)
    98  	if err != nil {
    99  		t.Fatal("Store.Fetch().Read() error =", err)
   100  	}
   101  	err = rc.Close()
   102  	if err != nil {
   103  		t.Error("Store.Fetch().Close() error =", err)
   104  	}
   105  	if !bytes.Equal(got, content) {
   106  		t.Errorf("Store.Fetch() = %v, want %v", got, content)
   107  	}
   108  	internalStorage := s.storage.(*cas.Memory)
   109  	if got := len(internalStorage.Map()); got != 1 {
   110  		t.Errorf("storage.Map() = %v, want %v", got, 1)
   111  	}
   112  }
   113  
   114  func TestStoreContentNotFound(t *testing.T) {
   115  	content := []byte("hello world")
   116  	desc := ocispec.Descriptor{
   117  		MediaType: "test",
   118  		Digest:    digest.FromBytes(content),
   119  		Size:      int64(len(content)),
   120  	}
   121  
   122  	s := New()
   123  	ctx := context.Background()
   124  
   125  	exists, err := s.Exists(ctx, desc)
   126  	if err != nil {
   127  		t.Error("Store.Exists() error =", err)
   128  	}
   129  	if exists {
   130  		t.Errorf("Store.Exists() = %v, want %v", exists, false)
   131  	}
   132  
   133  	_, err = s.Fetch(ctx, desc)
   134  	if !errors.Is(err, errdef.ErrNotFound) {
   135  		t.Errorf("Store.Fetch() error = %v, want %v", err, errdef.ErrNotFound)
   136  	}
   137  }
   138  
   139  func TestStoreContentAlreadyExists(t *testing.T) {
   140  	content := []byte("hello world")
   141  	desc := ocispec.Descriptor{
   142  		MediaType: "test",
   143  		Digest:    digest.FromBytes(content),
   144  		Size:      int64(len(content)),
   145  	}
   146  
   147  	s := New()
   148  	ctx := context.Background()
   149  
   150  	err := s.Push(ctx, desc, bytes.NewReader(content))
   151  	if err != nil {
   152  		t.Fatal("Store.Push() error =", err)
   153  	}
   154  
   155  	err = s.Push(ctx, desc, bytes.NewReader(content))
   156  	if !errors.Is(err, errdef.ErrAlreadyExists) {
   157  		t.Errorf("Store.Push() error = %v, want %v", err, errdef.ErrAlreadyExists)
   158  	}
   159  }
   160  
   161  func TestStoreContentBadPush(t *testing.T) {
   162  	content := []byte("hello world")
   163  	desc := ocispec.Descriptor{
   164  		MediaType: "test",
   165  		Digest:    digest.FromBytes(content),
   166  		Size:      int64(len(content)),
   167  	}
   168  
   169  	s := New()
   170  	ctx := context.Background()
   171  
   172  	err := s.Push(ctx, desc, strings.NewReader("foobar"))
   173  	if err == nil {
   174  		t.Errorf("Store.Push() error = %v, wantErr %v", err, true)
   175  	}
   176  }
   177  
   178  func TestStoreTagNotFound(t *testing.T) {
   179  	ref := "foobar"
   180  
   181  	s := New()
   182  	ctx := context.Background()
   183  
   184  	_, err := s.Resolve(ctx, ref)
   185  	if !errors.Is(err, errdef.ErrNotFound) {
   186  		t.Errorf("Store.Resolve() error = %v, want %v", err, errdef.ErrNotFound)
   187  	}
   188  }
   189  
   190  func TestStoreTagUnknownContent(t *testing.T) {
   191  	content := []byte("hello world")
   192  	desc := ocispec.Descriptor{
   193  		MediaType: "test",
   194  		Digest:    digest.FromBytes(content),
   195  		Size:      int64(len(content)),
   196  	}
   197  	ref := "foobar"
   198  
   199  	s := New()
   200  	ctx := context.Background()
   201  
   202  	err := s.Tag(ctx, desc, ref)
   203  	if !errors.Is(err, errdef.ErrNotFound) {
   204  		t.Errorf("Store.Resolve() error = %v, want %v", err, errdef.ErrNotFound)
   205  	}
   206  }
   207  
   208  func TestStoreRepeatTag(t *testing.T) {
   209  	generate := func(content []byte) ocispec.Descriptor {
   210  		return ocispec.Descriptor{
   211  			MediaType: "test",
   212  			Digest:    digest.FromBytes(content),
   213  			Size:      int64(len(content)),
   214  		}
   215  	}
   216  	ref := "foobar"
   217  
   218  	s := New()
   219  	ctx := context.Background()
   220  
   221  	// get internal resolver
   222  	internalResolver := s.resolver.(*resolver.Memory)
   223  
   224  	// initial tag
   225  	content := []byte("hello world")
   226  	desc := generate(content)
   227  	err := s.Push(ctx, desc, bytes.NewReader(content))
   228  	if err != nil {
   229  		t.Fatal("Store.Push() error =", err)
   230  	}
   231  
   232  	err = s.Tag(ctx, desc, ref)
   233  	if err != nil {
   234  		t.Fatal("Store.Tag() error =", err)
   235  	}
   236  
   237  	gotDesc, err := s.Resolve(ctx, ref)
   238  	if err != nil {
   239  		t.Fatal("Store.Resolve() error =", err)
   240  	}
   241  	if !reflect.DeepEqual(gotDesc, desc) {
   242  		t.Errorf("Store.Resolve() = %v, want %v", gotDesc, desc)
   243  	}
   244  	if got := len(internalResolver.Map()); got != 1 {
   245  		t.Errorf("resolver.Map() = %v, want %v", got, 1)
   246  	}
   247  
   248  	// repeat tag
   249  	content = []byte("foo")
   250  	desc = generate(content)
   251  	err = s.Push(ctx, desc, bytes.NewReader(content))
   252  	if err != nil {
   253  		t.Fatal("Store.Push() error =", err)
   254  	}
   255  
   256  	err = s.Tag(ctx, desc, ref)
   257  	if err != nil {
   258  		t.Fatal("Store.Tag() error =", err)
   259  	}
   260  
   261  	gotDesc, err = s.Resolve(ctx, ref)
   262  	if err != nil {
   263  		t.Fatal("Store.Resolve() error =", err)
   264  	}
   265  	if !reflect.DeepEqual(gotDesc, desc) {
   266  		t.Errorf("Store.Resolve() = %v, want %v", gotDesc, desc)
   267  	}
   268  	if got := len(internalResolver.Map()); got != 1 {
   269  		t.Errorf("resolver.Map() = %v, want %v", got, 1)
   270  	}
   271  
   272  	// repeat tag
   273  	content = []byte("bar")
   274  	desc = generate(content)
   275  	err = s.Push(ctx, desc, bytes.NewReader(content))
   276  	if err != nil {
   277  		t.Fatal("Store.Push() error =", err)
   278  	}
   279  
   280  	err = s.Tag(ctx, desc, ref)
   281  	if err != nil {
   282  		t.Fatal("Store.Tag() error =", err)
   283  	}
   284  
   285  	gotDesc, err = s.Resolve(ctx, ref)
   286  	if err != nil {
   287  		t.Fatal("Store.Resolve() error =", err)
   288  	}
   289  	if !reflect.DeepEqual(gotDesc, desc) {
   290  		t.Errorf("Store.Resolve() = %v, want %v", gotDesc, desc)
   291  	}
   292  	if got := len(internalResolver.Map()); got != 1 {
   293  		t.Errorf("resolver.Map() = %v, want %v", got, 1)
   294  	}
   295  }
   296  
   297  func TestStorePredecessors(t *testing.T) {
   298  	s := New()
   299  	ctx := context.Background()
   300  
   301  	// generate test content
   302  	var blobs [][]byte
   303  	var descs []ocispec.Descriptor
   304  	appendBlob := func(mediaType string, blob []byte) {
   305  		blobs = append(blobs, blob)
   306  		descs = append(descs, ocispec.Descriptor{
   307  			MediaType: mediaType,
   308  			Digest:    digest.FromBytes(blob),
   309  			Size:      int64(len(blob)),
   310  		})
   311  	}
   312  	generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) {
   313  		manifest := ocispec.Manifest{
   314  			Config: config,
   315  			Layers: layers,
   316  		}
   317  		manifestJSON, err := json.Marshal(manifest)
   318  		if err != nil {
   319  			t.Fatal(err)
   320  		}
   321  		appendBlob(ocispec.MediaTypeImageManifest, manifestJSON)
   322  	}
   323  	generateIndex := func(manifests ...ocispec.Descriptor) {
   324  		index := ocispec.Index{
   325  			Manifests: manifests,
   326  		}
   327  		indexJSON, err := json.Marshal(index)
   328  		if err != nil {
   329  			t.Fatal(err)
   330  		}
   331  		appendBlob(ocispec.MediaTypeImageIndex, indexJSON)
   332  	}
   333  	generateArtifactManifest := func(subject ocispec.Descriptor, blobs ...ocispec.Descriptor) {
   334  		var manifest spec.Artifact
   335  		manifest.Subject = &subject
   336  		manifest.Blobs = append(manifest.Blobs, blobs...)
   337  		manifestJSON, err := json.Marshal(manifest)
   338  		if err != nil {
   339  			t.Fatal(err)
   340  		}
   341  		appendBlob(spec.MediaTypeArtifactManifest, manifestJSON)
   342  	}
   343  
   344  	appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0
   345  	appendBlob(ocispec.MediaTypeImageLayer, []byte("foo"))     // Blob 1
   346  	appendBlob(ocispec.MediaTypeImageLayer, []byte("bar"))     // Blob 2
   347  	appendBlob(ocispec.MediaTypeImageLayer, []byte("hello"))   // Blob 3
   348  	generateManifest(descs[0], descs[1:3]...)                  // Blob 4
   349  	generateManifest(descs[0], descs[3])                       // Blob 5
   350  	generateManifest(descs[0], descs[1:4]...)                  // Blob 6
   351  	generateIndex(descs[4:6]...)                               // Blob 7
   352  	generateIndex(descs[6])                                    // Blob 8
   353  	generateIndex()                                            // Blob 9
   354  	generateIndex(descs[7:10]...)                              // Blob 10
   355  	appendBlob(ocispec.MediaTypeImageLayer, []byte("sig_1"))   // Blob 11
   356  	generateArtifactManifest(descs[6], descs[11])              // Blob 12
   357  	appendBlob(ocispec.MediaTypeImageLayer, []byte("sig_2"))   // Blob 13
   358  	generateArtifactManifest(descs[10], descs[13])             // Blob 14
   359  
   360  	eg, egCtx := errgroup.WithContext(ctx)
   361  	for i := range blobs {
   362  		eg.Go(func(i int) func() error {
   363  			return func() error {
   364  				err := s.Push(egCtx, descs[i], bytes.NewReader(blobs[i]))
   365  				if err != nil {
   366  					return fmt.Errorf("failed to push test content to src: %d: %v", i, err)
   367  				}
   368  				return nil
   369  			}
   370  		}(i))
   371  	}
   372  	if err := eg.Wait(); err != nil {
   373  		t.Fatal(err)
   374  	}
   375  
   376  	// verify predecessors
   377  	wants := [][]ocispec.Descriptor{
   378  		descs[4:7],            // Blob 0
   379  		{descs[4], descs[6]},  // Blob 1
   380  		{descs[4], descs[6]},  // Blob 2
   381  		{descs[5], descs[6]},  // Blob 3
   382  		{descs[7]},            // Blob 4
   383  		{descs[7]},            // Blob 5
   384  		{descs[8], descs[12]}, // Blob 6
   385  		{descs[10]},           // Blob 7
   386  		{descs[10]},           // Blob 8
   387  		{descs[10]},           // Blob 9
   388  		{descs[14]},           // Blob 10
   389  		{descs[12]},           // Blob 11
   390  		nil,                   // Blob 12
   391  		{descs[14]},           // Blob 13
   392  		nil,                   // Blob 14
   393  	}
   394  	for i, want := range wants {
   395  		predecessors, err := s.Predecessors(ctx, descs[i])
   396  		if err != nil {
   397  			t.Errorf("Store.Predecessors(%d) error = %v", i, err)
   398  		}
   399  		if !equalDescriptorSet(predecessors, want) {
   400  			t.Errorf("Store.Predecessors(%d) = %v, want %v", i, predecessors, want)
   401  		}
   402  	}
   403  }
   404  
   405  func equalDescriptorSet(actual []ocispec.Descriptor, expected []ocispec.Descriptor) bool {
   406  	if len(actual) != len(expected) {
   407  		return false
   408  	}
   409  	contains := func(node ocispec.Descriptor) bool {
   410  		for _, candidate := range actual {
   411  			if reflect.DeepEqual(candidate, node) {
   412  				return true
   413  			}
   414  		}
   415  		return false
   416  	}
   417  	for _, node := range expected {
   418  		if !contains(node) {
   419  			return false
   420  		}
   421  	}
   422  	return true
   423  }