Live video on the AT Protocol
at natb/command-errors 243 lines 8.8 kB view raw
1package moderation 2 3import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/bluesky-social/indigo/api/bsky" 10 lexutil "github.com/bluesky-social/indigo/lex/util" 11 "github.com/stretchr/testify/require" 12 "stream.place/streamplace/pkg/streamplace" 13) 14 15func TestPermissionChecker_CheckPermission_StreamerSelfModeration(t *testing.T) { 16 mod := newMockModel() 17 pc := NewPermissionChecker(mod) 18 19 streamerDID := "did:plc:streamer123" 20 21 err := pc.CheckPermission(context.Background(), streamerDID, streamerDID, "createBlock") 22 require.NoError(t, err, "streamer should have permission for self-moderation") 23 24 err = pc.CheckPermission(context.Background(), streamerDID, streamerDID, "createGate") 25 require.NoError(t, err, "streamer should have permission for self-moderation") 26 27 err = pc.CheckPermission(context.Background(), streamerDID, streamerDID, "updateLivestream") 28 require.NoError(t, err, "streamer should have permission for self-moderation") 29} 30 31func TestPermissionChecker_CheckPermission_WithCorrectPermission(t *testing.T) { 32 mod := newMockModel() 33 pc := NewPermissionChecker(mod) 34 35 ctx := context.Background() 36 streamerDID := "did:plc:streamer123" 37 moderatorDID := "did:plc:moderator456" 38 39 mod.addPermissionView(streamerDID, moderatorDID, []string{"ban", "hide"}, nil) 40 41 err := pc.CheckPermission(ctx, moderatorDID, streamerDID, "createBlock") 42 require.NoError(t, err, "moderator with 'ban' permission should be able to createBlock") 43 44 err = pc.CheckPermission(ctx, moderatorDID, streamerDID, "createGate") 45 require.NoError(t, err, "moderator with 'hide' permission should be able to createGate") 46} 47 48func TestPermissionChecker_CheckPermission_WithWrongPermission(t *testing.T) { 49 mod := newMockModel() 50 pc := NewPermissionChecker(mod) 51 52 ctx := context.Background() 53 streamerDID := "did:plc:streamer123" 54 moderatorDID := "did:plc:moderator456" 55 56 mod.addPermissionView(streamerDID, moderatorDID, []string{"hide"}, nil) 57 58 err := pc.CheckPermission(ctx, moderatorDID, streamerDID, "createBlock") 59 require.Error(t, err, "moderator with only 'hide' permission should not be able to createBlock") 60 require.Contains(t, err.Error(), "does not have permission 'ban'") 61} 62 63func TestPermissionChecker_CheckPermission_WithoutAnyPermission(t *testing.T) { 64 mod := newMockModel() 65 pc := NewPermissionChecker(mod) 66 67 ctx := context.Background() 68 streamerDID := "did:plc:streamer123" 69 moderatorDID := "did:plc:moderator456" 70 71 err := pc.CheckPermission(ctx, moderatorDID, streamerDID, "createBlock") 72 require.Error(t, err, "moderator without any delegation should be denied") 73 require.Contains(t, err.Error(), "does not have permission") 74} 75 76func TestPermissionChecker_CheckPermission_UnknownAction(t *testing.T) { 77 mod := newMockModel() 78 pc := NewPermissionChecker(mod) 79 80 streamerDID := "did:plc:streamer123" 81 82 err := pc.CheckPermission(context.Background(), streamerDID, streamerDID, "unknownAction") 83 require.Error(t, err) 84 require.Contains(t, err.Error(), "unknown action") 85} 86 87func TestPermissionChecker_HasPermission(t *testing.T) { 88 mod := newMockModel() 89 pc := NewPermissionChecker(mod) 90 91 ctx := context.Background() 92 streamerDID := "did:plc:streamer123" 93 moderatorDID := "did:plc:moderator456" 94 95 mod.addPermissionView(streamerDID, moderatorDID, []string{"ban", "hide"}, nil) 96 97 has, err := pc.HasPermission(ctx, moderatorDID, streamerDID, PermissionBan) 98 require.NoError(t, err) 99 require.True(t, has, "should have 'ban' permission") 100 101 has, err = pc.HasPermission(ctx, moderatorDID, streamerDID, PermissionHide) 102 require.NoError(t, err) 103 require.True(t, has, "should have 'hide' permission") 104 105 has, err = pc.HasPermission(ctx, moderatorDID, streamerDID, PermissionLivestreamManage) 106 require.NoError(t, err) 107 require.False(t, has, "should not have 'livestream.manage' permission") 108} 109 110func TestActionPermissions_Mapping(t *testing.T) { 111 require.Equal(t, PermissionBan, ActionPermissions["createBlock"]) 112 require.Equal(t, PermissionBan, ActionPermissions["deleteBlock"]) 113 require.Equal(t, PermissionHide, ActionPermissions["createGate"]) 114 require.Equal(t, PermissionHide, ActionPermissions["deleteGate"]) 115 require.Equal(t, PermissionLivestreamManage, ActionPermissions["updateLivestream"]) 116} 117 118func TestPermissionChecker_HasPermission_MultipleSeparateRecords(t *testing.T) { 119 mod := newMockModel() 120 pc := NewPermissionChecker(mod) 121 122 ctx := context.Background() 123 streamerDID := "did:plc:streamer123" 124 moderatorDID := "did:plc:moderator456" 125 126 mod.addPermissionView(streamerDID, moderatorDID, []string{"ban"}, nil) 127 mod.addPermissionView(streamerDID, moderatorDID, []string{"hide"}, nil) 128 129 hasBan, err := pc.HasPermission(ctx, moderatorDID, streamerDID, PermissionBan) 130 require.NoError(t, err) 131 require.True(t, hasBan, "should have 'ban' permission from first record") 132 133 hasHide, err := pc.HasPermission(ctx, moderatorDID, streamerDID, PermissionHide) 134 require.NoError(t, err) 135 require.True(t, hasHide, "should have 'hide' permission from second record") 136 137 err = pc.CheckPermission(ctx, moderatorDID, streamerDID, "createBlock") 138 require.NoError(t, err, "should allow createBlock with 'ban' permission from separate record") 139 140 err = pc.CheckPermission(ctx, moderatorDID, streamerDID, "createGate") 141 require.NoError(t, err, "should allow createGate with 'hide' permission from separate record") 142} 143 144func TestPermissionChecker_HasPermission_ExpiredDelegation(t *testing.T) { 145 mod := newMockModel() 146 pc := NewPermissionChecker(mod) 147 148 ctx := context.Background() 149 streamerDID := "did:plc:streamer123" 150 moderatorDID := "did:plc:moderator456" 151 152 expiredTime := time.Now().Add(-1 * time.Hour) 153 mod.addPermissionView(streamerDID, moderatorDID, []string{"ban"}, &expiredTime) 154 155 hasPermission, err := pc.HasPermission(ctx, moderatorDID, streamerDID, PermissionBan) 156 require.NoError(t, err) 157 require.False(t, hasPermission, "should deny permission for expired delegation") 158 159 err = pc.CheckPermission(ctx, moderatorDID, streamerDID, "createBlock") 160 require.Error(t, err, "should deny action for expired delegation") 161 require.Contains(t, err.Error(), "does not have permission") 162} 163 164func TestPermissionChecker_HasPermission_NotYetExpired(t *testing.T) { 165 mod := newMockModel() 166 pc := NewPermissionChecker(mod) 167 168 ctx := context.Background() 169 streamerDID := "did:plc:streamer123" 170 moderatorDID := "did:plc:moderator456" 171 172 futureTime := time.Now().Add(1 * time.Hour) 173 mod.addPermissionView(streamerDID, moderatorDID, []string{"ban", "hide"}, &futureTime) 174 175 hasPermission, err := pc.HasPermission(ctx, moderatorDID, streamerDID, PermissionBan) 176 require.NoError(t, err) 177 require.True(t, hasPermission, "should allow permission for not-yet-expired delegation") 178 179 err = pc.CheckPermission(ctx, moderatorDID, streamerDID, "createBlock") 180 require.NoError(t, err, "should allow action for not-yet-expired delegation") 181} 182 183func TestPermissionChecker_HasPermission_NoExpiration(t *testing.T) { 184 mod := newMockModel() 185 pc := NewPermissionChecker(mod) 186 187 ctx := context.Background() 188 streamerDID := "did:plc:streamer123" 189 moderatorDID := "did:plc:moderator456" 190 191 mod.addPermissionView(streamerDID, moderatorDID, []string{"ban", "hide"}, nil) 192 193 hasPermission, err := pc.HasPermission(ctx, moderatorDID, streamerDID, PermissionBan) 194 require.NoError(t, err) 195 require.True(t, hasPermission, "should allow permission for delegation with no expiration") 196 197 err = pc.CheckPermission(ctx, moderatorDID, streamerDID, "createBlock") 198 require.NoError(t, err, "should allow action for delegation with no expiration") 199} 200 201type mockModel struct { 202 delegations map[string][]*streamplace.ModerationDefs_PermissionView 203} 204 205func newMockModel() *mockModel { 206 return &mockModel{ 207 delegations: make(map[string][]*streamplace.ModerationDefs_PermissionView), 208 } 209} 210 211func (m *mockModel) addPermissionView(streamerDID, moderatorDID string, permissions []string, expirationTime *time.Time) { 212 key := streamerDID + "_" + moderatorDID 213 214 var expTimeStr *string 215 if expirationTime != nil { 216 str := expirationTime.Format(time.RFC3339) 217 expTimeStr = &str 218 } 219 220 permRecord := &streamplace.ModerationPermission{ 221 Moderator: moderatorDID, 222 Permissions: permissions, 223 ExpirationTime: expTimeStr, 224 } 225 226 view := &streamplace.ModerationDefs_PermissionView{ 227 Uri: fmt.Sprintf("at://%s/place.stream.moderation.permission/test", streamerDID), 228 Cid: "bafytest", 229 Author: &bsky.ActorDefs_ProfileViewBasic{Did: streamerDID}, 230 Record: &lexutil.LexiconTypeDecoder{Val: permRecord}, 231 } 232 233 m.delegations[key] = append(m.delegations[key], view) 234} 235 236func (m *mockModel) GetModerationDelegations(ctx context.Context, streamerDID, moderatorDID string) ([]*streamplace.ModerationDefs_PermissionView, error) { 237 key := streamerDID + "_" + moderatorDID 238 delegations, exists := m.delegations[key] 239 if !exists { 240 return nil, nil 241 } 242 return delegations, nil 243}