github.com/dtroyer-salad/og2/v2@v2.0.0-20240412154159-c47231610877/content/reader_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
    17  
    18  import (
    19  	"bytes"
    20  	_ "crypto/sha256"
    21  	"errors"
    22  	"io"
    23  	"reflect"
    24  	"testing"
    25  
    26  	"github.com/opencontainers/go-digest"
    27  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    28  	"oras.land/oras-go/v2/internal/spec"
    29  )
    30  
    31  func TestVerifyReader_NewVerifyReader(t *testing.T) {
    32  	content := []byte("example content")
    33  
    34  	// Default no-resume path
    35  	r := bytes.NewReader(content)
    36  	desc := ocispec.Descriptor{
    37  		MediaType: ocispec.MediaTypeImageLayer,
    38  		Digest:    digest.FromBytes(content),
    39  		Size:      int64(len(content) + 1),
    40  	}
    41  	vr := NewVerifyReader(r, desc)
    42  	if vr.resume {
    43  		t.Fatalf("resume is set: %s", desc.Annotations)
    44  	}
    45  
    46  	// Resume is enabled and we expect to read something
    47  	r = bytes.NewReader(content)
    48  	desc = ocispec.Descriptor{
    49  		MediaType: ocispec.MediaTypeImageLayer,
    50  		Digest:    digest.FromBytes(content),
    51  		Size:      int64(len(content) + 1),
    52  		Annotations: map[string]string{
    53  			spec.AnnotationResumeDownload: "true",
    54  			spec.AnnotationResumeOffset:   "3",
    55  		},
    56  	}
    57  	vr = NewVerifyReader(r, desc)
    58  	if !vr.resume {
    59  		t.Fatalf("resume is not set: %+v", desc.Annotations)
    60  	}
    61  
    62  	// Resume is enabled and we expect to read something with hash
    63  	d := digest.FromBytes(content)
    64  	h, _ := EncodeHash(d.Algorithm().Hash())
    65  	r = bytes.NewReader(content)
    66  	desc = ocispec.Descriptor{
    67  		MediaType: ocispec.MediaTypeImageLayer,
    68  		Digest:    d,
    69  		Size:      int64(len(content) + 1),
    70  		Annotations: map[string]string{
    71  			spec.AnnotationResumeDownload: "true",
    72  			spec.AnnotationResumeOffset:   "3",
    73  			spec.AnnotationResumeHash:     h,
    74  		},
    75  	}
    76  	vr = NewVerifyReader(r, desc)
    77  	if !vr.resume {
    78  		t.Fatalf("resume is not set: %+v", desc.Annotations)
    79  	}
    80  }
    81  
    82  func TestVerifyReader_Read(t *testing.T) {
    83  	// matched content and descriptor with small buffer
    84  	content := []byte("example content")
    85  	desc := NewDescriptorFromBytes("test", content)
    86  	r := bytes.NewReader(content)
    87  	vr := NewVerifyReader(r, desc)
    88  	buf := make([]byte, 5)
    89  	n, err := vr.Read(buf)
    90  	if err != nil {
    91  		t.Fatal("Read() error = ", err)
    92  	}
    93  	if !bytes.Equal(buf, []byte("examp")) {
    94  		t.Fatalf("incorrect read content: %s", buf)
    95  	}
    96  	if n != 5 {
    97  		t.Fatalf("incorrect number of bytes read: %d", n)
    98  	}
    99  
   100  	// matched content and descriptor with sufficient buffer
   101  	content = []byte("foo foo")
   102  	desc = NewDescriptorFromBytes("test", content)
   103  	r = bytes.NewReader(content)
   104  	vr = NewVerifyReader(r, desc)
   105  	buf = make([]byte, len(content))
   106  	n, err = vr.Read(buf)
   107  	if err != nil {
   108  		t.Fatal("Read() error = ", err)
   109  	}
   110  	if n != len(content) {
   111  		t.Fatalf("incorrect number of bytes read: %d", n)
   112  	}
   113  	if !bytes.Equal(buf, content) {
   114  		t.Fatalf("incorrect read content: %s", buf)
   115  	}
   116  
   117  	// mismatched content and descriptor with sufficient buffer
   118  	r = bytes.NewReader([]byte("bar"))
   119  	vr = NewVerifyReader(r, desc)
   120  	buf = make([]byte, 5)
   121  	n, err = vr.Read(buf)
   122  	if err != nil {
   123  		t.Fatal("Read() error = ", err)
   124  	}
   125  	if n != 3 {
   126  		t.Fatalf("incorrect number of bytes read: %d", n)
   127  	}
   128  }
   129  
   130  func TestVerifyReader_Read_Resume(t *testing.T) {
   131  	// matched content and descriptor with small buffer
   132  	content := []byte("example content")
   133  	desc := NewDescriptorFromBytes("test", content)
   134  	r := bytes.NewReader(content)
   135  	vr := NewVerifyReader(r, desc)
   136  	vr.resume = true
   137  	buf := make([]byte, 5)
   138  	n, err := vr.Read(buf)
   139  	if err != nil {
   140  		t.Fatal("Read() error = ", err)
   141  	}
   142  	if !bytes.Equal(buf, []byte("examp")) {
   143  		t.Fatalf("incorrect read content: %s", buf)
   144  	}
   145  	if n != 5 {
   146  		t.Fatalf("incorrect number of bytes read: %d", n)
   147  	}
   148  
   149  	// matched content and descriptor with sufficient buffer
   150  	content = []byte("foo foo")
   151  	desc = NewDescriptorFromBytes("test", content)
   152  	r = bytes.NewReader(content)
   153  	vr = NewVerifyReader(r, desc)
   154  	vr.resume = true
   155  	buf = make([]byte, len(content))
   156  	n, err = vr.Read(buf)
   157  	if err != nil {
   158  		t.Fatal("Read() error = ", err)
   159  	}
   160  	if n != len(content) {
   161  		t.Fatalf("incorrect number of bytes read: %d", n)
   162  	}
   163  	if !bytes.Equal(buf, content) {
   164  		t.Fatalf("incorrect read content: %s", buf)
   165  	}
   166  
   167  	// mismatched content and descriptor with sufficient buffer
   168  	r = bytes.NewReader([]byte("bar"))
   169  	vr = NewVerifyReader(r, desc)
   170  	vr.resume = true
   171  	buf = make([]byte, 5)
   172  	n, err = vr.Read(buf)
   173  	if err != nil {
   174  		t.Fatal("Read() error = ", err)
   175  	}
   176  	if n != 3 {
   177  		t.Fatalf("incorrect number of bytes read: %d", n)
   178  	}
   179  }
   180  
   181  func TestVerifyReader_Verify(t *testing.T) {
   182  	// matched content and descriptor
   183  	content := []byte("example content")
   184  	desc := NewDescriptorFromBytes("test", content)
   185  	r := bytes.NewReader(content)
   186  	vr := NewVerifyReader(r, desc)
   187  	buf := make([]byte, len(content))
   188  	if _, err := vr.Read(buf); err != nil {
   189  		t.Fatal("Read() error = ", err)
   190  	}
   191  	if err := vr.Verify(); err != nil {
   192  		t.Fatal("Verify() error = ", err)
   193  	}
   194  	if !bytes.Equal(buf, content) {
   195  		t.Fatalf("incorrect read content: %s", buf)
   196  	}
   197  
   198  	// mismatched content and descriptor, read size larger than descriptor size
   199  	content = []byte("foo")
   200  	r = bytes.NewReader(content)
   201  	desc = ocispec.Descriptor{
   202  		MediaType: ocispec.MediaTypeImageLayer,
   203  		Digest:    digest.FromBytes(content),
   204  		Size:      int64(len(content)) - 1}
   205  	vr = NewVerifyReader(r, desc)
   206  	buf = make([]byte, len(content))
   207  	if _, err := vr.Read(buf); err != nil {
   208  		t.Fatal("Read() error = ", err)
   209  	}
   210  	if err := vr.Verify(); !errors.Is(err, ErrTrailingData) {
   211  		t.Fatalf("Verify() error = %v, want %v", err, ErrTrailingData)
   212  	}
   213  	// call vr.Verify again, the result should be the same
   214  	if err := vr.Verify(); !errors.Is(err, ErrTrailingData) {
   215  		t.Fatalf("2nd Verify() error = %v, want %v", err, ErrTrailingData)
   216  	}
   217  
   218  	// mismatched content and descriptor, read size smaller than descriptor size
   219  	content = []byte("foo")
   220  	r = bytes.NewReader(content)
   221  	desc = ocispec.Descriptor{
   222  		MediaType: ocispec.MediaTypeImageLayer,
   223  		Digest:    digest.FromBytes(content),
   224  		Size:      int64(len(content)) + 1}
   225  	vr = NewVerifyReader(r, desc)
   226  	buf = make([]byte, len(content))
   227  	if _, err := vr.Read(buf); err != nil {
   228  		t.Fatal("Read() error = ", err)
   229  	}
   230  	if err := vr.Verify(); !errors.Is(err, errEarlyVerify) {
   231  		t.Fatalf("Verify() error = %v, want %v", err, errEarlyVerify)
   232  	}
   233  	// call vr.Verify again, the result should be the same
   234  	if err := vr.Verify(); !errors.Is(err, errEarlyVerify) {
   235  		t.Fatalf("Verify() error = %v, want %v", err, errEarlyVerify)
   236  	}
   237  
   238  	// mismatched content and descriptor, wrong digest
   239  	content = []byte("bar")
   240  	r = bytes.NewReader(content)
   241  	desc = NewDescriptorFromBytes("test", []byte("foo"))
   242  	vr = NewVerifyReader(r, desc)
   243  	buf = make([]byte, len(content))
   244  	if _, err := vr.Read(buf); err != nil {
   245  		t.Fatal("Read() error = ", err)
   246  	}
   247  	if err := vr.Verify(); !errors.Is(err, ErrMismatchedDigest) {
   248  		t.Fatalf("Verify() error = %v, want %v", err, ErrMismatchedDigest)
   249  	}
   250  	// call vr.Verify again, the result should be the same
   251  	if err := vr.Verify(); !errors.Is(err, ErrMismatchedDigest) {
   252  		t.Fatalf("2nd Verify() error = %v, want %v", err, ErrMismatchedDigest)
   253  	}
   254  }
   255  
   256  func TestVerifyReader_Verify_Resume(t *testing.T) {
   257  	// matched content and descriptor
   258  	content := []byte("example content")
   259  	desc := ocispec.Descriptor{
   260  		MediaType: "test",
   261  		Digest:    digest.FromBytes(content),
   262  		Size:      int64(len(content)),
   263  		Annotations: map[string]string{
   264  			spec.AnnotationResumeDownload: "true",
   265  			spec.AnnotationResumeOffset:   "3",
   266  		},
   267  	}
   268  	r := bytes.NewReader(content)
   269  	vr := NewVerifyReader(r, desc)
   270  	buf := make([]byte, len(content))
   271  	if _, err := vr.Read(buf); err != nil {
   272  		t.Fatal("Read() error = ", err)
   273  	}
   274  	if err := vr.Verify(); err != nil {
   275  		t.Fatal("Verify() error = ", err)
   276  	}
   277  	if !bytes.Equal(buf, content) {
   278  		t.Fatalf("incorrect read content: %s", buf)
   279  	}
   280  
   281  	// mismatched content and descriptor, read size larger than descriptor size
   282  	content = []byte("foo")
   283  	r = bytes.NewReader(content)
   284  	desc = ocispec.Descriptor{
   285  		MediaType: "test",
   286  		Digest:    digest.FromBytes(content),
   287  		Size:      int64(len(content) - 1),
   288  		Annotations: map[string]string{
   289  			spec.AnnotationResumeDownload: "true",
   290  			spec.AnnotationResumeOffset:   "3",
   291  		},
   292  	}
   293  	vr = NewVerifyReader(r, desc)
   294  	buf = make([]byte, len(content))
   295  	if _, err := vr.Read(buf); err != nil {
   296  		t.Fatal("Read() error = ", err)
   297  	}
   298  	if err := vr.Verify(); !errors.Is(err, ErrTrailingData) {
   299  		t.Fatalf("Verify() error = %v, want %v", err, ErrTrailingData)
   300  	}
   301  	// call vr.Verify again, the result should be the same
   302  	if err := vr.Verify(); !errors.Is(err, ErrTrailingData) {
   303  		t.Fatalf("2nd Verify() error = %v, want %v", err, ErrTrailingData)
   304  	}
   305  
   306  	// mismatched content and descriptor, read size smaller than descriptor size
   307  	content = []byte("foo")
   308  	r = bytes.NewReader(content)
   309  	desc = ocispec.Descriptor{
   310  		MediaType: "test",
   311  		Digest:    digest.FromBytes(content),
   312  		Size:      int64(len(content) + 1),
   313  		Annotations: map[string]string{
   314  			spec.AnnotationResumeDownload: "true",
   315  			spec.AnnotationResumeOffset:   "3",
   316  		},
   317  	}
   318  	vr = NewVerifyReader(r, desc)
   319  	buf = make([]byte, len(content))
   320  	if _, err := vr.Read(buf); err != nil {
   321  		t.Fatal("Read() error = ", err)
   322  	}
   323  	if err := vr.Verify(); !errors.Is(err, errEarlyVerify) {
   324  		t.Fatalf("Verify() error = %v, want %v", err, errEarlyVerify)
   325  	}
   326  	// call vr.Verify again, the result should be the same
   327  	if err := vr.Verify(); !errors.Is(err, errEarlyVerify) {
   328  		t.Fatalf("Verify() error = %v, want %v", err, errEarlyVerify)
   329  	}
   330  
   331  	// mismatched content and descriptor, wrong digest
   332  	content = []byte("bar")
   333  	r = bytes.NewReader(content)
   334  	desc = ocispec.Descriptor{
   335  		MediaType: "test",
   336  		Digest:    digest.FromBytes([]byte("foo")),
   337  		Size:      int64(len(content)),
   338  		Annotations: map[string]string{
   339  			spec.AnnotationResumeDownload: "true",
   340  			spec.AnnotationResumeOffset:   "3",
   341  		},
   342  	}
   343  	vr = NewVerifyReader(r, desc)
   344  	buf = make([]byte, len(content))
   345  	if _, err := vr.Read(buf); err != nil {
   346  		t.Fatal("Read() error = ", err)
   347  	}
   348  	if err := vr.Verify(); !errors.Is(err, ErrMismatchedDigest) {
   349  		t.Fatalf("Verify() error = %v, want %v", err, ErrMismatchedDigest)
   350  	}
   351  	// call vr.Verify again, the result should be the same
   352  	if err := vr.Verify(); !errors.Is(err, ErrMismatchedDigest) {
   353  		t.Fatalf("2nd Verify() error = %v, want %v", err, ErrMismatchedDigest)
   354  	}
   355  }
   356  
   357  func TestReadAll_CorrectDescriptor(t *testing.T) {
   358  	content := []byte("example content")
   359  	desc := NewDescriptorFromBytes("test", content)
   360  	r := bytes.NewReader([]byte(content))
   361  	got, err := ReadAll(r, desc)
   362  	if err != nil {
   363  		t.Fatal("ReadAll() error = ", err)
   364  	}
   365  	if !bytes.Equal(got, content) {
   366  		t.Errorf("ReadAll() = %v, want %v", got, content)
   367  	}
   368  }
   369  
   370  func TestReadAll_CorrectDescriptor_Resume(t *testing.T) {
   371  	content := []byte("example content")
   372  	desc := ocispec.Descriptor{
   373  		MediaType: "test",
   374  		Digest:    digest.FromBytes(content),
   375  		Size:      int64(len(content)),
   376  		Annotations: map[string]string{
   377  			spec.AnnotationResumeDownload: "true",
   378  			spec.AnnotationResumeOffset:   "3",
   379  		},
   380  	}
   381  	r := bytes.NewReader([]byte(content))
   382  	got, err := ReadAll(r, desc)
   383  	if err != nil {
   384  		t.Fatal("ReadAll() error = ", err)
   385  	}
   386  	if !bytes.Equal(got, content) {
   387  		t.Errorf("ReadAll() = %v, want %v", got, content)
   388  	}
   389  }
   390  
   391  func TestReadAll_ReadSizeSmallerThanDescriptorSize(t *testing.T) {
   392  	content := []byte("example content")
   393  	desc := ocispec.Descriptor{
   394  		MediaType: ocispec.MediaTypeImageLayer,
   395  		Digest:    digest.FromBytes(content),
   396  		Size:      int64(len(content) + 1)}
   397  	r := bytes.NewReader([]byte(content))
   398  	_, err := ReadAll(r, desc)
   399  	if err == nil || !errors.Is(err, io.ErrUnexpectedEOF) {
   400  		t.Errorf("ReadAll() error = %v, want %v", err, io.ErrUnexpectedEOF)
   401  	}
   402  }
   403  
   404  func TestReadAll_ReadSizeSmallerThanDescriptorSize_Resume(t *testing.T) {
   405  	content := []byte("example content")
   406  	desc := ocispec.Descriptor{
   407  		MediaType: ocispec.MediaTypeImageLayer,
   408  		Digest:    digest.FromBytes(content),
   409  		Size:      int64(len(content) + 1),
   410  		Annotations: map[string]string{
   411  			spec.AnnotationResumeDownload: "true",
   412  			spec.AnnotationResumeOffset:   "3",
   413  		},
   414  	}
   415  	r := bytes.NewReader([]byte(content))
   416  	_, err := ReadAll(r, desc)
   417  	if err == nil || !errors.Is(err, io.EOF) {
   418  		t.Errorf("ReadAll() error = %v, want %v", err, io.EOF)
   419  	}
   420  }
   421  
   422  func TestReadAll_ReadSizeLargerThanDescriptorSize(t *testing.T) {
   423  	content := []byte("example content")
   424  	desc := ocispec.Descriptor{
   425  		MediaType: ocispec.MediaTypeImageLayer,
   426  		Digest:    digest.FromBytes(content),
   427  		Size:      int64(len(content) - 1)}
   428  	r := bytes.NewReader([]byte(content))
   429  	_, err := ReadAll(r, desc)
   430  	if err == nil || !errors.Is(err, ErrTrailingData) {
   431  		t.Errorf("ReadAll() error = %v, want %v", err, ErrTrailingData)
   432  	}
   433  }
   434  
   435  func TestReadAll_ReadSizeLargerThanDescriptorSize_Resume(t *testing.T) {
   436  	content := []byte("example content")
   437  	desc := ocispec.Descriptor{
   438  		MediaType: ocispec.MediaTypeImageLayer,
   439  		Digest:    digest.FromBytes(content),
   440  		Size:      int64(len(content) - 1),
   441  		Annotations: map[string]string{
   442  			spec.AnnotationResumeDownload: "true",
   443  			spec.AnnotationResumeOffset:   "3",
   444  		},
   445  	}
   446  	r := bytes.NewReader([]byte(content))
   447  	_, err := ReadAll(r, desc)
   448  	if err == nil || !errors.Is(err, ErrTrailingData) {
   449  		t.Errorf("ReadAll() error = %v, want %v", err, ErrTrailingData)
   450  	}
   451  }
   452  
   453  func TestReadAll_InvalidDigest(t *testing.T) {
   454  	content := []byte("example content")
   455  	desc := NewDescriptorFromBytes("test", []byte("another content"))
   456  	r := bytes.NewReader([]byte(content))
   457  	_, err := ReadAll(r, desc)
   458  	if err == nil || !errors.Is(err, ErrMismatchedDigest) {
   459  		t.Errorf("ReadAll() error = %v, want %v", err, ErrMismatchedDigest)
   460  	}
   461  }
   462  
   463  func TestReadAll_InvalidDigest_Resume(t *testing.T) {
   464  	content := []byte("example content")
   465  	desc := ocispec.Descriptor{
   466  		MediaType: "test",
   467  		Digest:    digest.FromBytes([]byte("another content")),
   468  		Size:      int64(len(content)),
   469  		Annotations: map[string]string{
   470  			spec.AnnotationResumeDownload: "true",
   471  			spec.AnnotationResumeOffset:   "3",
   472  		},
   473  	}
   474  	r := bytes.NewReader([]byte(content))
   475  	_, err := ReadAll(r, desc)
   476  	if err == nil || !errors.Is(err, ErrMismatchedDigest) {
   477  		t.Errorf("ReadAll() error = %v, want %v", err, ErrMismatchedDigest)
   478  	}
   479  }
   480  
   481  func TestReadAll_EmptyContent(t *testing.T) {
   482  	content := []byte("")
   483  	desc := NewDescriptorFromBytes("test", content)
   484  	r := bytes.NewReader([]byte(content))
   485  	got, err := ReadAll(r, desc)
   486  	if err != nil {
   487  		t.Fatal("ReadAll() error = ", err)
   488  	}
   489  	if !bytes.Equal(got, content) {
   490  		t.Errorf("ReadAll() = %v, want %v", got, content)
   491  	}
   492  }
   493  
   494  func TestReadAll_EmptyContent_Resume(t *testing.T) {
   495  	content := []byte("")
   496  	desc := ocispec.Descriptor{
   497  		MediaType: "test",
   498  		Digest:    digest.FromBytes(content),
   499  		Size:      int64(len(content)),
   500  		Annotations: map[string]string{
   501  			spec.AnnotationResumeDownload: "true",
   502  			spec.AnnotationResumeOffset:   "3",
   503  		},
   504  	}
   505  	r := bytes.NewReader([]byte(content))
   506  	got, err := ReadAll(r, desc)
   507  	if err != nil {
   508  		t.Fatal("ReadAll() error = ", err)
   509  	}
   510  	if !bytes.Equal(got, content) {
   511  		t.Errorf("ReadAll() = %v, want %v", got, content)
   512  	}
   513  }
   514  
   515  func TestReadAll_InvalidDescriptorSize(t *testing.T) {
   516  	content := []byte("example content")
   517  	desc := ocispec.Descriptor{
   518  		MediaType: ocispec.MediaTypeImageLayer,
   519  		Digest:    digest.FromBytes(content),
   520  		Size:      -1,
   521  	}
   522  	r := bytes.NewReader([]byte(content))
   523  	_, err := ReadAll(r, desc)
   524  	if err == nil || !errors.Is(err, ErrInvalidDescriptorSize) {
   525  		t.Errorf("ReadAll() error = %v, want %v", err, ErrInvalidDescriptorSize)
   526  	}
   527  }
   528  
   529  func TestReadAll_InvalidDescriptorSize_Resume(t *testing.T) {
   530  	content := []byte("example content")
   531  	desc := ocispec.Descriptor{
   532  		MediaType: ocispec.MediaTypeImageLayer,
   533  		Digest:    digest.FromBytes(content),
   534  		Size:      -1,
   535  		Annotations: map[string]string{
   536  			spec.AnnotationResumeDownload: "true",
   537  			spec.AnnotationResumeOffset:   "3",
   538  		},
   539  	}
   540  	r := bytes.NewReader([]byte(content))
   541  	_, err := ReadAll(r, desc)
   542  	if err == nil || !errors.Is(err, ErrInvalidDescriptorSize) {
   543  		t.Errorf("ReadAll() error = %v, want %v", err, ErrInvalidDescriptorSize)
   544  	}
   545  }
   546  
   547  func TestEncodeDecodeHash(t *testing.T) {
   548  	content := []byte("example content")
   549  
   550  	d := digest.FromBytes(content)
   551  	eh, err := EncodeHash(d.Algorithm().Hash())
   552  	if err != nil {
   553  		t.Fatal("EncodeHash failed =", err)
   554  	}
   555  	if eh == "" {
   556  		t.Fatal("EncodeHash returned empty")
   557  	}
   558  
   559  	dh, err := DecodeHash(eh, d)
   560  	if err != nil {
   561  		t.Fatal("DecodeHash failed =", err)
   562  	}
   563  	if !reflect.DeepEqual(dh, d.Algorithm().Hash()) {
   564  		t.Fatalf("EncodeHash/DecodeHash error = %v, want %v", dh, d.Algorithm().Hash())
   565  	}
   566  }