using GmRelay.Web.Services; namespace GmRelay.Bot.Tests.Web; public sealed class AuthorizedSessionServiceTests { [Fact] public async Task GetUpcomingSessionsForGmAsync_ReturnsSessions_WhenGroupBelongsToGm() { var gmId = 1001L; var groupId = Guid.NewGuid(); var store = new FakeSessionStore( groups: [ new(groupId, 42, "Alpha", gmId) ], sessions: [ new(Guid.NewGuid(), groupId, "Session A", DateTime.UtcNow, "Planned", "https://example.test/a", Guid.NewGuid(), 10, 42) ]); var service = new AuthorizedSessionService(store); var sessions = await service.GetUpcomingSessionsForGmAsync(groupId, gmId); Assert.NotNull(sessions); Assert.Single(sessions); Assert.Equal("Session A", sessions[0].Title); } [Fact] public async Task GetUpcomingSessionsForGmAsync_ReturnsNull_WhenGroupBelongsToAnotherGm() { var groupId = Guid.NewGuid(); var store = new FakeSessionStore( groups: [ new(groupId, 42, "Alpha", 2002L) ]); var service = new AuthorizedSessionService(store); var sessions = await service.GetUpcomingSessionsForGmAsync(groupId, 1001L); Assert.Null(sessions); } [Fact] public async Task GetSessionForGmAsync_ReturnsSession_WhenSessionBelongsToOwnedGroup() { var gmId = 1001L; var groupId = Guid.NewGuid(); var sessionId = Guid.NewGuid(); var store = new FakeSessionStore( groups: [ new(groupId, 42, "Alpha", gmId) ], sessions: [ new(sessionId, groupId, "Session A", DateTime.UtcNow, "Planned", "https://example.test/a", Guid.NewGuid(), 10, 42) ]); var service = new AuthorizedSessionService(store); var session = await service.GetSessionForGmAsync(sessionId, gmId); Assert.NotNull(session); Assert.Equal(sessionId, session.Id); } [Fact] public async Task GetSessionForGmAsync_ReturnsNull_WhenSessionBelongsToAnotherGm() { var groupId = Guid.NewGuid(); var sessionId = Guid.NewGuid(); var store = new FakeSessionStore( groups: [ new(groupId, 42, "Alpha", 2002L) ], sessions: [ new(sessionId, groupId, "Session A", DateTime.UtcNow, "Planned", "https://example.test/a", Guid.NewGuid(), 10, 42) ]); var service = new AuthorizedSessionService(store); var session = await service.GetSessionForGmAsync(sessionId, 1001L); Assert.Null(session); } [Fact] public async Task UpdateSessionForGmAsync_Throws_WhenSessionBelongsToAnotherGm() { var groupId = Guid.NewGuid(); var sessionId = Guid.NewGuid(); var store = new FakeSessionStore( groups: [ new(groupId, 42, "Alpha", 2002L) ], sessions: [ new(sessionId, groupId, "Session A", DateTime.UtcNow, "Planned", "https://example.test/a", Guid.NewGuid(), 10, 42) ]); var service = new AuthorizedSessionService(store); var action = () => service.UpdateSessionForGmAsync(sessionId, 1001L, "Updated", DateTime.UtcNow.AddDays(1), "https://example.test/b"); await Assert.ThrowsAsync(action); Assert.False(store.UpdateCalled); } [Fact] public async Task UpdateSessionForGmAsync_UpdatesOwnedSession() { var gmId = 1001L; var groupId = Guid.NewGuid(); var sessionId = Guid.NewGuid(); var scheduledAt = DateTime.UtcNow.AddDays(1); var store = new FakeSessionStore( groups: [ new(groupId, 42, "Alpha", gmId) ], sessions: [ new(sessionId, groupId, "Session A", DateTime.UtcNow, "Planned", "https://example.test/a", Guid.NewGuid(), 10, 42) ]); var service = new AuthorizedSessionService(store); await service.UpdateSessionForGmAsync(sessionId, gmId, "Updated", scheduledAt, "https://example.test/b"); Assert.True(store.UpdateCalled); Assert.Equal(groupId, store.LastUpdatedGroupId); Assert.Equal(sessionId, store.LastUpdatedSessionId); Assert.Equal("Updated", store.LastUpdatedTitle); Assert.Equal(scheduledAt, store.LastUpdatedScheduledAt); Assert.Equal("https://example.test/b", store.LastUpdatedJoinLink); } private sealed class FakeSessionStore( IEnumerable? groups = null, IEnumerable? sessions = null) : ISessionStore { private readonly Dictionary groupsById = groups?.ToDictionary(group => group.Id) ?? []; private readonly Dictionary sessionsById = sessions?.ToDictionary(session => session.Id) ?? []; public bool UpdateCalled { get; private set; } public Guid? LastUpdatedSessionId { get; private set; } public Guid? LastUpdatedGroupId { get; private set; } public string? LastUpdatedTitle { get; private set; } public DateTime? LastUpdatedScheduledAt { get; private set; } public string? LastUpdatedJoinLink { get; private set; } public Task> GetGroupsForGmAsync(long gmId) => Task.FromResult(groupsById.Values.Where(group => group.GmTelegramId == gmId).ToList()); public Task GetGroupAsync(Guid groupId) { groupsById.TryGetValue(groupId, out var group); return Task.FromResult(group); } public Task> GetUpcomingSessionsAsync(Guid groupId) => Task.FromResult(sessionsById.Values.Where(session => session.GroupId == groupId).ToList()); public Task GetSessionAsync(Guid sessionId) { sessionsById.TryGetValue(sessionId, out var session); return Task.FromResult(session); } public Task UpdateSessionAsync(Guid sessionId, Guid groupId, string title, DateTime scheduledAt, string joinLink) { UpdateCalled = true; LastUpdatedSessionId = sessionId; LastUpdatedGroupId = groupId; LastUpdatedTitle = title; LastUpdatedScheduledAt = scheduledAt; LastUpdatedJoinLink = joinLink; return Task.CompletedTask; } } }