github.com/dtroyer-salad/og2/v2@v2.0.0-20240412154159-c47231610877/internal/cas/proxy_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 cas
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	_ "crypto/sha256"
    22  	"errors"
    23  	"io"
    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/errdef"
    29  )
    30  
    31  func TestProxyCache(t *testing.T) {
    32  	content := []byte("hello world")
    33  	desc := ocispec.Descriptor{
    34  		MediaType: "test",
    35  		Digest:    digest.FromBytes(content),
    36  		Size:      int64(len(content)),
    37  	}
    38  
    39  	ctx := context.Background()
    40  	base := NewMemory()
    41  	err := base.Push(ctx, desc, bytes.NewReader(content))
    42  	if err != nil {
    43  		t.Fatal("Memory.Push() error =", err)
    44  	}
    45  	s := NewProxy(base, NewMemory())
    46  
    47  	// first fetch
    48  	exists, err := s.Exists(ctx, desc)
    49  	if err != nil {
    50  		t.Fatal("Proxy.Exists() error =", err)
    51  	}
    52  	if !exists {
    53  		t.Errorf("Proxy.Exists() = %v, want %v", exists, true)
    54  	}
    55  	rc, err := s.Fetch(ctx, desc)
    56  	if err != nil {
    57  		t.Fatal("Proxy.Fetch() error =", err)
    58  	}
    59  	got, err := io.ReadAll(rc)
    60  	if err != nil {
    61  		t.Fatal("Proxy.Fetch().Read() error =", err)
    62  	}
    63  	err = rc.Close()
    64  	if err != nil {
    65  		t.Error("Proxy.Fetch().Close() error =", err)
    66  	}
    67  	if !bytes.Equal(got, content) {
    68  		t.Errorf("Proxy.Fetch() = %v, want %v", got, content)
    69  	}
    70  
    71  	// repeated fetch should not touch base CAS
    72  	// nil base will generate panic if the base CAS is touched
    73  	s.ReadOnlyStorage = nil
    74  
    75  	exists, err = s.Exists(ctx, desc)
    76  	if err != nil {
    77  		t.Fatal("Proxy.Exists() error =", err)
    78  	}
    79  	if !exists {
    80  		t.Errorf("Proxy.Exists() = %v, want %v", exists, true)
    81  	}
    82  	rc, err = s.Fetch(ctx, desc)
    83  	if err != nil {
    84  		t.Fatal("Proxy.Fetch() error =", err)
    85  	}
    86  	got, err = io.ReadAll(rc)
    87  	if err != nil {
    88  		t.Fatal("Proxy.Fetch().Read() error =", err)
    89  	}
    90  	err = rc.Close()
    91  	if err != nil {
    92  		t.Error("Proxy.Fetch().Close() error =", err)
    93  	}
    94  	if !bytes.Equal(got, content) {
    95  		t.Errorf("Proxy.Fetch() = %v, want %v", got, content)
    96  	}
    97  }
    98  
    99  func TestProxy_FetchCached_NotCachedContent(t *testing.T) {
   100  	content := []byte("hello world")
   101  	desc := ocispec.Descriptor{
   102  		MediaType: "test",
   103  		Digest:    digest.FromBytes(content),
   104  		Size:      int64(len(content)),
   105  	}
   106  
   107  	ctx := context.Background()
   108  	base := NewMemory()
   109  	err := base.Push(ctx, desc, bytes.NewReader(content))
   110  	if err != nil {
   111  		t.Fatal("Memory.Push() error =", err)
   112  	}
   113  	s := NewProxy(base, NewMemory())
   114  
   115  	// FetchCached should fetch from the base CAS
   116  	exists, err := s.Exists(ctx, desc)
   117  	if err != nil {
   118  		t.Fatal("Proxy.Exists() error =", err)
   119  	}
   120  	if !exists {
   121  		t.Errorf("Proxy.Exists() = %v, want %v", exists, true)
   122  	}
   123  	rc, err := s.FetchCached(ctx, desc)
   124  	if err != nil {
   125  		t.Fatal("Proxy.Fetch() error =", err)
   126  	}
   127  	got, err := io.ReadAll(rc)
   128  	if err != nil {
   129  		t.Fatal("Proxy.Fetch().Read() error =", err)
   130  	}
   131  	err = rc.Close()
   132  	if err != nil {
   133  		t.Error("Proxy.Fetch().Close() error =", err)
   134  	}
   135  	if !bytes.Equal(got, content) {
   136  		t.Errorf("Proxy.Fetch() = %v, want %v", got, content)
   137  	}
   138  
   139  	// the content should not exist in the cache
   140  	exists, err = s.Cache.Exists(ctx, desc)
   141  	if err != nil {
   142  		t.Fatal("Proxy.Cache.Exists() error =", err)
   143  	}
   144  
   145  	if exists {
   146  		t.Errorf("Proxy.Cache.Exists()() = %v, want %v", exists, false)
   147  	}
   148  }
   149  
   150  func TestProxy_FetchCached_CachedContent(t *testing.T) {
   151  	content := []byte("hello world")
   152  	desc := ocispec.Descriptor{
   153  		MediaType: "test",
   154  		Digest:    digest.FromBytes(content),
   155  		Size:      int64(len(content)),
   156  	}
   157  
   158  	ctx := context.Background()
   159  	base := NewMemory()
   160  	err := base.Push(ctx, desc, bytes.NewReader(content))
   161  	if err != nil {
   162  		t.Fatal("Memory.Push() error =", err)
   163  	}
   164  	s := NewProxy(base, NewMemory())
   165  
   166  	// first fetch
   167  	exists, err := s.Exists(ctx, desc)
   168  	if err != nil {
   169  		t.Fatal("Proxy.Exists() error =", err)
   170  	}
   171  	if !exists {
   172  		t.Errorf("Proxy.Exists() = %v, want %v", exists, true)
   173  	}
   174  	rc, err := s.Fetch(ctx, desc)
   175  	if err != nil {
   176  		t.Fatal("Proxy.Fetch() error =", err)
   177  	}
   178  	got, err := io.ReadAll(rc)
   179  	if err != nil {
   180  		t.Fatal("Proxy.Fetch().Read() error =", err)
   181  	}
   182  	err = rc.Close()
   183  	if err != nil {
   184  		t.Error("Proxy.Fetch().Close() error =", err)
   185  	}
   186  	if !bytes.Equal(got, content) {
   187  		t.Errorf("Proxy.Fetch() = %v, want %v", got, content)
   188  	}
   189  
   190  	// the subsequent FetchCached should not touch base CAS
   191  	// nil base will generate panic if the base CAS is touched
   192  	s.ReadOnlyStorage = nil
   193  
   194  	exists, err = s.Exists(ctx, desc)
   195  	if err != nil {
   196  		t.Fatal("Proxy.Exists() error =", err)
   197  	}
   198  	if !exists {
   199  		t.Errorf("Proxy.Exists() = %v, want %v", exists, true)
   200  	}
   201  	rc, err = s.FetchCached(ctx, desc)
   202  	if err != nil {
   203  		t.Fatal("Proxy.Fetch() error =", err)
   204  	}
   205  	got, err = io.ReadAll(rc)
   206  	if err != nil {
   207  		t.Fatal("Proxy.Fetch().Read() error =", err)
   208  	}
   209  	err = rc.Close()
   210  	if err != nil {
   211  		t.Error("Proxy.Fetch().Close() error =", err)
   212  	}
   213  	if !bytes.Equal(got, content) {
   214  		t.Errorf("Proxy.Fetch() = %v, want %v", got, content)
   215  	}
   216  }
   217  
   218  func TestProxy_StopCaching(t *testing.T) {
   219  	content := []byte("hello world")
   220  	desc := ocispec.Descriptor{
   221  		MediaType: "test",
   222  		Digest:    digest.FromBytes(content),
   223  		Size:      int64(len(content)),
   224  	}
   225  
   226  	ctx := context.Background()
   227  	base := NewMemory()
   228  	err := base.Push(ctx, desc, bytes.NewReader(content))
   229  	if err != nil {
   230  		t.Fatal("Memory.Push() error =", err)
   231  	}
   232  	s := NewProxy(base, NewMemory())
   233  
   234  	// FetchCached should fetch from the base CAS
   235  	exists, err := s.Exists(ctx, desc)
   236  	if err != nil {
   237  		t.Fatal("Proxy.Exists() error =", err)
   238  	}
   239  	if !exists {
   240  		t.Errorf("Proxy.Exists() = %v, want %v", exists, true)
   241  	}
   242  
   243  	// test StopCaching
   244  	s.StopCaching = true
   245  	rc, err := s.Fetch(ctx, desc)
   246  	if err != nil {
   247  		t.Fatal("Proxy.Fetch() error =", err)
   248  	}
   249  	got, err := io.ReadAll(rc)
   250  	if err != nil {
   251  		t.Fatal("Proxy.Fetch().Read() error =", err)
   252  	}
   253  	err = rc.Close()
   254  	if err != nil {
   255  		t.Error("Proxy.Fetch().Close() error =", err)
   256  	}
   257  	if !bytes.Equal(got, content) {
   258  		t.Errorf("Proxy.Fetch() = %v, want %v", got, content)
   259  	}
   260  
   261  	// the content should not exist in the cache
   262  	exists, err = s.Cache.Exists(ctx, desc)
   263  	if err != nil {
   264  		t.Fatal("Proxy.Cache.Exists() error =", err)
   265  	}
   266  
   267  	if exists {
   268  		t.Errorf("Proxy.Cache.Exists()() = %v, want %v", exists, false)
   269  	}
   270  }
   271  
   272  func TestProxyWithLimit_WithinLimit(t *testing.T) {
   273  	content := []byte("hello world")
   274  	desc := ocispec.Descriptor{
   275  		MediaType: "test",
   276  		Digest:    digest.FromBytes(content),
   277  		Size:      int64(len(content)),
   278  	}
   279  
   280  	ctx := context.Background()
   281  	base := NewMemory()
   282  	err := base.Push(ctx, desc, bytes.NewReader(content))
   283  	if err != nil {
   284  		t.Fatal("Memory.Push() error =", err)
   285  	}
   286  	s := NewProxyWithLimit(base, NewMemory(), 4*1024*1024)
   287  
   288  	// first fetch
   289  	exists, err := s.Exists(ctx, desc)
   290  	if err != nil {
   291  		t.Fatal("Proxy.Exists() error =", err)
   292  	}
   293  	if !exists {
   294  		t.Errorf("Proxy.Exists() = %v, want %v", exists, true)
   295  	}
   296  	rc, err := s.Fetch(ctx, desc)
   297  	if err != nil {
   298  		t.Fatal("Proxy.Fetch() error =", err)
   299  	}
   300  	got, err := io.ReadAll(rc)
   301  	if err != nil {
   302  		t.Fatal("Proxy.Fetch().Read() error =", err)
   303  	}
   304  	err = rc.Close()
   305  	if err != nil {
   306  		t.Error("Proxy.Fetch().Close() error =", err)
   307  	}
   308  	if !bytes.Equal(got, content) {
   309  		t.Errorf("Proxy.Fetch() = %v, want %v", got, content)
   310  	}
   311  
   312  	// repeated fetch should not touch base CAS
   313  	// nil base will generate panic if the base CAS is touched
   314  	s.ReadOnlyStorage = nil
   315  
   316  	exists, err = s.Exists(ctx, desc)
   317  	if err != nil {
   318  		t.Fatal("Proxy.Exists() error =", err)
   319  	}
   320  	if !exists {
   321  		t.Errorf("Proxy.Exists() = %v, want %v", exists, true)
   322  	}
   323  	rc, err = s.Fetch(ctx, desc)
   324  	if err != nil {
   325  		t.Fatal("Proxy.Fetch() error =", err)
   326  	}
   327  	got, err = io.ReadAll(rc)
   328  	if err != nil {
   329  		t.Fatal("Proxy.Fetch().Read() error =", err)
   330  	}
   331  	err = rc.Close()
   332  	if err != nil {
   333  		t.Error("Proxy.Fetch().Close() error =", err)
   334  	}
   335  	if !bytes.Equal(got, content) {
   336  		t.Errorf("Proxy.Fetch() = %v, want %v", got, content)
   337  	}
   338  }
   339  
   340  func TestProxyWithLimit_ExceedsLimit(t *testing.T) {
   341  	content := []byte("hello world")
   342  	desc := ocispec.Descriptor{
   343  		MediaType: "test",
   344  		Digest:    digest.FromBytes(content),
   345  		Size:      int64(len(content)),
   346  	}
   347  
   348  	ctx := context.Background()
   349  	base := NewMemory()
   350  	err := base.Push(ctx, desc, bytes.NewReader(content))
   351  	if err != nil {
   352  		t.Fatal("Memory.Push() error =", err)
   353  	}
   354  	s := NewProxyWithLimit(base, NewMemory(), 1)
   355  
   356  	// test fetch
   357  	exists, err := s.Exists(ctx, desc)
   358  	if err != nil {
   359  		t.Fatal("Proxy.Exists() error =", err)
   360  	}
   361  	if !exists {
   362  		t.Errorf("Proxy.Exists() = %v, want %v", exists, true)
   363  	}
   364  	rc, err := s.Fetch(ctx, desc)
   365  	if err != nil {
   366  		t.Fatal("Proxy.Fetch() error =", err)
   367  	}
   368  	_, err = io.ReadAll(rc)
   369  	if !errors.Is(err, errdef.ErrSizeExceedsLimit) {
   370  		t.Fatalf("Proxy.Fetch().Read() error = %v, wantErr %v", err, errdef.ErrSizeExceedsLimit)
   371  	}
   372  }