github.com/dtroyer-salad/og2/v2@v2.0.0-20240412154159-c47231610877/content_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 oras_test
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"io"
    25  	"net/http"
    26  	"net/http/httptest"
    27  	"net/url"
    28  	"reflect"
    29  	"strconv"
    30  	"strings"
    31  	"testing"
    32  
    33  	"github.com/opencontainers/go-digest"
    34  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    35  	"oras.land/oras-go/v2"
    36  	"oras.land/oras-go/v2/content/memory"
    37  	"oras.land/oras-go/v2/errdef"
    38  	"oras.land/oras-go/v2/internal/cas"
    39  	"oras.land/oras-go/v2/registry/remote"
    40  )
    41  
    42  func TestTag_Memory(t *testing.T) {
    43  	target := memory.New()
    44  	// generate test content
    45  	var blobs [][]byte
    46  	var descs []ocispec.Descriptor
    47  	appendBlob := func(mediaType string, blob []byte) {
    48  		blobs = append(blobs, blob)
    49  		descs = append(descs, ocispec.Descriptor{
    50  			MediaType: mediaType,
    51  			Digest:    digest.FromBytes(blob),
    52  			Size:      int64(len(blob)),
    53  		})
    54  	}
    55  	generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) {
    56  		manifest := ocispec.Manifest{
    57  			Config: config,
    58  			Layers: layers,
    59  		}
    60  		manifestJSON, err := json.Marshal(manifest)
    61  		if err != nil {
    62  			t.Fatal(err)
    63  		}
    64  		appendBlob(ocispec.MediaTypeImageManifest, manifestJSON)
    65  	}
    66  
    67  	appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0
    68  	appendBlob(ocispec.MediaTypeImageLayer, []byte("foo"))     // Blob 1
    69  	appendBlob(ocispec.MediaTypeImageLayer, []byte("bar"))     // Blob 2
    70  	generateManifest(descs[0], descs[1:3]...)                  // Blob 3
    71  
    72  	ctx := context.Background()
    73  	for i := range blobs {
    74  		err := target.Push(ctx, descs[i], bytes.NewReader(blobs[i]))
    75  		if err != nil {
    76  			t.Fatalf("failed to push test content to src: %d: %v", i, err)
    77  		}
    78  	}
    79  
    80  	manifestDesc := descs[3]
    81  	ref := "foobar"
    82  	err := target.Tag(ctx, manifestDesc, ref)
    83  	if err != nil {
    84  		t.Fatal("fail to tag manifestDesc node", err)
    85  	}
    86  
    87  	// test Tag
    88  	gotDesc, err := oras.Tag(ctx, target, ref, "myTestingTag")
    89  	if err != nil {
    90  		t.Fatalf("failed to retag using oras.Tag with err: %v", err)
    91  	}
    92  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
    93  		t.Errorf("oras.Tag() = %v, want %v", gotDesc, manifestDesc)
    94  	}
    95  
    96  	// verify tag
    97  	gotDesc, err = target.Resolve(ctx, "myTestingTag")
    98  	if err != nil {
    99  		t.Fatal("target.Resolve() error =", err)
   100  	}
   101  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   102  		t.Errorf("target.Resolve() = %v, want %v", gotDesc, manifestDesc)
   103  	}
   104  }
   105  
   106  func TestTag_Repository(t *testing.T) {
   107  	index := []byte(`{"manifests":[]}`)
   108  	indexDesc := ocispec.Descriptor{
   109  		MediaType: ocispec.MediaTypeImageIndex,
   110  		Digest:    digest.FromBytes(index),
   111  		Size:      int64(len(index)),
   112  	}
   113  	src := "foobar"
   114  	dst := "myTag"
   115  	var gotIndex []byte
   116  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   117  		switch {
   118  		case r.Method == http.MethodGet && (r.URL.Path == "/v2/test/manifests/"+indexDesc.Digest.String() || r.URL.Path == "/v2/test/manifests/"+src):
   119  			if accept := r.Header.Get("Accept"); !strings.Contains(accept, indexDesc.MediaType) {
   120  				t.Errorf("manifest not convertable: %s", accept)
   121  				w.WriteHeader(http.StatusBadRequest)
   122  				return
   123  			}
   124  			w.Header().Set("Content-Type", indexDesc.MediaType)
   125  			w.Header().Set("Docker-Content-Digest", indexDesc.Digest.String())
   126  			if _, err := w.Write(index); err != nil {
   127  				t.Errorf("failed to write %q: %v", r.URL, err)
   128  			}
   129  		case r.Method == http.MethodPut && r.URL.Path == "/v2/test/manifests/"+dst:
   130  			if contentType := r.Header.Get("Content-Type"); contentType != indexDesc.MediaType {
   131  				w.WriteHeader(http.StatusBadRequest)
   132  				break
   133  			}
   134  			buf := bytes.NewBuffer(nil)
   135  			if _, err := buf.ReadFrom(r.Body); err != nil {
   136  				t.Errorf("fail to read: %v", err)
   137  			}
   138  			gotIndex = buf.Bytes()
   139  			w.Header().Set("Docker-Content-Digest", indexDesc.Digest.String())
   140  			w.WriteHeader(http.StatusCreated)
   141  		default:
   142  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   143  			w.WriteHeader(http.StatusNotFound)
   144  		}
   145  	}))
   146  	defer ts.Close()
   147  	uri, err := url.Parse(ts.URL)
   148  	if err != nil {
   149  		t.Fatalf("invalid test http server: %v", err)
   150  	}
   151  
   152  	repoName := uri.Host + "/test"
   153  	repo, err := remote.NewRepository(repoName)
   154  	if err != nil {
   155  		t.Fatalf("NewRepository() error = %v", err)
   156  	}
   157  	repo.PlainHTTP = true
   158  	ctx := context.Background()
   159  
   160  	// test with manifest tag
   161  	gotDesc, err := oras.Tag(ctx, repo, src, dst)
   162  	if err != nil {
   163  		t.Fatalf("Repository.TagReference() error = %v", err)
   164  	}
   165  	if !bytes.Equal(gotIndex, index) {
   166  		t.Errorf("Repository.TagReference() = %v, want %v", gotIndex, index)
   167  	}
   168  	if !reflect.DeepEqual(gotDesc, indexDesc) {
   169  		t.Errorf("oras.Tag() = %v, want %v", gotDesc, indexDesc)
   170  	}
   171  
   172  	// test with manifest digest
   173  	gotDesc, err = oras.Tag(ctx, repo, indexDesc.Digest.String(), dst)
   174  	if err != nil {
   175  		t.Fatalf("Repository.TagReference() error = %v", err)
   176  	}
   177  	if !bytes.Equal(gotIndex, index) {
   178  		t.Errorf("Repository.TagReference() = %v, want %v", gotIndex, index)
   179  	}
   180  	if !reflect.DeepEqual(gotDesc, indexDesc) {
   181  		t.Errorf("oras.Tag() = %v, want %v", gotDesc, indexDesc)
   182  	}
   183  
   184  	// test with manifest tag@digest
   185  	tagDigestRef := src + "@" + indexDesc.Digest.String()
   186  	gotDesc, err = oras.Tag(ctx, repo, tagDigestRef, dst)
   187  	if err != nil {
   188  		t.Fatalf("Repository.TagReference() error = %v", err)
   189  	}
   190  	if !bytes.Equal(gotIndex, index) {
   191  		t.Errorf("Repository.TagReference() = %v, want %v", gotIndex, index)
   192  	}
   193  	if !reflect.DeepEqual(gotDesc, indexDesc) {
   194  		t.Errorf("oras.Tag() = %v, want %v", gotDesc, indexDesc)
   195  	}
   196  
   197  	// test with manifest FQDN
   198  	fqdnRef := repoName + ":" + tagDigestRef
   199  	gotDesc, err = oras.Tag(ctx, repo, fqdnRef, dst)
   200  	if err != nil {
   201  		t.Fatalf("Repository.TagReference() error = %v", err)
   202  	}
   203  	if !bytes.Equal(gotIndex, index) {
   204  		t.Errorf("Repository.TagReference() = %v, want %v", gotIndex, index)
   205  	}
   206  	if !reflect.DeepEqual(gotDesc, indexDesc) {
   207  		t.Errorf("oras.Tag() = %v, want %v", gotDesc, indexDesc)
   208  	}
   209  }
   210  
   211  func TestTagN_Memory(t *testing.T) {
   212  	target := memory.New()
   213  	// generate test content
   214  	var blobs [][]byte
   215  	var descs []ocispec.Descriptor
   216  	appendBlob := func(mediaType string, blob []byte) {
   217  		blobs = append(blobs, blob)
   218  		descs = append(descs, ocispec.Descriptor{
   219  			MediaType: mediaType,
   220  			Digest:    digest.FromBytes(blob),
   221  			Size:      int64(len(blob)),
   222  		})
   223  	}
   224  	generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) {
   225  		manifest := ocispec.Manifest{
   226  			Config: config,
   227  			Layers: layers,
   228  		}
   229  		manifestJSON, err := json.Marshal(manifest)
   230  		if err != nil {
   231  			t.Fatal(err)
   232  		}
   233  		appendBlob(ocispec.MediaTypeImageManifest, manifestJSON)
   234  	}
   235  
   236  	appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0
   237  	appendBlob(ocispec.MediaTypeImageLayer, []byte("foo"))     // Blob 1
   238  	appendBlob(ocispec.MediaTypeImageLayer, []byte("bar"))     // Blob 2
   239  	generateManifest(descs[0], descs[1:3]...)                  // Blob 3
   240  
   241  	ctx := context.Background()
   242  	for i := range blobs {
   243  		err := target.Push(ctx, descs[i], bytes.NewReader(blobs[i]))
   244  		if err != nil {
   245  			t.Fatalf("failed to push test content to src: %d: %v", i, err)
   246  		}
   247  	}
   248  
   249  	manifestDesc := descs[3]
   250  	srcRef := "foobar"
   251  	err := target.Tag(ctx, manifestDesc, srcRef)
   252  	if err != nil {
   253  		t.Fatalf("oras.Tag(%s) error = %v", srcRef, err)
   254  	}
   255  
   256  	// test TagN with empty dstReferences
   257  	_, err = oras.TagN(ctx, target, srcRef, nil, oras.DefaultTagNOptions)
   258  	if !errors.Is(err, errdef.ErrMissingReference) {
   259  		t.Fatalf("oras.TagN() error = %v, wantErr %v", err, errdef.ErrMissingReference)
   260  	}
   261  
   262  	// test TagN with single dstReferences
   263  	dstRef := "single"
   264  	gotDesc, err := oras.TagN(ctx, target, srcRef, []string{dstRef}, oras.DefaultTagNOptions)
   265  	if err != nil {
   266  		t.Fatalf("failed to retag using oras.Tag with err: %v", err)
   267  	}
   268  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   269  		t.Errorf("oras.TagN() = %v, want %v", gotDesc, manifestDesc)
   270  	}
   271  
   272  	// verify tag
   273  	gotDesc, err = target.Resolve(ctx, dstRef)
   274  	if err != nil {
   275  		t.Fatal("target.Resolve() error =", err)
   276  	}
   277  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   278  		t.Errorf("target.Resolve() = %v, want %v", gotDesc, manifestDesc)
   279  	}
   280  
   281  	// test TagN with single dstReferences and MaxMetadataBytes = 1
   282  	// should not return error
   283  	dstRef = "single2"
   284  	opts := oras.TagNOptions{
   285  		MaxMetadataBytes: 1,
   286  	}
   287  	gotDesc, err = oras.TagN(ctx, target, srcRef, []string{dstRef}, opts)
   288  	if err != nil {
   289  		t.Fatalf("failed to retag using oras.Tag with err: %v", err)
   290  	}
   291  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   292  		t.Errorf("oras.TagN() = %v, want %v", gotDesc, manifestDesc)
   293  	}
   294  
   295  	// verify tag
   296  	gotDesc, err = target.Resolve(ctx, dstRef)
   297  	if err != nil {
   298  		t.Fatal("target.Resolve() error =", err)
   299  	}
   300  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   301  		t.Errorf("target.Resolve() = %v, want %v", gotDesc, manifestDesc)
   302  	}
   303  
   304  	// test TagN with multiple references
   305  	dstRefs := []string{"foo", "bar", "baz"}
   306  	gotDesc, err = oras.TagN(ctx, target, srcRef, dstRefs, oras.DefaultTagNOptions)
   307  	if err != nil {
   308  		t.Fatal("oras.TagN() error =", err)
   309  	}
   310  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   311  		t.Errorf("oras.TagN() = %v, want %v", gotDesc, manifestDesc)
   312  	}
   313  
   314  	// verify multiple references
   315  	for _, ref := range dstRefs {
   316  		gotDesc, err := target.Resolve(ctx, ref)
   317  		if err != nil {
   318  			t.Fatal("target.Resolve() error =", err)
   319  		}
   320  		if !reflect.DeepEqual(gotDesc, manifestDesc) {
   321  			t.Errorf("target.Resolve() = %v, want %v", gotDesc, manifestDesc)
   322  		}
   323  	}
   324  
   325  	// test TagN with multiple references and MaxMetadataBytes = 1
   326  	// should not return error
   327  	dstRefs = []string{"tag1", "tag2", "tag3"}
   328  	opts = oras.TagNOptions{
   329  		MaxMetadataBytes: 1,
   330  	}
   331  	gotDesc, err = oras.TagN(ctx, target, srcRef, dstRefs, opts)
   332  	if err != nil {
   333  		t.Fatal("oras.TagN() error =", err)
   334  	}
   335  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   336  		t.Errorf("oras.TagN() = %v, want %v", gotDesc, manifestDesc)
   337  	}
   338  
   339  	// verify multiple references
   340  	for _, ref := range dstRefs {
   341  		gotDesc, err := target.Resolve(ctx, ref)
   342  		if err != nil {
   343  			t.Fatal("target.Resolve() error =", err)
   344  		}
   345  		if !reflect.DeepEqual(gotDesc, manifestDesc) {
   346  			t.Errorf("target.Resolve() = %v, want %v", gotDesc, manifestDesc)
   347  		}
   348  	}
   349  }
   350  
   351  func TestTagN_Repository(t *testing.T) {
   352  	index := []byte(`{"manifests":[]}`)
   353  	indexDesc := ocispec.Descriptor{
   354  		MediaType: ocispec.MediaTypeImageIndex,
   355  		Digest:    digest.FromBytes(index),
   356  		Size:      int64(len(index)),
   357  	}
   358  	srcRef := "foobar"
   359  	refFoo := "foo"
   360  	refBar := "bar"
   361  	refTag1 := "tag1"
   362  	refTag2 := "tag2"
   363  	dstRefs := []string{refFoo, refBar}
   364  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   365  		switch {
   366  		case r.Method == http.MethodGet && (r.URL.Path == "/v2/test/manifests/"+indexDesc.Digest.String() ||
   367  			r.URL.Path == "/v2/test/manifests/"+srcRef):
   368  			if accept := r.Header.Get("Accept"); !strings.Contains(accept, indexDesc.MediaType) {
   369  				t.Errorf("manifest not convertable: %s", accept)
   370  				w.WriteHeader(http.StatusBadRequest)
   371  				return
   372  			}
   373  			w.Header().Set("Content-Type", indexDesc.MediaType)
   374  			w.Header().Set("Docker-Content-Digest", indexDesc.Digest.String())
   375  			if _, err := w.Write(index); err != nil {
   376  				t.Errorf("failed to write %q: %v", r.URL, err)
   377  			}
   378  		case r.Method == http.MethodHead &&
   379  			(r.URL.Path == "/v2/test/manifests/"+refFoo ||
   380  				r.URL.Path == "/v2/test/manifests/"+refBar ||
   381  				r.URL.Path == "/v2/test/manifests/"+refTag1 ||
   382  				r.URL.Path == "/v2/test/manifests/"+refTag2):
   383  			if accept := r.Header.Get("Accept"); !strings.Contains(accept, indexDesc.MediaType) {
   384  				t.Errorf("manifest not convertable: %s", accept)
   385  				w.WriteHeader(http.StatusBadRequest)
   386  				return
   387  			}
   388  			w.Header().Set("Content-Type", indexDesc.MediaType)
   389  			w.Header().Set("Docker-Content-Digest", indexDesc.Digest.String())
   390  			w.Header().Set("Content-Length", strconv.Itoa(int(indexDesc.Size)))
   391  		case r.Method == http.MethodPut &&
   392  			(r.URL.Path == "/v2/test/manifests/"+refFoo ||
   393  				r.URL.Path == "/v2/test/manifests/"+refBar ||
   394  				r.URL.Path == "/v2/test/manifests/"+refTag1 ||
   395  				r.URL.Path == "/v2/test/manifests/"+refTag2):
   396  			if contentType := r.Header.Get("Content-Type"); contentType != indexDesc.MediaType {
   397  				w.WriteHeader(http.StatusBadRequest)
   398  				break
   399  			}
   400  			buf := bytes.NewBuffer(nil)
   401  			if _, err := buf.ReadFrom(r.Body); err != nil {
   402  				t.Errorf("fail to read: %v", err)
   403  			}
   404  			w.Header().Set("Docker-Content-Digest", indexDesc.Digest.String())
   405  			w.WriteHeader(http.StatusCreated)
   406  		default:
   407  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   408  			w.WriteHeader(http.StatusNotFound)
   409  		}
   410  	}))
   411  	defer ts.Close()
   412  	uri, err := url.Parse(ts.URL)
   413  	if err != nil {
   414  		t.Fatalf("invalid test http server: %v", err)
   415  	}
   416  
   417  	repoName := uri.Host + "/test"
   418  	repo, err := remote.NewRepository(repoName)
   419  	if err != nil {
   420  		t.Fatalf("NewRepository() error = %v", err)
   421  	}
   422  	repo.PlainHTTP = true
   423  	ctx := context.Background()
   424  
   425  	// test TagN with empty dstReferences
   426  	_, err = oras.TagN(ctx, repo, srcRef, nil, oras.DefaultTagNOptions)
   427  	if !errors.Is(err, errdef.ErrMissingReference) {
   428  		t.Fatalf("oras.TagN() error = %v, wantErr %v", err, errdef.ErrMissingReference)
   429  	}
   430  
   431  	// test TagN with single dstReferences
   432  	gotDesc, err := oras.TagN(ctx, repo, srcRef, []string{refTag1}, oras.DefaultTagNOptions)
   433  	if err != nil {
   434  		t.Fatalf("failed to retag using oras.Tag with err: %v", err)
   435  	}
   436  	if !reflect.DeepEqual(gotDesc, indexDesc) {
   437  		t.Errorf("oras.TagN() = %v, want %v", gotDesc, indexDesc)
   438  	}
   439  
   440  	// verify tag
   441  	gotDesc, err = repo.Resolve(ctx, refTag1)
   442  	if err != nil {
   443  		t.Fatal("target.Resolve() error =", err)
   444  	}
   445  	if !reflect.DeepEqual(gotDesc, indexDesc) {
   446  		t.Errorf("target.Resolve() = %v, want %v", gotDesc, indexDesc)
   447  	}
   448  
   449  	// test TagN with single dstReferences and MaxMetadataBytes = 1
   450  	// should not return error
   451  	opts := oras.TagNOptions{
   452  		MaxMetadataBytes: 1,
   453  	}
   454  	gotDesc, err = oras.TagN(ctx, repo, srcRef, []string{refTag2}, opts)
   455  	if err != nil {
   456  		t.Fatalf("failed to retag using oras.Tag with err: %v", err)
   457  	}
   458  	if !reflect.DeepEqual(gotDesc, indexDesc) {
   459  		t.Errorf("oras.TagN() = %v, want %v", gotDesc, indexDesc)
   460  	}
   461  
   462  	// verify tag
   463  	gotDesc, err = repo.Resolve(ctx, refTag2)
   464  	if err != nil {
   465  		t.Fatal("target.Resolve() error =", err)
   466  	}
   467  	if !reflect.DeepEqual(gotDesc, indexDesc) {
   468  		t.Errorf("target.Resolve() = %v, want %v", gotDesc, indexDesc)
   469  	}
   470  
   471  	// test TagN with multiple references
   472  	gotDesc, err = oras.TagN(ctx, repo, srcRef, dstRefs, oras.DefaultTagNOptions)
   473  	if err != nil {
   474  		t.Fatal("oras.TagN() error =", err)
   475  	}
   476  	if !reflect.DeepEqual(gotDesc, indexDesc) {
   477  		t.Errorf("oras.TagN() = %v, want %v", gotDesc, indexDesc)
   478  	}
   479  
   480  	// verify multiple references
   481  	for _, ref := range dstRefs {
   482  		gotDesc, err := repo.Resolve(ctx, ref)
   483  		if err != nil {
   484  			t.Fatal("target.Resolve() error =", err)
   485  		}
   486  		if !reflect.DeepEqual(gotDesc, indexDesc) {
   487  			t.Errorf("target.Resolve() = %v, want %v", gotDesc, indexDesc)
   488  		}
   489  	}
   490  
   491  	// test TagN with multiple references and MaxMetadataBytes = 1
   492  	// should return ErrSizeExceedsLimit
   493  	dstRefs = []string{refTag1, refTag2}
   494  	opts = oras.TagNOptions{
   495  		MaxMetadataBytes: 1,
   496  	}
   497  	_, err = oras.TagN(ctx, repo, srcRef, dstRefs, opts)
   498  	if !errors.Is(err, errdef.ErrSizeExceedsLimit) {
   499  		t.Fatalf("oras.TagN() error = %v, wantErr %v", err, errdef.ErrSizeExceedsLimit)
   500  	}
   501  }
   502  
   503  func TestResolve_Memory(t *testing.T) {
   504  	target := memory.New()
   505  	arc_1 := "test-arc-1"
   506  	os_1 := "test-os-1"
   507  	variant_1 := "v1"
   508  	variant_2 := "v2"
   509  
   510  	// generate test content
   511  	var blobs [][]byte
   512  	var descs []ocispec.Descriptor
   513  	appendBlob := func(mediaType string, blob []byte) {
   514  		blobs = append(blobs, blob)
   515  		descs = append(descs, ocispec.Descriptor{
   516  			MediaType: mediaType,
   517  			Digest:    digest.FromBytes(blob),
   518  			Size:      int64(len(blob)),
   519  		})
   520  	}
   521  	appendManifest := func(arc, os, variant string, mediaType string, blob []byte) {
   522  		blobs = append(blobs, blob)
   523  		descs = append(descs, ocispec.Descriptor{
   524  			MediaType: mediaType,
   525  			Digest:    digest.FromBytes(blob),
   526  			Size:      int64(len(blob)),
   527  			Platform: &ocispec.Platform{
   528  				Architecture: arc,
   529  				OS:           os,
   530  				Variant:      variant,
   531  			},
   532  		})
   533  	}
   534  	generateManifest := func(arc, os, variant string, config ocispec.Descriptor, layers ...ocispec.Descriptor) {
   535  		manifest := ocispec.Manifest{
   536  			Config: config,
   537  			Layers: layers,
   538  		}
   539  		manifestJSON, err := json.Marshal(manifest)
   540  		if err != nil {
   541  			t.Fatal(err)
   542  		}
   543  		appendManifest(arc, os, variant, ocispec.MediaTypeImageManifest, manifestJSON)
   544  	}
   545  
   546  	appendBlob(ocispec.MediaTypeImageConfig, []byte(`{"mediaType":"application/vnd.oci.image.config.v1+json",
   547  "created":"2022-07-29T08:13:55Z",
   548  "author":"test author",
   549  "architecture":"test-arc-1",
   550  "os":"test-os-1",
   551  "variant":"v1"}`)) // Blob 0
   552  	appendBlob(ocispec.MediaTypeImageLayer, []byte("foo"))            // Blob 1
   553  	appendBlob(ocispec.MediaTypeImageLayer, []byte("bar"))            // Blob 2
   554  	generateManifest(arc_1, os_1, variant_1, descs[0], descs[1:3]...) // Blob 3
   555  
   556  	ctx := context.Background()
   557  	for i := range blobs {
   558  		err := target.Push(ctx, descs[i], bytes.NewReader(blobs[i]))
   559  		if err != nil {
   560  			t.Fatalf("failed to push test content to src: %d: %v", i, err)
   561  		}
   562  	}
   563  
   564  	manifestDesc := descs[3]
   565  	ref := "foobar"
   566  	err := target.Tag(ctx, manifestDesc, ref)
   567  	if err != nil {
   568  		t.Fatal("fail to tag manifestDesc node", err)
   569  	}
   570  
   571  	// test Resolve with default resolve options
   572  	resolveOptions := oras.DefaultResolveOptions
   573  	gotDesc, err := oras.Resolve(ctx, target, ref, resolveOptions)
   574  
   575  	if err != nil {
   576  		t.Fatal("oras.Resolve() error =", err)
   577  	}
   578  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   579  		t.Errorf("oras.Resolve() = %v, want %v", gotDesc, manifestDesc)
   580  	}
   581  
   582  	// test Resolve with empty resolve options
   583  	gotDesc, err = oras.Resolve(ctx, target, ref, oras.ResolveOptions{})
   584  	if err != nil {
   585  		t.Fatal("oras.Resolve() error =", err)
   586  	}
   587  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   588  		t.Errorf("oras.Resolve() = %v, want %v", gotDesc, manifestDesc)
   589  	}
   590  
   591  	// test Resolve with MaxMetadataBytes = 1
   592  	resolveOptions = oras.ResolveOptions{
   593  		MaxMetadataBytes: 1,
   594  	}
   595  	gotDesc, err = oras.Resolve(ctx, target, ref, resolveOptions)
   596  	if err != nil {
   597  		t.Fatal("oras.Resolve() error =", err)
   598  	}
   599  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   600  		t.Errorf("oras.Resolve() = %v, want %v", gotDesc, manifestDesc)
   601  	}
   602  
   603  	// test Resolve with TargetPlatform
   604  	resolveOptions = oras.ResolveOptions{
   605  		TargetPlatform: &ocispec.Platform{
   606  			Architecture: arc_1,
   607  			OS:           os_1,
   608  		},
   609  	}
   610  	gotDesc, err = oras.Resolve(ctx, target, ref, resolveOptions)
   611  	if err != nil {
   612  		t.Fatal("oras.Resolve() error =", err)
   613  	}
   614  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   615  		t.Errorf("oras.Resolve() = %v, want %v", gotDesc, manifestDesc)
   616  	}
   617  
   618  	// test Resolve with TargetPlatform and MaxMetadataBytes = 1
   619  	resolveOptions = oras.ResolveOptions{
   620  		TargetPlatform: &ocispec.Platform{
   621  			Architecture: arc_1,
   622  			OS:           os_1,
   623  		},
   624  		MaxMetadataBytes: 1,
   625  	}
   626  	gotDesc, err = oras.Resolve(ctx, target, ref, resolveOptions)
   627  	if err != nil {
   628  		t.Fatal("oras.Resolve() error =", err)
   629  	}
   630  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   631  		t.Errorf("oras.Resolve() = %v, want %v", gotDesc, manifestDesc)
   632  	}
   633  
   634  	// test Resolve with TargetPlatform but there is no matching node
   635  	// Should return not found error
   636  	resolveOptions = oras.ResolveOptions{
   637  		TargetPlatform: &ocispec.Platform{
   638  			Architecture: arc_1,
   639  			OS:           os_1,
   640  			Variant:      variant_2,
   641  		},
   642  	}
   643  	_, err = oras.Resolve(ctx, target, ref, resolveOptions)
   644  	expected := fmt.Sprintf("%s: %v: platform in manifest does not match target platform", manifestDesc.Digest, errdef.ErrNotFound)
   645  	if err.Error() != expected {
   646  		t.Fatalf("oras.Resolve() error = %v, wantErr %v", err, expected)
   647  	}
   648  }
   649  
   650  func TestResolve_Repository(t *testing.T) {
   651  	arc_1 := "test-arc-1"
   652  	arc_2 := "test-arc-2"
   653  	os_1 := "test-os-1"
   654  	var digest_1 digest.Digest = "sha256:11ec3af9dfeb49c89ef71877ba85249be527e4dda9d1d74d99dc618d1a5fa151"
   655  
   656  	manifestDesc := ocispec.Descriptor{
   657  		MediaType: ocispec.MediaTypeImageManifest,
   658  		Digest:    digest_1,
   659  		Size:      484,
   660  		Platform: &ocispec.Platform{
   661  			Architecture: arc_1,
   662  			OS:           os_1,
   663  		},
   664  	}
   665  
   666  	index := []byte(`{"manifests":[{
   667  "mediaType":"application/vnd.oci.image.manifest.v1+json",
   668  "digest":"sha256:11ec3af9dfeb49c89ef71877ba85249be527e4dda9d1d74d99dc618d1a5fa151",
   669  "size":484,
   670  "platform":{"architecture":"test-arc-1","os":"test-os-1"}},{
   671  "mediaType":"application/vnd.oci.image.manifest.v1+json",
   672  "digest":"sha256:b955aefa63749f07fad84ab06a45a951368e3ac79799bc44a158fac1bb8ca208",
   673  "size":337,
   674  "platform":{"architecture":"test-arc-2","os":"test-os-2"}}]}`)
   675  	indexDesc := ocispec.Descriptor{
   676  		MediaType: ocispec.MediaTypeImageIndex,
   677  		Digest:    digest.FromBytes(index),
   678  		Size:      int64(len(index)),
   679  	}
   680  	src := "foobar"
   681  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   682  		switch {
   683  		case r.Method == http.MethodGet && (r.URL.Path == "/v2/test/manifests/"+indexDesc.Digest.String() || r.URL.Path == "/v2/test/manifests/"+src):
   684  			if accept := r.Header.Get("Accept"); !strings.Contains(accept, indexDesc.MediaType) {
   685  				t.Errorf("manifest not convertable: %s", accept)
   686  				w.WriteHeader(http.StatusBadRequest)
   687  				return
   688  			}
   689  			w.Header().Set("Content-Type", indexDesc.MediaType)
   690  			w.Header().Set("Docker-Content-Digest", indexDesc.Digest.String())
   691  			if _, err := w.Write(index); err != nil {
   692  				t.Errorf("failed to write %q: %v", r.URL, err)
   693  			}
   694  		default:
   695  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   696  			w.WriteHeader(http.StatusNotFound)
   697  		}
   698  	}))
   699  	defer ts.Close()
   700  	uri, err := url.Parse(ts.URL)
   701  	if err != nil {
   702  		t.Fatalf("invalid test http server: %v", err)
   703  	}
   704  
   705  	repoName := uri.Host + "/test"
   706  	repo, err := remote.NewRepository(repoName)
   707  	if err != nil {
   708  		t.Fatalf("NewRepository() error = %v", err)
   709  	}
   710  	repo.PlainHTTP = true
   711  	ctx := context.Background()
   712  
   713  	// test Resolve with TargetPlatform
   714  	resolveOptions := oras.ResolveOptions{
   715  		TargetPlatform: &ocispec.Platform{
   716  			Architecture: arc_1,
   717  			OS:           os_1,
   718  		},
   719  	}
   720  	gotDesc, err := oras.Resolve(ctx, repo, src, resolveOptions)
   721  	if err != nil {
   722  		t.Fatal("oras.Resolve() error =", err)
   723  	}
   724  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   725  		t.Errorf("oras.Resolve() = %v, want %v", gotDesc, manifestDesc)
   726  	}
   727  
   728  	// test Resolve with TargetPlatform and MaxMetadataBytes = 1
   729  	resolveOptions = oras.ResolveOptions{
   730  		TargetPlatform: &ocispec.Platform{
   731  			Architecture: arc_1,
   732  			OS:           os_1,
   733  		},
   734  		MaxMetadataBytes: 1,
   735  	}
   736  	_, err = oras.Resolve(ctx, repo, src, resolveOptions)
   737  	if !errors.Is(err, errdef.ErrSizeExceedsLimit) {
   738  		t.Fatalf("oras.Resolve() error = %v, wantErr %v", err, errdef.ErrSizeExceedsLimit)
   739  	}
   740  
   741  	// test Resolve with TargetPlatform but there is no matching node
   742  	// Should return not found error
   743  	resolveOptions = oras.ResolveOptions{
   744  		TargetPlatform: &ocispec.Platform{
   745  			Architecture: arc_1,
   746  			OS:           arc_2,
   747  		},
   748  	}
   749  	_, err = oras.Resolve(ctx, repo, src, resolveOptions)
   750  	expected := fmt.Sprintf("%s: %v: no matching manifest was found in the manifest list", indexDesc.Digest, errdef.ErrNotFound)
   751  	if err.Error() != expected {
   752  		t.Fatalf("oras.Resolve() error = %v, wantErr %v", err, expected)
   753  	}
   754  }
   755  
   756  func TestFetch_Memory(t *testing.T) {
   757  	target := memory.New()
   758  	arc_1 := "test-arc-1"
   759  	os_1 := "test-os-1"
   760  	variant_1 := "v1"
   761  	variant_2 := "v2"
   762  
   763  	// generate test content
   764  	var blobs [][]byte
   765  	var descs []ocispec.Descriptor
   766  	appendBlob := func(mediaType string, blob []byte) {
   767  		blobs = append(blobs, blob)
   768  		descs = append(descs, ocispec.Descriptor{
   769  			MediaType: mediaType,
   770  			Digest:    digest.FromBytes(blob),
   771  			Size:      int64(len(blob)),
   772  		})
   773  	}
   774  	appendManifest := func(arc, os, variant string, mediaType string, blob []byte) {
   775  		blobs = append(blobs, blob)
   776  		descs = append(descs, ocispec.Descriptor{
   777  			MediaType: mediaType,
   778  			Digest:    digest.FromBytes(blob),
   779  			Size:      int64(len(blob)),
   780  			Platform: &ocispec.Platform{
   781  				Architecture: arc,
   782  				OS:           os,
   783  				Variant:      variant,
   784  			},
   785  		})
   786  	}
   787  	generateManifest := func(arc, os, variant string, config ocispec.Descriptor, layers ...ocispec.Descriptor) {
   788  		manifest := ocispec.Manifest{
   789  			Config: config,
   790  			Layers: layers,
   791  		}
   792  		manifestJSON, err := json.Marshal(manifest)
   793  		if err != nil {
   794  			t.Fatal(err)
   795  		}
   796  		appendManifest(arc, os, variant, ocispec.MediaTypeImageManifest, manifestJSON)
   797  	}
   798  
   799  	appendBlob(ocispec.MediaTypeImageConfig, []byte(`{"mediaType":"application/vnd.oci.image.config.v1+json",
   800  "created":"2022-07-29T08:13:55Z",
   801  "author":"test author",
   802  "architecture":"test-arc-1",
   803  "os":"test-os-1",
   804  "variant":"v1"}`)) // Blob 0
   805  	appendBlob(ocispec.MediaTypeImageLayer, []byte("foo"))            // Blob 1
   806  	appendBlob(ocispec.MediaTypeImageLayer, []byte("bar"))            // Blob 2
   807  	generateManifest(arc_1, os_1, variant_1, descs[0], descs[1:3]...) // Blob 3
   808  
   809  	ctx := context.Background()
   810  	for i := range blobs {
   811  		err := target.Push(ctx, descs[i], bytes.NewReader(blobs[i]))
   812  		if err != nil {
   813  			t.Fatalf("failed to push test content to src: %d: %v", i, err)
   814  		}
   815  	}
   816  
   817  	manifestDesc := descs[3]
   818  	manifestTag := "foobar"
   819  	err := target.Tag(ctx, manifestDesc, manifestTag)
   820  	if err != nil {
   821  		t.Fatal("fail to tag manifestDesc node", err)
   822  	}
   823  	blobRef := "blob"
   824  	err = target.Tag(ctx, descs[2], blobRef)
   825  	if err != nil {
   826  		t.Fatal("fail to tag manifestDesc node", err)
   827  	}
   828  
   829  	// test Fetch with empty FetchOptions
   830  	gotDesc, rc, err := oras.Fetch(ctx, target, manifestTag, oras.FetchOptions{})
   831  	if err != nil {
   832  		t.Fatal("oras.Fetch() error =", err)
   833  	}
   834  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   835  		t.Errorf("oras.Fetch() = %v, want %v", gotDesc, manifestDesc)
   836  	}
   837  	got, err := io.ReadAll(rc)
   838  	if err != nil {
   839  		t.Fatal("oras.Fetch().Read() error =", err)
   840  	}
   841  	err = rc.Close()
   842  	if err != nil {
   843  		t.Error("Store.Fetch().Close() error =", err)
   844  	}
   845  	if !bytes.Equal(got, blobs[3]) {
   846  		t.Errorf("oras.Fetch() = %v, want %v", got, blobs[3])
   847  	}
   848  
   849  	// test FetchBytes with default FetchBytes options
   850  	gotDesc, rc, err = oras.Fetch(ctx, target, manifestTag, oras.DefaultFetchOptions)
   851  	if err != nil {
   852  		t.Fatal("oras.Fetch() error =", err)
   853  	}
   854  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   855  		t.Errorf("oras.Fetch() = %v, want %v", gotDesc, manifestDesc)
   856  	}
   857  	got, err = io.ReadAll(rc)
   858  	if err != nil {
   859  		t.Fatal("oras.Fetch().Read() error =", err)
   860  	}
   861  	err = rc.Close()
   862  	if err != nil {
   863  		t.Error("Store.Fetch().Close() error =", err)
   864  	}
   865  	if !bytes.Equal(got, blobs[3]) {
   866  		t.Errorf("oras.Fetch() = %v, want %v", got, blobs[3])
   867  	}
   868  
   869  	// test FetchBytes with wrong reference
   870  	randomRef := "whatever"
   871  	_, _, err = oras.Fetch(ctx, target, randomRef, oras.DefaultFetchOptions)
   872  	if !errors.Is(err, errdef.ErrNotFound) {
   873  		t.Fatalf("oras.Fetch() error = %v, wantErr %v", err, errdef.ErrNotFound)
   874  	}
   875  
   876  	// test Fetch with TargetPlatform
   877  	opts := oras.FetchOptions{
   878  		ResolveOptions: oras.ResolveOptions{
   879  			TargetPlatform: &ocispec.Platform{
   880  				Architecture: arc_1,
   881  				OS:           os_1,
   882  			},
   883  		},
   884  	}
   885  	gotDesc, rc, err = oras.Fetch(ctx, target, manifestTag, opts)
   886  	if err != nil {
   887  		t.Fatal("oras.Fetch() error =", err)
   888  	}
   889  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
   890  		t.Errorf("oras.Fetch() = %v, want %v", gotDesc, manifestDesc)
   891  	}
   892  	got, err = io.ReadAll(rc)
   893  	if err != nil {
   894  		t.Fatal("oras.Fetch().Read() error =", err)
   895  	}
   896  	err = rc.Close()
   897  	if err != nil {
   898  		t.Error("Store.Fetch().Close() error =", err)
   899  	}
   900  	if !bytes.Equal(got, blobs[3]) {
   901  		t.Errorf("oras.Fetch() = %v, want %v", got, blobs[3])
   902  	}
   903  
   904  	// test Fetch with TargetPlatform but there is no matching node
   905  	// should return not found error
   906  	opts = oras.FetchOptions{
   907  		ResolveOptions: oras.ResolveOptions{
   908  			TargetPlatform: &ocispec.Platform{
   909  				Architecture: arc_1,
   910  				OS:           os_1,
   911  				Variant:      variant_2,
   912  			},
   913  		},
   914  	}
   915  	_, _, err = oras.Fetch(ctx, target, manifestTag, opts)
   916  	expected := fmt.Sprintf("%s: %v: platform in manifest does not match target platform", manifestDesc.Digest, errdef.ErrNotFound)
   917  	if err.Error() != expected {
   918  		t.Fatalf("oras.Fetch() error = %v, wantErr %v", err, expected)
   919  	}
   920  
   921  	// test FetchBytes on blob with TargetPlatform
   922  	// should return unsupported error
   923  	opts = oras.FetchOptions{
   924  		ResolveOptions: oras.ResolveOptions{
   925  			TargetPlatform: &ocispec.Platform{
   926  				Architecture: arc_1,
   927  				OS:           os_1,
   928  			},
   929  		},
   930  	}
   931  	_, _, err = oras.Fetch(ctx, target, blobRef, opts)
   932  	if !errors.Is(err, errdef.ErrUnsupported) {
   933  		t.Fatalf("oras.Fetch() error = %v, wantErr %v", err, errdef.ErrUnsupported)
   934  	}
   935  }
   936  
   937  func TestFetch_Repository(t *testing.T) {
   938  	arc_1 := "test-arc-1"
   939  	arc_2 := "test-arc-2"
   940  	os_1 := "test-os-1"
   941  	os_2 := "test-os-2"
   942  	blob := []byte("hello world")
   943  	blobDesc := ocispec.Descriptor{
   944  		MediaType: "test",
   945  		Digest:    digest.FromBytes(blob),
   946  		Size:      int64(len(blob)),
   947  	}
   948  	manifest := []byte(`{"layers":[]}`)
   949  	manifestDesc := ocispec.Descriptor{
   950  		MediaType: ocispec.MediaTypeImageManifest,
   951  		Digest:    digest.FromBytes(manifest),
   952  		Size:      int64(len(manifest)),
   953  		Platform: &ocispec.Platform{
   954  			Architecture: arc_1,
   955  			OS:           os_1,
   956  		},
   957  	}
   958  	manifest2 := []byte("test manifest")
   959  	manifestDesc2 := ocispec.Descriptor{
   960  		MediaType: ocispec.MediaTypeImageManifest,
   961  		Digest:    digest.FromBytes(manifest2),
   962  		Size:      int64(len(manifest2)),
   963  		Platform: &ocispec.Platform{
   964  			Architecture: arc_2,
   965  			OS:           os_2,
   966  		},
   967  	}
   968  	indexContent := ocispec.Index{
   969  		MediaType: ocispec.MediaTypeImageIndex,
   970  		Manifests: []ocispec.Descriptor{
   971  			manifestDesc,
   972  			manifestDesc2,
   973  		},
   974  	}
   975  	index, err := json.Marshal(indexContent)
   976  	if err != nil {
   977  		t.Fatal("failed to marshal index", err)
   978  	}
   979  	indexDesc := ocispec.Descriptor{
   980  		MediaType: ocispec.MediaTypeImageIndex,
   981  		Digest:    digest.FromBytes(index),
   982  		Size:      int64(len(index)),
   983  	}
   984  	ref := "foobar"
   985  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   986  		switch {
   987  		case r.Method == http.MethodGet && (r.URL.Path == "/v2/test/manifests/"+indexDesc.Digest.String() || r.URL.Path == "/v2/test/manifests/"+ref):
   988  			if accept := r.Header.Get("Accept"); !strings.Contains(accept, indexDesc.MediaType) {
   989  				t.Errorf("manifest not convertable: %s", accept)
   990  				w.WriteHeader(http.StatusBadRequest)
   991  				return
   992  			}
   993  			w.Header().Set("Content-Type", indexDesc.MediaType)
   994  			w.Header().Set("Docker-Content-Digest", indexDesc.Digest.String())
   995  			if _, err := w.Write(index); err != nil {
   996  				t.Errorf("failed to write %q: %v", r.URL, err)
   997  			}
   998  		case r.URL.Path == "/v2/test/manifests/"+manifestDesc.Digest.String():
   999  			if accept := r.Header.Get("Accept"); !strings.Contains(accept, manifestDesc.MediaType) {
  1000  				t.Errorf("manifest not convertable: %s", accept)
  1001  				w.WriteHeader(http.StatusBadRequest)
  1002  				return
  1003  			}
  1004  			w.Header().Set("Content-Type", manifestDesc.MediaType)
  1005  			w.Header().Set("Docker-Content-Digest", manifestDesc.Digest.String())
  1006  			if _, err := w.Write(manifest); err != nil {
  1007  				t.Errorf("failed to write %q: %v", r.URL, err)
  1008  			}
  1009  		case r.URL.Path == "/v2/test/blobs/"+blobDesc.Digest.String():
  1010  			w.Header().Set("Content-Type", "application/octet-stream")
  1011  			w.Header().Set("Docker-Content-Digest", blobDesc.Digest.String())
  1012  			if _, err := w.Write(blob); err != nil {
  1013  				t.Errorf("failed to write %q: %v", r.URL, err)
  1014  			}
  1015  		default:
  1016  			w.WriteHeader(http.StatusNotFound)
  1017  		}
  1018  	}))
  1019  	defer ts.Close()
  1020  	uri, err := url.Parse(ts.URL)
  1021  	if err != nil {
  1022  		t.Fatalf("invalid test http server: %v", err)
  1023  	}
  1024  
  1025  	repoName := uri.Host + "/test"
  1026  	repo, err := remote.NewRepository(repoName)
  1027  	if err != nil {
  1028  		t.Fatalf("NewRepository() error = %v", err)
  1029  	}
  1030  	repo.PlainHTTP = true
  1031  	ctx := context.Background()
  1032  
  1033  	// test Fetch with empty option by valid manifest tag
  1034  	gotDesc, rc, err := oras.Fetch(ctx, repo, ref, oras.FetchOptions{})
  1035  	if err != nil {
  1036  		t.Fatal("oras.Fetch() error =", err)
  1037  	}
  1038  	if !reflect.DeepEqual(gotDesc, indexDesc) {
  1039  		t.Errorf("oras.Fetch() = %v, want %v", gotDesc, indexDesc)
  1040  	}
  1041  	got, err := io.ReadAll(rc)
  1042  	if err != nil {
  1043  		t.Fatal("oras.Fetch().Read() error =", err)
  1044  	}
  1045  	err = rc.Close()
  1046  	if err != nil {
  1047  		t.Error("Store.Fetch().Close() error =", err)
  1048  	}
  1049  	if !bytes.Equal(got, index) {
  1050  		t.Errorf("oras.Fetch() = %v, want %v", got, index)
  1051  	}
  1052  
  1053  	// test Fetch with DefaultFetchOptions by valid manifest tag
  1054  	gotDesc, rc, err = oras.Fetch(ctx, repo, ref, oras.DefaultFetchOptions)
  1055  	if err != nil {
  1056  		t.Fatal("oras.Fetch() error =", err)
  1057  	}
  1058  	if !reflect.DeepEqual(gotDesc, indexDesc) {
  1059  		t.Errorf("oras.Fetch() = %v, want %v", gotDesc, indexDesc)
  1060  	}
  1061  	got, err = io.ReadAll(rc)
  1062  	if err != nil {
  1063  		t.Fatal("oras.Fetch().Read() error =", err)
  1064  	}
  1065  	err = rc.Close()
  1066  	if err != nil {
  1067  		t.Error("Store.Fetch().Close() error =", err)
  1068  	}
  1069  	if !bytes.Equal(got, index) {
  1070  		t.Errorf("oras.Fetch() = %v, want %v", got, index)
  1071  	}
  1072  
  1073  	// test Fetch with empty option by blob digest
  1074  	gotDesc, rc, err = oras.Fetch(ctx, repo.Blobs(), blobDesc.Digest.String(), oras.FetchOptions{})
  1075  	if err != nil {
  1076  		t.Fatalf("oras.Fetch() error = %v", err)
  1077  	}
  1078  	if gotDesc.Digest != blobDesc.Digest || gotDesc.Size != blobDesc.Size {
  1079  		t.Errorf("oras.Fetch() = %v, want %v", gotDesc, blobDesc)
  1080  	}
  1081  	got, err = io.ReadAll(rc)
  1082  	if err != nil {
  1083  		t.Fatal("oras.Fetch().Read() error =", err)
  1084  	}
  1085  	err = rc.Close()
  1086  	if err != nil {
  1087  		t.Error("Store.Fetch().Close() error =", err)
  1088  	}
  1089  	if !bytes.Equal(got, blob) {
  1090  		t.Errorf("oras.Fetch() = %v, want %v", got, blob)
  1091  	}
  1092  
  1093  	// test FetchBytes with DefaultFetchBytesOptions by blob digest
  1094  	gotDesc, rc, err = oras.Fetch(ctx, repo.Blobs(), blobDesc.Digest.String(), oras.DefaultFetchOptions)
  1095  	if err != nil {
  1096  		t.Fatalf("oras.Fetch() error = %v", err)
  1097  	}
  1098  	if gotDesc.Digest != blobDesc.Digest || gotDesc.Size != blobDesc.Size {
  1099  		t.Errorf("oras.Fetch() = %v, want %v", gotDesc, blobDesc)
  1100  	}
  1101  	got, err = io.ReadAll(rc)
  1102  	if err != nil {
  1103  		t.Fatal("oras.Fetch().Read() error =", err)
  1104  	}
  1105  	err = rc.Close()
  1106  	if err != nil {
  1107  		t.Error("Store.Fetch().Close() error =", err)
  1108  	}
  1109  	if !bytes.Equal(got, blob) {
  1110  		t.Errorf("oras.Fetch() = %v, want %v", got, blob)
  1111  	}
  1112  
  1113  	// test FetchBytes with wrong reference
  1114  	randomRef := "whatever"
  1115  	_, _, err = oras.Fetch(ctx, repo, randomRef, oras.DefaultFetchOptions)
  1116  	if !errors.Is(err, errdef.ErrNotFound) {
  1117  		t.Fatalf("oras.Fetch() error = %v, wantErr %v", err, errdef.ErrNotFound)
  1118  	}
  1119  
  1120  	// test FetchBytes with TargetPlatform
  1121  	opts := oras.FetchOptions{
  1122  		ResolveOptions: oras.ResolveOptions{
  1123  			TargetPlatform: &ocispec.Platform{
  1124  				Architecture: arc_1,
  1125  				OS:           os_1,
  1126  			},
  1127  		},
  1128  	}
  1129  
  1130  	gotDesc, rc, err = oras.Fetch(ctx, repo, ref, opts)
  1131  	if err != nil {
  1132  		t.Fatal("oras.Fetch() error =", err)
  1133  	}
  1134  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
  1135  		t.Errorf("oras.Fetch() = %v, want %v", gotDesc, manifestDesc)
  1136  	}
  1137  	got, err = io.ReadAll(rc)
  1138  	if err != nil {
  1139  		t.Fatal("oras.Fetch().Read() error =", err)
  1140  	}
  1141  	err = rc.Close()
  1142  	if err != nil {
  1143  		t.Error("Store.Fetch().Close() error =", err)
  1144  	}
  1145  	if !bytes.Equal(got, manifest) {
  1146  		t.Errorf("oras.Fetch() = %v, want %v", got, manifest)
  1147  	}
  1148  
  1149  	// test FetchBytes with TargetPlatform but there is no matching node
  1150  	// Should return not found error
  1151  	opts = oras.FetchOptions{
  1152  		ResolveOptions: oras.ResolveOptions{
  1153  			TargetPlatform: &ocispec.Platform{
  1154  				Architecture: arc_1,
  1155  				OS:           arc_2,
  1156  			},
  1157  		},
  1158  	}
  1159  	_, _, err = oras.Fetch(ctx, repo, ref, opts)
  1160  	expected := fmt.Sprintf("%s: %v: no matching manifest was found in the manifest list", indexDesc.Digest, errdef.ErrNotFound)
  1161  	if err.Error() != expected {
  1162  		t.Fatalf("oras.Fetch() error = %v, wantErr %v", err, expected)
  1163  	}
  1164  
  1165  	// test FetchBytes on blob with TargetPlatform
  1166  	// should return unsupported error
  1167  	opts = oras.FetchOptions{
  1168  		ResolveOptions: oras.ResolveOptions{
  1169  			TargetPlatform: &ocispec.Platform{
  1170  				Architecture: arc_1,
  1171  				OS:           os_1,
  1172  			},
  1173  		},
  1174  	}
  1175  	_, _, err = oras.Fetch(ctx, repo.Blobs(), blobDesc.Digest.String(), opts)
  1176  	if !errors.Is(err, errdef.ErrUnsupported) {
  1177  		t.Fatalf("oras.Fetch() error = %v, wantErr %v", err, errdef.ErrUnsupported)
  1178  	}
  1179  }
  1180  
  1181  func TestFetchBytes_Memory(t *testing.T) {
  1182  	target := memory.New()
  1183  	arc_1 := "test-arc-1"
  1184  	os_1 := "test-os-1"
  1185  	variant_1 := "v1"
  1186  	variant_2 := "v2"
  1187  
  1188  	// generate test content
  1189  	var blobs [][]byte
  1190  	var descs []ocispec.Descriptor
  1191  	appendBlob := func(mediaType string, blob []byte) {
  1192  		blobs = append(blobs, blob)
  1193  		descs = append(descs, ocispec.Descriptor{
  1194  			MediaType: mediaType,
  1195  			Digest:    digest.FromBytes(blob),
  1196  			Size:      int64(len(blob)),
  1197  		})
  1198  	}
  1199  	appendManifest := func(arc, os, variant string, mediaType string, blob []byte) {
  1200  		blobs = append(blobs, blob)
  1201  		descs = append(descs, ocispec.Descriptor{
  1202  			MediaType: mediaType,
  1203  			Digest:    digest.FromBytes(blob),
  1204  			Size:      int64(len(blob)),
  1205  			Platform: &ocispec.Platform{
  1206  				Architecture: arc,
  1207  				OS:           os,
  1208  				Variant:      variant,
  1209  			},
  1210  		})
  1211  	}
  1212  	generateManifest := func(arc, os, variant string, config ocispec.Descriptor, layers ...ocispec.Descriptor) {
  1213  		manifest := ocispec.Manifest{
  1214  			Config: config,
  1215  			Layers: layers,
  1216  		}
  1217  		manifestJSON, err := json.Marshal(manifest)
  1218  		if err != nil {
  1219  			t.Fatal(err)
  1220  		}
  1221  		appendManifest(arc, os, variant, ocispec.MediaTypeImageManifest, manifestJSON)
  1222  	}
  1223  
  1224  	appendBlob(ocispec.MediaTypeImageConfig, []byte(`{"mediaType":"application/vnd.oci.image.config.v1+json",
  1225  "created":"2022-07-29T08:13:55Z",
  1226  "author":"test author",
  1227  "architecture":"test-arc-1",
  1228  "os":"test-os-1",
  1229  "variant":"v1"}`)) // Blob 0
  1230  	appendBlob(ocispec.MediaTypeImageLayer, []byte("foo"))            // Blob 1
  1231  	appendBlob(ocispec.MediaTypeImageLayer, []byte("bar"))            // Blob 2
  1232  	generateManifest(arc_1, os_1, variant_1, descs[0], descs[1:3]...) // Blob 3
  1233  
  1234  	ctx := context.Background()
  1235  	for i := range blobs {
  1236  		err := target.Push(ctx, descs[i], bytes.NewReader(blobs[i]))
  1237  		if err != nil {
  1238  			t.Fatalf("failed to push test content to src: %d: %v", i, err)
  1239  		}
  1240  	}
  1241  
  1242  	manifestDesc := descs[3]
  1243  	manifestTag := "foobar"
  1244  	err := target.Tag(ctx, manifestDesc, manifestTag)
  1245  	if err != nil {
  1246  		t.Fatal("fail to tag manifestDesc node", err)
  1247  	}
  1248  	blobRef := "blob"
  1249  	err = target.Tag(ctx, descs[2], blobRef)
  1250  	if err != nil {
  1251  		t.Fatal("fail to tag manifestDesc node", err)
  1252  	}
  1253  
  1254  	// test FetchBytes with empty FetchBytes options
  1255  	gotDesc, gotBytes, err := oras.FetchBytes(ctx, target, manifestTag, oras.FetchBytesOptions{})
  1256  	if err != nil {
  1257  		t.Fatal("oras.FetchBytes() error =", err)
  1258  	}
  1259  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
  1260  		t.Errorf("oras.FetchBytes() = %v, want %v", gotDesc, manifestDesc)
  1261  	}
  1262  	if !bytes.Equal(gotBytes, blobs[3]) {
  1263  		t.Errorf("oras.FetchBytes() = %v, want %v", gotBytes, blobs[3])
  1264  	}
  1265  
  1266  	// test FetchBytes with default FetchBytes options
  1267  	gotDesc, gotBytes, err = oras.FetchBytes(ctx, target, manifestTag, oras.DefaultFetchBytesOptions)
  1268  	if err != nil {
  1269  		t.Fatal("oras.FetchBytes() error =", err)
  1270  	}
  1271  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
  1272  		t.Errorf("oras.FetchBytes() = %v, want %v", gotDesc, manifestDesc)
  1273  	}
  1274  	if !bytes.Equal(gotBytes, blobs[3]) {
  1275  		t.Errorf("oras.FetchBytes() = %v, want %v", gotBytes, blobs[3])
  1276  	}
  1277  
  1278  	// test FetchBytes with wrong reference
  1279  	randomRef := "whatever"
  1280  	_, _, err = oras.FetchBytes(ctx, target, randomRef, oras.DefaultFetchBytesOptions)
  1281  	if !errors.Is(err, errdef.ErrNotFound) {
  1282  		t.Fatalf("oras.FetchBytes() error = %v, wantErr %v", err, errdef.ErrNotFound)
  1283  	}
  1284  
  1285  	// test FetchBytes with MaxBytes = 1
  1286  	_, _, err = oras.FetchBytes(ctx, target, manifestTag, oras.FetchBytesOptions{MaxBytes: 1})
  1287  	if !errors.Is(err, errdef.ErrSizeExceedsLimit) {
  1288  		t.Fatalf("oras.FetchBytes() error = %v, wantErr %v", err, errdef.ErrSizeExceedsLimit)
  1289  	}
  1290  
  1291  	// test FetchBytes with TargetPlatform
  1292  	opts := oras.FetchBytesOptions{
  1293  		FetchOptions: oras.FetchOptions{
  1294  			ResolveOptions: oras.ResolveOptions{
  1295  				TargetPlatform: &ocispec.Platform{
  1296  					Architecture: arc_1,
  1297  					OS:           os_1,
  1298  				},
  1299  			},
  1300  		},
  1301  	}
  1302  	gotDesc, gotBytes, err = oras.FetchBytes(ctx, target, manifestTag, opts)
  1303  	if err != nil {
  1304  		t.Fatal("oras.FetchBytes() error =", err)
  1305  	}
  1306  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
  1307  		t.Errorf("oras.FetchBytes() = %v, want %v", gotDesc, manifestDesc)
  1308  	}
  1309  	if !bytes.Equal(gotBytes, blobs[3]) {
  1310  		t.Errorf("oras.FetchBytes() = %v, want %v", gotBytes, blobs[3])
  1311  	}
  1312  
  1313  	// test FetchBytes with TargetPlatform and MaxBytes = 1
  1314  	// should return size exceed error
  1315  	opts = oras.FetchBytesOptions{
  1316  		FetchOptions: oras.FetchOptions{
  1317  			ResolveOptions: oras.ResolveOptions{
  1318  				TargetPlatform: &ocispec.Platform{
  1319  					Architecture: arc_1,
  1320  					OS:           os_1,
  1321  				},
  1322  			},
  1323  		},
  1324  		MaxBytes: 1,
  1325  	}
  1326  	_, _, err = oras.FetchBytes(ctx, target, manifestTag, opts)
  1327  	if !errors.Is(err, errdef.ErrSizeExceedsLimit) {
  1328  		t.Fatalf("oras.FetchBytes() error = %v, wantErr %v", err, errdef.ErrSizeExceedsLimit)
  1329  	}
  1330  
  1331  	// test FetchBytes with TargetPlatform but there is no matching node
  1332  	// should return not found error
  1333  	opts = oras.FetchBytesOptions{
  1334  		FetchOptions: oras.FetchOptions{
  1335  			ResolveOptions: oras.ResolveOptions{
  1336  				TargetPlatform: &ocispec.Platform{
  1337  					Architecture: arc_1,
  1338  					OS:           os_1,
  1339  					Variant:      variant_2,
  1340  				},
  1341  			},
  1342  		},
  1343  	}
  1344  	_, _, err = oras.FetchBytes(ctx, target, manifestTag, opts)
  1345  	expected := fmt.Sprintf("%s: %v: platform in manifest does not match target platform", manifestDesc.Digest, errdef.ErrNotFound)
  1346  	if err.Error() != expected {
  1347  		t.Fatalf("oras.FetchBytes() error = %v, wantErr %v", err, expected)
  1348  	}
  1349  
  1350  	// test FetchBytes on blob with TargetPlatform
  1351  	// should return unsupported error
  1352  	opts = oras.FetchBytesOptions{
  1353  		FetchOptions: oras.FetchOptions{
  1354  			ResolveOptions: oras.ResolveOptions{
  1355  				TargetPlatform: &ocispec.Platform{
  1356  					Architecture: arc_1,
  1357  					OS:           os_1,
  1358  				},
  1359  			},
  1360  		},
  1361  	}
  1362  	_, _, err = oras.FetchBytes(ctx, target, blobRef, opts)
  1363  	if !errors.Is(err, errdef.ErrUnsupported) {
  1364  		t.Fatalf("oras.FetchBytes() error = %v, wantErr %v", err, errdef.ErrUnsupported)
  1365  	}
  1366  }
  1367  
  1368  func TestFetchBytes_Repository(t *testing.T) {
  1369  	arc_1 := "test-arc-1"
  1370  	arc_2 := "test-arc-2"
  1371  	os_1 := "test-os-1"
  1372  	os_2 := "test-os-2"
  1373  	blob := []byte("hello world")
  1374  	blobDesc := ocispec.Descriptor{
  1375  		MediaType: "test",
  1376  		Digest:    digest.FromBytes(blob),
  1377  		Size:      int64(len(blob)),
  1378  	}
  1379  	manifest := []byte(`{"layers":[]}`)
  1380  	manifestDesc := ocispec.Descriptor{
  1381  		MediaType: ocispec.MediaTypeImageManifest,
  1382  		Digest:    digest.FromBytes(manifest),
  1383  		Size:      int64(len(manifest)),
  1384  		Platform: &ocispec.Platform{
  1385  			Architecture: arc_1,
  1386  			OS:           os_1,
  1387  		},
  1388  	}
  1389  	manifest2 := []byte("test manifest")
  1390  	manifestDesc2 := ocispec.Descriptor{
  1391  		MediaType: ocispec.MediaTypeImageManifest,
  1392  		Digest:    digest.FromBytes(manifest2),
  1393  		Size:      int64(len(manifest2)),
  1394  		Platform: &ocispec.Platform{
  1395  			Architecture: arc_2,
  1396  			OS:           os_2,
  1397  		},
  1398  	}
  1399  	indexContent := ocispec.Index{
  1400  		MediaType: ocispec.MediaTypeImageIndex,
  1401  		Manifests: []ocispec.Descriptor{
  1402  			manifestDesc,
  1403  			manifestDesc2,
  1404  		},
  1405  	}
  1406  	index, err := json.Marshal(indexContent)
  1407  	if err != nil {
  1408  		t.Fatal("failed to marshal index", err)
  1409  	}
  1410  	indexDesc := ocispec.Descriptor{
  1411  		MediaType: ocispec.MediaTypeImageIndex,
  1412  		Digest:    digest.FromBytes(index),
  1413  		Size:      int64(len(index)),
  1414  	}
  1415  	ref := "foobar"
  1416  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1417  		switch {
  1418  		case r.Method == http.MethodGet && (r.URL.Path == "/v2/test/manifests/"+indexDesc.Digest.String() || r.URL.Path == "/v2/test/manifests/"+ref):
  1419  			if accept := r.Header.Get("Accept"); !strings.Contains(accept, indexDesc.MediaType) {
  1420  				t.Errorf("manifest not convertable: %s", accept)
  1421  				w.WriteHeader(http.StatusBadRequest)
  1422  				return
  1423  			}
  1424  			w.Header().Set("Content-Type", indexDesc.MediaType)
  1425  			w.Header().Set("Docker-Content-Digest", indexDesc.Digest.String())
  1426  			if _, err := w.Write(index); err != nil {
  1427  				t.Errorf("failed to write %q: %v", r.URL, err)
  1428  			}
  1429  		case r.URL.Path == "/v2/test/manifests/"+manifestDesc.Digest.String():
  1430  			if accept := r.Header.Get("Accept"); !strings.Contains(accept, manifestDesc.MediaType) {
  1431  				t.Errorf("manifest not convertable: %s", accept)
  1432  				w.WriteHeader(http.StatusBadRequest)
  1433  				return
  1434  			}
  1435  			w.Header().Set("Content-Type", manifestDesc.MediaType)
  1436  			w.Header().Set("Docker-Content-Digest", manifestDesc.Digest.String())
  1437  			if _, err := w.Write(manifest); err != nil {
  1438  				t.Errorf("failed to write %q: %v", r.URL, err)
  1439  			}
  1440  		case r.URL.Path == "/v2/test/blobs/"+blobDesc.Digest.String():
  1441  			w.Header().Set("Content-Type", "application/octet-stream")
  1442  			w.Header().Set("Docker-Content-Digest", blobDesc.Digest.String())
  1443  			if _, err := w.Write(blob); err != nil {
  1444  				t.Errorf("failed to write %q: %v", r.URL, err)
  1445  			}
  1446  		default:
  1447  			w.WriteHeader(http.StatusNotFound)
  1448  		}
  1449  	}))
  1450  	defer ts.Close()
  1451  	uri, err := url.Parse(ts.URL)
  1452  	if err != nil {
  1453  		t.Fatalf("invalid test http server: %v", err)
  1454  	}
  1455  
  1456  	repoName := uri.Host + "/test"
  1457  	repo, err := remote.NewRepository(repoName)
  1458  	if err != nil {
  1459  		t.Fatalf("NewRepository() error = %v", err)
  1460  	}
  1461  	repo.PlainHTTP = true
  1462  	ctx := context.Background()
  1463  
  1464  	// test FetchBytes with empty option by valid manifest tag
  1465  	gotDesc, gotBytes, err := oras.FetchBytes(ctx, repo, ref, oras.FetchBytesOptions{})
  1466  	if err != nil {
  1467  		t.Fatalf("oras.FetchBytes() error = %v", err)
  1468  	}
  1469  	if !reflect.DeepEqual(gotDesc, indexDesc) {
  1470  		t.Errorf("oras.FetchBytes() = %v, want %v", gotDesc, indexDesc)
  1471  	}
  1472  	if !bytes.Equal(gotBytes, index) {
  1473  		t.Errorf("oras.FetchBytes() = %v, want %v", gotBytes, index)
  1474  	}
  1475  
  1476  	// test FetchBytes with DefaultFetchBytesOptions by valid manifest tag
  1477  	gotDesc, gotBytes, err = oras.FetchBytes(ctx, repo, ref, oras.DefaultFetchBytesOptions)
  1478  	if err != nil {
  1479  		t.Fatalf("oras.FetchBytes() error = %v", err)
  1480  	}
  1481  	if !reflect.DeepEqual(gotDesc, indexDesc) {
  1482  		t.Errorf("oras.FetchBytes() = %v, want %v", gotDesc, indexDesc)
  1483  	}
  1484  	if !bytes.Equal(gotBytes, index) {
  1485  		t.Errorf("oras.FetchBytes() = %v, want %v", gotBytes, index)
  1486  	}
  1487  
  1488  	// test FetchBytes with empty option by blob digest
  1489  	gotDesc, gotBytes, err = oras.FetchBytes(ctx, repo.Blobs(), blobDesc.Digest.String(), oras.FetchBytesOptions{})
  1490  	if err != nil {
  1491  		t.Fatalf("oras.FetchBytes() error = %v", err)
  1492  	}
  1493  	if gotDesc.Digest != blobDesc.Digest || gotDesc.Size != blobDesc.Size {
  1494  		t.Errorf("oras.FetchBytes() = %v, want %v", gotDesc, blobDesc)
  1495  	}
  1496  	if !bytes.Equal(gotBytes, blob) {
  1497  		t.Errorf("oras.FetchBytes() = %v, want %v", gotBytes, blob)
  1498  	}
  1499  
  1500  	// test FetchBytes with DefaultFetchBytesOptions by blob digest
  1501  	gotDesc, gotBytes, err = oras.FetchBytes(ctx, repo.Blobs(), blobDesc.Digest.String(), oras.DefaultFetchBytesOptions)
  1502  	if err != nil {
  1503  		t.Fatalf("oras.FetchBytes() error = %v", err)
  1504  	}
  1505  	if gotDesc.Digest != blobDesc.Digest || gotDesc.Size != blobDesc.Size {
  1506  		t.Errorf("oras.FetchBytes() = %v, want %v", gotDesc, blobDesc)
  1507  	}
  1508  	if !bytes.Equal(gotBytes, blob) {
  1509  		t.Errorf("oras.FetchBytes() = %v, want %v", gotBytes, blob)
  1510  	}
  1511  
  1512  	// test FetchBytes with MaxBytes = 1
  1513  	_, _, err = oras.FetchBytes(ctx, repo, ref, oras.FetchBytesOptions{MaxBytes: 1})
  1514  	if !errors.Is(err, errdef.ErrSizeExceedsLimit) {
  1515  		t.Fatalf("oras.FetchBytes() error = %v, wantErr %v", err, errdef.ErrSizeExceedsLimit)
  1516  	}
  1517  
  1518  	// test FetchBytes with wrong reference
  1519  	randomRef := "whatever"
  1520  	_, _, err = oras.FetchBytes(ctx, repo, randomRef, oras.DefaultFetchBytesOptions)
  1521  	if !errors.Is(err, errdef.ErrNotFound) {
  1522  		t.Fatalf("oras.FetchBytes() error = %v, wantErr %v", err, errdef.ErrNotFound)
  1523  	}
  1524  
  1525  	// test FetchBytes with TargetPlatform
  1526  	opts := oras.FetchBytesOptions{
  1527  		FetchOptions: oras.FetchOptions{
  1528  			ResolveOptions: oras.ResolveOptions{
  1529  				TargetPlatform: &ocispec.Platform{
  1530  					Architecture: arc_1,
  1531  					OS:           os_1,
  1532  				},
  1533  			},
  1534  		},
  1535  	}
  1536  	gotDesc, gotBytes, err = oras.FetchBytes(ctx, repo, ref, opts)
  1537  	if err != nil {
  1538  		t.Fatal("oras.FetchBytes() error =", err)
  1539  	}
  1540  	if !reflect.DeepEqual(gotDesc, manifestDesc) {
  1541  		t.Errorf("oras.FetchBytes() = %v, want %v", gotDesc, manifestDesc)
  1542  	}
  1543  	if !bytes.Equal(gotBytes, manifest) {
  1544  		t.Errorf("oras.FetchBytes() = %v, want %v", gotBytes, manifest)
  1545  	}
  1546  
  1547  	// test FetchBytes with TargetPlatform and MaxBytes = 1
  1548  	// should return size exceed error
  1549  	opts = oras.FetchBytesOptions{
  1550  		FetchOptions: oras.FetchOptions{
  1551  			ResolveOptions: oras.ResolveOptions{
  1552  				TargetPlatform: &ocispec.Platform{
  1553  					Architecture: arc_1,
  1554  					OS:           os_1,
  1555  				},
  1556  			},
  1557  		},
  1558  		MaxBytes: 1,
  1559  	}
  1560  	_, _, err = oras.FetchBytes(ctx, repo, ref, opts)
  1561  	if !errors.Is(err, errdef.ErrSizeExceedsLimit) {
  1562  		t.Fatalf("oras.FetchBytes() error = %v, wantErr %v", err, errdef.ErrSizeExceedsLimit)
  1563  	}
  1564  
  1565  	// test FetchBytes with TargetPlatform but there is no matching node
  1566  	// Should return not found error
  1567  	opts = oras.FetchBytesOptions{
  1568  		FetchOptions: oras.FetchOptions{
  1569  			ResolveOptions: oras.ResolveOptions{
  1570  				TargetPlatform: &ocispec.Platform{
  1571  					Architecture: arc_1,
  1572  					OS:           arc_2,
  1573  				},
  1574  			},
  1575  		},
  1576  	}
  1577  	_, _, err = oras.FetchBytes(ctx, repo, ref, opts)
  1578  	expected := fmt.Sprintf("%s: %v: no matching manifest was found in the manifest list", indexDesc.Digest, errdef.ErrNotFound)
  1579  	if err.Error() != expected {
  1580  		t.Fatalf("oras.FetchBytes() error = %v, wantErr %v", err, expected)
  1581  	}
  1582  
  1583  	// test FetchBytes on blob with TargetPlatform
  1584  	// should return unsupported error
  1585  	opts = oras.FetchBytesOptions{
  1586  		FetchOptions: oras.FetchOptions{
  1587  			ResolveOptions: oras.ResolveOptions{
  1588  				TargetPlatform: &ocispec.Platform{
  1589  					Architecture: arc_1,
  1590  					OS:           os_1,
  1591  				},
  1592  			},
  1593  		},
  1594  	}
  1595  	_, _, err = oras.FetchBytes(ctx, repo.Blobs(), blobDesc.Digest.String(), opts)
  1596  	if !errors.Is(err, errdef.ErrUnsupported) {
  1597  		t.Fatalf("oras.FetchBytes() error = %v, wantErr %v", err, errdef.ErrUnsupported)
  1598  	}
  1599  }
  1600  
  1601  func TestPushBytes_Memory(t *testing.T) {
  1602  	s := cas.NewMemory()
  1603  
  1604  	content := []byte("hello world")
  1605  	mediaType := "test"
  1606  	descTest := ocispec.Descriptor{
  1607  		MediaType: mediaType,
  1608  		Digest:    digest.FromBytes(content),
  1609  		Size:      int64(len(content)),
  1610  	}
  1611  	descOctet := ocispec.Descriptor{
  1612  		MediaType: "application/octet-stream",
  1613  		Digest:    digest.FromBytes(content),
  1614  		Size:      int64(len(content)),
  1615  	}
  1616  	descEmpty := ocispec.Descriptor{
  1617  		MediaType: mediaType,
  1618  		Digest:    digest.FromBytes(nil),
  1619  		Size:      0,
  1620  	}
  1621  
  1622  	ctx := context.Background()
  1623  	// test PushBytes with specified media type
  1624  	gotDesc, err := oras.PushBytes(ctx, s, mediaType, content)
  1625  	if err != nil {
  1626  		t.Fatal("oras.PushBytes() error =", err)
  1627  	}
  1628  	if !reflect.DeepEqual(gotDesc, descTest) {
  1629  		t.Errorf("oras.PushBytes() = %v, want %v", gotDesc, descTest)
  1630  	}
  1631  	rc, err := s.Fetch(ctx, gotDesc)
  1632  	if err != nil {
  1633  		t.Fatal("Memory.Fetch() error =", err)
  1634  	}
  1635  	got, err := io.ReadAll(rc)
  1636  	if err != nil {
  1637  		t.Fatal("Memory.Fetch().Read() error =", err)
  1638  	}
  1639  	err = rc.Close()
  1640  	if err != nil {
  1641  		t.Error("Memory.Fetch().Close() error =", err)
  1642  	}
  1643  	if !bytes.Equal(got, content) {
  1644  		t.Errorf("Memory.Fetch() = %v, want %v", got, content)
  1645  	}
  1646  
  1647  	// test PushBytes with existing content
  1648  	_, err = oras.PushBytes(ctx, s, mediaType, content)
  1649  	if !errors.Is(err, errdef.ErrAlreadyExists) {
  1650  		t.Errorf("oras.PushBytes() error = %v, wantErr %v", err, errdef.ErrAlreadyExists)
  1651  	}
  1652  
  1653  	// test PushBytes with empty media type
  1654  	gotDesc, err = oras.PushBytes(ctx, s, "", content)
  1655  	if err != nil {
  1656  		t.Fatal("oras.PushBytes() error =", err)
  1657  	}
  1658  	if !reflect.DeepEqual(gotDesc, descOctet) {
  1659  		t.Errorf("oras.PushBytes() = %v, want %v", gotDesc, descOctet)
  1660  	}
  1661  	rc, err = s.Fetch(ctx, gotDesc)
  1662  	if err != nil {
  1663  		t.Fatal("Memory.Fetch() error =", err)
  1664  	}
  1665  	got, err = io.ReadAll(rc)
  1666  	if err != nil {
  1667  		t.Fatal("Memory.Fetch().Read() error =", err)
  1668  	}
  1669  	err = rc.Close()
  1670  	if err != nil {
  1671  		t.Error("Memory.Fetch().Close() error =", err)
  1672  	}
  1673  	if !bytes.Equal(got, content) {
  1674  		t.Errorf("Memory.Fetch() = %v, want %v", got, content)
  1675  	}
  1676  
  1677  	// test PushBytes with empty content
  1678  	gotDesc, err = oras.PushBytes(ctx, s, mediaType, nil)
  1679  	if err != nil {
  1680  		t.Fatal("oras.PushBytes() error =", err)
  1681  	}
  1682  	if !reflect.DeepEqual(gotDesc, descEmpty) {
  1683  		t.Errorf("oras.PushBytes() = %v, want %v", gotDesc, descEmpty)
  1684  	}
  1685  	rc, err = s.Fetch(ctx, gotDesc)
  1686  	if err != nil {
  1687  		t.Fatal("Memory.Fetch() error =", err)
  1688  	}
  1689  	got, err = io.ReadAll(rc)
  1690  	if err != nil {
  1691  		t.Fatal("Memory.Fetch().Read() error =", err)
  1692  	}
  1693  	err = rc.Close()
  1694  	if err != nil {
  1695  		t.Error("Memory.Fetch().Close() error =", err)
  1696  	}
  1697  	if !bytes.Equal(got, nil) {
  1698  		t.Errorf("Memory.Fetch() = %v, want %v", got, nil)
  1699  	}
  1700  }
  1701  
  1702  func TestPushBytes_Repository(t *testing.T) {
  1703  	blob := []byte("hello world")
  1704  	blobMediaType := "test"
  1705  	blobDesc := ocispec.Descriptor{
  1706  		MediaType: blobMediaType,
  1707  		Digest:    digest.FromBytes(blob),
  1708  		Size:      int64(len(blob)),
  1709  	}
  1710  	var gotBlob []byte
  1711  	index := []byte(`{"manifests":[]}`)
  1712  	indexMediaType := ocispec.MediaTypeImageIndex
  1713  	indexDesc := ocispec.Descriptor{
  1714  		MediaType: indexMediaType,
  1715  		Digest:    digest.FromBytes(index),
  1716  		Size:      int64(len(index)),
  1717  	}
  1718  	var gotIndex []byte
  1719  	uuid := "4fd53bc9-565d-4527-ab80-3e051ac4880c"
  1720  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1721  		switch {
  1722  		case r.Method == http.MethodPost && r.URL.Path == "/v2/test/blobs/uploads/":
  1723  			w.Header().Set("Location", "/v2/test/blobs/uploads/"+uuid)
  1724  			w.WriteHeader(http.StatusAccepted)
  1725  			return
  1726  		case r.Method == http.MethodPut && r.URL.Path == "/v2/test/blobs/uploads/"+uuid:
  1727  			if contentType := r.Header.Get("Content-Type"); contentType != "application/octet-stream" {
  1728  				w.WriteHeader(http.StatusBadRequest)
  1729  				break
  1730  			}
  1731  			if contentDigest := r.URL.Query().Get("digest"); contentDigest != blobDesc.Digest.String() {
  1732  				w.WriteHeader(http.StatusBadRequest)
  1733  				break
  1734  			}
  1735  			buf := bytes.NewBuffer(nil)
  1736  			if _, err := buf.ReadFrom(r.Body); err != nil {
  1737  				t.Errorf("fail to read: %v", err)
  1738  			}
  1739  			gotBlob = buf.Bytes()
  1740  			w.Header().Set("Docker-Content-Digest", blobDesc.Digest.String())
  1741  			w.WriteHeader(http.StatusCreated)
  1742  			return
  1743  		case r.Method == http.MethodPut && r.URL.Path == "/v2/test/manifests/"+indexDesc.Digest.String():
  1744  			if contentType := r.Header.Get("Content-Type"); contentType != indexDesc.MediaType {
  1745  				w.WriteHeader(http.StatusBadRequest)
  1746  				break
  1747  			}
  1748  			buf := bytes.NewBuffer(nil)
  1749  			if _, err := buf.ReadFrom(r.Body); err != nil {
  1750  				t.Errorf("fail to read: %v", err)
  1751  			}
  1752  			gotIndex = buf.Bytes()
  1753  			w.Header().Set("Docker-Content-Digest", indexDesc.Digest.String())
  1754  			w.WriteHeader(http.StatusCreated)
  1755  			return
  1756  		default:
  1757  			w.WriteHeader(http.StatusForbidden)
  1758  		}
  1759  		t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1760  	}))
  1761  	defer ts.Close()
  1762  	uri, err := url.Parse(ts.URL)
  1763  	if err != nil {
  1764  		t.Fatalf("invalid test http server: %v", err)
  1765  	}
  1766  
  1767  	repo, err := remote.NewRepository(uri.Host + "/test")
  1768  	if err != nil {
  1769  		t.Fatalf("NewRepository() error = %v", err)
  1770  	}
  1771  	repo.PlainHTTP = true
  1772  	ctx := context.Background()
  1773  
  1774  	// test PushBytes with blob
  1775  	gotDesc, err := oras.PushBytes(ctx, repo.Blobs(), blobMediaType, blob)
  1776  	if err != nil {
  1777  		t.Fatal("oras.PushBytes() error =", err)
  1778  	}
  1779  	if !reflect.DeepEqual(gotDesc, blobDesc) {
  1780  		t.Errorf("oras.PushBytes() = %v, want %v", gotDesc, blobDesc)
  1781  	}
  1782  	if !bytes.Equal(gotBlob, blob) {
  1783  		t.Errorf("oras.PushBytes() = %v, want %v", gotBlob, blob)
  1784  	}
  1785  
  1786  	// test PushBytes with manifest
  1787  	gotDesc, err = oras.PushBytes(ctx, repo, indexMediaType, index)
  1788  	if err != nil {
  1789  		t.Fatal("oras.PushBytes() error =", err)
  1790  	}
  1791  	if err != nil {
  1792  		t.Fatal("oras.PushBytes() error =", err)
  1793  	}
  1794  	if !reflect.DeepEqual(gotDesc, indexDesc) {
  1795  		t.Errorf("oras.PushBytes() = %v, want %v", gotDesc, indexDesc)
  1796  	}
  1797  	if !bytes.Equal(gotIndex, index) {
  1798  		t.Errorf("oras.PushBytes() = %v, want %v", gotIndex, index)
  1799  	}
  1800  }
  1801  
  1802  func TestTagBytesN_Memory(t *testing.T) {
  1803  	s := memory.New()
  1804  
  1805  	content := []byte("hello world")
  1806  	mediaType := "test"
  1807  	descTest := ocispec.Descriptor{
  1808  		MediaType: mediaType,
  1809  		Digest:    digest.FromBytes(content),
  1810  		Size:      int64(len(content)),
  1811  	}
  1812  	descOctet := ocispec.Descriptor{
  1813  		MediaType: "application/octet-stream",
  1814  		Digest:    digest.FromBytes(content),
  1815  		Size:      int64(len(content)),
  1816  	}
  1817  	descEmpty := ocispec.Descriptor{
  1818  		MediaType: mediaType,
  1819  		Digest:    digest.FromBytes(nil),
  1820  		Size:      0,
  1821  	}
  1822  
  1823  	ctx := context.Background()
  1824  	// test TagBytesN with no reference
  1825  	gotDesc, err := oras.TagBytesN(ctx, s, mediaType, content, nil, oras.DefaultTagBytesNOptions)
  1826  	if err != nil {
  1827  		t.Fatal("oras.TagBytes() error =", err)
  1828  	}
  1829  	if !reflect.DeepEqual(gotDesc, descTest) {
  1830  		t.Errorf("oras.TagBytes() = %v, want %v", gotDesc, descTest)
  1831  	}
  1832  	rc, err := s.Fetch(ctx, gotDesc)
  1833  	if err != nil {
  1834  		t.Fatal("Memory.Fetch() error =", err)
  1835  	}
  1836  	got, err := io.ReadAll(rc)
  1837  	if err != nil {
  1838  		t.Fatal("Memory.Fetch().Read() error =", err)
  1839  	}
  1840  	err = rc.Close()
  1841  	if err != nil {
  1842  		t.Error("Memory.Fetch().Close() error =", err)
  1843  	}
  1844  	if !bytes.Equal(got, content) {
  1845  		t.Errorf("Memory.Fetch() = %v, want %v", got, content)
  1846  	}
  1847  
  1848  	// test TagBytesN with multiple references
  1849  	refs := []string{"foo", "bar", "baz"}
  1850  	gotDesc, err = oras.TagBytesN(ctx, s, mediaType, content, refs, oras.DefaultTagBytesNOptions)
  1851  	if err != nil {
  1852  		t.Fatal("oras.TagBytes() error =", err)
  1853  	}
  1854  	if !reflect.DeepEqual(gotDesc, descTest) {
  1855  		t.Fatalf("oras.TagBytes() = %v, want %v", gotDesc, descTest)
  1856  	}
  1857  	for _, ref := range refs {
  1858  		gotDesc, err := s.Resolve(ctx, ref)
  1859  		if err != nil {
  1860  			t.Fatal("Memory.Resolve() error =", err)
  1861  		}
  1862  		if !reflect.DeepEqual(gotDesc, descTest) {
  1863  			t.Fatalf("oras.PushBytes() = %v, want %v", gotDesc, descTest)
  1864  		}
  1865  	}
  1866  	rc, err = s.Fetch(ctx, gotDesc)
  1867  	if err != nil {
  1868  		t.Fatal("Memory.Fetch() error =", err)
  1869  	}
  1870  	got, err = io.ReadAll(rc)
  1871  	if err != nil {
  1872  		t.Fatal("Memory.Fetch().Read() error =", err)
  1873  	}
  1874  	err = rc.Close()
  1875  	if err != nil {
  1876  		t.Error("Memory.Fetch().Close() error =", err)
  1877  	}
  1878  	if !bytes.Equal(got, content) {
  1879  		t.Errorf("Memory.Fetch() = %v, want %v", got, content)
  1880  	}
  1881  
  1882  	// test TagBytesN with empty media type and multiple references
  1883  	gotDesc, err = oras.TagBytesN(ctx, s, "", content, refs, oras.DefaultTagBytesNOptions)
  1884  	if err != nil {
  1885  		t.Fatal("oras.TagBytes() error =", err)
  1886  	}
  1887  	if !reflect.DeepEqual(gotDesc, descOctet) {
  1888  		t.Fatalf("oras.TagBytes() = %v, want %v", gotDesc, descOctet)
  1889  	}
  1890  	for _, ref := range refs {
  1891  		gotDesc, err := s.Resolve(ctx, ref)
  1892  		if err != nil {
  1893  			t.Fatal("Memory.Resolve() error =", err)
  1894  		}
  1895  		if !reflect.DeepEqual(gotDesc, descOctet) {
  1896  			t.Fatalf("oras.PushBytes() = %v, want %v", gotDesc, descOctet)
  1897  		}
  1898  	}
  1899  	rc, err = s.Fetch(ctx, gotDesc)
  1900  	if err != nil {
  1901  		t.Fatal("Memory.Fetch() error =", err)
  1902  	}
  1903  	got, err = io.ReadAll(rc)
  1904  	if err != nil {
  1905  		t.Fatal("Memory.Fetch().Read() error =", err)
  1906  	}
  1907  	err = rc.Close()
  1908  	if err != nil {
  1909  		t.Error("Memory.Fetch().Close() error =", err)
  1910  	}
  1911  	if !bytes.Equal(got, content) {
  1912  		t.Errorf("Memory.Fetch() = %v, want %v", got, content)
  1913  	}
  1914  
  1915  	// test TagBytesN with empty content and multiple references
  1916  	gotDesc, err = oras.TagBytesN(ctx, s, mediaType, nil, refs, oras.DefaultTagBytesNOptions)
  1917  	if err != nil {
  1918  		t.Fatal("oras.TagBytes() error =", err)
  1919  	}
  1920  	if !reflect.DeepEqual(gotDesc, descEmpty) {
  1921  		t.Fatalf("oras.TagBytes() = %v, want %v", gotDesc, descEmpty)
  1922  	}
  1923  	for _, ref := range refs {
  1924  		gotDesc, err := s.Resolve(ctx, ref)
  1925  		if err != nil {
  1926  			t.Fatal("Memory.Resolve() error =", err)
  1927  		}
  1928  		if !reflect.DeepEqual(gotDesc, descEmpty) {
  1929  			t.Fatalf("oras.TagBytes() = %v, want %v", gotDesc, descEmpty)
  1930  		}
  1931  	}
  1932  	rc, err = s.Fetch(ctx, gotDesc)
  1933  	if err != nil {
  1934  		t.Fatal("Memory.Fetch() error =", err)
  1935  	}
  1936  	got, err = io.ReadAll(rc)
  1937  	if err != nil {
  1938  		t.Fatal("Memory.Fetch().Read() error =", err)
  1939  	}
  1940  	err = rc.Close()
  1941  	if err != nil {
  1942  		t.Error("Memory.Fetch().Close() error =", err)
  1943  	}
  1944  	if !bytes.Equal(got, nil) {
  1945  		t.Errorf("Memory.Fetch() = %v, want %v", got, nil)
  1946  	}
  1947  }
  1948  
  1949  func TestTagBytesN_Repository(t *testing.T) {
  1950  	index := []byte(`{"manifests":[]}`)
  1951  	indexMediaType := ocispec.MediaTypeImageIndex
  1952  	indexDesc := ocispec.Descriptor{
  1953  		MediaType: indexMediaType,
  1954  		Digest:    digest.FromBytes(index),
  1955  		Size:      int64(len(index)),
  1956  	}
  1957  	refFoo := "foo"
  1958  	refBar := "bar"
  1959  	refs := []string{refFoo, refBar}
  1960  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1961  		switch {
  1962  		case r.Method == http.MethodPut &&
  1963  			(r.URL.Path == "/v2/test/manifests/"+indexDesc.Digest.String() ||
  1964  				r.URL.Path == "/v2/test/manifests/"+refFoo ||
  1965  				r.URL.Path == "/v2/test/manifests/"+refBar):
  1966  			if contentType := r.Header.Get("Content-Type"); contentType != indexDesc.MediaType {
  1967  				w.WriteHeader(http.StatusBadRequest)
  1968  				break
  1969  			}
  1970  			buf := bytes.NewBuffer(nil)
  1971  			if _, err := buf.ReadFrom(r.Body); err != nil {
  1972  				t.Errorf("fail to read: %v", err)
  1973  			}
  1974  			w.Header().Set("Docker-Content-Digest", indexDesc.Digest.String())
  1975  			w.WriteHeader(http.StatusCreated)
  1976  			return
  1977  		case (r.Method == http.MethodHead || r.Method == http.MethodGet) &&
  1978  			(r.URL.Path == "/v2/test/manifests/"+indexDesc.Digest.String() ||
  1979  				r.URL.Path == "/v2/test/manifests/"+refFoo ||
  1980  				r.URL.Path == "/v2/test/manifests/"+refBar):
  1981  			if accept := r.Header.Get("Accept"); !strings.Contains(accept, indexDesc.MediaType) {
  1982  				t.Errorf("manifest not convertable: %s", accept)
  1983  				w.WriteHeader(http.StatusBadRequest)
  1984  				return
  1985  			}
  1986  			w.Header().Set("Content-Type", indexDesc.MediaType)
  1987  			w.Header().Set("Docker-Content-Digest", indexDesc.Digest.String())
  1988  			w.Header().Set("Content-Length", strconv.Itoa(int(indexDesc.Size)))
  1989  			if r.Method == http.MethodGet {
  1990  				if _, err := w.Write(index); err != nil {
  1991  					t.Errorf("failed to write %q: %v", r.URL, err)
  1992  				}
  1993  			}
  1994  		default:
  1995  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1996  			w.WriteHeader(http.StatusForbidden)
  1997  		}
  1998  	}))
  1999  	defer ts.Close()
  2000  	uri, err := url.Parse(ts.URL)
  2001  	if err != nil {
  2002  		t.Fatalf("invalid test http server: %v", err)
  2003  	}
  2004  
  2005  	repo, err := remote.NewRepository(uri.Host + "/test")
  2006  	if err != nil {
  2007  		t.Fatalf("NewRepository() error = %v", err)
  2008  	}
  2009  	repo.PlainHTTP = true
  2010  	ctx := context.Background()
  2011  
  2012  	// test TagBytesN with no reference
  2013  	gotDesc, err := oras.TagBytesN(ctx, repo, indexMediaType, index, nil, oras.DefaultTagBytesNOptions)
  2014  	if err != nil {
  2015  		t.Fatal("oras.TagBytes() error =", err)
  2016  	}
  2017  	if !reflect.DeepEqual(gotDesc, indexDesc) {
  2018  		t.Errorf("oras.TagBytes() = %v, want %v", gotDesc, indexDesc)
  2019  	}
  2020  	rc, err := repo.Fetch(ctx, gotDesc)
  2021  	if err != nil {
  2022  		t.Fatal("Repository.Fetch() error =", err)
  2023  	}
  2024  	got, err := io.ReadAll(rc)
  2025  	if err != nil {
  2026  		t.Fatal("Repository.Fetch().Read() error =", err)
  2027  	}
  2028  	err = rc.Close()
  2029  	if err != nil {
  2030  		t.Error("Repository.Fetch().Close() error =", err)
  2031  	}
  2032  	if !bytes.Equal(got, index) {
  2033  		t.Errorf("Repository.Fetch() = %v, want %v", got, index)
  2034  	}
  2035  
  2036  	// test TagBytesN with multiple references
  2037  	gotDesc, err = oras.TagBytesN(ctx, repo, indexMediaType, index, refs, oras.DefaultTagBytesNOptions)
  2038  	if err != nil {
  2039  		t.Fatal("oras.TagBytes() error =", err)
  2040  	}
  2041  	if !reflect.DeepEqual(gotDesc, indexDesc) {
  2042  		t.Fatalf("oras.TagBytes() = %v, want %v", gotDesc, indexDesc)
  2043  	}
  2044  	for _, ref := range refs {
  2045  		gotDesc, err := repo.Resolve(ctx, ref)
  2046  		if err != nil {
  2047  			t.Fatal("Repository.Resolve() error =", err)
  2048  		}
  2049  		if !reflect.DeepEqual(gotDesc, indexDesc) {
  2050  			t.Fatalf("oras.TagBytes() = %v, want %v", gotDesc, indexDesc)
  2051  		}
  2052  	}
  2053  	rc, err = repo.Fetch(ctx, gotDesc)
  2054  	if err != nil {
  2055  		t.Fatal("Repository.Fetch() error =", err)
  2056  	}
  2057  	got, err = io.ReadAll(rc)
  2058  	if err != nil {
  2059  		t.Fatal("Repository.Fetch().Read() error =", err)
  2060  	}
  2061  	err = rc.Close()
  2062  	if err != nil {
  2063  		t.Error("Repository.Fetch().Close() error =", err)
  2064  	}
  2065  	if !bytes.Equal(got, index) {
  2066  		t.Errorf("Repository.Fetch() = %v, want %v", got, index)
  2067  	}
  2068  }
  2069  
  2070  func TestTagBytes(t *testing.T) {
  2071  	s := memory.New()
  2072  
  2073  	content := []byte("hello world")
  2074  	mediaType := "test"
  2075  	descTest := ocispec.Descriptor{
  2076  		MediaType: mediaType,
  2077  		Digest:    digest.FromBytes(content),
  2078  		Size:      int64(len(content)),
  2079  	}
  2080  
  2081  	ctx := context.Background()
  2082  	ref := "foobar"
  2083  	// test TagBytes
  2084  	gotDesc, err := oras.TagBytes(ctx, s, mediaType, content, ref)
  2085  	if err != nil {
  2086  		t.Fatal("oras.TagBytes() error =", err)
  2087  	}
  2088  	if !reflect.DeepEqual(gotDesc, descTest) {
  2089  		t.Errorf("oras.TagBytes() = %v, want %v", gotDesc, descTest)
  2090  	}
  2091  	gotDesc, err = s.Resolve(ctx, ref)
  2092  	if err != nil {
  2093  		t.Fatal("Memory.Resolve() error =", err)
  2094  	}
  2095  	if !reflect.DeepEqual(gotDesc, descTest) {
  2096  		t.Fatalf("oras.TagBytes() = %v, want %v", gotDesc, descTest)
  2097  	}
  2098  	rc, err := s.Fetch(ctx, gotDesc)
  2099  	if err != nil {
  2100  		t.Fatal("Memory.Fetch() error =", err)
  2101  	}
  2102  	got, err := io.ReadAll(rc)
  2103  	if err != nil {
  2104  		t.Fatal("Memory.Fetch().Read() error =", err)
  2105  	}
  2106  	err = rc.Close()
  2107  	if err != nil {
  2108  		t.Error("Memory.Fetch().Close() error =", err)
  2109  	}
  2110  	if !bytes.Equal(got, content) {
  2111  		t.Errorf("Memory.Fetch() = %v, want %v", got, content)
  2112  	}
  2113  }