diff --git a/pkg/service/interfaces.go b/pkg/service/interfaces.go index 51260ffa3..8f1ed4950 100644 --- a/pkg/service/interfaces.go +++ b/pkg/service/interfaces.go @@ -26,6 +26,7 @@ type RoomStore interface { DeleteParticipant(ctx context.Context, roomName, identity string) error } +//counterfeiter:generate . RORoomStore type RORoomStore interface { LoadRoom(ctx context.Context, name string) (*livekit.Room, error) ListRooms(ctx context.Context) ([]*livekit.Room, error) @@ -34,6 +35,7 @@ type RORoomStore interface { ListParticipants(ctx context.Context, roomName string) ([]*livekit.ParticipantInfo, error) } +//counterfeiter:generate . RoomAllocator type RoomAllocator interface { CreateRoom(ctx context.Context, req *livekit.CreateRoomRequest) (*livekit.Room, error) } diff --git a/pkg/service/roomservice.go b/pkg/service/roomservice.go index 3f468c1dd..2e9d06bef 100644 --- a/pkg/service/roomservice.go +++ b/pkg/service/roomservice.go @@ -18,7 +18,7 @@ type RoomService struct { roomStore RORoomStore } -func NewRoomService(ra RoomAllocator, rs RORoomStore, router routing.MessageRouter) (svc livekit.RoomService, err error) { +func NewRoomService(ra RoomAllocator, rs RORoomStore, router routing.MessageRouter) (svc *RoomService, err error) { svc = &RoomService{ router: router, roomAllocator: ra, @@ -62,12 +62,12 @@ func (s *RoomService) DeleteRoom(ctx context.Context, req *livekit.DeleteRoomReq if err := EnsureCreatePermission(ctx); err != nil { return nil, twirpAuthError(err) } - - if err := s.writeRoomMessage(ctx, req.Room, "", &livekit.RTCNodeMessage{ + err := s.router.WriteRoomRTC(ctx, req.Room, "", &livekit.RTCNodeMessage{ Message: &livekit.RTCNodeMessage_DeleteRoom{ DeleteRoom: req, }, - }); err != nil { + }) + if err != nil { return nil, err } diff --git a/pkg/service/roomservice_test.go b/pkg/service/roomservice_test.go new file mode 100644 index 000000000..3c563c502 --- /dev/null +++ b/pkg/service/roomservice_test.go @@ -0,0 +1,66 @@ +package service_test + +import ( + "context" + "testing" + + "github.com/livekit/livekit-server/pkg/routing/routingfakes" + "github.com/livekit/livekit-server/pkg/service" + "github.com/livekit/livekit-server/pkg/service/servicefakes" + "github.com/livekit/protocol/auth" + livekit "github.com/livekit/protocol/proto" + "github.com/stretchr/testify/require" +) + +const grantsKey = "grants" + +func TestDeleteRoom(t *testing.T) { + t.Run("normal deletion", func(t *testing.T) { + svc := newTestRoomService() + grant := &auth.ClaimGrants{ + Video: &auth.VideoGrant{ + RoomCreate: true, + }, + } + ctx := context.WithValue(context.Background(), grantsKey, grant) + _, err := svc.DeleteRoom(ctx, &livekit.DeleteRoomRequest{ + Room: "testroom", + }) + require.NoError(t, err) + }) + + t.Run("missing permissions", func(t *testing.T) { + svc := newTestRoomService() + grant := &auth.ClaimGrants{ + Video: &auth.VideoGrant{}, + } + ctx := context.WithValue(context.Background(), grantsKey, grant) + _, err := svc.DeleteRoom(ctx, &livekit.DeleteRoomRequest{ + Room: "testroom", + }) + require.Error(t, err) + }) +} + +func newTestRoomService() *TestRoomService { + router := &routingfakes.FakeRouter{} + allocator := &servicefakes.FakeRoomAllocator{} + store := &servicefakes.FakeRORoomStore{} + svc, err := service.NewRoomService(allocator, store, router) + if err != nil { + panic(err) + } + return &TestRoomService{ + RoomService: *svc, + router: router, + allocator: allocator, + store: store, + } +} + +type TestRoomService struct { + service.RoomService + router *routingfakes.FakeRouter + allocator *servicefakes.FakeRoomAllocator + store *servicefakes.FakeRORoomStore +} diff --git a/pkg/service/servicefakes/fake_room_allocator.go b/pkg/service/servicefakes/fake_room_allocator.go new file mode 100644 index 000000000..51f622fa6 --- /dev/null +++ b/pkg/service/servicefakes/fake_room_allocator.go @@ -0,0 +1,120 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package servicefakes + +import ( + "context" + "sync" + + "github.com/livekit/livekit-server/pkg/service" + livekit "github.com/livekit/protocol/proto" +) + +type FakeRoomAllocator struct { + CreateRoomStub func(context.Context, *livekit.CreateRoomRequest) (*livekit.Room, error) + createRoomMutex sync.RWMutex + createRoomArgsForCall []struct { + arg1 context.Context + arg2 *livekit.CreateRoomRequest + } + createRoomReturns struct { + result1 *livekit.Room + result2 error + } + createRoomReturnsOnCall map[int]struct { + result1 *livekit.Room + result2 error + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeRoomAllocator) CreateRoom(arg1 context.Context, arg2 *livekit.CreateRoomRequest) (*livekit.Room, error) { + fake.createRoomMutex.Lock() + ret, specificReturn := fake.createRoomReturnsOnCall[len(fake.createRoomArgsForCall)] + fake.createRoomArgsForCall = append(fake.createRoomArgsForCall, struct { + arg1 context.Context + arg2 *livekit.CreateRoomRequest + }{arg1, arg2}) + stub := fake.CreateRoomStub + fakeReturns := fake.createRoomReturns + fake.recordInvocation("CreateRoom", []interface{}{arg1, arg2}) + fake.createRoomMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeRoomAllocator) CreateRoomCallCount() int { + fake.createRoomMutex.RLock() + defer fake.createRoomMutex.RUnlock() + return len(fake.createRoomArgsForCall) +} + +func (fake *FakeRoomAllocator) CreateRoomCalls(stub func(context.Context, *livekit.CreateRoomRequest) (*livekit.Room, error)) { + fake.createRoomMutex.Lock() + defer fake.createRoomMutex.Unlock() + fake.CreateRoomStub = stub +} + +func (fake *FakeRoomAllocator) CreateRoomArgsForCall(i int) (context.Context, *livekit.CreateRoomRequest) { + fake.createRoomMutex.RLock() + defer fake.createRoomMutex.RUnlock() + argsForCall := fake.createRoomArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeRoomAllocator) CreateRoomReturns(result1 *livekit.Room, result2 error) { + fake.createRoomMutex.Lock() + defer fake.createRoomMutex.Unlock() + fake.CreateRoomStub = nil + fake.createRoomReturns = struct { + result1 *livekit.Room + result2 error + }{result1, result2} +} + +func (fake *FakeRoomAllocator) CreateRoomReturnsOnCall(i int, result1 *livekit.Room, result2 error) { + fake.createRoomMutex.Lock() + defer fake.createRoomMutex.Unlock() + fake.CreateRoomStub = nil + if fake.createRoomReturnsOnCall == nil { + fake.createRoomReturnsOnCall = make(map[int]struct { + result1 *livekit.Room + result2 error + }) + } + fake.createRoomReturnsOnCall[i] = struct { + result1 *livekit.Room + result2 error + }{result1, result2} +} + +func (fake *FakeRoomAllocator) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.createRoomMutex.RLock() + defer fake.createRoomMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeRoomAllocator) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ service.RoomAllocator = new(FakeRoomAllocator) diff --git a/pkg/service/servicefakes/fake_roroom_store.go b/pkg/service/servicefakes/fake_roroom_store.go new file mode 100644 index 000000000..2d0850809 --- /dev/null +++ b/pkg/service/servicefakes/fake_roroom_store.go @@ -0,0 +1,363 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package servicefakes + +import ( + "context" + "sync" + + "github.com/livekit/livekit-server/pkg/service" + livekit "github.com/livekit/protocol/proto" +) + +type FakeRORoomStore struct { + ListParticipantsStub func(context.Context, string) ([]*livekit.ParticipantInfo, error) + listParticipantsMutex sync.RWMutex + listParticipantsArgsForCall []struct { + arg1 context.Context + arg2 string + } + listParticipantsReturns struct { + result1 []*livekit.ParticipantInfo + result2 error + } + listParticipantsReturnsOnCall map[int]struct { + result1 []*livekit.ParticipantInfo + result2 error + } + ListRoomsStub func(context.Context) ([]*livekit.Room, error) + listRoomsMutex sync.RWMutex + listRoomsArgsForCall []struct { + arg1 context.Context + } + listRoomsReturns struct { + result1 []*livekit.Room + result2 error + } + listRoomsReturnsOnCall map[int]struct { + result1 []*livekit.Room + result2 error + } + LoadParticipantStub func(context.Context, string, string) (*livekit.ParticipantInfo, error) + loadParticipantMutex sync.RWMutex + loadParticipantArgsForCall []struct { + arg1 context.Context + arg2 string + arg3 string + } + loadParticipantReturns struct { + result1 *livekit.ParticipantInfo + result2 error + } + loadParticipantReturnsOnCall map[int]struct { + result1 *livekit.ParticipantInfo + result2 error + } + LoadRoomStub func(context.Context, string) (*livekit.Room, error) + loadRoomMutex sync.RWMutex + loadRoomArgsForCall []struct { + arg1 context.Context + arg2 string + } + loadRoomReturns struct { + result1 *livekit.Room + result2 error + } + loadRoomReturnsOnCall map[int]struct { + result1 *livekit.Room + result2 error + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeRORoomStore) ListParticipants(arg1 context.Context, arg2 string) ([]*livekit.ParticipantInfo, error) { + fake.listParticipantsMutex.Lock() + ret, specificReturn := fake.listParticipantsReturnsOnCall[len(fake.listParticipantsArgsForCall)] + fake.listParticipantsArgsForCall = append(fake.listParticipantsArgsForCall, struct { + arg1 context.Context + arg2 string + }{arg1, arg2}) + stub := fake.ListParticipantsStub + fakeReturns := fake.listParticipantsReturns + fake.recordInvocation("ListParticipants", []interface{}{arg1, arg2}) + fake.listParticipantsMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeRORoomStore) ListParticipantsCallCount() int { + fake.listParticipantsMutex.RLock() + defer fake.listParticipantsMutex.RUnlock() + return len(fake.listParticipantsArgsForCall) +} + +func (fake *FakeRORoomStore) ListParticipantsCalls(stub func(context.Context, string) ([]*livekit.ParticipantInfo, error)) { + fake.listParticipantsMutex.Lock() + defer fake.listParticipantsMutex.Unlock() + fake.ListParticipantsStub = stub +} + +func (fake *FakeRORoomStore) ListParticipantsArgsForCall(i int) (context.Context, string) { + fake.listParticipantsMutex.RLock() + defer fake.listParticipantsMutex.RUnlock() + argsForCall := fake.listParticipantsArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeRORoomStore) ListParticipantsReturns(result1 []*livekit.ParticipantInfo, result2 error) { + fake.listParticipantsMutex.Lock() + defer fake.listParticipantsMutex.Unlock() + fake.ListParticipantsStub = nil + fake.listParticipantsReturns = struct { + result1 []*livekit.ParticipantInfo + result2 error + }{result1, result2} +} + +func (fake *FakeRORoomStore) ListParticipantsReturnsOnCall(i int, result1 []*livekit.ParticipantInfo, result2 error) { + fake.listParticipantsMutex.Lock() + defer fake.listParticipantsMutex.Unlock() + fake.ListParticipantsStub = nil + if fake.listParticipantsReturnsOnCall == nil { + fake.listParticipantsReturnsOnCall = make(map[int]struct { + result1 []*livekit.ParticipantInfo + result2 error + }) + } + fake.listParticipantsReturnsOnCall[i] = struct { + result1 []*livekit.ParticipantInfo + result2 error + }{result1, result2} +} + +func (fake *FakeRORoomStore) ListRooms(arg1 context.Context) ([]*livekit.Room, error) { + fake.listRoomsMutex.Lock() + ret, specificReturn := fake.listRoomsReturnsOnCall[len(fake.listRoomsArgsForCall)] + fake.listRoomsArgsForCall = append(fake.listRoomsArgsForCall, struct { + arg1 context.Context + }{arg1}) + stub := fake.ListRoomsStub + fakeReturns := fake.listRoomsReturns + fake.recordInvocation("ListRooms", []interface{}{arg1}) + fake.listRoomsMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeRORoomStore) ListRoomsCallCount() int { + fake.listRoomsMutex.RLock() + defer fake.listRoomsMutex.RUnlock() + return len(fake.listRoomsArgsForCall) +} + +func (fake *FakeRORoomStore) ListRoomsCalls(stub func(context.Context) ([]*livekit.Room, error)) { + fake.listRoomsMutex.Lock() + defer fake.listRoomsMutex.Unlock() + fake.ListRoomsStub = stub +} + +func (fake *FakeRORoomStore) ListRoomsArgsForCall(i int) context.Context { + fake.listRoomsMutex.RLock() + defer fake.listRoomsMutex.RUnlock() + argsForCall := fake.listRoomsArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeRORoomStore) ListRoomsReturns(result1 []*livekit.Room, result2 error) { + fake.listRoomsMutex.Lock() + defer fake.listRoomsMutex.Unlock() + fake.ListRoomsStub = nil + fake.listRoomsReturns = struct { + result1 []*livekit.Room + result2 error + }{result1, result2} +} + +func (fake *FakeRORoomStore) ListRoomsReturnsOnCall(i int, result1 []*livekit.Room, result2 error) { + fake.listRoomsMutex.Lock() + defer fake.listRoomsMutex.Unlock() + fake.ListRoomsStub = nil + if fake.listRoomsReturnsOnCall == nil { + fake.listRoomsReturnsOnCall = make(map[int]struct { + result1 []*livekit.Room + result2 error + }) + } + fake.listRoomsReturnsOnCall[i] = struct { + result1 []*livekit.Room + result2 error + }{result1, result2} +} + +func (fake *FakeRORoomStore) LoadParticipant(arg1 context.Context, arg2 string, arg3 string) (*livekit.ParticipantInfo, error) { + fake.loadParticipantMutex.Lock() + ret, specificReturn := fake.loadParticipantReturnsOnCall[len(fake.loadParticipantArgsForCall)] + fake.loadParticipantArgsForCall = append(fake.loadParticipantArgsForCall, struct { + arg1 context.Context + arg2 string + arg3 string + }{arg1, arg2, arg3}) + stub := fake.LoadParticipantStub + fakeReturns := fake.loadParticipantReturns + fake.recordInvocation("LoadParticipant", []interface{}{arg1, arg2, arg3}) + fake.loadParticipantMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeRORoomStore) LoadParticipantCallCount() int { + fake.loadParticipantMutex.RLock() + defer fake.loadParticipantMutex.RUnlock() + return len(fake.loadParticipantArgsForCall) +} + +func (fake *FakeRORoomStore) LoadParticipantCalls(stub func(context.Context, string, string) (*livekit.ParticipantInfo, error)) { + fake.loadParticipantMutex.Lock() + defer fake.loadParticipantMutex.Unlock() + fake.LoadParticipantStub = stub +} + +func (fake *FakeRORoomStore) LoadParticipantArgsForCall(i int) (context.Context, string, string) { + fake.loadParticipantMutex.RLock() + defer fake.loadParticipantMutex.RUnlock() + argsForCall := fake.loadParticipantArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 +} + +func (fake *FakeRORoomStore) LoadParticipantReturns(result1 *livekit.ParticipantInfo, result2 error) { + fake.loadParticipantMutex.Lock() + defer fake.loadParticipantMutex.Unlock() + fake.LoadParticipantStub = nil + fake.loadParticipantReturns = struct { + result1 *livekit.ParticipantInfo + result2 error + }{result1, result2} +} + +func (fake *FakeRORoomStore) LoadParticipantReturnsOnCall(i int, result1 *livekit.ParticipantInfo, result2 error) { + fake.loadParticipantMutex.Lock() + defer fake.loadParticipantMutex.Unlock() + fake.LoadParticipantStub = nil + if fake.loadParticipantReturnsOnCall == nil { + fake.loadParticipantReturnsOnCall = make(map[int]struct { + result1 *livekit.ParticipantInfo + result2 error + }) + } + fake.loadParticipantReturnsOnCall[i] = struct { + result1 *livekit.ParticipantInfo + result2 error + }{result1, result2} +} + +func (fake *FakeRORoomStore) LoadRoom(arg1 context.Context, arg2 string) (*livekit.Room, error) { + fake.loadRoomMutex.Lock() + ret, specificReturn := fake.loadRoomReturnsOnCall[len(fake.loadRoomArgsForCall)] + fake.loadRoomArgsForCall = append(fake.loadRoomArgsForCall, struct { + arg1 context.Context + arg2 string + }{arg1, arg2}) + stub := fake.LoadRoomStub + fakeReturns := fake.loadRoomReturns + fake.recordInvocation("LoadRoom", []interface{}{arg1, arg2}) + fake.loadRoomMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeRORoomStore) LoadRoomCallCount() int { + fake.loadRoomMutex.RLock() + defer fake.loadRoomMutex.RUnlock() + return len(fake.loadRoomArgsForCall) +} + +func (fake *FakeRORoomStore) LoadRoomCalls(stub func(context.Context, string) (*livekit.Room, error)) { + fake.loadRoomMutex.Lock() + defer fake.loadRoomMutex.Unlock() + fake.LoadRoomStub = stub +} + +func (fake *FakeRORoomStore) LoadRoomArgsForCall(i int) (context.Context, string) { + fake.loadRoomMutex.RLock() + defer fake.loadRoomMutex.RUnlock() + argsForCall := fake.loadRoomArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeRORoomStore) LoadRoomReturns(result1 *livekit.Room, result2 error) { + fake.loadRoomMutex.Lock() + defer fake.loadRoomMutex.Unlock() + fake.LoadRoomStub = nil + fake.loadRoomReturns = struct { + result1 *livekit.Room + result2 error + }{result1, result2} +} + +func (fake *FakeRORoomStore) LoadRoomReturnsOnCall(i int, result1 *livekit.Room, result2 error) { + fake.loadRoomMutex.Lock() + defer fake.loadRoomMutex.Unlock() + fake.LoadRoomStub = nil + if fake.loadRoomReturnsOnCall == nil { + fake.loadRoomReturnsOnCall = make(map[int]struct { + result1 *livekit.Room + result2 error + }) + } + fake.loadRoomReturnsOnCall[i] = struct { + result1 *livekit.Room + result2 error + }{result1, result2} +} + +func (fake *FakeRORoomStore) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.listParticipantsMutex.RLock() + defer fake.listParticipantsMutex.RUnlock() + fake.listRoomsMutex.RLock() + defer fake.listRoomsMutex.RUnlock() + fake.loadParticipantMutex.RLock() + defer fake.loadParticipantMutex.RUnlock() + fake.loadRoomMutex.RLock() + defer fake.loadRoomMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeRORoomStore) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ service.RORoomStore = new(FakeRORoomStore) diff --git a/pkg/service/wire.go b/pkg/service/wire.go index 46249a15a..431f3a315 100644 --- a/pkg/service/wire.go +++ b/pkg/service/wire.go @@ -10,6 +10,7 @@ import ( "github.com/go-redis/redis/v8" "github.com/google/wire" + livekit "github.com/livekit/protocol/proto" "github.com/pkg/errors" "github.com/livekit/protocol/auth" @@ -32,6 +33,7 @@ func InitializeServer(conf *config.Config, currentNode routing.LocalNode) (*Live createWebhookNotifier, routing.CreateRouter, wire.Bind(new(routing.MessageRouter), new(routing.Router)), + wire.Bind(new(livekit.RoomService), new(*RoomService)), telemetry.NewAnalyticsService, telemetry.NewTelemetryService, NewRecordingService,