namespace RpgRoller.Tests; public sealed class ServiceSkillGroupAndOwnershipTests { [Fact] public void SkillGroups_CanBeManagedAndAssignedWithinCharacterScope() { using var harness = ServiceTestSupport.CreateHarness(4, 3, 2); var service = harness.Service; service.Register("gm", "Password123", "GM"); service.Register("owner", "Password123", "Owner"); service.Register("other", "Password123", "Other"); var gmSession = ServiceTestSupport.GetValue(service.Login("gm", "Password123")).SessionToken; var ownerSession = ServiceTestSupport.GetValue(service.Login("owner", "Password123")).SessionToken; var otherSession = ServiceTestSupport.GetValue(service.Login("other", "Password123")).SessionToken; var campaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Main", "d6")); var ownerCharacter = ServiceTestSupport.GetValue(service.CreateCharacter(ownerSession, "Owner Char", campaign.Id)); var otherCharacter = ServiceTestSupport.GetValue(service.CreateCharacter(otherSession, "Other Char", campaign.Id)); var ownerGroup = ServiceTestSupport.GetValue(service.CreateSkillGroup(ownerSession, ownerCharacter.Id, "Combat", "2D+1", 1, true)); Assert.False(service.CreateSkillGroup(ownerSession, otherCharacter.Id, "Not Allowed", "2D+1", 1, true).Succeeded); Assert.False(service.UpdateSkillGroup(otherSession, ownerGroup.Id, "Renamed by Other", "2D+1", 1, true).Succeeded); var renamedGroup = ServiceTestSupport.GetValue(service.UpdateSkillGroup(gmSession, ownerGroup.Id, "Battle", "3D+2", 2, false)); Assert.Equal("Battle", renamedGroup.Name); Assert.Equal("3D+2", renamedGroup.DiceRollDefinition); Assert.Equal(2, renamedGroup.WildDice); Assert.False(renamedGroup.AllowFumble); var skill = ServiceTestSupport.GetValue(service.CreateSkill(ownerSession, ownerCharacter.Id, "Strike", "2D+1", 1, true, renamedGroup.Id)); Assert.Equal(renamedGroup.Id, skill.SkillGroupId); var otherGroup = ServiceTestSupport.GetValue(service.CreateSkillGroup(otherSession, otherCharacter.Id, "Other Group", "2D+1", 1, true)); Assert.False(service.UpdateSkill(ownerSession, skill.Id, "Strike", "2D+1", 1, true, otherGroup.Id).Succeeded); var ungroupedSkill = ServiceTestSupport.GetValue(service.UpdateSkill(ownerSession, skill.Id, "Strike", "2D+1", 1, true, null)); Assert.Null(ungroupedSkill.SkillGroupId); var regroupedSkill = ServiceTestSupport.GetValue(service.UpdateSkill(ownerSession, skill.Id, "Strike", "2D+1", 1, true, renamedGroup.Id)); Assert.Equal(renamedGroup.Id, regroupedSkill.SkillGroupId); var deletedGroup = ServiceTestSupport.GetValue(service.DeleteSkillGroup(ownerSession, renamedGroup.Id)); Assert.True(deletedGroup); var afterGroupDelete = ServiceTestSupport.GetValue(service.GetCampaign(ownerSession, campaign.Id)); Assert.DoesNotContain(afterGroupDelete.SkillGroups, group => group.Id == renamedGroup.Id); Assert.Contains(afterGroupDelete.SkillGroups, group => group.Id == otherGroup.Id); Assert.Null(afterGroupDelete.Skills.Single(s => s.Id == regroupedSkill.Id).SkillGroupId); var deletedSkill = ServiceTestSupport.GetValue(service.DeleteSkill(ownerSession, regroupedSkill.Id)); Assert.True(deletedSkill); var ownerView = ServiceTestSupport.GetValue(service.GetCampaign(ownerSession, campaign.Id)); Assert.DoesNotContain(ownerView.SkillGroups, group => group.Id == renamedGroup.Id); Assert.Contains(ownerView.SkillGroups, group => group.Id == otherGroup.Id); Assert.DoesNotContain(ownerView.Skills, skillSummary => skillSummary.Id == regroupedSkill.Id); } [Fact] public void CharacterOwnerTransfer_RequiresGmPrivileges() { using var harness = ServiceTestSupport.CreateHarness(); var service = harness.Service; service.Register("gm", "Password123", "GM"); service.Register("owner", "Password123", "Owner"); service.Register("receiver", "Password123", "Receiver"); var gmSession = ServiceTestSupport.GetValue(service.Login("gm", "Password123")).SessionToken; var ownerSession = ServiceTestSupport.GetValue(service.Login("owner", "Password123")).SessionToken; var receiverSession = ServiceTestSupport.GetValue(service.Login("receiver", "Password123")).SessionToken; var campaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Main", "d6")); var character = ServiceTestSupport.GetValue(service.CreateCharacter(ownerSession, "Transfer Me", campaign.Id)); Assert.True(service.ActivateCharacter(ownerSession, character.Id).Succeeded); var ownerTransferAttempt = service.UpdateCharacter(ownerSession, character.Id, "Transfer Me", campaign.Id, "receiver"); Assert.False(ownerTransferAttempt.Succeeded); var missingOwnerAttempt = service.UpdateCharacter(gmSession, character.Id, "Transfer Me", campaign.Id, "missing-user"); Assert.False(missingOwnerAttempt.Succeeded); var gmTransfer = ServiceTestSupport.GetValue(service.UpdateCharacter(gmSession, character.Id, "Transferred", campaign.Id, "receiver")); var receiver = service.GetUserBySession(receiverSession); Assert.NotNull(receiver); Assert.Equal(receiver!.Id, gmTransfer.OwnerUserId); var previousOwnerMe = ServiceTestSupport.GetValue(service.GetMe(ownerSession)); Assert.Null(previousOwnerMe.ActiveCharacterId); Assert.False(service.ActivateCharacter(ownerSession, character.Id).Succeeded); Assert.True(service.ActivateCharacter(receiverSession, character.Id).Succeeded); } [Fact] public void CharacterUnlink_AllowsOwnerGmAndAdmin() { using var harness = ServiceTestSupport.CreateHarness(); var service = harness.Service; service.Register("gm", "Password123", "GM"); service.Register("owner", "Password123", "Owner"); service.Register("outsider", "Password123", "Outsider"); service.Register("admin2", "Password123", "Admin Two"); var gmSession = ServiceTestSupport.GetValue(service.Login("gm", "Password123")).SessionToken; var ownerSession = ServiceTestSupport.GetValue(service.Login("owner", "Password123")).SessionToken; var outsiderSession = ServiceTestSupport.GetValue(service.Login("outsider", "Password123")).SessionToken; var adminTwoSession = ServiceTestSupport.GetValue(service.Login("admin2", "Password123")).SessionToken; var campaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Main", "d6")); var character = ServiceTestSupport.GetValue(service.CreateCharacter(ownerSession, "Unlink Me", campaign.Id)); var outsiderUnlink = service.UpdateCharacter(outsiderSession, character.Id, "Unlink Me", null); Assert.False(outsiderUnlink.Succeeded); var ownerUnlink = ServiceTestSupport.GetValue(service.UpdateCharacter(ownerSession, character.Id, "Owner Unlink", null)); Assert.Null(ownerUnlink.CampaignId); var relinkByOwner = ServiceTestSupport.GetValue(service.UpdateCharacter(ownerSession, character.Id, "Relink", campaign.Id)); Assert.Equal(campaign.Id, relinkByOwner.CampaignId); var gmUnlink = ServiceTestSupport.GetValue(service.UpdateCharacter(gmSession, character.Id, "Gm Unlink", null)); Assert.Null(gmUnlink.CampaignId); var relinkByGm = ServiceTestSupport.GetValue(service.UpdateCharacter(gmSession, character.Id, "Relink Again", campaign.Id)); Assert.Equal(campaign.Id, relinkByGm.CampaignId); var adminTwo = service.GetUserBySession(adminTwoSession); Assert.NotNull(adminTwo); _ = ServiceTestSupport.GetValue(service.UpdateUserRoles(gmSession, adminTwo!.Id, [ "admin" ])); var adminUnlink = ServiceTestSupport.GetValue(service.UpdateCharacter(adminTwoSession, character.Id, "Admin Unlink", null)); Assert.Null(adminUnlink.CampaignId); } }