using Socialize.Api.Modules.Feedback.Data; using Socialize.Api.Modules.Feedback.Services; namespace Socialize.Tests.Feedback; public class FeedbackRulesTests { [Theory] [InlineData("Bug", FeedbackType.Bug)] [InlineData("suggestion", FeedbackType.Suggestion)] [InlineData("Request", FeedbackType.Request)] public void TryParseType_accepts_supported_types(string value, FeedbackType expected) { bool parsed = FeedbackRules.TryParseType(value, out FeedbackType type); Assert.True(parsed); Assert.Equal(expected, type); } [Theory] [InlineData("")] [InlineData("Question")] [InlineData("Incident")] public void TryParseType_rejects_unsupported_types(string value) { bool parsed = FeedbackRules.TryParseType(value, out _); Assert.False(parsed); } [Theory] [InlineData("New", FeedbackStatus.New)] [InlineData("Planned", FeedbackStatus.Planned)] [InlineData("Resolved", FeedbackStatus.Resolved)] [InlineData("Won't Do", FeedbackStatus.WontDo)] [InlineData("WontDo", FeedbackStatus.WontDo)] [InlineData("Cancelled", FeedbackStatus.Cancelled)] public void TryParseStatus_accepts_supported_statuses(string value, FeedbackStatus expected) { bool parsed = FeedbackRules.TryParseStatus(value, out FeedbackStatus status); Assert.True(parsed); Assert.Equal(expected, status); } [Fact] public void CanDeveloperSetStatus_rejects_cancelled_destination() { bool allowed = FeedbackRules.CanDeveloperSetStatus(FeedbackStatus.New, FeedbackStatus.Cancelled); Assert.False(allowed); } [Fact] public void CanDeveloperSetStatus_rejects_changes_after_cancelled() { bool allowed = FeedbackRules.CanDeveloperSetStatus(FeedbackStatus.Cancelled, FeedbackStatus.Planned); Assert.False(allowed); } [Fact] public void CanReporterCancel_rejects_cancelled_report() { bool allowed = FeedbackRules.CanReporterCancel(FeedbackStatus.Cancelled); Assert.False(allowed); } [Fact] public void CanReporterAccess_allows_report_owner() { Guid reporterUserId = Guid.NewGuid(); FeedbackReport report = new() { ReporterUserId = reporterUserId }; bool allowed = FeedbackAccessRules.CanReporterAccess(report, reporterUserId); Assert.True(allowed); } [Fact] public void CanReporterAccess_rejects_other_users() { FeedbackReport report = new() { ReporterUserId = Guid.NewGuid() }; bool allowed = FeedbackAccessRules.CanReporterAccess(report, Guid.NewGuid()); Assert.False(allowed); } [Fact] public void CanReporterCancel_requires_owner_and_non_final_status() { Guid reporterUserId = Guid.NewGuid(); FeedbackReport report = new() { ReporterUserId = reporterUserId, Status = FeedbackStatus.New, }; bool ownerAllowed = FeedbackAccessRules.CanReporterCancel(report, reporterUserId); bool otherUserAllowed = FeedbackAccessRules.CanReporterCancel(report, Guid.NewGuid()); Assert.True(ownerAllowed); Assert.False(otherUserAllowed); } [Fact] public void CanAccessScreenshot_allows_report_owner() { Guid reporterUserId = Guid.NewGuid(); FeedbackReport report = new() { ReporterUserId = reporterUserId }; bool allowed = FeedbackAccessRules.CanAccessScreenshot(report, reporterUserId, isDeveloper: false); Assert.True(allowed); } [Fact] public void CanAccessScreenshot_allows_developer() { FeedbackReport report = new() { ReporterUserId = Guid.NewGuid() }; bool allowed = FeedbackAccessRules.CanAccessScreenshot(report, Guid.NewGuid(), isDeveloper: true); Assert.True(allowed); } [Fact] public void CanAccessScreenshot_rejects_unrelated_non_developer() { FeedbackReport report = new() { ReporterUserId = Guid.NewGuid() }; bool allowed = FeedbackAccessRules.CanAccessScreenshot(report, Guid.NewGuid(), isDeveloper: false); Assert.False(allowed); } [Theory] [InlineData("image/png")] [InlineData("image/jpeg")] [InlineData("image/jpg")] public void Screenshot_content_type_allows_png_and_jpeg(string contentType) { bool allowed = FeedbackScreenshotRules.IsAllowedContentType(contentType); Assert.True(allowed); } [Theory] [InlineData("text/html")] [InlineData("application/pdf")] [InlineData("")] public void Screenshot_content_type_rejects_non_images(string contentType) { bool allowed = FeedbackScreenshotRules.IsAllowedContentType(contentType); Assert.False(allowed); } [Theory] [InlineData(1)] [InlineData(FeedbackScreenshotRules.MaxScreenshotBytes)] public void Screenshot_size_allows_non_empty_files_up_to_limit(long sizeBytes) { bool allowed = FeedbackScreenshotRules.IsAllowedSize(sizeBytes); Assert.True(allowed); } [Theory] [InlineData(0)] [InlineData(FeedbackScreenshotRules.MaxScreenshotBytes + 1)] public void Screenshot_size_rejects_empty_and_oversized_files(long sizeBytes) { bool allowed = FeedbackScreenshotRules.IsAllowedSize(sizeBytes); Assert.False(allowed); } [Fact] public void NormalizeTags_trims_deduplicates_and_orders() { IReadOnlyCollection tags = FeedbackRules.NormalizeTags([" mobile ", "bug", "Mobile", ""]); Assert.Equal(["bug", "mobile"], tags); } }