package acl import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/commonspace/object/accountdata" "github.com/anyproto/any-sync/commonspace/object/acl/list" "github.com/anyproto/any-sync/consensus/consensusclient" "github.com/anyproto/any-sync/consensus/consensusclient/mock_consensusclient" "github.com/anyproto/any-sync/consensus/consensusproto" "github.com/anyproto/any-sync/testutil/accounttest" ) var ctx = context.Background() func TestAclService_AddRecord(t *testing.T) { ownerKeys, err := accountdata.NewRandom() require.NoError(t, err) spaceId := "spaceId" ownerAcl, err := list.NewInMemoryDerivedAcl(spaceId, ownerKeys) require.NoError(t, err) inv, err := ownerAcl.RecordBuilder().BuildInvite() require.NoError(t, err) t.Run("success", func(t *testing.T) { fx := newFixture(t) defer fx.finish(t) expRes := list.WrapAclRecord(inv.InviteRec) var watcherCh = make(chan consensusclient.Watcher) fx.consCl.EXPECT().Watch(spaceId, gomock.Any()).DoAndReturn(func(spaceId string, w consensusclient.Watcher) error { go func() { w.AddConsensusRecords([]*consensusproto.RawRecordWithId{ ownerAcl.Root(), }) watcherCh <- w }() return nil }) fx.consCl.EXPECT().AddRecord(ctx, spaceId, inv.InviteRec).Return(expRes, nil) fx.consCl.EXPECT().UnWatch(spaceId) res, err := fx.AddRecord(ctx, spaceId, inv.InviteRec, Limits{ ReadMembers: 10, WriteMembers: 10, }) assert.Equal(t, expRes, res) assert.NoError(t, err) w := <-watcherCh w.AddConsensusRecords([]*consensusproto.RawRecordWithId{ expRes, }) }) t.Run("error", func(t *testing.T) { fx := newFixture(t) defer fx.finish(t) var testErr = errors.New("test") fx.consCl.EXPECT().Watch(spaceId, gomock.Any()).DoAndReturn(func(spaceId string, w consensusclient.Watcher) error { go func() { w.AddConsensusError(testErr) }() return nil }) fx.consCl.EXPECT().UnWatch(spaceId) res, err := fx.AddRecord(ctx, spaceId, inv.InviteRec, Limits{ ReadMembers: 10, WriteMembers: 10, }) assert.Nil(t, res) assert.EqualError(t, err, testErr.Error()) }) t.Run("limit exceed", func(t *testing.T) { fx := newFixture(t) defer fx.finish(t) _, err := fx.AddRecord(ctx, spaceId, inv.InviteRec, Limits{ ReadMembers: 1, WriteMembers: 1, }) assert.ErrorIs(t, err, ErrLimitExceed) }) } func TestAclService_RecordsAfter(t *testing.T) { ownerKeys, err := accountdata.NewRandom() require.NoError(t, err) spaceId := "spaceId" ownerAcl, err := list.NewInMemoryDerivedAcl(spaceId, ownerKeys) require.NoError(t, err) fx := newFixture(t) defer fx.finish(t) fx.consCl.EXPECT().Watch(spaceId, gomock.Any()).DoAndReturn(func(spaceId string, w consensusclient.Watcher) error { go func() { w.AddConsensusRecords([]*consensusproto.RawRecordWithId{ ownerAcl.Root(), }) }() return nil }) fx.consCl.EXPECT().UnWatch(spaceId) res, err := fx.RecordsAfter(ctx, spaceId, "") require.NoError(t, err) assert.Len(t, res, 1) } func TestAclService(t *testing.T) { ownerKeys, err := accountdata.NewRandom() require.NoError(t, err) spaceId := "spaceId" ownerAcl, err := list.NewInMemoryDerivedAcl(spaceId, ownerKeys) require.NoError(t, err) fx := newFixture(t) defer fx.finish(t) fx.consCl.EXPECT().Watch(spaceId, gomock.Any()).DoAndReturn(func(spaceId string, w consensusclient.Watcher) error { go func() { w.AddConsensusRecords([]*consensusproto.RawRecordWithId{ ownerAcl.Root(), }) }() return nil }) fx.consCl.EXPECT().UnWatch(spaceId) t.Run("permissions", func(t *testing.T) { res, err := fx.Permissions(ctx, ownerKeys.SignKey.GetPublic(), spaceId) require.NoError(t, err) assert.True(t, res.IsOwner()) }) t.Run("ownerPubKey", func(t *testing.T) { res, err := fx.OwnerPubKey(ctx, spaceId) require.NoError(t, err) assert.Equal(t, ownerKeys.SignKey.GetPublic().Account(), res.Account()) }) } func TestAclService_ReadState(t *testing.T) { ownerKeys, err := accountdata.NewRandom() require.NoError(t, err) spaceId := "spaceId" ownerAcl, err := list.NewInMemoryDerivedAcl(spaceId, ownerKeys) require.NoError(t, err) fx := newFixture(t) defer fx.finish(t) fx.consCl.EXPECT().Watch(spaceId, gomock.Any()).DoAndReturn(func(spaceId string, w consensusclient.Watcher) error { go func() { w.AddConsensusRecords([]*consensusproto.RawRecordWithId{ ownerAcl.Root(), }) }() return nil }) fx.consCl.EXPECT().UnWatch(spaceId) require.NoError(t, fx.ReadState(ctx, spaceId, func(s *list.AclState) error { assert.NotNil(t, s) return nil })) } func TestAclService_HasRecord(t *testing.T) { ownerKeys, err := accountdata.NewRandom() require.NoError(t, err) spaceId := "spaceId" ownerAcl, err := list.NewInMemoryDerivedAcl(spaceId, ownerKeys) require.NoError(t, err) fx := newFixture(t) defer fx.finish(t) fx.consCl.EXPECT().Watch(spaceId, gomock.Any()).DoAndReturn(func(spaceId string, w consensusclient.Watcher) error { go func() { w.AddConsensusRecords([]*consensusproto.RawRecordWithId{ ownerAcl.Root(), }) }() return nil }) fx.consCl.EXPECT().UnWatch(spaceId) has, err := fx.HasRecord(ctx, spaceId, ownerAcl.Root().Id) require.NoError(t, err) assert.True(t, has) has, err = fx.HasRecord(ctx, spaceId, "non-exists") require.NoError(t, err) assert.False(t, has) } func newFixture(t *testing.T) *fixture { ctrl := gomock.NewController(t) fx := &fixture{ a: new(app.App), ctrl: ctrl, consCl: mock_consensusclient.NewMockService(ctrl), AclService: New(), } fx.consCl.EXPECT().Name().Return(consensusclient.CName).AnyTimes() fx.consCl.EXPECT().Init(gomock.Any()).AnyTimes() fx.consCl.EXPECT().Run(gomock.Any()).AnyTimes() fx.consCl.EXPECT().Close(gomock.Any()).AnyTimes() fx.a.Register(fx.consCl).Register(fx.AclService).Register(&accounttest.AccountTestService{}) require.NoError(t, fx.a.Start(ctx)) return fx } type fixture struct { a *app.App ctrl *gomock.Controller consCl *mock_consensusclient.MockService AclService } func (fx *fixture) finish(t *testing.T) { require.NoError(t, fx.a.Close(ctx)) fx.ctrl.Finish() }