using Microsoft.AspNetCore.Http; using RpgRoller.Components; using RpgRoller.Contracts; using RpgRoller.Services; namespace RpgRoller.Tests; public sealed class WorkspaceQueryServiceTests { [Fact] public void SessionTokenAccessor_ReadsSessionCookieFromHttpContext() { var httpContext = new DefaultHttpContext(); httpContext.Request.Headers.Cookie = "rpgroller_session=session-token"; var accessor = new HttpContextAccessor { HttpContext = httpContext }; var sessionTokenAccessor = new WorkspaceSessionTokenAccessor(accessor); Assert.Equal("session-token", sessionTokenAccessor.GetRequiredSessionToken()); } [Fact] public async Task GetCampaignsAsync_UsesCapturedSessionToken() { var service = new StubGameService { GetCampaignsHandler = sessionToken => { Assert.Equal("server-session", sessionToken); return ServiceResult>.Success([new CampaignSummary(Guid.NewGuid(), "Alpha", "d6", new CampaignGmSummary(Guid.NewGuid(), "GM"), 1)]); } }; var queryService = new WorkspaceQueryService(service, CreateSessionTokenAccessor("server-session")); var campaigns = await queryService.GetCampaignsAsync(); Assert.Single(campaigns); } [Fact] public async Task GetMeAsync_MapsUnauthorizedServiceResultToApiRequestException() { var service = new StubGameService { GetMeHandler = _ => ServiceResult.Failure("unauthorized", "You must be logged in.") }; var queryService = new WorkspaceQueryService(service, CreateSessionTokenAccessor("expired-session")); var exception = await Assert.ThrowsAsync(queryService.GetMeAsync); Assert.Equal(401, exception.StatusCode); Assert.Equal("You must be logged in.", exception.Message); } private static WorkspaceSessionTokenAccessor CreateSessionTokenAccessor(string sessionToken) { var httpContext = new DefaultHttpContext(); httpContext.Request.Headers.Cookie = $"rpgroller_session={sessionToken}"; return new WorkspaceSessionTokenAccessor(new HttpContextAccessor { HttpContext = httpContext }); } private sealed class StubGameService : IGameService { public Func> GetMeHandler { get; init; } = _ => ServiceResult.Failure("unexpected_call", "Unexpected GetMe call."); public Func>> GetCampaignsHandler { get; init; } = _ => ServiceResult>.Failure("unexpected_call", "Unexpected GetCampaigns call."); public IReadOnlyList GetRulesets() => throw new NotSupportedException(); public ServiceResult Register(string username, string password, string displayName) => throw new NotSupportedException(); public ServiceResult<(UserSummary User, string SessionToken)> Login(string username, string password) => throw new NotSupportedException(); public void Logout(string sessionToken) => throw new NotSupportedException(); public UserSummary? GetUserBySession(string sessionToken) => throw new NotSupportedException(); public ServiceResult GetMe(string sessionToken) => GetMeHandler(sessionToken); public ServiceResult CreateCampaign(string sessionToken, string name, string rulesetId) => throw new NotSupportedException(); public ServiceResult> GetCampaigns(string sessionToken) => GetCampaignsHandler(sessionToken); public ServiceResult> GetCharacterCampaignOptions(string sessionToken) => throw new NotSupportedException(); public ServiceResult GetCampaign(string sessionToken, Guid campaignId) => throw new NotSupportedException(); public ServiceResult DeleteCampaign(string sessionToken, Guid campaignId) => throw new NotSupportedException(); public ServiceResult> GetUsernames(string sessionToken) => throw new NotSupportedException(); public ServiceResult> GetUsers(string sessionToken) => throw new NotSupportedException(); public ServiceResult UpdateUserRoles(string sessionToken, Guid userId, IReadOnlyList roles) => throw new NotSupportedException(); public ServiceResult DeleteUser(string sessionToken, Guid userId) => throw new NotSupportedException(); public ServiceResult CreateCharacter(string sessionToken, string name, Guid campaignId) => throw new NotSupportedException(); public ServiceResult UpdateCharacter(string sessionToken, Guid characterId, string name, Guid? campaignId, string? ownerUsername = null) => throw new NotSupportedException(); public ServiceResult DeleteCharacter(string sessionToken, Guid characterId) => throw new NotSupportedException(); public ServiceResult ActivateCharacter(string sessionToken, Guid characterId) => throw new NotSupportedException(); public ServiceResult> GetOwnCharacters(string sessionToken) => throw new NotSupportedException(); public ServiceResult CreateSkillGroup(string sessionToken, Guid characterId, string name, string diceRollDefinition, int wildDice, bool allowFumble) => throw new NotSupportedException(); public ServiceResult UpdateSkillGroup(string sessionToken, Guid skillGroupId, string name, string diceRollDefinition, int wildDice, bool allowFumble) => throw new NotSupportedException(); public ServiceResult DeleteSkillGroup(string sessionToken, Guid skillGroupId) => throw new NotSupportedException(); public ServiceResult CreateSkill(string sessionToken, Guid characterId, string name, string diceRollDefinition, int wildDice, bool allowFumble, Guid? skillGroupId = null) => throw new NotSupportedException(); public ServiceResult UpdateSkill(string sessionToken, Guid skillId, string name, string diceRollDefinition, int wildDice, bool allowFumble, Guid? skillGroupId = null) => throw new NotSupportedException(); public ServiceResult DeleteSkill(string sessionToken, Guid skillId) => throw new NotSupportedException(); public ServiceResult GetCharacterSheet(string sessionToken, Guid characterId) => throw new NotSupportedException(); public ServiceResult RollSkill(string sessionToken, Guid skillId, string visibility) => throw new NotSupportedException(); public ServiceResult> GetCampaignLog(string sessionToken, Guid campaignId) => throw new NotSupportedException(); public ServiceResult GetCampaignLogPage(string sessionToken, Guid campaignId, Guid? afterRollId = null, int? limit = null) => throw new NotSupportedException(); public ServiceResult GetRollDetail(string sessionToken, Guid rollId) => throw new NotSupportedException(); public ServiceResult GetCampaignStateSnapshot(string sessionToken, Guid campaignId) => throw new NotSupportedException(); } }