From fee137aee67ad9229730512d1ff56f36c68e95eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ty=C5=84ski?= Date: Sun, 4 Oct 2026 02:22:05 +0200 Subject: [PATCH] test(backend): add submission workflow, keyword and controller tests --- .../Application/KeywordExtractorTests.cs | 46 +++++++ .../Controllers/SubmissionsControllerTests.cs | 119 ++++++++++++++++++ .../Domain/SubmissionWorkflowTests.cs | 35 ++++++ 3 files changed, 200 insertions(+) create mode 100644 tests/TarninaHub.Tests/Application/KeywordExtractorTests.cs create mode 100644 tests/TarninaHub.Tests/Controllers/SubmissionsControllerTests.cs create mode 100644 tests/TarninaHub.Tests/Domain/SubmissionWorkflowTests.cs diff --git a/tests/TarninaHub.Tests/Application/KeywordExtractorTests.cs b/tests/TarninaHub.Tests/Application/KeywordExtractorTests.cs new file mode 100644 index 0000000..4ea521e --- /dev/null +++ b/tests/TarninaHub.Tests/Application/KeywordExtractorTests.cs @@ -0,0 +1,46 @@ +using TarninaHub.Application.Mappings; +using TarninaHub.Application.Text; +using TarninaHub.Domain.Enums; + +namespace TarninaHub.Tests.Application; + +public class KeywordExtractorTests +{ + [Fact] + public void Extract_RemovesStopWordsAndShortTokens() + { + var keywords = KeywordExtractor.Extract("To jest problem dla seniorów i seniorów w gminie"); + + Assert.Contains("seniorów", keywords); + Assert.Contains("problem", keywords); + Assert.DoesNotContain("jest", keywords); + Assert.DoesNotContain("dla", keywords); + Assert.DoesNotContain("i", keywords); + } + + [Fact] + public void Extract_RespectsMaxAndOrdersByFrequency() + { + var keywords = KeywordExtractor.Extract("transport transport transport zdrowie zdrowie rodzina", max: 2); + + Assert.Equal(2, keywords.Count); + Assert.Equal("transport", keywords[0]); + Assert.Equal("zdrowie", keywords[1]); + } + + [Fact] + public void Extract_ReturnsEmptyForBlankText() + { + Assert.Empty(KeywordExtractor.Extract(" ")); + } + + [Theory] + [InlineData(SubmissionStatus.New, "new")] + [InlineData(SubmissionStatus.InProgress, "in_progress")] + [InlineData(SubmissionStatus.Rejected, "rejected")] + public void StatusWire_RoundTrips(SubmissionStatus status, string wire) + { + Assert.Equal(wire, status.ToWire()); + Assert.Equal(status, SubmissionStatusWire.Parse(wire)); + } +} diff --git a/tests/TarninaHub.Tests/Controllers/SubmissionsControllerTests.cs b/tests/TarninaHub.Tests/Controllers/SubmissionsControllerTests.cs new file mode 100644 index 0000000..0eac884 --- /dev/null +++ b/tests/TarninaHub.Tests/Controllers/SubmissionsControllerTests.cs @@ -0,0 +1,119 @@ +using System.Security.Claims; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using TarninaHub.Application.Abstractions; +using TarninaHub.Application.Dtos; +using TarninaHub.Controllers; + +namespace TarninaHub.Tests.Controllers; + +public class SubmissionsControllerTests +{ + private static SubmissionsController CreateController(ISubmissionService service, string role = "operator") + { + var controller = new SubmissionsController(service); + var identity = new ClaimsIdentity( + [new Claim("sub", "user:tester"), new Claim("role", role)], + authenticationType: "test"); + + controller.ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal(identity) }, + }; + + return controller; + } + + private static SubmissionResponse SampleSubmission(string status) => new( + "submission:abc", + "problem:xyz", + "Title", + "Description", + "social_challenge:seniorzy", + "location:krakow", + "user:tester", + status, + ["seniorzy"], + DateTimeOffset.UtcNow, + null); + + [Fact] + public async Task UpdateStatus_RejectsInvalidTransition() + { + var service = new StubSubmissionService { Current = SampleSubmission("new") }; + var controller = CreateController(service); + + var result = await controller.UpdateStatus( + "submission:abc", + new UpdateSubmissionStatusRequest("matched"), + CancellationToken.None); + + Assert.IsType(result.Result); + Assert.Null(service.LastRequestedStatus); + } + + [Fact] + public async Task UpdateStatus_AcceptsValidTransition() + { + var service = new StubSubmissionService + { + Current = SampleSubmission("new"), + UpdateResult = SampleSubmission("triaged"), + }; + var controller = CreateController(service); + + var result = await controller.UpdateStatus( + "submission:abc", + new UpdateSubmissionStatusRequest("triaged"), + CancellationToken.None); + + Assert.IsType(result.Result); + Assert.Equal("triaged", service.LastRequestedStatus); + } + + [Fact] + public async Task GetById_ReturnsNotFoundWhenMissing() + { + var controller = CreateController(new StubSubmissionService()); + + var result = await controller.GetById("submission:missing", CancellationToken.None); + + Assert.IsType(result.Result); + } + + private sealed class StubSubmissionService : ISubmissionService + { + public SubmissionResponse? Current { get; set; } + + public SubmissionResponse? UpdateResult { get; set; } + + public string? LastRequestedStatus { get; private set; } + + public Task CreateAsync( + string authorId, + CreateSubmissionRequest request, + CancellationToken cancellationToken) => Task.FromResult(SampleSubmission("new")); + + public Task> ListAsync( + string viewerId, + bool isStaff, + string? status, + int page, + int pageSize, + CancellationToken cancellationToken) => Task.FromResult( + new PagedResponse([], page, pageSize, 0)); + + public Task GetAsync(string submissionId, CancellationToken cancellationToken) => + Task.FromResult(Current); + + public Task UpdateStatusAsync( + string submissionId, + string newStatus, + CancellationToken cancellationToken) + { + LastRequestedStatus = newStatus; + + return Task.FromResult(UpdateResult); + } + } +} diff --git a/tests/TarninaHub.Tests/Domain/SubmissionWorkflowTests.cs b/tests/TarninaHub.Tests/Domain/SubmissionWorkflowTests.cs new file mode 100644 index 0000000..246995b --- /dev/null +++ b/tests/TarninaHub.Tests/Domain/SubmissionWorkflowTests.cs @@ -0,0 +1,35 @@ +using TarninaHub.Domain.Enums; +using TarninaHub.Domain.Rules; + +namespace TarninaHub.Tests.Domain; + +public class SubmissionWorkflowTests +{ + [Theory] + [InlineData(SubmissionStatus.New, SubmissionStatus.Triaged, true)] + [InlineData(SubmissionStatus.New, SubmissionStatus.Matched, false)] + [InlineData(SubmissionStatus.Triaged, SubmissionStatus.Matched, true)] + [InlineData(SubmissionStatus.Matched, SubmissionStatus.InProgress, true)] + [InlineData(SubmissionStatus.InProgress, SubmissionStatus.Closed, true)] + [InlineData(SubmissionStatus.Closed, SubmissionStatus.Triaged, false)] + [InlineData(SubmissionStatus.Rejected, SubmissionStatus.New, false)] + public void CanTransition_EnforcesWorkflow(SubmissionStatus from, SubmissionStatus to, bool expected) + { + Assert.Equal(expected, SubmissionWorkflow.CanTransition(from, to)); + } + + [Fact] + public void CanTransition_AllowsSameStatus() + { + Assert.True(SubmissionWorkflow.CanTransition(SubmissionStatus.New, SubmissionStatus.New)); + } + + [Theory] + [InlineData(SubmissionStatus.Closed, true)] + [InlineData(SubmissionStatus.Rejected, true)] + [InlineData(SubmissionStatus.New, false)] + public void IsTerminal_RecognizesFinalStates(SubmissionStatus status, bool expected) + { + Assert.Equal(expected, SubmissionWorkflow.IsTerminal(status)); + } +}