oras.land/oras-go/v2@v2.5.1-0.20240520045656-aef90e4d04c4/content/graph_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 content_test
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"encoding/json"
    22  	"reflect"
    23  	"testing"
    24  
    25  	"github.com/opencontainers/go-digest"
    26  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    27  	"oras.land/oras-go/v2/content"
    28  	"oras.land/oras-go/v2/internal/cas"
    29  	"oras.land/oras-go/v2/internal/docker"
    30  	"oras.land/oras-go/v2/internal/spec"
    31  )
    32  
    33  func TestSuccessors_dockerManifest(t *testing.T) {
    34  	storage := cas.NewMemory()
    35  
    36  	// generate test content
    37  	var blobs [][]byte
    38  	var descs []ocispec.Descriptor
    39  	appendBlob := func(mediaType string, blob []byte) {
    40  		blobs = append(blobs, blob)
    41  		descs = append(descs, ocispec.Descriptor{
    42  			MediaType: mediaType,
    43  			Digest:    digest.FromBytes(blob),
    44  			Size:      int64(len(blob)),
    45  		})
    46  	}
    47  	generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) {
    48  		manifest := ocispec.Manifest{
    49  			Config: config,
    50  			Layers: layers,
    51  		}
    52  		manifestJSON, err := json.Marshal(manifest)
    53  		if err != nil {
    54  			t.Fatal(err)
    55  		}
    56  		appendBlob(docker.MediaTypeManifest, manifestJSON)
    57  	}
    58  
    59  	appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0
    60  	appendBlob(ocispec.MediaTypeImageLayer, []byte("foo"))     // Blob 1
    61  	appendBlob(ocispec.MediaTypeImageLayer, []byte("bar"))     // Blob 2
    62  	appendBlob(ocispec.MediaTypeImageLayer, []byte("hello"))   // Blob 3
    63  	generateManifest(descs[0], descs[1:4]...)                  // Blob 4
    64  
    65  	ctx := context.Background()
    66  	for i := range blobs {
    67  		err := storage.Push(ctx, descs[i], bytes.NewReader(blobs[i]))
    68  		if err != nil {
    69  			t.Fatalf("failed to push test content to src: %d: %v", i, err)
    70  		}
    71  	}
    72  
    73  	// test Successors
    74  	manifestDesc := descs[4]
    75  	got, err := content.Successors(ctx, storage, manifestDesc)
    76  	if err != nil {
    77  		t.Fatal("Successors() error =", err)
    78  	}
    79  	if want := descs[0:4]; !reflect.DeepEqual(got, want) {
    80  		t.Errorf("Successors() = %v, want %v", got, want)
    81  	}
    82  }
    83  
    84  func TestSuccessors_imageManifest(t *testing.T) {
    85  	storage := cas.NewMemory()
    86  
    87  	// generate test content
    88  	var blobs [][]byte
    89  	var descs []ocispec.Descriptor
    90  	appendBlob := func(mediaType string, blob []byte) {
    91  		blobs = append(blobs, blob)
    92  		descs = append(descs, ocispec.Descriptor{
    93  			MediaType: mediaType,
    94  			Digest:    digest.FromBytes(blob),
    95  			Size:      int64(len(blob)),
    96  		})
    97  	}
    98  	generateManifest := func(subject *ocispec.Descriptor, config ocispec.Descriptor, layers ...ocispec.Descriptor) {
    99  		manifest := ocispec.Manifest{
   100  			Subject: subject,
   101  			Config:  config,
   102  			Layers:  layers,
   103  		}
   104  		manifestJSON, err := json.Marshal(manifest)
   105  		if err != nil {
   106  			t.Fatal(err)
   107  		}
   108  		appendBlob(ocispec.MediaTypeImageManifest, manifestJSON)
   109  	}
   110  
   111  	appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0
   112  	appendBlob(ocispec.MediaTypeImageLayer, []byte("foo"))     // Blob 1
   113  	appendBlob(ocispec.MediaTypeImageLayer, []byte("bar"))     // Blob 2
   114  	appendBlob(ocispec.MediaTypeImageLayer, []byte("hello"))   // Blob 3
   115  	generateManifest(nil, descs[0], descs[1:4]...)             // Blob 4
   116  	appendBlob(ocispec.MediaTypeImageConfig, []byte("{}"))     // Blob 5
   117  	appendBlob("test/sig", []byte("sig"))                      // Blob 6
   118  	generateManifest(&descs[4], descs[5], descs[6])            // Blob 7
   119  
   120  	ctx := context.Background()
   121  	for i := range blobs {
   122  		err := storage.Push(ctx, descs[i], bytes.NewReader(blobs[i]))
   123  		if err != nil {
   124  			t.Fatalf("failed to push test content to src: %d: %v", i, err)
   125  		}
   126  	}
   127  
   128  	// test Successors: image manifest without a subject
   129  	manifestDesc := descs[4]
   130  	got, err := content.Successors(ctx, storage, manifestDesc)
   131  	if err != nil {
   132  		t.Fatal("Successors() error =", err)
   133  	}
   134  	if want := descs[0:4]; !reflect.DeepEqual(got, want) {
   135  		t.Errorf("Successors() = %v, want %v", got, want)
   136  	}
   137  
   138  	// test Successors: image manifest with a subject
   139  	manifestDesc = descs[7]
   140  	got, err = content.Successors(ctx, storage, manifestDesc)
   141  	if err != nil {
   142  		t.Fatal("Successors() error =", err)
   143  	}
   144  	if want := descs[4:7]; !reflect.DeepEqual(got, want) {
   145  		t.Errorf("Successors() = %v, want %v", got, want)
   146  	}
   147  }
   148  
   149  func TestSuccessors_dockerManifestList(t *testing.T) {
   150  	storage := cas.NewMemory()
   151  
   152  	// generate test content
   153  	var blobs [][]byte
   154  	var descs []ocispec.Descriptor
   155  	appendBlob := func(mediaType string, blob []byte) {
   156  		blobs = append(blobs, blob)
   157  		descs = append(descs, ocispec.Descriptor{
   158  			MediaType: mediaType,
   159  			Digest:    digest.FromBytes(blob),
   160  			Size:      int64(len(blob)),
   161  		})
   162  	}
   163  	generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) {
   164  		manifest := ocispec.Manifest{
   165  			Config: config,
   166  			Layers: layers,
   167  		}
   168  		manifestJSON, err := json.Marshal(manifest)
   169  		if err != nil {
   170  			t.Fatal(err)
   171  		}
   172  		appendBlob(docker.MediaTypeManifest, manifestJSON)
   173  	}
   174  	generateIndex := func(manifests ...ocispec.Descriptor) {
   175  		index := ocispec.Index{
   176  			Manifests: manifests,
   177  		}
   178  		indexJSON, err := json.Marshal(index)
   179  		if err != nil {
   180  			t.Fatal(err)
   181  		}
   182  		appendBlob(docker.MediaTypeManifestList, indexJSON)
   183  	}
   184  
   185  	appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0
   186  	appendBlob(ocispec.MediaTypeImageLayer, []byte("foo"))     // Blob 1
   187  	appendBlob(ocispec.MediaTypeImageLayer, []byte("bar"))     // Blob 2
   188  	appendBlob(ocispec.MediaTypeImageLayer, []byte("hello"))   // Blob 3
   189  	generateManifest(descs[0], descs[1:3]...)                  // Blob 4
   190  	generateManifest(descs[0], descs[3])                       // Blob 5
   191  	generateIndex(descs[4:6]...)                               // Blob 6
   192  
   193  	ctx := context.Background()
   194  	for i := range blobs {
   195  		err := storage.Push(ctx, descs[i], bytes.NewReader(blobs[i]))
   196  		if err != nil {
   197  			t.Fatalf("failed to push test content to src: %d: %v", i, err)
   198  		}
   199  	}
   200  
   201  	// test Successors
   202  	manifestDesc := descs[6]
   203  	got, err := content.Successors(ctx, storage, manifestDesc)
   204  	if err != nil {
   205  		t.Fatal("Successors() error =", err)
   206  	}
   207  	if want := descs[4:6]; !reflect.DeepEqual(got, want) {
   208  		t.Errorf("Successors() = %v, want %v", got, want)
   209  	}
   210  }
   211  
   212  func TestSuccessors_imageIndex(t *testing.T) {
   213  	storage := cas.NewMemory()
   214  
   215  	// generate test content
   216  	var blobs [][]byte
   217  	var descs []ocispec.Descriptor
   218  	appendBlob := func(mediaType string, blob []byte) {
   219  		blobs = append(blobs, blob)
   220  		descs = append(descs, ocispec.Descriptor{
   221  			MediaType: mediaType,
   222  			Digest:    digest.FromBytes(blob),
   223  			Size:      int64(len(blob)),
   224  		})
   225  	}
   226  	generateManifest := func(subject *ocispec.Descriptor, config ocispec.Descriptor, layers ...ocispec.Descriptor) {
   227  		manifest := ocispec.Manifest{
   228  			Subject: subject,
   229  			Config:  config,
   230  			Layers:  layers,
   231  		}
   232  		manifestJSON, err := json.Marshal(manifest)
   233  		if err != nil {
   234  			t.Fatal(err)
   235  		}
   236  		appendBlob(ocispec.MediaTypeImageManifest, manifestJSON)
   237  	}
   238  	generateIndex := func(subject *ocispec.Descriptor, manifests ...ocispec.Descriptor) {
   239  		index := ocispec.Index{
   240  			Subject:   subject,
   241  			Manifests: manifests,
   242  		}
   243  		indexJSON, err := json.Marshal(index)
   244  		if err != nil {
   245  			t.Fatal(err)
   246  		}
   247  		appendBlob(ocispec.MediaTypeImageIndex, indexJSON)
   248  	}
   249  
   250  	appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0
   251  	appendBlob(ocispec.MediaTypeImageLayer, []byte("foo"))     // Blob 1
   252  	appendBlob(ocispec.MediaTypeImageLayer, []byte("bar"))     // Blob 2
   253  	appendBlob(ocispec.MediaTypeImageLayer, []byte("hello"))   // Blob 3
   254  	generateManifest(nil, descs[0], descs[1:3]...)             // Blob 4
   255  	generateManifest(nil, descs[0], descs[3])                  // Blob 5
   256  	appendBlob(ocispec.MediaTypeImageConfig, []byte("{}"))     // Blob 6
   257  	appendBlob("test/sig", []byte("sig"))                      // Blob 7
   258  	generateManifest(&descs[4], descs[5], descs[6])            // Blob 8
   259  	generateIndex(&descs[8], descs[4:6]...)                    // Blob 9
   260  
   261  	ctx := context.Background()
   262  	for i := range blobs {
   263  		err := storage.Push(ctx, descs[i], bytes.NewReader(blobs[i]))
   264  		if err != nil {
   265  			t.Fatalf("failed to push test content to src: %d: %v", i, err)
   266  		}
   267  	}
   268  
   269  	// test Successors
   270  	manifestDesc := descs[9]
   271  	got, err := content.Successors(ctx, storage, manifestDesc)
   272  	if err != nil {
   273  		t.Fatal("Successors() error =", err)
   274  	}
   275  	if want := append([]ocispec.Descriptor{descs[8]}, descs[4:6]...); !reflect.DeepEqual(got, want) {
   276  		t.Errorf("Successors() = %v, want %v", got, want)
   277  	}
   278  }
   279  
   280  func TestSuccessors_artifactManifest(t *testing.T) {
   281  	storage := cas.NewMemory()
   282  
   283  	// generate test content
   284  	var blobs [][]byte
   285  	var descs []ocispec.Descriptor
   286  	appendBlob := func(mediaType string, blob []byte) {
   287  		blobs = append(blobs, blob)
   288  		descs = append(descs, ocispec.Descriptor{
   289  			MediaType: mediaType,
   290  			Digest:    digest.FromBytes(blob),
   291  			Size:      int64(len(blob)),
   292  		})
   293  	}
   294  	generateArtifactManifest := func(subject *ocispec.Descriptor, blobs ...ocispec.Descriptor) {
   295  		manifest := spec.Artifact{
   296  			Subject: subject,
   297  			Blobs:   blobs,
   298  		}
   299  		manifestJSON, err := json.Marshal(manifest)
   300  		if err != nil {
   301  			t.Fatal(err)
   302  		}
   303  		appendBlob(spec.MediaTypeArtifactManifest, manifestJSON)
   304  	}
   305  
   306  	appendBlob(ocispec.MediaTypeImageLayer, []byte("foo"))   // Blob 0
   307  	appendBlob(ocispec.MediaTypeImageLayer, []byte("bar"))   // Blob 1
   308  	appendBlob(ocispec.MediaTypeImageLayer, []byte("hello")) // Blob 2
   309  	generateArtifactManifest(nil, descs[0:3]...)             // Blob 3
   310  	appendBlob("test/sig", []byte("sig"))                    // Blob 4
   311  	generateArtifactManifest(&descs[3], descs[4])            // Blob 5
   312  
   313  	ctx := context.Background()
   314  	for i := range blobs {
   315  		err := storage.Push(ctx, descs[i], bytes.NewReader(blobs[i]))
   316  		if err != nil {
   317  			t.Fatalf("failed to push test content to src: %d: %v", i, err)
   318  		}
   319  	}
   320  
   321  	// test Successors: image manifest without a subject
   322  	manifestDesc := descs[3]
   323  	got, err := content.Successors(ctx, storage, manifestDesc)
   324  	if err != nil {
   325  		t.Fatal("Successors() error =", err)
   326  	}
   327  	if want := descs[0:3]; !reflect.DeepEqual(got, want) {
   328  		t.Errorf("Successors() = %v, want %v", got, want)
   329  	}
   330  
   331  	// test Successors: image manifest with a subject
   332  	manifestDesc = descs[5]
   333  	got, err = content.Successors(ctx, storage, manifestDesc)
   334  	if err != nil {
   335  		t.Fatal("Successors() error =", err)
   336  	}
   337  	if want := descs[3:5]; !reflect.DeepEqual(got, want) {
   338  		t.Errorf("Successors() = %v, want %v", got, want)
   339  	}
   340  }
   341  
   342  func TestSuccessors_otherMediaType(t *testing.T) {
   343  	storage := cas.NewMemory()
   344  
   345  	// generate test content
   346  	var blobs [][]byte
   347  	var descs []ocispec.Descriptor
   348  	appendBlob := func(mediaType string, blob []byte) {
   349  		blobs = append(blobs, blob)
   350  		descs = append(descs, ocispec.Descriptor{
   351  			MediaType: mediaType,
   352  			Digest:    digest.FromBytes(blob),
   353  			Size:      int64(len(blob)),
   354  		})
   355  	}
   356  	generateManifest := func(mediaType string, config ocispec.Descriptor, layers ...ocispec.Descriptor) {
   357  		manifest := ocispec.Manifest{
   358  			Config: config,
   359  			Layers: layers,
   360  		}
   361  		manifestJSON, err := json.Marshal(manifest)
   362  		if err != nil {
   363  			t.Fatal(err)
   364  		}
   365  		appendBlob(mediaType, manifestJSON)
   366  	}
   367  
   368  	appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0
   369  	appendBlob(ocispec.MediaTypeImageLayer, []byte("foo"))     // Blob 1
   370  	appendBlob(ocispec.MediaTypeImageLayer, []byte("bar"))     // Blob 2
   371  	appendBlob(ocispec.MediaTypeImageLayer, []byte("hello"))   // Blob 3
   372  	generateManifest("whatever", descs[0], descs[1:4]...)      // Blob 4
   373  
   374  	ctx := context.Background()
   375  	for i := range blobs {
   376  		err := storage.Push(ctx, descs[i], bytes.NewReader(blobs[i]))
   377  		if err != nil {
   378  			t.Fatalf("failed to push test content to src: %d: %v", i, err)
   379  		}
   380  	}
   381  
   382  	// test Successors: other media type
   383  	manifestDesc := descs[4]
   384  	got, err := content.Successors(ctx, storage, manifestDesc)
   385  	if err != nil {
   386  		t.Fatal("Successors() error =", err)
   387  	}
   388  	if got != nil {
   389  		t.Errorf("Successors() = %v, want nil", got)
   390  	}
   391  }