namespace RpgRoller.Tests; public sealed class ServiceHelperExtractionTests { [Fact] public void RoleSerializer_NormalizesParsesAndSerializesRoles() { var normalized = RoleSerializer.Normalize([" Admin ", "gm", "admin", "", "GM"]); var serialized = RoleSerializer.Serialize(normalized); var parsed = RoleSerializer.Parse(" admin,GM,admin "); Assert.Equal(["admin", "gm"], normalized); Assert.Equal("admin,gm", serialized); Assert.Equal(["admin", "gm"], parsed); Assert.True(RoleSerializer.HasRole(serialized, "ADMIN")); Assert.False(RoleSerializer.HasRole(serialized, "owner")); } [Theory] [InlineData("public", RollVisibility.Public)] [InlineData("PRIVATE", RollVisibility.Private)] public void RollVisibilityParser_ParsesKnownValues(string input, RollVisibility expected) { var result = RollVisibilityParser.Parse(input); Assert.True(result.Succeeded); Assert.Equal(expected, result.Value); } [Fact] public void RollVisibilityParser_RejectsUnknownValue() { var result = RollVisibilityParser.Parse("hidden"); Assert.False(result.Succeeded); Assert.Equal("invalid_visibility", result.Error!.Code); } [Fact] public void CustomRollOptionsResolver_ReturnsD6DefaultsOnlyForD6() { Assert.Equal((1, true, (int?)null), CustomRollOptionsResolver.Resolve(RulesetKind.D6)); Assert.Equal((0, false, (int?)null), CustomRollOptionsResolver.Resolve(RulesetKind.Dnd5e)); Assert.Equal((0, false, (int?)null), CustomRollOptionsResolver.Resolve(RulesetKind.Rolemaster)); } [Fact] public void SkillDefinitionValidator_ValidatesRulesetSpecificOptions() { var d6 = SkillDefinitionValidator.Validate(RulesetKind.D6, "2D+1", 1, true, null); var rolemaster = SkillDefinitionValidator.Validate(RulesetKind.Rolemaster, "d100!+15", 0, false, 5, true); var invalidD6 = SkillDefinitionValidator.Validate(RulesetKind.D6, "2D+1", 0, true, null); var invalidRolemaster = SkillDefinitionValidator.Validate(RulesetKind.Rolemaster, "d100!+15", 0, false, null); var invalidRetry = SkillDefinitionValidator.Validate(RulesetKind.Rolemaster, "d100+15", 0, false, null, true); Assert.True(d6.Succeeded); Assert.Equal(("2D+1", 1, true, (int?)null, false), d6.Value); Assert.True(rolemaster.Succeeded); Assert.Equal(("d100!+15", 0, false, (int?)5, true), rolemaster.Value); Assert.False(invalidD6.Succeeded); Assert.Equal("invalid_wild_dice", invalidD6.Error!.Code); Assert.False(invalidRolemaster.Succeeded); Assert.Equal("invalid_fumble_range", invalidRolemaster.Error!.Code); Assert.False(invalidRetry.Succeeded); Assert.Equal("invalid_rolemaster_retry", invalidRetry.Error!.Code); } [Fact] public void RolemasterRetryPolicy_ResolvesRetryBandsAndMarkers() { Assert.Equal(5, RolemasterRetryPolicy.ResolveAutoRetryBonus(77)); Assert.Equal(5, RolemasterRetryPolicy.ResolveAutoRetryBonus(90)); Assert.Equal(10, RolemasterRetryPolicy.ResolveAutoRetryBonus(91)); Assert.Equal(10, RolemasterRetryPolicy.ResolveAutoRetryBonus(110)); Assert.Null(RolemasterRetryPolicy.ResolveAutoRetryBonus(76)); Assert.Null(RolemasterRetryPolicy.ResolveAutoRetryBonus(111)); Assert.Equal(5, RolemasterRetryPolicy.TryExtractRetryBonus("68+10=78; retry(+5): 42+10=52; final=57")); Assert.Equal(10, RolemasterRetryPolicy.TryExtractRetryBonus("90+1=91; retry(+10): 32+1=33; final=43")); Assert.Null(RolemasterRetryPolicy.TryExtractRetryBonus("68+10=78")); } }