github.com/opcr-io/oras-go/v2@v2.0.0-20231122155130-eb4260d8a0ae/pack_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
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"encoding/json"
    22  	"errors"
    23  	"io"
    24  	"reflect"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/opcr-io/oras-go/v2/content"
    29  	"github.com/opcr-io/oras-go/v2/content/memory"
    30  	"github.com/opencontainers/go-digest"
    31  	specs "github.com/opencontainers/image-spec/specs-go"
    32  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    33  )
    34  
    35  func Test_Pack_Default(t *testing.T) {
    36  	s := memory.New()
    37  
    38  	// prepare test content
    39  	blobs := []ocispec.Descriptor{
    40  		content.NewDescriptorFromBytes("test", []byte("hello world")),
    41  		content.NewDescriptorFromBytes("test", []byte("goodbye world")),
    42  	}
    43  	artifactType := "application/vnd.test"
    44  
    45  	// test Pack
    46  	ctx := context.Background()
    47  	manifestDesc, err := Pack(ctx, s, artifactType, blobs, PackOptions{})
    48  	if err != nil {
    49  		t.Fatal("Oras.Pack() error =", err)
    50  	}
    51  
    52  	// test blobs
    53  	var manifest ocispec.Artifact
    54  	rc, err := s.Fetch(ctx, manifestDesc)
    55  	if err != nil {
    56  		t.Fatal("Store.Fetch() error =", err)
    57  	}
    58  	if err := json.NewDecoder(rc).Decode(&manifest); err != nil {
    59  		t.Fatal("error decoding manifest, error =", err)
    60  	}
    61  	if err := rc.Close(); err != nil {
    62  		t.Fatal("Store.Fetch().Close() error =", err)
    63  	}
    64  	if !reflect.DeepEqual(manifest.Blobs, blobs) {
    65  		t.Errorf("Store.Fetch() = %v, want %v", manifest.Blobs, blobs)
    66  	}
    67  
    68  	// test media type
    69  	if got := manifest.MediaType; got != ocispec.MediaTypeArtifactManifest {
    70  		t.Fatalf("got media type = %s, want %s", got, ocispec.MediaTypeArtifactManifest)
    71  	}
    72  
    73  	// test artifact type
    74  	if got := manifest.ArtifactType; got != artifactType {
    75  		t.Fatalf("got artifact type = %s, want %s", got, artifactType)
    76  	}
    77  
    78  	// test created time annotation
    79  	createdTime, ok := manifest.Annotations[ocispec.AnnotationArtifactCreated]
    80  	if !ok {
    81  		t.Errorf("Annotation %s = %v, want %v", ocispec.AnnotationArtifactCreated, ok, true)
    82  	}
    83  	_, err = time.Parse(time.RFC3339, createdTime)
    84  	if err != nil {
    85  		t.Errorf("error parsing created time: %s, error = %v", createdTime, err)
    86  	}
    87  }
    88  
    89  func Test_Pack_WithOptions(t *testing.T) {
    90  	s := memory.New()
    91  
    92  	// prepare test content
    93  	blobs := []ocispec.Descriptor{
    94  		content.NewDescriptorFromBytes("test", []byte("hello world")),
    95  		content.NewDescriptorFromBytes("test", []byte("goodbye world")),
    96  	}
    97  
    98  	artifactType := "application/vnd.test"
    99  	annotations := map[string]string{
   100  		ocispec.AnnotationArtifactCreated: "2000-01-01T00:00:00Z",
   101  	}
   102  	subjectManifest := []byte(`{"layers":[]}`)
   103  	subjectDesc := ocispec.Descriptor{
   104  		MediaType:    ocispec.MediaTypeImageManifest,
   105  		Digest:       digest.FromBytes(subjectManifest),
   106  		Size:         int64(len(subjectManifest)),
   107  		ArtifactType: artifactType,
   108  		Annotations:  annotations,
   109  	}
   110  
   111  	// test Pack
   112  	ctx := context.Background()
   113  	opts := PackOptions{
   114  		Subject:             &subjectDesc,
   115  		ManifestAnnotations: annotations,
   116  	}
   117  	manifestDesc, err := Pack(ctx, s, artifactType, blobs, opts)
   118  	if err != nil {
   119  		t.Fatal("Oras.Pack() error =", err)
   120  	}
   121  
   122  	expectedManifest := ocispec.Artifact{
   123  		MediaType:    ocispec.MediaTypeArtifactManifest,
   124  		ArtifactType: artifactType,
   125  		Blobs:        blobs,
   126  		Subject:      opts.Subject,
   127  		Annotations:  annotations,
   128  	}
   129  	expectedManifestBytes, err := json.Marshal(expectedManifest)
   130  	if err != nil {
   131  		t.Fatal("failed to marshal manifest:", err)
   132  	}
   133  
   134  	// test manifest
   135  	rc, err := s.Fetch(ctx, manifestDesc)
   136  	if err != nil {
   137  		t.Fatal("Store.Fetch() error =", err)
   138  	}
   139  	got, err := io.ReadAll(rc)
   140  	if err != nil {
   141  		t.Fatal("Store.Fetch().Read() error =", err)
   142  	}
   143  	err = rc.Close()
   144  	if err != nil {
   145  		t.Error("Store.Fetch().Close() error =", err)
   146  	}
   147  	if !bytes.Equal(got, expectedManifestBytes) {
   148  		t.Errorf("Store.Fetch() = %v, want %v", got, expectedManifestBytes)
   149  	}
   150  }
   151  
   152  func Test_Pack_NoBlob(t *testing.T) {
   153  	s := memory.New()
   154  
   155  	// test Pack
   156  	ctx := context.Background()
   157  	artifactType := "application/vnd.test"
   158  	manifestDesc, err := Pack(ctx, s, artifactType, nil, PackOptions{})
   159  	if err != nil {
   160  		t.Fatal("Oras.Pack() error =", err)
   161  	}
   162  
   163  	var manifest ocispec.Artifact
   164  	rc, err := s.Fetch(ctx, manifestDesc)
   165  	if err != nil {
   166  		t.Fatal("Store.Fetch() error =", err)
   167  	}
   168  	if err := json.NewDecoder(rc).Decode(&manifest); err != nil {
   169  		t.Fatal("error decoding manifest, error =", err)
   170  	}
   171  	if err := rc.Close(); err != nil {
   172  		t.Fatal("Store.Fetch().Close() error =", err)
   173  	}
   174  
   175  	// test blobs
   176  	var expectedBlobs []ocispec.Descriptor
   177  	if !reflect.DeepEqual(manifest.Blobs, expectedBlobs) {
   178  		t.Errorf("Store.Fetch() = %v, want %v", manifest.Blobs, expectedBlobs)
   179  	}
   180  }
   181  
   182  func Test_Pack_NoArtifactType(t *testing.T) {
   183  	s := memory.New()
   184  
   185  	ctx := context.Background()
   186  	manifestDesc, err := Pack(ctx, s, "", nil, PackOptions{})
   187  	if err != nil {
   188  		t.Fatal("Oras.Pack() error =", err)
   189  	}
   190  
   191  	var manifest ocispec.Artifact
   192  	rc, err := s.Fetch(ctx, manifestDesc)
   193  	if err != nil {
   194  		t.Fatal("Store.Fetch() error =", err)
   195  	}
   196  	if err := json.NewDecoder(rc).Decode(&manifest); err != nil {
   197  		t.Fatal("error decoding manifest, error =", err)
   198  	}
   199  	if err := rc.Close(); err != nil {
   200  		t.Fatal("Store.Fetch().Close() error =", err)
   201  	}
   202  
   203  	// test artifact type
   204  	if manifestDesc.ArtifactType != MediaTypeUnknownArtifact {
   205  		t.Fatalf("got artifact type = %s, want %s", manifestDesc.ArtifactType, MediaTypeUnknownArtifact)
   206  	}
   207  	if manifest.ArtifactType != MediaTypeUnknownArtifact {
   208  		t.Fatalf("got artifact type = %s, want %s", manifest.ArtifactType, MediaTypeUnknownArtifact)
   209  	}
   210  }
   211  
   212  func Test_Pack_InvalidDateTimeFormat(t *testing.T) {
   213  	s := memory.New()
   214  
   215  	ctx := context.Background()
   216  	opts := PackOptions{
   217  		ManifestAnnotations: map[string]string{
   218  			ocispec.AnnotationArtifactCreated: "2000/01/01 00:00:00",
   219  		},
   220  	}
   221  	artifactType := "application/vnd.test"
   222  	_, err := Pack(ctx, s, artifactType, nil, opts)
   223  	if err == nil || !errors.Is(err, ErrInvalidDateTimeFormat) {
   224  		t.Errorf("Oras.Pack() error = %v, wantErr = %v", err, ErrInvalidDateTimeFormat)
   225  	}
   226  }
   227  
   228  func Test_Pack_Image(t *testing.T) {
   229  	s := memory.New()
   230  
   231  	// prepare test content
   232  	layers := []ocispec.Descriptor{
   233  		content.NewDescriptorFromBytes("test", []byte("hello world")),
   234  		content.NewDescriptorFromBytes("test", []byte("goodbye world")),
   235  	}
   236  
   237  	// test Pack
   238  	ctx := context.Background()
   239  	artifactType := "testconfig"
   240  	manifestDesc, err := Pack(ctx, s, artifactType, layers, PackOptions{PackImageManifest: true})
   241  	if err != nil {
   242  		t.Fatal("Oras.Pack() error =", err)
   243  	}
   244  
   245  	var manifest ocispec.Manifest
   246  	rc, err := s.Fetch(ctx, manifestDesc)
   247  	if err != nil {
   248  		t.Fatal("Store.Fetch() error =", err)
   249  	}
   250  	if err := json.NewDecoder(rc).Decode(&manifest); err != nil {
   251  		t.Fatal("error decoding manifest, error =", err)
   252  	}
   253  	if err := rc.Close(); err != nil {
   254  		t.Fatal("Store.Fetch().Close() error =", err)
   255  	}
   256  
   257  	// test media type
   258  	got := manifest.MediaType
   259  	if got != ocispec.MediaTypeImageManifest {
   260  		t.Fatalf("got media type = %s, want %s", got, ocispec.MediaTypeImageManifest)
   261  	}
   262  
   263  	// test config
   264  	expectedConfigBytes := []byte("{}")
   265  	expectedConfig := ocispec.Descriptor{
   266  		MediaType: artifactType,
   267  		Digest:    digest.FromBytes(expectedConfigBytes),
   268  		Size:      int64(len(expectedConfigBytes)),
   269  	}
   270  	if !reflect.DeepEqual(manifest.Config, expectedConfig) {
   271  		t.Errorf("got config = %v, want %v", manifest.Config, expectedConfig)
   272  	}
   273  
   274  	// test layers
   275  	if !reflect.DeepEqual(manifest.Layers, layers) {
   276  		t.Errorf("got layers = %v, want %v", manifest.Layers, layers)
   277  	}
   278  
   279  	// test created time annotation
   280  	createdTime, ok := manifest.Annotations[ocispec.AnnotationCreated]
   281  	if !ok {
   282  		t.Errorf("Annotation %s = %v, want %v", ocispec.AnnotationCreated, ok, true)
   283  	}
   284  	_, err = time.Parse(time.RFC3339, createdTime)
   285  	if err != nil {
   286  		t.Errorf("error parsing created time: %s, error = %v", createdTime, err)
   287  	}
   288  }
   289  
   290  func Test_Pack_Image_WithOptions(t *testing.T) {
   291  	s := memory.New()
   292  
   293  	// prepare test content
   294  	layers := []ocispec.Descriptor{
   295  		content.NewDescriptorFromBytes("test", []byte("hello world")),
   296  		content.NewDescriptorFromBytes("test", []byte("goodbye world")),
   297  	}
   298  	configBytes := []byte("{}")
   299  	configDesc := content.NewDescriptorFromBytes("testconfig", configBytes)
   300  	configAnnotations := map[string]string{"foo": "bar"}
   301  	annotations := map[string]string{
   302  		ocispec.AnnotationCreated: "2000-01-01T00:00:00Z",
   303  	}
   304  	artifactType := "application/vnd.test"
   305  	subjectManifest := []byte(`{"layers":[]}`)
   306  	subjectDesc := ocispec.Descriptor{
   307  		MediaType: ocispec.MediaTypeImageManifest,
   308  		Digest:    digest.FromBytes(subjectManifest),
   309  		Size:      int64(len(subjectManifest)),
   310  	}
   311  
   312  	// test Pack with ConfigDescriptor
   313  	ctx := context.Background()
   314  	opts := PackOptions{
   315  		PackImageManifest:   true,
   316  		Subject:             &subjectDesc,
   317  		ConfigDescriptor:    &configDesc,
   318  		ConfigAnnotations:   configAnnotations,
   319  		ManifestAnnotations: annotations,
   320  	}
   321  	manifestDesc, err := Pack(ctx, s, artifactType, layers, opts)
   322  	if err != nil {
   323  		t.Fatal("Oras.Pack() error =", err)
   324  	}
   325  
   326  	expectedManifest := ocispec.Manifest{
   327  		Versioned: specs.Versioned{
   328  			SchemaVersion: 2, // historical value. does not pertain to OCI or docker version
   329  		},
   330  		MediaType:   ocispec.MediaTypeImageManifest,
   331  		Subject:     &subjectDesc,
   332  		Config:      configDesc,
   333  		Layers:      layers,
   334  		Annotations: annotations,
   335  	}
   336  	expectedManifestBytes, err := json.Marshal(expectedManifest)
   337  	if err != nil {
   338  		t.Fatal("failed to marshal manifest:", err)
   339  	}
   340  
   341  	rc, err := s.Fetch(ctx, manifestDesc)
   342  	if err != nil {
   343  		t.Fatal("Store.Fetch() error =", err)
   344  	}
   345  	got, err := io.ReadAll(rc)
   346  	if err != nil {
   347  		t.Fatal("Store.Fetch().Read() error =", err)
   348  	}
   349  	err = rc.Close()
   350  	if err != nil {
   351  		t.Error("Store.Fetch().Close() error =", err)
   352  	}
   353  	if !bytes.Equal(got, expectedManifestBytes) {
   354  		t.Errorf("Store.Fetch() = %v, want %v", string(got), string(expectedManifestBytes))
   355  	}
   356  
   357  	// test Pack without ConfigDescriptor
   358  	opts = PackOptions{
   359  		PackImageManifest:   true,
   360  		Subject:             &subjectDesc,
   361  		ConfigAnnotations:   configAnnotations,
   362  		ManifestAnnotations: annotations,
   363  	}
   364  	manifestDesc, err = Pack(ctx, s, artifactType, layers, opts)
   365  	if err != nil {
   366  		t.Fatal("Oras.Pack() error =", err)
   367  	}
   368  
   369  	expectedConfigDesc := content.NewDescriptorFromBytes(artifactType, configBytes)
   370  	expectedConfigDesc.Annotations = configAnnotations
   371  	expectedManifest = ocispec.Manifest{
   372  		Versioned: specs.Versioned{
   373  			SchemaVersion: 2, // historical value. does not pertain to OCI or docker version
   374  		},
   375  		MediaType:   ocispec.MediaTypeImageManifest,
   376  		Subject:     &subjectDesc,
   377  		Config:      expectedConfigDesc,
   378  		Layers:      layers,
   379  		Annotations: annotations,
   380  	}
   381  	expectedManifestBytes, err = json.Marshal(expectedManifest)
   382  	if err != nil {
   383  		t.Fatal("failed to marshal manifest:", err)
   384  	}
   385  
   386  	rc, err = s.Fetch(ctx, manifestDesc)
   387  	if err != nil {
   388  		t.Fatal("Store.Fetch() error =", err)
   389  	}
   390  	got, err = io.ReadAll(rc)
   391  	if err != nil {
   392  		t.Fatal("Store.Fetch().Read() error =", err)
   393  	}
   394  	err = rc.Close()
   395  	if err != nil {
   396  		t.Error("Store.Fetch().Close() error =", err)
   397  	}
   398  	if !bytes.Equal(got, expectedManifestBytes) {
   399  		t.Errorf("Store.Fetch() = %v, want %v", string(got), string(expectedManifestBytes))
   400  	}
   401  }
   402  
   403  func Test_Pack_Image_NoArtifactType(t *testing.T) {
   404  	s := memory.New()
   405  
   406  	ctx := context.Background()
   407  	manifestDesc, err := Pack(ctx, s, "", nil, PackOptions{PackImageManifest: true})
   408  	if err != nil {
   409  		t.Fatal("Oras.Pack() error =", err)
   410  	}
   411  
   412  	var manifest ocispec.Manifest
   413  	rc, err := s.Fetch(ctx, manifestDesc)
   414  	if err != nil {
   415  		t.Fatal("Store.Fetch() error =", err)
   416  	}
   417  	if err := json.NewDecoder(rc).Decode(&manifest); err != nil {
   418  		t.Fatal("error decoding manifest, error =", err)
   419  	}
   420  	if err := rc.Close(); err != nil {
   421  		t.Fatal("Store.Fetch().Close() error =", err)
   422  	}
   423  
   424  	// test artifact type and config media type
   425  	if manifestDesc.ArtifactType != MediaTypeUnknownConfig {
   426  		t.Fatalf("got artifact type = %s, want %s", manifestDesc.ArtifactType, MediaTypeUnknownConfig)
   427  	}
   428  	if manifest.Config.MediaType != MediaTypeUnknownConfig {
   429  		t.Fatalf("got artifact type = %s, want %s", manifest.Config.MediaType, MediaTypeUnknownConfig)
   430  	}
   431  }
   432  
   433  func Test_Pack_Image_NoLayer(t *testing.T) {
   434  	s := memory.New()
   435  
   436  	// test Pack
   437  	ctx := context.Background()
   438  	manifestDesc, err := Pack(ctx, s, "", nil, PackOptions{PackImageManifest: true})
   439  	if err != nil {
   440  		t.Fatal("Oras.Pack() error =", err)
   441  	}
   442  
   443  	var manifest ocispec.Manifest
   444  	rc, err := s.Fetch(ctx, manifestDesc)
   445  	if err != nil {
   446  		t.Fatal("Store.Fetch() error =", err)
   447  	}
   448  	if err := json.NewDecoder(rc).Decode(&manifest); err != nil {
   449  		t.Fatal("error decoding manifest, error =", err)
   450  	}
   451  	if err := rc.Close(); err != nil {
   452  		t.Fatal("Store.Fetch().Close() error =", err)
   453  	}
   454  
   455  	// test layers
   456  	expectedLayers := []ocispec.Descriptor{}
   457  	if !reflect.DeepEqual(manifest.Layers, expectedLayers) {
   458  		t.Errorf("got layers = %v, want %v", manifest.Layers, expectedLayers)
   459  	}
   460  }
   461  
   462  func Test_Pack_Image_InvalidDateTimeFormat(t *testing.T) {
   463  	s := memory.New()
   464  
   465  	ctx := context.Background()
   466  	opts := PackOptions{
   467  		PackImageManifest: true,
   468  		ManifestAnnotations: map[string]string{
   469  			ocispec.AnnotationCreated: "2000/01/01 00:00:00",
   470  		},
   471  	}
   472  	_, err := Pack(ctx, s, "", nil, opts)
   473  	if err == nil || !errors.Is(err, ErrInvalidDateTimeFormat) {
   474  		t.Errorf("Oras.Pack() error = %v, wantErr = %v", err, ErrInvalidDateTimeFormat)
   475  	}
   476  }