diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 2e97c1d..f717755 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -29,55 +29,47 @@ env: jobs: build: - runs-on: windows-latest + runs-on: ubuntu-latest steps: + - name: Set version id: versioner run: | - if($env:GITHUB_EVENT_NAME -like "release") { - #example refs/tags/v2.0.33 - $parts = $env:GITHUB_REF.Split("/") - $version=$parts[2] - # remove the V from the version, .net builder doesn't accept strings - $version = $version.Replace("v", "") - SET $version=%version:~1% - } - else { - $version="0.0.$env:GITHUB_RUN_NUMBER" - } - echo "::set-output name=VERSION::$version" - Write-Host "$env:GITHUB_EVENT_NAME ($env:GITHUB_REF) generated version $version" + if [[ ${{ github.event_name }} == 'release' ]]; then + version="${github.ref##*/}" + version="${version/[^0-9.]/}" + else + version="0.0.${{ github.run_number }}" + # Add your commands for non-release events (command B) + fi + + echo "${{ github.event_name }} ${{ github.ref }} generated version $version" + echo "Version=${version}" >> $GITHUB_OUTPUT + - name: Setup .NET core 3.1.x - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: '3.1.x' - - uses: actions/checkout@v2 - - name: Create folder - run: mkdir BuildReports + - uses: actions/checkout@v4 - name: Install dependencies - run: dotnet restore --verbosity m > BuildReports/Restore.txt + run: dotnet restore --verbosity m - name: Build - run: | - Write-Host "Version ${{steps.versioner.outputs.VERSION}}" - dotnet build --no-restore --verbosity m --configuration Release /p:Version=${{ steps.versioner.outputs.VERSION }} > BuildReports/Build.txt + run: dotnet build --no-restore --verbosity m --configuration Release /p:Version=${{ steps.versioner.outputs.Version }} - name: Test - run: dotnet test --no-build --configuration Release > BuildReports/Tests.txt + run: dotnet test --no-build --configuration Release --verbosity quiet --logger "trx" --results-directory "TestResults" + - name: Test Report + uses: dorny/test-reporter@v1 + if: success() || failure() + with: + name: Tests Reports + path: TestResults/* + reporter: dotnet-trx - name: Copy generated nuget file - shell: bash run: find . -name "SystemTestingTools*.nupkg" -exec cp "{}" ./ \; - - name: Set build report artifacts - if: ${{ always() }} # run this step even if previous steps failed - uses: actions/upload-artifact@v2 - with: - name: BuildReports - path: | - BuildReports/** - retention-days: 7 - if-no-files-found: error - name: Set nuget package artifact if: ${{ success() }} # run this step even if previous steps failed - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: NugetPackage path: SystemTestingTools*.nupkg @@ -89,7 +81,7 @@ jobs: if: github.event_name == 'release' runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v4 with: name: NugetPackage - name: Push to NuGet Feed diff --git a/Examples/MovieProject/MovieProject.Tests/MovieProject.InterceptionUnhappyTests/GetMovieTests.cs b/Examples/MovieProject/MovieProject.Tests/MovieProject.InterceptionUnhappyTests/GetMovieTests.cs index a0cde78..27d9190 100644 --- a/Examples/MovieProject/MovieProject.Tests/MovieProject.InterceptionUnhappyTests/GetMovieTests.cs +++ b/Examples/MovieProject/MovieProject.Tests/MovieProject.InterceptionUnhappyTests/GetMovieTests.cs @@ -27,7 +27,10 @@ public async Task When_UserAsksForMovie_ButWrongUrl_Then_FindResponseInPreApprov // arrange var client = Fixture.Server.CreateClient(); client.CreateSession(); - string errorMessage = "GET http://www.omdbapifake.com/?apikey=863d6589&type=movie&t=matrix received exception [No such host is known.]"; + + var possibleErrorMessages = new string[] { "GET http://www.omdbapifake.com/?apikey=863d6589&type=movie&t=matrix received exception [No such host is known.]", // windows + "GET http://www.omdbapifake.com/?apikey=863d6589&type=movie&t=matrix received exception [Name or service not known]" }; // ubuntu + // act var httpResponse = await client.GetAsync("/api/movie/matrix"); @@ -37,7 +40,7 @@ public async Task When_UserAsksForMovie_ButWrongUrl_Then_FindResponseInPreApprov // assert logs var logs = client.GetSessionLogs(); logs.Should().HaveCount(1); - logs[0].ToString().Should().Be($"Error: {errorMessage}"); + logs[0].ToString().Should().StartWith($"Error: ").And.ContainAny(possibleErrorMessages); // assert outgoing AssertOutgoingRequests(client); @@ -66,7 +69,7 @@ async Task CheckResponse() { // assert return httpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - httpResponse.GetHeaderValue("SystemTestingToolsStub").Should().Be($@"Recording [omdb/pre-approved/happy/matrix] reason {errorMessage}"); + httpResponse.GetHeaderValue("SystemTestingToolsStub").Should().StartWith($@"Recording [omdb/pre-approved/happy/matrix] reason ").And.ContainAny(possibleErrorMessages); var movie = await httpResponse.ReadJsonBody(); movie.Id.Should().Be("tt0133093"); @@ -88,17 +91,22 @@ public async Task When_UserAsksForMovie_ButWrongUrl_AndNoRecordingFound_Then_Ret using (new AssertionScope()) { // assert logs - string errorMessage = "GET http://www.omdbapifake.com/?apikey=863d6589&type=movie&t=gibberish received exception [No such host is known.]"; + + string errorMessageWindows = "GET http://www.omdbapifake.com/?apikey=863d6589&type=movie&t=gibberish received exception [No such host is known.]"; // windows error message + string errorMessageUbuntu = "GET http://www.omdbapifake.com/?apikey=863d6589&type=movie&t=gibberish received exception [Name or service not known]"; // ubuntu error message + var logs = client.GetSessionLogs(); logs.Should().HaveCount(1); - logs[0].ToString().Should().Be($"Error: {errorMessage}"); + logs[0].ToString().Should().BeOneOf($"Error: {errorMessageWindows}", $"Error: {errorMessageUbuntu}"); // assert outgoing AssertOutgoingRequests(client); // assert return httpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - httpResponse.GetHeaderValue("SystemTestingToolsStub").Should().Be($@"Recording [omdb/pre-approved/happy/last_fallback] reason {errorMessage} and could not find better match"); + httpResponse.GetHeaderValue("SystemTestingToolsStub").Should() + .BeOneOf($@"Recording [omdb/pre-approved/happy/last_fallback] reason {errorMessageWindows} and could not find better match", + $@"Recording [omdb/pre-approved/happy/last_fallback] reason {errorMessageUbuntu} and could not find better match"); var movie = await httpResponse.ReadJsonBody(); movie.Id.Should().Be("tt0123456"); diff --git a/Examples/MovieProject/MovieProject.Tests/MovieProject.IsolatedTests/ComponentTesting/MathHappyTests.cs b/Examples/MovieProject/MovieProject.Tests/MovieProject.IsolatedTests/ComponentTesting/MathHappyTests.cs index d01a0cd..fb6fa8c 100644 --- a/Examples/MovieProject/MovieProject.Tests/MovieProject.IsolatedTests/ComponentTesting/MathHappyTests.cs +++ b/Examples/MovieProject/MovieProject.Tests/MovieProject.IsolatedTests/ComponentTesting/MathHappyTests.cs @@ -1,6 +1,7 @@ using FluentAssertions; using FluentAssertions.Execution; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; @@ -29,11 +30,11 @@ public async Task When_CallingManyWcfMethods_Then_CanPerformMath_Successfully() // arrange var client = Fixture.Server.CreateClient(); client.CreateSession(); - - var addResponse = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/MathWcf/Real_Responses/Happy/200_Add.txt"); + + var addResponse = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/MathWCF/Real_Responses/Happy/200_Add.txt"); client.AppendHttpCallStub(HttpMethod.Post, new System.Uri(Url), addResponse, new Dictionary() { { "SOAPAction", @"""http://tempuri.org/Add""" } }); - var minusResponse = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/MathWcf/Real_Responses/Happy/200_Minus.txt"); + var minusResponse = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/MathWCF/Real_Responses/Happy/200_Minus.txt"); client.AppendHttpCallStub(HttpMethod.Post, new System.Uri(Url), minusResponse, new Dictionary() { { "SOAPAction", @"""http://tempuri.org/Subtract""" } }); // act diff --git a/Tool/SystemTestingTools.UnitTests/MockEndpointUnhappyTests.cs b/Tool/SystemTestingTools.UnitTests/MockEndpointUnhappyTests.cs index f94419d..5801760 100644 --- a/Tool/SystemTestingTools.UnitTests/MockEndpointUnhappyTests.cs +++ b/Tool/SystemTestingTools.UnitTests/MockEndpointUnhappyTests.cs @@ -1,5 +1,6 @@ using FluentAssertions; using System; +using System.IO; using System.Text.RegularExpressions; using Xunit; @@ -22,8 +23,8 @@ public void FileDoesntExist() Action act = () => ResponseFactory.FromFiddlerLikeResponseFile(fullFileName); - var exception = Assert.Throws(act); - exception.Message.Should().Be($"Could not find file '{fullFileName}'"); + var exception = Assert.Throws(act); + exception.Message.Should().StartWith($"Could not find file '{fullFileName}', there are 0 other files in the folder "); } [Fact] diff --git a/Tool/SystemTestingTools.UnitTests/RecordingManagerTests.cs b/Tool/SystemTestingTools.UnitTests/RecordingManagerTests.cs index a8cfa04..73d4464 100644 --- a/Tool/SystemTestingTools.UnitTests/RecordingManagerTests.cs +++ b/Tool/SystemTestingTools.UnitTests/RecordingManagerTests.cs @@ -120,8 +120,8 @@ public async Task GetRecordings_Including_Old_One_And_ResendAndUpdate_Works() // asserts recordings.Count.Should().Be(3); - recordings[0].File.Should().Be(@"happy/200_ContainsSomeFields_PacificChallenge"); - recordings[0].FileFullPath.Should().EndWith(@"recordings_temp\happy\200_ContainsSomeFields_PacificChallenge.txt"); + recordings[0].File.Should().Be(@"happy/200_ContainsSomeFields_PacificChallenge"); + recordings[0].FileFullPath.Should().EndWith(Path.Combine("recordings_temp","happy","200_ContainsSomeFields_PacificChallenge.txt")); await AssertHappyRecording(recordings[1]); await AssertUnhappyRecording(recordings[2]); diff --git a/Tool/SystemTestingTools.UnitTests/ValueObjectsTests.cs b/Tool/SystemTestingTools.UnitTests/ValueObjectsTests.cs index e918eda..1eb3a50 100644 --- a/Tool/SystemTestingTools.UnitTests/ValueObjectsTests.cs +++ b/Tool/SystemTestingTools.UnitTests/ValueObjectsTests.cs @@ -2,6 +2,7 @@ using FluentAssertions.Execution; using System; using System.IO; +using System.Numerics; using System.Text.RegularExpressions; using Xunit; @@ -54,13 +55,15 @@ public void FileFullPath_Empty_Unhappy() } [Fact] - public void FileFullPath_FileDoesntExist_Unhappy() + public void FileFullPath_FolderDoesntExist_Unhappy() { var fullFileName = FilesFolder + @"happy/401_InvalidKeyAAA"; - var ex = Assert.Throws(() => { FileFullPath file = fullFileName; }); + var ex = Assert.Throws(() => { FileFullPath file = fullFileName; }); - ex.Message.Should().Be($"Could not find file '{fullFileName}.txt'"); + var folder = Path.GetDirectoryName(fullFileName); + + ex.Message.Should().StartWith($"Could not find folder '{folder}', the only folder that exist is "); } [Fact] diff --git a/Tool/SystemTestingTools/Internal/RecordingFormatter.cs b/Tool/SystemTestingTools/Internal/RecordingFormatter.cs index 4c564f5..b3106e2 100644 --- a/Tool/SystemTestingTools/Internal/RecordingFormatter.cs +++ b/Tool/SystemTestingTools/Internal/RecordingFormatter.cs @@ -5,6 +5,7 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; +using System.ServiceModel.Channels; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -153,7 +154,7 @@ public static bool IsValid(string content) // part 1 = date time of the recording // part 2 = request details // part 3 = response details - private static Regex RecordingRegex = new Regex(@".+?\nDate:(.+?)\n.+?REQUEST.+?\n(.+?)--\!\?@Divider:.+?\n(.*)", RegexOptions.Compiled | RegexOptions.Singleline); + private static Regex RecordingRegex = new Regex(@".+?\nDate:(.+?)\n.+?REQUEST.*?\n(.+?)--\!\?@Divider:.+?\n(.*)", RegexOptions.Compiled | RegexOptions.Singleline); private static Regex DateRegex = new Regex(@"(2.+?)\(", RegexOptions.Compiled | RegexOptions.Singleline); @@ -179,16 +180,25 @@ public static Recording Read(string content) // headers // white space // body (if any) - private static Regex RequestRegex = new Regex(@"(.+?) (.+?)\n(.+?)(\r\r|\n\n|\r\n\r\n)(.*)", RegexOptions.Compiled | RegexOptions.Singleline); + private static Regex RequestRegex = new Regex(@"^(.+?) (.+?)\n(.+?)(\r\r|\n\n|\r\n\r\n)(.*)", RegexOptions.Compiled | RegexOptions.Singleline); private static HttpRequestMessage GetRequest(string requestContent) { - var match = RequestRegex.Match(requestContent); + var match = RequestRegex.Match(requestContent); - if (!match.Success) throw new ApplicationException("Could not parse request data"); + if (!match.Success) throw new ApplicationException($"Could not parse request data"); var result = new HttpRequestMessage(); - result.Method = new HttpMethod(match.Groups[1].Value); + var method = match.Groups[1].Value.Trim(); + try + { + result.Method = new HttpMethod(method); + } + catch (System.FormatException ex) + { + throw new Exception($"Method [{method}] is invalid", ex); + } + result.RequestUri = new Uri(match.Groups[2].Value); result.Content = Helper.ParseHeadersAndBody(match.Groups[3].Value, match.Groups[5].Value, result.Headers); diff --git a/Tool/SystemTestingTools/Internal/RecordingManager.cs b/Tool/SystemTestingTools/Internal/RecordingManager.cs index bac7d95..f4d4f16 100644 --- a/Tool/SystemTestingTools/Internal/RecordingManager.cs +++ b/Tool/SystemTestingTools/Internal/RecordingManager.cs @@ -62,6 +62,7 @@ public List GetRecordings(FolderAbsolutePath folder) recording.File = StandardizeFileNameForDisplay(folder, fullFilePath); list.Add(recording); } + list = list.OrderBy(c => c.File).ToList(); // we sort so it's the same order in windows and linux, so we can more easily test it return list; } diff --git a/Tool/SystemTestingTools/ValueObjects/FileFullPath.cs b/Tool/SystemTestingTools/ValueObjects/FileFullPath.cs index 230c467..ae6f2d7 100644 --- a/Tool/SystemTestingTools/ValueObjects/FileFullPath.cs +++ b/Tool/SystemTestingTools/ValueObjects/FileFullPath.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using static SystemTestingTools.Helper; using static SystemTestingTools.Internal.Enums; @@ -17,7 +18,30 @@ public class FileFullPath : StringValueObject public FileFullPath(string value) : base(value) { if (!Path.HasExtension(_value)) _value += ".txt"; - if (!File.Exists(_value)) throw new ArgumentException($"Could not find file '{_value}'"); + + var folder = Path.GetDirectoryName(_value); + + CheckFolderExists(folder); + + if (!File.Exists(_value)) + { + var filesCount = Directory.GetFiles(folder).Length; + throw new FileNotFoundException($"Could not find file '{_value}', there are {filesCount} other files in the folder {folder}"); + } + } + + private static void CheckFolderExists(string folder) + { + if (Directory.Exists(folder)) return; + + string parentFolder = folder; + do + { + parentFolder = Path.GetDirectoryName(parentFolder); + } while (!Directory.Exists(parentFolder) && !string.IsNullOrEmpty(parentFolder)); + + + throw new DirectoryNotFoundException($"Could not find folder '{folder}', the only folder that exist is '{parentFolder}'"); } public string ReadContent()