Add custom campaign roll composer

This commit is contained in:
2026-04-04 19:58:00 +02:00
parent 7248b60395
commit 9e6e6fe8c7
21 changed files with 502 additions and 47 deletions

View File

@@ -225,4 +225,43 @@ public sealed class ServiceSkillRollTests
Assert.False(outsiderPublicDetail.Succeeded);
Assert.Equal("roll_not_found", outsiderPublicDetail.Error!.Code);
}
[Fact]
public void CustomRoll_UsesCampaignRuleset_AndAppearsAsCustomRollInLog()
{
using var harness = ServiceTestSupport.CreateHarness(20);
var service = harness.Service;
service.Register("gm-custom", "Password123", "GM");
service.Register("owner-custom", "Password123", "Owner");
service.Register("other-custom", "Password123", "Other");
var gmSession = ServiceTestSupport.GetValue(service.Login("gm-custom", "Password123")).SessionToken;
var ownerSession = ServiceTestSupport.GetValue(service.Login("owner-custom", "Password123")).SessionToken;
var otherSession = ServiceTestSupport.GetValue(service.Login("other-custom", "Password123")).SessionToken;
var campaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Custom", "dnd5e"));
var character = ServiceTestSupport.GetValue(service.CreateCharacter(ownerSession, "Hero", campaign.Id));
var invalidExpression = service.RollCustom(ownerSession, character.Id, "bad", "public");
Assert.False(invalidExpression.Succeeded);
Assert.Equal("invalid_expression", invalidExpression.Error!.Code);
var forbiddenRoll = service.RollCustom(otherSession, character.Id, "1d20+5", "public");
Assert.False(forbiddenRoll.Succeeded);
Assert.Equal("forbidden", forbiddenRoll.Error!.Code);
var customRoll = ServiceTestSupport.GetValue(service.RollCustom(ownerSession, character.Id, "1d20+5", "private"));
Assert.Equal(Guid.Empty, customRoll.SkillId);
Assert.StartsWith("1d20+5 => ", customRoll.Breakdown, StringComparison.Ordinal);
var logPage = ServiceTestSupport.GetValue(service.GetCampaignLogPage(gmSession, campaign.Id, limit: 5));
var entry = Assert.Single(logPage.Entries);
Assert.Equal("Custom roll", entry.SkillName);
Assert.Equal("Private (GM view)", entry.VisibilityLabel);
Assert.Contains("n20", Assert.IsType<string[]>(entry.EventBadges));
var log = ServiceTestSupport.GetValue(service.GetCampaignLog(ownerSession, campaign.Id));
Assert.Equal("Custom roll", Assert.Single(log).SkillName);
}
}

View File

@@ -50,6 +50,7 @@ public sealed class WorkspaceQueryServiceTests
Assert.Equal(401, exception.StatusCode);
Assert.Equal("You must be logged in.", exception.Message);
Assert.Equal("unauthorized", exception.ErrorCode);
}
private static WorkspaceSessionTokenAccessor CreateSessionTokenAccessor(string sessionToken)
@@ -95,6 +96,7 @@ public sealed class WorkspaceQueryServiceTests
public ServiceResult<bool> DeleteSkill(string sessionToken, Guid skillId) => throw new NotSupportedException();
public ServiceResult<CharacterSheet> GetCharacterSheet(string sessionToken, Guid characterId) => throw new NotSupportedException();
public ServiceResult<RollResult> RollSkill(string sessionToken, Guid skillId, string visibility) => throw new NotSupportedException();
public ServiceResult<RollResult> RollCustom(string sessionToken, Guid characterId, string expression, string visibility) => throw new NotSupportedException();
public ServiceResult<IReadOnlyList<CampaignLogEntry>> GetCampaignLog(string sessionToken, Guid campaignId) => throw new NotSupportedException();
public ServiceResult<CampaignLogPage> GetCampaignLogPage(string sessionToken, Guid campaignId, Guid? afterRollId = null, int? limit = null) => throw new NotSupportedException();
public ServiceResult<CampaignRollDetail> GetRollDetail(string sessionToken, Guid rollId) => throw new NotSupportedException();