Skip to content

Commit

Permalink
v1.6.0 (#22)
Browse files Browse the repository at this point in the history
* Use GitHub actions for the CI and build the NuGet packages.
* Remove dependency to FluentAssertions library.
  • Loading branch information
GillesTourreau authored Feb 11, 2025
1 parent 91b7877 commit 15b40d0
Show file tree
Hide file tree
Showing 15 changed files with 295 additions and 149 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/github-actions-ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Continuous Integration

on:
pull_request:
branches: [ "main" ]
push:
branches: [ "releases/**" ]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup .NET 6.x
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.x'

- name: Build
run: dotnet build --property:Configuration=Debug "PosInformatique.Logging.Assertions.sln"

- name: Test with the dotnet CLI
run: dotnet test --property:Configuration=Debug "PosInformatique.Logging.Assertions.sln"
36 changes: 36 additions & 0 deletions .github/workflows/github-actions-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Release

on:
workflow_dispatch:
inputs:
VersionPrefix:
type: string
description: The version of the library
required: true
default: 1.6.0
VersionSuffix:
type: string
description: The version suffix of the library (for example rc.1)

run-name: ${{ inputs.VersionPrefix }}-${{ inputs.VersionSuffix }}

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup .NET 6.x
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.x'

- name: Build
run: dotnet pack
--property:Configuration=Release
--property:VersionPrefix=${{ github.event.inputs.VersionPrefix }}
--property:VersionSuffix=${{ github.event.inputs.VersionSuffix }}
"src/Logging.Assertions/Logging.Assertions.csproj"

- name: Publish the package to nuget.org
run: dotnet nuget push "src/Logging.Assertions/bin/Release/*.nupkg" --api-key "${{ secrets.NUGET_APIKEY }}" --source https://api.nuget.org/v3/index.json
11 changes: 7 additions & 4 deletions PosInformatique.Logging.Assertions.sln
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A62DA28F
tests\.editorconfig = tests\.editorconfig
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{7B54C795-CB70-4A3D-846F-90A6732121B8}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{F37BD0D2-6340-49A1-87A3-F5FFB9EE3FC3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{BBCFBA69-80C5-4053-BA6E-B65583CA19D7}"
ProjectSection(SolutionItems) = preProject
build\azure-pipelines-ci.yaml = build\azure-pipelines-ci.yaml
build\azure-pipelines-release.yaml = build\azure-pipelines-release.yaml
.github\workflows\github-actions-ci.yaml = .github\workflows\github-actions-ci.yaml
.github\workflows\github-actions-release.yml = .github\workflows\github-actions-release.yml
EndProjectSection
EndProject
Global
Expand All @@ -49,7 +51,8 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{A62DA28F-4ECA-428B-B015-98C716DA7DAF} = {6CE746AC-E3B0-4912-89BA-23EA2733830A}
{7B54C795-CB70-4A3D-846F-90A6732121B8} = {6CE746AC-E3B0-4912-89BA-23EA2733830A}
{F37BD0D2-6340-49A1-87A3-F5FFB9EE3FC3} = {6CE746AC-E3B0-4912-89BA-23EA2733830A}
{BBCFBA69-80C5-4053-BA6E-B65583CA19D7} = {F37BD0D2-6340-49A1-87A3-F5FFB9EE3FC3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B3A52E02-62EF-499F-A509-8DF050E26B77}
Expand Down
12 changes: 1 addition & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ logger.SetupSequence()
```

> Here we use the FluentAssertions library to check the arguments values of the log message template, but of course you can use your
favorite assertion framework to check it.
favorite assertion library to check it.

The second way allows also to check the arguments of the log template message by there index (*it is not what I recommand,
because if the trainee developper in your team change the name of the arguments name in the log message template, you will not
Expand Down Expand Up @@ -476,13 +476,3 @@ Both usage offers the same fluent assertion methods.
and the version 2.0.0 of the [Microsoft.Extensions.Logging.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Abstractions) NuGet package. So this library can be used with
different .NET architecture projects (.NET Framework, .NET Core, Xamarin,...) and also with old versions of the
[Microsoft.Extensions.Logging.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Abstractions) NuGet package.

- The [PosInformatique.Logging.Assertions](https://www.nuget.org/packages/PosInformatique.Logging.Assertions/) library
depends of the [FluentAssertions](https://github.com/fluentassertions/fluentassertions) library
for internal assertions which is more pretty to read in the exceptions message. It is use the version 6.0.0 and of course it is compatible
with the earlier version of it.

- Also, the [PosInformatique.Logging.Assertions](https://www.nuget.org/packages/PosInformatique.Logging.Assertions/) library
use the internal [FluentAssertions](https://github.com/fluentassertions/fluentassertions) unit test
provider engine detection to throw an exception (when an assertion is false) depending of the engine used to execute
the unit test. For example, `XunitException` if the unit test engine used is `Xunit`.
24 changes: 0 additions & 24 deletions build/azure-pipelines-ci.yaml

This file was deleted.

54 changes: 0 additions & 54 deletions build/azure-pipelines-release.yaml

This file was deleted.

81 changes: 81 additions & 0 deletions src/Logging.Assertions/AssertionHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//-----------------------------------------------------------------------
// <copyright file="AssertionHelper.cs" company="P.O.S Informatique">
// Copyright (c) P.O.S Informatique. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace PosInformatique.Logging.Assertions
{
internal static class AssertionHelper
{
public static string ToString(object? @object)
{
if (@object is null)
{
return "null";
}

return @object.ToString();
}

public static void BeEquivalentTo(object actual, object expected)
{
if (!Equals(actual, expected))
{
throw new LoggingAssertionFailedException($"Expected value: {ToString(expected)}{Environment.NewLine}Actual value: {ToString(actual)}");
}
}

public static void BeEquivalentTo(IDictionary<string, object> actual, IDictionary<string, object> expected)
{
if (actual.Count != expected.Count)
{
if (expected.Count > actual.Count)
{
var missingKeys = new List<string>();

foreach (var expectedKey in expected.Keys)
{
if (!actual.ContainsKey(expectedKey))
{
missingKeys.Add(expectedKey);
}
}

var missingKeysList = string.Join(", ", missingKeys.Select(k => "\"" + k + "\""));

throw new LoggingAssertionFailedException($"Expected state to be a dictionary with {expected.Count} item(s), but it misses key(s) {{{missingKeysList}}}");
}
else
{
var additionalKeys = new List<string>();

foreach (var actualKey in actual.Keys)
{
if (!expected.ContainsKey(actualKey))
{
additionalKeys.Add(actualKey);
}
}

var additionalKeysList = string.Join(", ", additionalKeys.Select(k => "\"" + k + "\""));

throw new LoggingAssertionFailedException($"Expected state to be a dictionary with {expected.Count} item(s), but has additional key(s) {{{additionalKeysList}}}");
}
}
}

public static void BeSameAs(Exception actual, Exception expected)
{
if (actual.GetType() != expected.GetType())
{
throw new LoggingAssertionFailedException($"Expected exception to refer to {expected.GetType().FullName} with message \"{expected.Message}\", but found {actual.GetType().FullName} with message \"{actual.Message}\".");
}

if (!actual.Message.Equals(expected.Message, StringComparison.InvariantCulture))
{
throw new LoggingAssertionFailedException($"Expected exception to refer to {expected.GetType().FullName} with message \"{expected.Message}\", but found {actual.GetType().FullName} with message \"{actual.Message}\".");
}
}
}
}
1 change: 0 additions & 1 deletion src/Logging.Assertions/ILoggerMockSetupSequenceError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

namespace PosInformatique.Logging.Assertions
{
using FluentAssertions;
using Microsoft.Extensions.Logging;

/// <summary>
Expand Down
22 changes: 10 additions & 12 deletions src/Logging.Assertions/LoggerMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
namespace PosInformatique.Logging.Assertions
{
using System.Globalization;
using FluentAssertions;
using FluentAssertions.Common;
using Microsoft.Extensions.Logging;

/// <summary>
Expand Down Expand Up @@ -64,7 +62,7 @@ public void VerifyLogs()
var missingCalls = this.expectedLogActions.Skip(this.expectedLogActionsIndex).Select(m => "- " + m.ExceptionDisplayMessage);
var missingCallsString = string.Join(Environment.NewLine, missingCalls);

Services.ThrowException($"Logger has been called few times (Expected: {this.expectedLogActions.Count} calls, Actual: {this.expectedLogActionsIndex} calls).{Environment.NewLine}{missingCallsString}");
throw new LoggingAssertionFailedException($"Logger has been called few times (Expected: {this.expectedLogActions.Count} calls, Actual: {this.expectedLogActionsIndex} calls).{Environment.NewLine}{missingCallsString}");
}
}

Expand Down Expand Up @@ -177,14 +175,14 @@ private TLogAction GetCurrentExpectedLogAction<TLogAction>(string methodCall)
{
if (this.mock.expectedLogActionsIndex >= this.mock.expectedLogActions.Count)
{
Services.ThrowException($"The ILogger has been called too many times (Expected: {this.mock.expectedLogActions.Count} calls)");
throw new LoggingAssertionFailedException($"The ILogger has been called too many times (Expected: {this.mock.expectedLogActions.Count} calls)");
}

var expectedLogAction = this.mock.expectedLogActions[this.mock.expectedLogActionsIndex];

if (expectedLogAction is not TLogAction expectedLogActionTyped)
{
Services.ThrowException($"The '{methodCall}' method has been called but expected other action (Expected: {expectedLogAction.Name})");
throw new LoggingAssertionFailedException($"The '{methodCall}' method has been called but expected other action (Expected: {expectedLogAction.Name})");
throw new NotImplementedException("Must not be called.");
}

Expand Down Expand Up @@ -248,7 +246,7 @@ public void Assert<TState>(LogLevel logLevel, TState state, Exception? exception
{
if (logLevel != this.logLevel)
{
Services.ThrowException($"Wrong log level for the Log() method call. (Expected: {this.logLevel}, Actual: {logLevel})");
throw new LoggingAssertionFailedException($"Wrong log level for the Log() method call. (Expected: {this.logLevel}, Actual: {logLevel})");
}

if (this.arguments != null)
Expand All @@ -259,12 +257,12 @@ public void Assert<TState>(LogLevel logLevel, TState state, Exception? exception

if (originalMessage != this.message)
{
Services.ThrowException($"Wrong log message for the Log({logLevel}) method call. (Expected: '{this.message}', Actual: '{originalMessage}')");
throw new LoggingAssertionFailedException($"Wrong log message for the Log({logLevel}) method call. (Expected: '{this.message}', Actual: '{originalMessage}')");
}

if (stateAsList.Count - 1 != this.arguments.Count)
{
Services.ThrowException($"Incorrect template message argument count for the '{originalMessage}' template message. (Expected: '{this.arguments.Count}', Actual: '{stateAsList.Count - 1}')");
throw new LoggingAssertionFailedException($"Incorrect template message argument count for the '{originalMessage}' template message. (Expected: '{this.arguments.Count}', Actual: '{stateAsList.Count - 1}')");
}

var messageArguments = new LogMessageTemplateArguments(
Expand All @@ -278,15 +276,15 @@ public void Assert<TState>(LogLevel logLevel, TState state, Exception? exception

if (message != this.message)
{
Services.ThrowException($"Wrong log message for the Log({logLevel}) method call. (Expected: '{this.message}', Actual: '{message}')");
throw new LoggingAssertionFailedException($"Wrong log message for the Log({logLevel}) method call. (Expected: '{this.message}', Actual: '{message}')");
}
}

if (this.exception != null)
{
if (exception is null)
{
Services.ThrowException($"Expected an exception but no exeception has been thrown.");
throw new LoggingAssertionFailedException($"Expected an exception but no exeception has been thrown.");
}
else
{
Expand Down Expand Up @@ -327,7 +325,7 @@ public override void Assert<TState>(TState state)
}
else
{
Services.ThrowException($"The 'BeginScope()' has been called with a wrong state argument type (Expected: {typeof(TExpectedState).Name}, Actual: {typeof(TState).Name}).");
throw new LoggingAssertionFailedException($"The 'BeginScope()' has been called with a wrong state argument type (Expected: {typeof(TExpectedState).Name}, Actual: {typeof(TState).Name}).");
}
}
}
Expand Down Expand Up @@ -381,7 +379,7 @@ public void Assert(LogLevel logLevel)
{
if (this.expectedLogLevel != logLevel)
{
Services.ThrowException($"The 'IsEnabled()' has been called with a wrong log level (Expected: {this.expectedLogLevel}, Actual: {logLevel}).");
throw new LoggingAssertionFailedException($"The 'IsEnabled()' has been called with a wrong log level (Expected: {this.expectedLogLevel}, Actual: {logLevel}).");
}
}
}
Expand Down
Loading

0 comments on commit 15b40d0

Please sign in to comment.