github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datastore/proxy/readonly_test.go (about)

     1  package proxy
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/mock"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/authzed/spicedb/internal/datastore/common"
    11  	"github.com/authzed/spicedb/internal/datastore/proxy/proxy_test"
    12  	"github.com/authzed/spicedb/internal/datastore/revisions"
    13  	"github.com/authzed/spicedb/pkg/datastore"
    14  	core "github.com/authzed/spicedb/pkg/proto/core/v1"
    15  	"github.com/authzed/spicedb/pkg/tuple"
    16  )
    17  
    18  func newReadOnlyMock() (*proxy_test.MockDatastore, *proxy_test.MockReader) {
    19  	dsMock := &proxy_test.MockDatastore{}
    20  	readerMock := &proxy_test.MockReader{}
    21  
    22  	dsMock.On("ReadWriteTx").Panic("read-only proxy should never open a read-write transaction").Maybe()
    23  	dsMock.On("SnapshotReader", mock.Anything).Return(readerMock).Maybe()
    24  
    25  	return dsMock, readerMock
    26  }
    27  
    28  func TestRWOperationErrors(t *testing.T) {
    29  	require := require.New(t)
    30  
    31  	delegate, _ := newReadOnlyMock()
    32  
    33  	ds := NewReadonlyDatastore(delegate)
    34  	ctx := context.Background()
    35  
    36  	rev, err := ds.ReadWriteTx(ctx, func(ctx context.Context, rwt datastore.ReadWriteTransaction) error {
    37  		return rwt.DeleteNamespaces(ctx, "fake")
    38  	})
    39  	require.ErrorAs(err, &datastore.ErrReadOnly{})
    40  	require.Equal(datastore.NoRevision, rev)
    41  
    42  	rev, err = ds.ReadWriteTx(ctx, func(ctx context.Context, rwt datastore.ReadWriteTransaction) error {
    43  		return rwt.WriteNamespaces(ctx, &core.NamespaceDefinition{Name: "user"})
    44  	})
    45  	require.ErrorAs(err, &datastore.ErrReadOnly{})
    46  	require.Equal(datastore.NoRevision, rev)
    47  
    48  	rev, err = common.WriteTuples(ctx, ds, core.RelationTupleUpdate_CREATE, tuple.Parse("user:test#boss@user:boss"))
    49  	require.ErrorAs(err, &datastore.ErrReadOnly{})
    50  	require.Equal(datastore.NoRevision, rev)
    51  }
    52  
    53  var expectedRevision = revisions.NewForTransactionID(123)
    54  
    55  func TestReadyStatePassthrough(t *testing.T) {
    56  	require := require.New(t)
    57  
    58  	delegate, _ := newReadOnlyMock()
    59  	ds := NewReadonlyDatastore(delegate)
    60  	ctx := context.Background()
    61  
    62  	delegate.On("ReadyState").Return(datastore.ReadyState{IsReady: true}, nil).Times(1)
    63  
    64  	resp, err := ds.ReadyState(ctx)
    65  	require.NoError(err)
    66  	require.True(resp.IsReady)
    67  	delegate.AssertExpectations(t)
    68  }
    69  
    70  func TestOptimizedRevisionPassthrough(t *testing.T) {
    71  	require := require.New(t)
    72  
    73  	delegate, _ := newReadOnlyMock()
    74  	ds := NewReadonlyDatastore(delegate)
    75  	ctx := context.Background()
    76  
    77  	delegate.On("OptimizedRevision").Return(expectedRevision, nil).Times(1)
    78  
    79  	revision, err := ds.OptimizedRevision(ctx)
    80  	require.NoError(err)
    81  	require.Equal(expectedRevision, revision)
    82  	delegate.AssertExpectations(t)
    83  }
    84  
    85  func TestHeadRevisionPassthrough(t *testing.T) {
    86  	require := require.New(t)
    87  
    88  	delegate, _ := newReadOnlyMock()
    89  	ds := NewReadonlyDatastore(delegate)
    90  	ctx := context.Background()
    91  
    92  	delegate.On("HeadRevision").Return(expectedRevision, nil).Times(1)
    93  
    94  	revision, err := ds.HeadRevision(ctx)
    95  	require.NoError(err)
    96  	require.Equal(expectedRevision, revision)
    97  	delegate.AssertExpectations(t)
    98  }
    99  
   100  func TestCheckRevisionPassthrough(t *testing.T) {
   101  	require := require.New(t)
   102  
   103  	delegate, _ := newReadOnlyMock()
   104  	ds := NewReadonlyDatastore(delegate)
   105  	ctx := context.Background()
   106  
   107  	delegate.On("CheckRevision", expectedRevision).Return(nil).Times(1)
   108  
   109  	err := ds.CheckRevision(ctx, expectedRevision)
   110  	require.NoError(err)
   111  	delegate.AssertExpectations(t)
   112  }
   113  
   114  func TestWatchPassthrough(t *testing.T) {
   115  	delegate, _ := newReadOnlyMock()
   116  	ds := NewReadonlyDatastore(delegate)
   117  	ctx := context.Background()
   118  
   119  	delegate.On("Watch", expectedRevision).Return(
   120  		make(<-chan *datastore.RevisionChanges),
   121  		make(<-chan error),
   122  	).Times(1)
   123  
   124  	ds.Watch(ctx, expectedRevision, datastore.WatchJustRelationships())
   125  	delegate.AssertExpectations(t)
   126  }
   127  
   128  func TestSnapshotReaderPassthrough(t *testing.T) {
   129  	require := require.New(t)
   130  
   131  	delegate, reader := newReadOnlyMock()
   132  	ds := NewReadonlyDatastore(delegate)
   133  	ctx := context.Background()
   134  
   135  	reader.On("ReadNamespaceByName", "fake").Return(nil, expectedRevision, nil).Times(1)
   136  
   137  	_, rev, err := ds.SnapshotReader(expectedRevision).ReadNamespaceByName(ctx, "fake")
   138  	require.NoError(err)
   139  	require.True(expectedRevision.Equal(rev))
   140  	delegate.AssertExpectations(t)
   141  	reader.AssertExpectations(t)
   142  }