github.com/letsencrypt/boulder@v0.20251208.0/wfe2/cache_test.go (about)

     1  package wfe2
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/jmhodges/clock"
    10  	"google.golang.org/grpc"
    11  	"google.golang.org/protobuf/types/known/timestamppb"
    12  
    13  	corepb "github.com/letsencrypt/boulder/core/proto"
    14  	"github.com/letsencrypt/boulder/metrics"
    15  	sapb "github.com/letsencrypt/boulder/sa/proto"
    16  	"github.com/letsencrypt/boulder/test"
    17  )
    18  
    19  type recordingBackend struct {
    20  	requests []int64
    21  }
    22  
    23  func (rb *recordingBackend) GetRegistration(
    24  	ctx context.Context,
    25  	regID *sapb.RegistrationID,
    26  	opts ...grpc.CallOption,
    27  ) (*corepb.Registration, error) {
    28  	rb.requests = append(rb.requests, regID.Id)
    29  	return &corepb.Registration{
    30  		Id: regID.Id,
    31  	}, nil
    32  }
    33  
    34  func TestCacheAddRetrieve(t *testing.T) {
    35  	ctx := context.Background()
    36  	backend := &recordingBackend{}
    37  
    38  	cache := NewAccountCache(backend, 10, time.Second, clock.NewFake(), metrics.NoopRegisterer)
    39  
    40  	result, err := cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
    41  	test.AssertNotError(t, err, "getting registration")
    42  	test.AssertEquals(t, result.Id, int64(1234))
    43  	test.AssertEquals(t, len(backend.requests), 1)
    44  
    45  	// Request it again. This should hit the cache so our backend should not see additional requests.
    46  	result, err = cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
    47  	test.AssertNotError(t, err, "getting registration")
    48  	test.AssertEquals(t, result.Id, int64(1234))
    49  	test.AssertEquals(t, len(backend.requests), 1)
    50  }
    51  
    52  // Test that the cache copies values before giving them out, so code that receives a cached
    53  // value can't modify the cache's contents.
    54  func TestCacheCopy(t *testing.T) {
    55  	ctx := context.Background()
    56  	backend := &recordingBackend{}
    57  
    58  	cache := NewAccountCache(backend, 10, time.Second, clock.NewFake(), metrics.NoopRegisterer)
    59  
    60  	acct, err := cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
    61  	test.AssertNotError(t, err, "getting registration")
    62  	test.AssertEquals(t, len(backend.requests), 1)
    63  	origTimestamp := acct.CreatedAt
    64  
    65  	test.AssertEquals(t, cache.cache.Len(), 1)
    66  
    67  	// Request it again. This should hit the cache.
    68  	result, err := cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
    69  	test.AssertNotError(t, err, "getting registration")
    70  	test.AssertEquals(t, len(backend.requests), 1)
    71  
    72  	// Modify a pointer value inside the result
    73  	result.CreatedAt = timestamppb.New(time.Now().Add(24 * time.Hour))
    74  
    75  	result, err = cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
    76  	test.AssertNotError(t, err, "getting registration")
    77  	test.AssertEquals(t, len(backend.requests), 1)
    78  
    79  	test.AssertDeepEquals(t, result.CreatedAt, origTimestamp)
    80  }
    81  
    82  // Test that the cache expires values.
    83  func TestCacheExpires(t *testing.T) {
    84  	ctx := context.Background()
    85  	backend := &recordingBackend{}
    86  
    87  	clk := clock.NewFake()
    88  	cache := NewAccountCache(backend, 10, time.Second, clk, metrics.NoopRegisterer)
    89  
    90  	_, err := cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
    91  	test.AssertNotError(t, err, "getting registration")
    92  	test.AssertEquals(t, len(backend.requests), 1)
    93  
    94  	// Request it again. This should hit the cache.
    95  	_, err = cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
    96  	test.AssertNotError(t, err, "getting registration")
    97  	test.AssertEquals(t, len(backend.requests), 1)
    98  
    99  	test.AssertEquals(t, cache.cache.Len(), 1)
   100  
   101  	// "Sleep" 10 seconds to expire the entry
   102  	clk.Sleep(10 * time.Second)
   103  
   104  	// This should not hit the cache
   105  	_, err = cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
   106  	test.AssertNotError(t, err, "getting registration")
   107  	test.AssertEquals(t, len(backend.requests), 2)
   108  }
   109  
   110  type wrongIDBackend struct{}
   111  
   112  func (wib wrongIDBackend) GetRegistration(
   113  	ctx context.Context,
   114  	regID *sapb.RegistrationID,
   115  	opts ...grpc.CallOption,
   116  ) (*corepb.Registration, error) {
   117  	return &corepb.Registration{
   118  		Id: regID.Id + 1,
   119  	}, nil
   120  }
   121  
   122  func TestWrongId(t *testing.T) {
   123  	ctx := context.Background()
   124  	cache := NewAccountCache(wrongIDBackend{}, 10, time.Second, clock.NewFake(), metrics.NoopRegisterer)
   125  
   126  	_, err := cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
   127  	test.AssertError(t, err, "expected error when backend returns wrong ID")
   128  }
   129  
   130  type errorBackend struct{}
   131  
   132  func (eb errorBackend) GetRegistration(ctx context.Context,
   133  	regID *sapb.RegistrationID,
   134  	opts ...grpc.CallOption,
   135  ) (*corepb.Registration, error) {
   136  	return nil, errors.New("some error")
   137  }
   138  
   139  func TestErrorPassthrough(t *testing.T) {
   140  	ctx := context.Background()
   141  	cache := NewAccountCache(errorBackend{}, 10, time.Second, clock.NewFake(), metrics.NoopRegisterer)
   142  
   143  	_, err := cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
   144  	test.AssertError(t, err, "expected error when backend errors")
   145  	test.AssertEquals(t, err.Error(), "some error")
   146  }