Skip to content

Commit

Permalink
Add general testing framework that can be used by projects (#428)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kaliumhexacyanoferrat authored Dec 19, 2023
1 parent a508411 commit 542af86
Show file tree
Hide file tree
Showing 88 changed files with 8,339 additions and 8,164 deletions.
23 changes: 15 additions & 8 deletions GenHTTP.sln
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenHTTP.Modules.Scriban", "
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{A7930BE4-0549-4197-B139-B1A73E74B464}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenHTTP.Testing.Acceptance", "Testing\GenHTTP.Testing.Acceptance.csproj", "{3B00D7C4-B8E9-4163-9E4C-67044BB26B34}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenHTTP.Modules.Webservices", "Modules\Webservices\GenHTTP.Modules.Webservices.csproj", "{3096F48C-60D7-4471-A7E4-315B6227C351}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenHTTP.Modules.Razor", "Modules\Razor\GenHTTP.Modules.Razor.csproj", "{9022F8C2-B465-40FD-BC59-428C034C4470}"
Expand Down Expand Up @@ -102,6 +100,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenHTTP.Modules.Functional"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenHTTP.Modules.Protobuf", "Modules\Protobuf\GenHTTP.Modules.Protobuf.csproj", "{0D9957DE-8FE2-44A1-B9D7-EADD1DBDBD6E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenHTTP.Testing.Acceptance", "Testing\Acceptance\GenHTTP.Testing.Acceptance.csproj", "{C5067243-AFBA-4A17-BD61-DDB503367EA3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenHTTP.Testing", "Testing\Testing\GenHTTP.Testing.csproj", "{FC7F7D69-5ED0-4D3B-B201-EDBCEC71B8DB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -120,10 +122,6 @@ Global
{7FA5BD2F-C093-4F30-ACD9-A870C7A0DAF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7FA5BD2F-C093-4F30-ACD9-A870C7A0DAF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7FA5BD2F-C093-4F30-ACD9-A870C7A0DAF2}.Release|Any CPU.Build.0 = Release|Any CPU
{3B00D7C4-B8E9-4163-9E4C-67044BB26B34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B00D7C4-B8E9-4163-9E4C-67044BB26B34}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B00D7C4-B8E9-4163-9E4C-67044BB26B34}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B00D7C4-B8E9-4163-9E4C-67044BB26B34}.Release|Any CPU.Build.0 = Release|Any CPU
{3096F48C-60D7-4471-A7E4-315B6227C351}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3096F48C-60D7-4471-A7E4-315B6227C351}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3096F48C-60D7-4471-A7E4-315B6227C351}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -252,6 +250,14 @@ Global
{0D9957DE-8FE2-44A1-B9D7-EADD1DBDBD6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D9957DE-8FE2-44A1-B9D7-EADD1DBDBD6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D9957DE-8FE2-44A1-B9D7-EADD1DBDBD6E}.Release|Any CPU.Build.0 = Release|Any CPU
{C5067243-AFBA-4A17-BD61-DDB503367EA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5067243-AFBA-4A17-BD61-DDB503367EA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5067243-AFBA-4A17-BD61-DDB503367EA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5067243-AFBA-4A17-BD61-DDB503367EA3}.Release|Any CPU.Build.0 = Release|Any CPU
{FC7F7D69-5ED0-4D3B-B201-EDBCEC71B8DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC7F7D69-5ED0-4D3B-B201-EDBCEC71B8DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC7F7D69-5ED0-4D3B-B201-EDBCEC71B8DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC7F7D69-5ED0-4D3B-B201-EDBCEC71B8DB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -260,7 +266,6 @@ Global
{59E1C489-E9E9-4374-8CB9-A2005722152D} = {AFBFE61E-0C33-42F6-9370-9F5088EB8633}
{C2D2B8D0-CA23-4790-8C71-B245A0AB81FB} = {06A861A6-D030-4129-9F8F-4EED698D00A6}
{7FA5BD2F-C093-4F30-ACD9-A870C7A0DAF2} = {23B23225-275E-4F52-8B29-6F44C85B6ACE}
{3B00D7C4-B8E9-4163-9E4C-67044BB26B34} = {A7930BE4-0549-4197-B139-B1A73E74B464}
{3096F48C-60D7-4471-A7E4-315B6227C351} = {23B23225-275E-4F52-8B29-6F44C85B6ACE}
{9022F8C2-B465-40FD-BC59-428C034C4470} = {23B23225-275E-4F52-8B29-6F44C85B6ACE}
{9BB6732E-BFE1-4ABE-B8A6-4522DF72EDDD} = {23B23225-275E-4F52-8B29-6F44C85B6ACE}
Expand Down Expand Up @@ -295,9 +300,11 @@ Global
{69D6CA16-1332-41B6-8994-6AD943915F25} = {23B23225-275E-4F52-8B29-6F44C85B6ACE}
{2A2BCF94-BBDF-49A7-8B87-931E665507F0} = {23B23225-275E-4F52-8B29-6F44C85B6ACE}
{0D9957DE-8FE2-44A1-B9D7-EADD1DBDBD6E} = {23B23225-275E-4F52-8B29-6F44C85B6ACE}
{C5067243-AFBA-4A17-BD61-DDB503367EA3} = {A7930BE4-0549-4197-B139-B1A73E74B464}
{FC7F7D69-5ED0-4D3B-B201-EDBCEC71B8DB} = {A7930BE4-0549-4197-B139-B1A73E74B464}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
LessCompiler = 2603124e-1287-4d61-9540-6ac3efad4eb9
SolutionGuid = {9C67B3AF-0BF6-4E21-8C39-3F74CFCF9632}
LessCompiler = 2603124e-1287-4d61-9540-6ac3efad4eb9
EndGlobalSection
EndGlobal
160 changes: 80 additions & 80 deletions Testing/AssertX.cs → Testing/Acceptance/AssertX.cs
Original file line number Diff line number Diff line change
@@ -1,80 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GenHTTP.Testing.Acceptance
{

/// <summary>
/// Compatibility assertions for XUnit.
/// </summary>
public static class AssertX
{

public static void Contains(string searchFor, string? content) => Assert.IsTrue(content?.Contains(searchFor) ?? false);

public static void DoesNotContain(string searchFor, string? content) => Assert.IsFalse(content?.Contains(searchFor) ?? false);

public static void StartsWith(string searchFor, string? content) => Assert.IsTrue(content?.StartsWith(searchFor) ?? false);

public static void EndsWith(string searchFor, string? content) => Assert.IsTrue(content?.EndsWith(searchFor) ?? false);

public static void Single<T>(IEnumerable<T> collection) => Assert.IsTrue(collection.Count() == 1);

public static void Empty<T>(IEnumerable<T> collection) => Assert.IsFalse(collection.Any());

public static void Contains<T>(T value, IEnumerable<T> collection) => Assert.IsTrue(collection.Contains(value));

public static void DoesNotContain<T>(T value, IEnumerable<T> collection) => Assert.IsFalse(collection.Contains(value));

public static void IsNullOrEmpty(string? value) => Assert.IsTrue(string.IsNullOrEmpty(value));

/// <summary>
/// Raises an assertion expection if the response does not have the expected status code
/// and additionally prints information about the response to be able to further debug
/// issues in workflow runs.
/// </summary>
/// <param name="response">The response to be evaluated</param>
/// <param name="expectedStatus">The expected status code to check for</param>
public static async Task AssertStatusAsync(this HttpResponseMessage response, HttpStatusCode expectedStatus)
{
if (response.StatusCode != expectedStatus)
{
var builder = new StringBuilder();

builder.AppendLine($"Response returned with status '{response.StatusCode}', expected '{expectedStatus}'.");
builder.AppendLine();

builder.AppendLine("Headers");
builder.AppendLine();

foreach (var header in response.Headers)
{
builder.AppendLine($" {header.Key} = {string.Join(',', header.Value.ToList())}");
}

builder.AppendLine();

var content = await response.Content.ReadAsStringAsync();

if (!string.IsNullOrEmpty(content))
{
builder.AppendLine("Body");
builder.AppendLine();

builder.AppendLine(content);
}

throw new AssertFailedException(builder.ToString());
}
}

}

}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GenHTTP.Testing.Acceptance
{

/// <summary>
/// Compatibility assertions for XUnit.
/// </summary>
public static class AssertX
{

public static void Contains(string searchFor, string? content) => Assert.IsTrue(content?.Contains(searchFor) ?? false);

public static void DoesNotContain(string searchFor, string? content) => Assert.IsFalse(content?.Contains(searchFor) ?? false);

public static void StartsWith(string searchFor, string? content) => Assert.IsTrue(content?.StartsWith(searchFor) ?? false);

public static void EndsWith(string searchFor, string? content) => Assert.IsTrue(content?.EndsWith(searchFor) ?? false);

public static void Single<T>(IEnumerable<T> collection) => Assert.IsTrue(collection.Count() == 1);

public static void Empty<T>(IEnumerable<T> collection) => Assert.IsFalse(collection.Any());

public static void Contains<T>(T value, IEnumerable<T> collection) => Assert.IsTrue(collection.Contains(value));

public static void DoesNotContain<T>(T value, IEnumerable<T> collection) => Assert.IsFalse(collection.Contains(value));

public static void IsNullOrEmpty(string? value) => Assert.IsTrue(string.IsNullOrEmpty(value));

/// <summary>
/// Raises an assertion expection if the response does not have the expected status code
/// and additionally prints information about the response to be able to further debug
/// issues in workflow runs.
/// </summary>
/// <param name="response">The response to be evaluated</param>
/// <param name="expectedStatus">The expected status code to check for</param>
public static async Task AssertStatusAsync(this HttpResponseMessage response, HttpStatusCode expectedStatus)
{
if (response.StatusCode != expectedStatus)
{
var builder = new StringBuilder();

builder.AppendLine($"Response returned with status '{response.StatusCode}', expected '{expectedStatus}'.");
builder.AppendLine();

builder.AppendLine("Headers");
builder.AppendLine();

foreach (var header in response.Headers)
{
builder.AppendLine($" {header.Key} = {string.Join(',', header.Value.ToList())}");
}

builder.AppendLine();

var content = await response.Content.ReadAsStringAsync();

if (!string.IsNullOrEmpty(content))
{
builder.AppendLine("Body");
builder.AppendLine();

builder.AppendLine(content);
}

throw new AssertFailedException(builder.ToString());
}
}

}

}
Original file line number Diff line number Diff line change
@@ -1,80 +1,82 @@
using System;
using System.Net;
using System.Threading.Tasks;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GenHTTP.Testing.Acceptance.Engine
{

[TestClass]
public sealed class BasicTests
{

[TestMethod]
public async Task TestBuilder()
{
using var runner = new TestRunner();

runner.Host.RequestMemoryLimit(128)
.TransferBufferSize(128)
.RequestReadTimeout(TimeSpan.FromSeconds(2))
.Backlog(1);

runner.Start();

using var response = await runner.GetResponse();

await response.AssertStatusAsync(HttpStatusCode.NotFound);
}

[TestMethod]
public async Task TestLegacyHttp()
{
using var runner = TestRunner.Run();

using var client = TestRunner.GetClient(version: new Version(1, 0));

using var response = await runner.GetResponse();

await response.AssertStatusAsync(HttpStatusCode.NotFound);
}

[TestMethod]
public async Task TestConnectionClose()
{
using var runner = TestRunner.Run();

var request = runner.GetRequest();
request.Headers.Add("Connection", "close");

using var response = await runner.GetResponse(request);

await response.AssertStatusAsync(HttpStatusCode.NotFound);
Assert.IsTrue(response.Headers.Connection.Contains("Close"));
}

[TestMethod]
public async Task TestEmptyQuery()
{
using var runner = TestRunner.Run();

using var response = await runner.GetResponse("/?");

await response.AssertStatusAsync(HttpStatusCode.NotFound);
}


[TestMethod]
public async Task TestKeepalive()
{
using var runner = TestRunner.Run();

using var response = await runner.GetResponse();

Assert.IsTrue(response.Headers.Connection.Contains("Keep-Alive"));
}

}

}
using System;
using System.Net;
using System.Threading.Tasks;

using GenHTTP.Modules.Layouting;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GenHTTP.Testing.Acceptance.Engine
{

[TestClass]
public sealed class BasicTests
{

[TestMethod]
public async Task TestBuilder()
{
using var runner = new TestHost(Layout.Create());

runner.Host.RequestMemoryLimit(128)
.TransferBufferSize(128)
.RequestReadTimeout(TimeSpan.FromSeconds(2))
.Backlog(1);

runner.Start();

using var response = await runner.GetResponseAsync();

await response.AssertStatusAsync(HttpStatusCode.NotFound);
}

[TestMethod]
public async Task TestLegacyHttp()
{
using var runner = TestHost.Run(Layout.Create());

using var client = TestHost.GetClient(protocolVersion: new Version(1, 0));

using var response = await runner.GetResponseAsync();

await response.AssertStatusAsync(HttpStatusCode.NotFound);
}

[TestMethod]
public async Task TestConnectionClose()
{
using var runner = TestHost.Run(Layout.Create());

var request = runner.GetRequest();
request.Headers.Add("Connection", "close");

using var response = await runner.GetResponseAsync(request);

await response.AssertStatusAsync(HttpStatusCode.NotFound);
Assert.IsTrue(response.Headers.Connection.Contains("Close"));
}

[TestMethod]
public async Task TestEmptyQuery()
{
using var runner = TestHost.Run(Layout.Create());

using var response = await runner.GetResponseAsync("/?");

await response.AssertStatusAsync(HttpStatusCode.NotFound);
}


[TestMethod]
public async Task TestKeepalive()
{
using var runner = TestHost.Run(Layout.Create());

using var response = await runner.GetResponseAsync();

Assert.IsTrue(response.Headers.Connection.Contains("Keep-Alive"));
}

}

}
Loading

0 comments on commit 542af86

Please sign in to comment.