boulder/wfe2/cache_test.go

146 lines
4.6 KiB
Go

package wfe2
import (
"context"
"errors"
"testing"
"time"
"github.com/jmhodges/clock"
corepb "github.com/letsencrypt/boulder/core/proto"
"github.com/letsencrypt/boulder/metrics"
sapb "github.com/letsencrypt/boulder/sa/proto"
"github.com/letsencrypt/boulder/test"
"google.golang.org/grpc"
)
type recordingBackend struct {
requests []int64
}
func (rb *recordingBackend) GetRegistration(
ctx context.Context,
regID *sapb.RegistrationID,
opts ...grpc.CallOption,
) (*corepb.Registration, error) {
rb.requests = append(rb.requests, regID.Id)
return &corepb.Registration{
Id: regID.Id,
Contact: []string{"example@example.com"},
}, nil
}
func TestCacheAddRetrieve(t *testing.T) {
ctx := context.Background()
backend := &recordingBackend{}
cache := NewAccountCache(backend, 10, time.Second, clock.NewFake(), metrics.NoopRegisterer)
result, err := cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
test.AssertNotError(t, err, "getting registration")
test.AssertEquals(t, result.Id, int64(1234))
test.AssertEquals(t, len(backend.requests), 1)
// Request it again. This should hit the cache so our backend should not see additional requests.
result, err = cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
test.AssertNotError(t, err, "getting registration")
test.AssertEquals(t, result.Id, int64(1234))
test.AssertEquals(t, len(backend.requests), 1)
}
// Test that the cache copies values before giving them out, so code that receives a cached
// value can't modify the cache's contents.
func TestCacheCopy(t *testing.T) {
ctx := context.Background()
backend := &recordingBackend{}
cache := NewAccountCache(backend, 10, time.Second, clock.NewFake(), metrics.NoopRegisterer)
_, err := cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
test.AssertNotError(t, err, "getting registration")
test.AssertEquals(t, len(backend.requests), 1)
test.AssertEquals(t, cache.cache.Len(), 1)
// Request it again. This should hit the cache.
result, err := cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
test.AssertNotError(t, err, "getting registration")
test.AssertEquals(t, len(backend.requests), 1)
// Modify a pointer value inside the result
result.Contact[0] = "different@example.com"
result, err = cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
test.AssertNotError(t, err, "getting registration")
test.AssertEquals(t, len(backend.requests), 1)
test.AssertDeepEquals(t, result.Contact, []string{"example@example.com"})
}
// Test that the cache expires values.
func TestCacheExpires(t *testing.T) {
ctx := context.Background()
backend := &recordingBackend{}
clk := clock.NewFake()
cache := NewAccountCache(backend, 10, time.Second, clk, metrics.NoopRegisterer)
_, err := cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
test.AssertNotError(t, err, "getting registration")
test.AssertEquals(t, len(backend.requests), 1)
// Request it again. This should hit the cache.
_, err = cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
test.AssertNotError(t, err, "getting registration")
test.AssertEquals(t, len(backend.requests), 1)
test.AssertEquals(t, cache.cache.Len(), 1)
// "Sleep" 10 seconds to expire the entry
clk.Sleep(10 * time.Second)
// This should not hit the cache
_, err = cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
test.AssertNotError(t, err, "getting registration")
test.AssertEquals(t, len(backend.requests), 2)
}
type wrongIDBackend struct{}
func (wib wrongIDBackend) GetRegistration(
ctx context.Context,
regID *sapb.RegistrationID,
opts ...grpc.CallOption,
) (*corepb.Registration, error) {
return &corepb.Registration{
Id: regID.Id + 1,
Contact: []string{"example@example.com"},
}, nil
}
func TestWrongId(t *testing.T) {
ctx := context.Background()
cache := NewAccountCache(wrongIDBackend{}, 10, time.Second, clock.NewFake(), metrics.NoopRegisterer)
_, err := cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
test.AssertError(t, err, "expected error when backend returns wrong ID")
}
type errorBackend struct{}
func (eb errorBackend) GetRegistration(ctx context.Context,
regID *sapb.RegistrationID,
opts ...grpc.CallOption,
) (*corepb.Registration, error) {
return nil, errors.New("some error")
}
func TestErrorPassthrough(t *testing.T) {
ctx := context.Background()
cache := NewAccountCache(errorBackend{}, 10, time.Second, clock.NewFake(), metrics.NoopRegisterer)
_, err := cache.GetRegistration(ctx, &sapb.RegistrationID{Id: 1234})
test.AssertError(t, err, "expected error when backend errors")
test.AssertEquals(t, err.Error(), "some error")
}