feat(web): dashboard session deletion and E2E coverage for issue #150
- Add DeleteSessionAsync to ISessionStore/SessionService (unpublish portfolio card, remove bot-created empty forum topic, update batch message). - Add DeleteSessionForCurrentUserAsync to AuthorizedSessionService with audit log. - Add delete button + confirmation dialog to GroupDetails.razor. - Extend dashboard Playwright tests with edit persistence and delete verification. - Update AuthorizedSessionServiceTests with delete authorization coverage. - Mark issue #150 as done in tests/e2e/README.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -815,6 +815,7 @@ public sealed class AuthorizedPortfolioServiceTests
|
||||
public Task<WebSession?> GetSessionAsync(Guid sessionId) => throw new NotImplementedException();
|
||||
public Task<WebSessionBatch?> GetBatchAsync(Guid batchId) => throw new NotImplementedException();
|
||||
public Task UpdateSessionAsync(Guid sessionId, Guid groupId, string title, DateTime scheduledAt, string joinLink, int? maxPlayers) => throw new NotImplementedException();
|
||||
public Task<string?> DeleteSessionAsync(Guid sessionId, Guid groupId) => throw new NotImplementedException();
|
||||
public Task PromoteWaitlistedPlayerAsync(Guid sessionId, Guid groupId) => throw new NotImplementedException();
|
||||
public Task UpdateBatchDetailsAsync(Guid batchId, Guid groupId, string title, string joinLink) => throw new NotImplementedException();
|
||||
public Task UpdateBatchNotificationModeAsync(Guid batchId, Guid groupId, SessionNotificationMode notificationMode) => throw new NotImplementedException();
|
||||
|
||||
@@ -765,6 +765,58 @@ public sealed class AuthorizedSessionServiceTests
|
||||
Assert.False(store.CreateBatchFromTemplateCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteSessionForCurrentUserAsync_Deletes_WhenSessionBelongsToManager()
|
||||
{
|
||||
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, 4, 1, 0)
|
||||
]);
|
||||
var accessor = CreateAccessor(gmId.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
var service = new AuthorizedSessionService(store, accessor);
|
||||
|
||||
await service.DeleteSessionForCurrentUserAsync(sessionId);
|
||||
|
||||
Assert.True(store.DeleteCalled);
|
||||
Assert.Equal(sessionId, store.LastDeletedSessionId);
|
||||
Assert.Equal(groupId, store.LastDeletedGroupId);
|
||||
Assert.Null(await store.GetSessionAsync(sessionId));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteSessionForCurrentUserAsync_Throws_WhenSessionDoesNotBelongToManager()
|
||||
{
|
||||
var gmId = 1001L;
|
||||
var otherGmId = 2002L;
|
||||
var groupId = Guid.NewGuid();
|
||||
var otherGroupId = Guid.NewGuid();
|
||||
var sessionId = Guid.NewGuid();
|
||||
var store = new FakeSessionStore(
|
||||
groups:
|
||||
[
|
||||
new(groupId, 42, "Alpha", gmId),
|
||||
new(otherGroupId, 43, "Beta", otherGmId)
|
||||
],
|
||||
sessions:
|
||||
[
|
||||
new(sessionId, otherGroupId, "Session B", DateTime.UtcNow, "Planned", "https://example.test/b", Guid.NewGuid(), 10, 43, 4, 1, 0)
|
||||
]);
|
||||
var accessor = CreateAccessor(gmId.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
var service = new AuthorizedSessionService(store, accessor);
|
||||
|
||||
await Assert.ThrowsAsync<SessionAccessDeniedException>(
|
||||
() => service.DeleteSessionForCurrentUserAsync(sessionId));
|
||||
Assert.False(store.DeleteCalled);
|
||||
}
|
||||
|
||||
private sealed class FakeSessionStore(
|
||||
IEnumerable<WebGameGroup>? groups = null,
|
||||
IEnumerable<WebSession>? sessions = null,
|
||||
@@ -796,6 +848,9 @@ public sealed class AuthorizedSessionServiceTests
|
||||
public DateTime? LastUpdatedScheduledAt { get; private set; }
|
||||
public string? LastUpdatedJoinLink { get; private set; }
|
||||
public int? LastUpdatedMaxPlayers { get; private set; }
|
||||
public bool DeleteCalled { get; private set; }
|
||||
public Guid? LastDeletedSessionId { get; private set; }
|
||||
public Guid? LastDeletedGroupId { get; private set; }
|
||||
public Guid? LastPromotedSessionId { get; private set; }
|
||||
public Guid? LastPromotedGroupId { get; private set; }
|
||||
public Guid? LastUpdatedBatchId { get; private set; }
|
||||
@@ -1036,6 +1091,15 @@ public sealed class AuthorizedSessionServiceTests
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<string?> DeleteSessionAsync(Guid sessionId, Guid groupId)
|
||||
{
|
||||
DeleteCalled = true;
|
||||
LastDeletedSessionId = sessionId;
|
||||
LastDeletedGroupId = groupId;
|
||||
sessionsById.Remove(sessionId);
|
||||
return Task.FromResult<string?>("Deleted session");
|
||||
}
|
||||
|
||||
public Task PromoteWaitlistedPlayerAsync(Guid sessionId, Guid groupId)
|
||||
{
|
||||
PromoteCalled = true;
|
||||
|
||||
Reference in New Issue
Block a user