using GmRelay.Web.Services.Portfolio; namespace GmRelay.Bot.Tests.Web; public sealed class PortfolioContractsTests { [Fact] public void PublicPortfolioCard_ShouldExposeOnlySanitizedPublicProperties() { AssertNoForbiddenPropertyNames(); } [Fact] public void PublicPortfolioGame_ShouldExposeOnlySanitizedPublicProperties() { AssertNoForbiddenPropertyNames(); } [Fact] public void PublicPortfolioMaster_ShouldExposeOnlySanitizedPublicProperties() { AssertNoForbiddenPropertyNames(); } [Fact] public void PublicPortfolioReview_ShouldExposeOnlySanitizedPublicProperties() { AssertNoForbiddenPropertyNames(); } [Fact] public void PublicPortfolioCard_ShouldExposeExpectedProperties() { var names = typeof(PublicPortfolioCard).GetProperties().Select(p => p.Name).ToArray(); Assert.Contains("Slug", names); Assert.Contains("Title", names); Assert.Contains("CoverPath", names); Assert.Contains("System", names); Assert.Contains("Format", names); Assert.Contains("CompletedAt", names); } [Fact] public void PublicPortfolioGame_ShouldExposeExpectedProperties() { var names = typeof(PublicPortfolioGame).GetProperties().Select(p => p.Name).ToArray(); Assert.Contains("Slug", names); Assert.Contains("Title", names); Assert.Contains("Description", names); Assert.Contains("CoverPath", names); Assert.Contains("System", names); Assert.Contains("Format", names); Assert.Contains("CompletedAt", names); Assert.Contains("ClubName", names); Assert.Contains("ClubSlug", names); Assert.Contains("Masters", names); Assert.Contains("Reviews", names); } [Fact] public void PublicPortfolioMaster_ShouldExposeExpectedProperties() { var names = typeof(PublicPortfolioMaster).GetProperties().Select(p => p.Name).ToArray(); Assert.Contains("Slug", names); Assert.Contains("DisplayName", names); } [Fact] public void PublicPortfolioReview_ShouldExposeExpectedProperties() { var names = typeof(PublicPortfolioReview).GetProperties().Select(p => p.Name).ToArray(); Assert.Contains("AuthorDisplayName", names); Assert.Contains("Body", names); Assert.Contains("CreatedAt", names); } [Fact] public void IPortfolioStore_ShouldExposeAllRequiredMethods() { var interfaceType = typeof(IPortfolioStore); var methodNames = interfaceType.GetMethods().Select(m => m.Name).ToArray(); Assert.Contains("GetPublicPortfolioGamesForMasterAsync", methodNames); Assert.Contains("GetPublicPortfolioGamesForClubAsync", methodNames); Assert.Contains("GetPublicPortfolioGameBySlugAsync", methodNames); Assert.Contains("GetPortfolioGamesForGroupAsync", methodNames); Assert.Contains("GetPortfolioGameGroupIdAsync", methodNames); Assert.Contains("GetPortfolioGameForManagementAsync", methodNames); Assert.Contains("GetEligibleCompletedSessionsAsync", methodNames); Assert.Contains("GetPortfolioMasterOptionsAsync", methodNames); Assert.Contains("CreatePortfolioDraftAsync", methodNames); Assert.Contains("UpdatePortfolioDraftAsync", methodNames); Assert.Contains("SetPortfolioCoverAsync", methodNames); Assert.Contains("DeletePortfolioGameAsync", methodNames); Assert.Contains("SetPortfolioPublicationAsync", methodNames); Assert.Contains("ModeratePortfolioReviewAsync", methodNames); Assert.Contains("GetReviewSubmissionStateAsync", methodNames); Assert.Contains("SubmitPortfolioReviewAsync", methodNames); } private static void AssertNoForbiddenPropertyNames() { var forbidden = new[] { "Id", "External", "Telegram", "Discord", "Moderator", "StorageKey", "PhysicalPath", "JoinLink", "Session" }; var names = typeof(T).GetProperties().Select(p => p.Name).ToArray(); foreach (var forbiddenFragment in forbidden) { Assert.DoesNotContain(names, name => name.Contains(forbiddenFragment, StringComparison.Ordinal)); } } }