diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml index dcbf6097..552fd6ed 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/backend-ci.yml @@ -195,15 +195,38 @@ jobs: comment-identifier: "${{ env.WORKFLOW_SHORT_NAME }}-CodeCoverageSummary" comment-content: ${{ steps.code-coverage-summary.outputs.markdown }} - - name: Inspect code - uses: muno92/resharper_inspectcode@v1 - if: ${{ github.event_name == 'pull_request' }} + - name: Stryker + id: stryker + run: | + dotnet stryker --reporter "html" --reporter "json" --reporter "markdown" --solution Backend.sln --output StrykerOutput + cp -r StrykerOutput/reports StrykerReports + cat StrykerReports/mutation-report.md >> $GITHUB_STEP_SUMMARY + echo "markdown<> $GITHUB_OUTPUT + cat StrykerReports/mutation-report.md >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - uses: actions/upload-artifact@v4 with: - workingDirectory: ${{ env.BACKEND_SOLUTION_PATH }} - solutionPath: Backend.sln - dotnetVersion: ${{ env.DOTNET_VERSION }} - failOnIssue: false - solutionWideAnalysis: true - include: | - **.cs - ignoreIssueType: PropertyCanBeMadeInitOnly.Global + name: StrykerReports + path: ${{ env.BACKEND_SOLUTION_PATH }}/StrykerReports + + - name: "Create or Update PR Comment" + uses: im-open/update-pr-comment@v1.2.2 + if: ${{ always() && github.event_name == 'pull_request' }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + comment-identifier: "${{ env.WORKFLOW_SHORT_NAME }}-Stryker" + comment-content: ${{ steps.stryker.outputs.markdown }} + + # - name: Inspect code + # uses: muno92/resharper_inspectcode@v1 + # if: ${{ github.event_name == 'pull_request' }} + # with: + # workingDirectory: ${{ env.BACKEND_SOLUTION_PATH }} + # solutionPath: Backend.sln + # dotnetVersion: ${{ env.DOTNET_VERSION }} + # failOnIssue: false + # solutionWideAnalysis: true + # include: | + # **.cs + # ignoreIssueType: PropertyCanBeMadeInitOnly.Global diff --git a/tests/backend/WebApi.Tests/Controllers/BloggingControllerTests.cs b/tests/backend/WebApi.Tests/Controllers/BloggingControllerTests.cs index 32f8fd24..18848eda 100644 --- a/tests/backend/WebApi.Tests/Controllers/BloggingControllerTests.cs +++ b/tests/backend/WebApi.Tests/Controllers/BloggingControllerTests.cs @@ -44,6 +44,8 @@ public async Task GetBlogs_ReturnsListOfBlogs() var returnedBlogs = okResult.Value as IEnumerable; Assert.That(returnedBlogs, Is.Not.Null); Assert.That(returnedBlogs.Count(), Is.EqualTo(2)); + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "GetBlogs was called"); + _loggerMock.VerifyNoError(); } [Test] @@ -66,6 +68,9 @@ public async Task GetBlog_ValidId_ReturnsBlog() Assert.That(returnedBlog.Title, Is.EqualTo(MockBlog.Title)); Assert.That(returnedBlog.Url, Is.EqualTo(MockBlog.Url)); }); + + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "GetBlog was called with id 1"); + _loggerMock.VerifyNoError(); } [Test] @@ -80,6 +85,8 @@ public async Task GetBlog_InvalidId_ReturnsNotFound() // Assert Assert.That(result.Result, Is.InstanceOf()); + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "GetBlog was called with id 1"); + _loggerMock.VerifyNoError(); } [Test] @@ -113,6 +120,9 @@ public async Task PostBlog_NoId_StoresBlog() Assert.That(returnedBlog.Title, Is.EqualTo(MockBlog.Title)); Assert.That(returnedBlog.Url, Is.EqualTo(MockBlog.Url)); }); + + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "PostBlog was called"); + _loggerMock.VerifyNoError(); } [Test] @@ -127,6 +137,8 @@ public async Task PostBlog_InvalidId_ReturnsNotFound() // Assert Assert.That(result.Result, Is.InstanceOf()); + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "PostBlog was called"); + _loggerMock.VerifyNoError(); } [Test] @@ -152,6 +164,8 @@ public async Task GetPosts_ReturnsListOfPosts() var returnedPosts = okResult.Value as IEnumerable; Assert.That(returnedPosts, Is.Not.Null); Assert.That(returnedPosts.Count(), Is.EqualTo(2)); + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "GetPosts was called with id 1"); + _loggerMock.VerifyNoError(); } [Test] @@ -175,6 +189,9 @@ public async Task GetPost_ValidId_ReturnsPost() Assert.That(returnedPost.BlogId, Is.EqualTo(MockPost.BlogId)); Assert.That(returnedPost.Content, Is.EqualTo(MockPost.Content)); }); + + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "GetPost was called with id 1"); + _loggerMock.VerifyNoError(); } [Test] @@ -189,6 +206,8 @@ public async Task GetPost_InvalidId_ReturnsNotFound() // Assert Assert.That(result.Result, Is.InstanceOf()); + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "GetPost was called with id 1"); + _loggerMock.VerifyNoError(); } [Test] @@ -223,6 +242,9 @@ public async Task PostPost_NoId_StoresPost() Assert.That(returnedPost.BlogId, Is.EqualTo(MockPost.BlogId)); Assert.That(returnedPost.Content, Is.EqualTo(MockPost.Content)); }); + + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "PostPost was called"); + _loggerMock.VerifyNoError(); } [Test] @@ -237,5 +259,7 @@ public async Task PostPost_InvalidId_ReturnsNotFound() // Assert Assert.That(result.Result, Is.InstanceOf()); + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "PostPost was called"); + _loggerMock.VerifyNoError(); } } \ No newline at end of file diff --git a/tests/backend/WebApi.Tests/Controllers/ServiceControllerTests.cs b/tests/backend/WebApi.Tests/Controllers/ServiceControllerTests.cs index 48a723b6..6c1b42c6 100644 --- a/tests/backend/WebApi.Tests/Controllers/ServiceControllerTests.cs +++ b/tests/backend/WebApi.Tests/Controllers/ServiceControllerTests.cs @@ -27,6 +27,8 @@ public async Task Version_ReturnsOkResult() // Assert Assert.That(result, Is.InstanceOf>()); Assert.That(result.Result, Is.InstanceOf()); + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "Version was called"); + _loggerMock.VerifyNoError(); } [Test] @@ -50,6 +52,9 @@ public async Task Version_ReturnsVersionWithCorrectProperties() Assert.That(version.EnvironmentName, Is.InstanceOf()); Assert.That(version.InformationalVersion, Is.InstanceOf()); }); + + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "Version was called"); + _loggerMock.VerifyNoError(); } [Test] @@ -65,21 +70,7 @@ public async Task Ping_ReturnsOkResult() Assert.That(okResult.Value, Is.Not.Null); Assert.That(okResult.Value, Is.AssignableFrom>()); Assert.That(okResult.Value is GenericValue {Value: "Ok"}); - } - - [Test] - public async Task Ping_LogsInformationMessage() - { - // Act - await _controller.Ping(); - - // Assert - _loggerMock.Verify(static logger => logger.Log( - It.Is(static logLevel => logLevel == LogLevel.Information), - It.Is(static eventId => eventId.Id == 0), - It.Is(static (@object, type) => @object.ToString() == "Ping was called" && type.Name == "FormattedLogValues"), - It.IsAny(), - It.IsAny>()), - Times.Once); + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "Ping was called"); + _loggerMock.VerifyNoError(); } } \ No newline at end of file diff --git a/tests/backend/WebApi.Tests/Controllers/WeatherForecastControllerTests.cs b/tests/backend/WebApi.Tests/Controllers/WeatherForecastControllerTests.cs index 15226abb..4a3a61ea 100644 --- a/tests/backend/WebApi.Tests/Controllers/WeatherForecastControllerTests.cs +++ b/tests/backend/WebApi.Tests/Controllers/WeatherForecastControllerTests.cs @@ -41,7 +41,7 @@ public async Task Get_ReturnsFiveWeatherForecasts() var okResult = (OkObjectResult) result.Result; Assert.That(okResult.Value, Is.Not.Null); Assert.That(okResult.Value, Is.AssignableFrom()); - Assert.That(((WeatherForecast[]) okResult.Value).Count(), Is.EqualTo(5)); + Assert.That(((WeatherForecast[]) okResult.Value).Length, Is.EqualTo(5)); } [Test] @@ -56,11 +56,19 @@ public async Task Get_ReturnsWeatherForecastsWithCorrectProperties() var okResult = (OkObjectResult) result.Result; Assert.That(okResult.Value, Is.Not.Null); Assert.That(okResult.Value, Is.AssignableFrom()); + var expectedValueRange = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + foreach (var weatherForecast in (WeatherForecast[]) okResult.Value) { Assert.That(weatherForecast.Date, Is.InstanceOf()); Assert.That(weatherForecast.TemperatureC, Is.InstanceOf()); + Assert.That(weatherForecast.TemperatureC, Is.LessThan(56)); + Assert.That(weatherForecast.TemperatureC, Is.GreaterThan(-19)); Assert.That(weatherForecast.Summary, Is.InstanceOf()); + Assert.That(expectedValueRange, Contains.Item(weatherForecast.Summary)); } } @@ -71,12 +79,6 @@ public async Task Get_LogsInformationMessage() await _controller.Get(); // Assert - _loggerMock.Verify(static logger => logger.Log( - It.Is(static logLevel => logLevel == LogLevel.Information), - It.Is(static eventId => eventId.Id == 0), - It.Is(static (@object, type) => @object.ToString() == "GetWeatherForecast was called" && type.Name == "FormattedLogValues"), - It.IsAny(), - It.IsAny>()), - Times.Once); + _loggerMock.VerifyLog(LogLevel.Information, Times.Once(), "GetWeatherForecast was called"); } } \ No newline at end of file diff --git a/tests/backend/WebApi.Tests/MockHelper.cs b/tests/backend/WebApi.Tests/MockHelper.cs index 68983c91..fe61dc71 100644 --- a/tests/backend/WebApi.Tests/MockHelper.cs +++ b/tests/backend/WebApi.Tests/MockHelper.cs @@ -14,4 +14,6 @@ public static void VerifyLog(this Mock> loggerMock, LogLevel level It.IsAny(), It.IsAny>()), times); + + public static void VerifyNoError(this Mock> loggerMock) => loggerMock.VerifyLog(LogLevel.Error, Times.Never()); } \ No newline at end of file