diff --git a/Frank.Testing.ApiTesting/ApiTestingHarness.cs b/Frank.Testing.ApiTesting/ApiTestingHarness.cs new file mode 100644 index 0000000..c6644b9 --- /dev/null +++ b/Frank.Testing.ApiTesting/ApiTestingHarness.cs @@ -0,0 +1,92 @@ +using System.Diagnostics; + +namespace Frank.Testing.ApiTesting; + +public class ApiTestingHarness +{ + private readonly IHttpClientFactory _httpClientFactory; + + public ApiTestingHarness(IHttpClientFactory httpClientFactory) + { + _httpClientFactory = httpClientFactory; + } + + public List AssertionGroups { get; set; } + + public async Task> RunAssertionsAsync() + { + var assertionResults = new List(); + + foreach (var group in AssertionGroups) + { + var tasks = new List>(); + + foreach (var assertion in group.Assertions) + { + tasks.Add(RunAssertionAsync(assertion)); + } + + var results = await Task.WhenAll(tasks); + + assertionResults.AddRange(results); + } + + return assertionResults; + } + + private async Task RunAssertionAsync(IAssertion assertion) + { + var result = new Result + { + AssertionName = assertion.Name, + IsSuccess = false, + ErrorMessage = string.Empty + }; + + var stopwatch = Stopwatch.StartNew(); + try + { + using var client = _httpClientFactory.CreateClient(); + client.Timeout = assertion.Timeout; + + var request = new HttpRequestMessage(assertion.Method, assertion.Endpoint); + request.Content = assertion.RequestContent; + + var response = await client.SendAsync(request); + + if (response.StatusCode == assertion.ExpectedResponseCode) + { + if (assertion.ExpectedResponseContent != null) + { + var responseContent = await response.Content.ReadAsStringAsync(); + + if (responseContent == await assertion.ExpectedResponseContent.ReadAsStringAsync()) + { + result.IsSuccess = true; + } + else + { + result.ErrorMessage = "Response content does not match expected content."; + } + } + else + { + result.IsSuccess = true; + } + } + else + { + result.ErrorMessage = $"Expected response code {assertion.ExpectedResponseCode}, but received {response.StatusCode}."; + } + } + catch (Exception ex) + { + result.ErrorMessage = ex.Message; + } + stopwatch.Stop(); + + result.ElapsedTime = stopwatch.Elapsed; + + return result; + } +} \ No newline at end of file diff --git a/Frank.Testing.ApiTesting/Assertion.cs b/Frank.Testing.ApiTesting/Assertion.cs new file mode 100644 index 0000000..ea1175f --- /dev/null +++ b/Frank.Testing.ApiTesting/Assertion.cs @@ -0,0 +1,37 @@ +using System.Net; + +namespace Frank.Testing.ApiTesting; + +public class Assertion +{ + public string? Name { get; set; } + + public Uri Endpoint { get; set; } + + public HttpMethod Method { get; set; } + + public TimeSpan Timeout { get; set; } + + public TRequest? Request { get; set; } + + public TResponse? ExpectedResponse { get; set; } + + public HttpStatusCode ExpectedResponseCode { get; set; } +} + +public class Assertion : IAssertion +{ + public string? Name { get; set; } + + public Uri Endpoint { get; set; } + + public HttpMethod Method { get; set; } + + public TimeSpan Timeout { get; set; } + + public HttpContent? RequestContent { get; set; } + + public HttpContent? ExpectedResponseContent { get; set; } + + public HttpStatusCode ExpectedResponseCode { get; set; } +} \ No newline at end of file diff --git a/Frank.Testing.ApiTesting/AssertionGroup.cs b/Frank.Testing.ApiTesting/AssertionGroup.cs new file mode 100644 index 0000000..181c37d --- /dev/null +++ b/Frank.Testing.ApiTesting/AssertionGroup.cs @@ -0,0 +1,7 @@ +namespace Frank.Testing.ApiTesting; + +public class AssertionGroup +{ + public string GroupName { get; set; } + public List Assertions { get; set; } +} \ No newline at end of file diff --git a/Frank.Testing.ApiTesting/AssertionPrecursor.cs b/Frank.Testing.ApiTesting/AssertionPrecursor.cs new file mode 100644 index 0000000..9a28faa --- /dev/null +++ b/Frank.Testing.ApiTesting/AssertionPrecursor.cs @@ -0,0 +1,18 @@ +using System.Net; + +namespace Frank.Testing.ApiTesting; + +public class AssertionPrecursor +{ + public string? Name { get; set; } + + public Uri Endpoint { get; set; } + + public HttpMethod Method { get; set; } + + public TimeSpan Timeout { get; set; } + + public TResponse? ExpectedResponse { get; set; } + + public HttpStatusCode ExpectedResponseCode { get; set; } +} \ No newline at end of file diff --git a/Frank.Testing.ApiTesting/Frank.Testing.ApiTesting.csproj b/Frank.Testing.ApiTesting/Frank.Testing.ApiTesting.csproj new file mode 100644 index 0000000..6c3c1ee --- /dev/null +++ b/Frank.Testing.ApiTesting/Frank.Testing.ApiTesting.csproj @@ -0,0 +1,12 @@ + + + + Test your api with this simple library + api, test, rest, http, client + + + + + + + diff --git a/Frank.Testing.ApiTesting/IAssertion.cs b/Frank.Testing.ApiTesting/IAssertion.cs new file mode 100644 index 0000000..588747b --- /dev/null +++ b/Frank.Testing.ApiTesting/IAssertion.cs @@ -0,0 +1,14 @@ +using System.Net; + +namespace Frank.Testing.ApiTesting; + +public interface IAssertion +{ + string? Name { get; set; } + Uri Endpoint { get; set; } + HttpMethod Method { get; set; } + TimeSpan Timeout { get; set; } + HttpContent? RequestContent { get; set; } + HttpContent? ExpectedResponseContent { get; set; } + HttpStatusCode ExpectedResponseCode { get; set; } +} \ No newline at end of file diff --git a/Frank.Testing.ApiTesting/IAssertionGroup.cs b/Frank.Testing.ApiTesting/IAssertionGroup.cs new file mode 100644 index 0000000..eccb990 --- /dev/null +++ b/Frank.Testing.ApiTesting/IAssertionGroup.cs @@ -0,0 +1,8 @@ +namespace Frank.Testing.ApiTesting; + +public interface IAssertionGroup +{ + string GroupName { get; set; } + + SortedList Assertions { get; set; } +} \ No newline at end of file diff --git a/Frank.Testing.ApiTesting/Result.cs b/Frank.Testing.ApiTesting/Result.cs new file mode 100644 index 0000000..764cd4a --- /dev/null +++ b/Frank.Testing.ApiTesting/Result.cs @@ -0,0 +1,11 @@ +namespace Frank.Testing.ApiTesting; + +public class Result +{ + public string AssertionName { get; set; } + public bool IsSuccess { get; set; } + public string? ErrorMessage { get; set; } + public TimeSpan ElapsedTime { get; set; } + + public HttpContent? ResponseContent { get; set; } +} \ No newline at end of file diff --git a/Frank.Testing.ApiTesting/ResultGroup.cs b/Frank.Testing.ApiTesting/ResultGroup.cs new file mode 100644 index 0000000..16e99b7 --- /dev/null +++ b/Frank.Testing.ApiTesting/ResultGroup.cs @@ -0,0 +1,7 @@ +namespace Frank.Testing.ApiTesting; + +public class ResultGroup +{ + public string GroupName { get; set; } + public List AssertionResults { get; set; } +} \ No newline at end of file diff --git a/Frank.Testing.ApiTesting/ResultsFormatter.cs b/Frank.Testing.ApiTesting/ResultsFormatter.cs new file mode 100644 index 0000000..e6e886e --- /dev/null +++ b/Frank.Testing.ApiTesting/ResultsFormatter.cs @@ -0,0 +1,36 @@ +namespace Frank.Testing.ApiTesting; + +using System.Collections.Generic; +using System.Xml.Linq; + +public class ResultsFormatter +{ + public string FormatResults(List groups) + { + var xDocument = new XDocument(); + var root = new XElement("TestGroups"); + + foreach (var group in groups) + { + var groupElement = new XElement("Group", + new XAttribute("Name", group.GroupName)); + + foreach (var assertion in group.AssertionResults) + { + var testElement = new XElement("Test", + new XElement("Name", assertion.AssertionName), + new XElement("Success", assertion.IsSuccess), + new XElement("ErrorMessage", assertion.ErrorMessage ?? string.Empty), + new XElement("ElapsedTime", assertion.ElapsedTime.ToString("g")) + ); + + groupElement.Add(testElement); + } + + root.Add(groupElement); + } + + xDocument.Add(root); + return xDocument.ToString(); + } +} diff --git a/Frank.Testing.Logging/DictionaryExtensions.cs b/Frank.Testing.Logging/DictionaryExtensions.cs deleted file mode 100644 index 5618bb5..0000000 --- a/Frank.Testing.Logging/DictionaryExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Frank.Testing.Logging; - -internal static class DictionaryExtensions -{ - public static TValue GetOrCreate( - this IDictionary dictionary, - TKey key, - Func valueFactory) - { - if (dictionary.TryGetValue(key, out TValue? value) && value != null) - return value; - - value = valueFactory(); - dictionary[key] = value; - return value; - } -} \ No newline at end of file diff --git a/Frank.Testing.Logging/Frank.Testing.Logging.csproj b/Frank.Testing.Logging/Frank.Testing.Logging.csproj index bcb2738..17ca64a 100644 --- a/Frank.Testing.Logging/Frank.Testing.Logging.csproj +++ b/Frank.Testing.Logging/Frank.Testing.Logging.csproj @@ -6,11 +6,11 @@ - - + + - + diff --git a/Frank.Testing.Logging/LoggingBuilderExtensions.cs b/Frank.Testing.Logging/LoggingBuilderExtensions.cs index 71862b8..cec129d 100644 --- a/Frank.Testing.Logging/LoggingBuilderExtensions.cs +++ b/Frank.Testing.Logging/LoggingBuilderExtensions.cs @@ -19,9 +19,15 @@ public static class LoggingBuilderExtensions /// The modified ILoggingBuilder with the test logging added. public static ILoggingBuilder AddPulseFlowTestLoggingProvider(this ILoggingBuilder builder, ITestOutputHelper outputHelper, LogLevel logLevel = LogLevel.Debug) { + builder.ClearProviders(); builder.AddPulseFlow(); builder.Services.AddSingleton(outputHelper); - builder.Services.AddSingleton(); + builder.Services.Configure(options => + { + options.LogLevel = logLevel; + }); + builder.Services.AddPulseFlow(flowBuilder => flowBuilder.AddFlow()); + builder.Services.AddSingleton(provider => new TestLoggerProvider(provider.GetRequiredService())); return builder; } } \ No newline at end of file diff --git a/Frank.Testing.Logging/PulseFlowTestLogger.cs b/Frank.Testing.Logging/PulseFlowTestLogger.cs index d4f0116..54c62de 100644 --- a/Frank.Testing.Logging/PulseFlowTestLogger.cs +++ b/Frank.Testing.Logging/PulseFlowTestLogger.cs @@ -1,16 +1,19 @@ using Frank.PulseFlow; using Frank.PulseFlow.Logging; +using Frank.Reflection; using Microsoft.Extensions.Logging; namespace Frank.Testing.Logging; -public class PulseFlowTestLogger(IConduit conduit) : ILogger +public class PulseFlowTestLogger(IConduit conduit, string categoryName) : ILogger { public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) - => conduit.SendAsync(new LogPulse(logLevel, eventId, state, exception, formatter, "TestLogger")).GetAwaiter().GetResult(); + => conduit.SendAsync(new LogPulse(logLevel, eventId, exception, categoryName, formatter(state, exception))).GetAwaiter().GetResult(); public bool IsEnabled(LogLevel logLevel) => true; public IDisposable? BeginScope(TState state) where TState : notnull => new PulseFlowLoggerScope(state); -} \ No newline at end of file +} + +public class PulseFlowTestLogger(IConduit conduit) : PulseFlowTestLogger(conduit, typeof(T).GetDisplayName()), ILogger; \ No newline at end of file diff --git a/Frank.Testing.Logging/ServiceCollectionExtensions.cs b/Frank.Testing.Logging/ServiceCollectionExtensions.cs index 6d39ba4..0eb4a2a 100644 --- a/Frank.Testing.Logging/ServiceCollectionExtensions.cs +++ b/Frank.Testing.Logging/ServiceCollectionExtensions.cs @@ -16,6 +16,10 @@ public static class ServiceCollectionExtensions /// The modified IServiceCollection with the test logging added. public static IServiceCollection AddTestLogging(this IServiceCollection services, ITestOutputHelper outputHelper, LogLevel logLevel = LogLevel.Debug) { + services.Configure(options => + { + options.LogLevel = logLevel; + }); services.AddLogging(builder => builder.AddPulseFlowTestLoggingProvider(outputHelper, logLevel)); return services; } diff --git a/Frank.Testing.Logging/SimpleTestLogger.cs b/Frank.Testing.Logging/SimpleTestLogger.cs new file mode 100644 index 0000000..99011fe --- /dev/null +++ b/Frank.Testing.Logging/SimpleTestLogger.cs @@ -0,0 +1,41 @@ +using Frank.Reflection; + +using Microsoft.Extensions.Logging; + +using Xunit.Abstractions; + +namespace Frank.Testing.Logging; + +public class SimpleTestLogger : SimpleTestLogger, ILogger +{ + public SimpleTestLogger(ITestOutputHelper outputHelper, LogLevel logLevel) : base(outputHelper, logLevel, typeof(T).GetDisplayName()) + { + } +} + +public class SimpleTestLogger : ILogger +{ + private readonly ITestOutputHelper _outputHelper; + private readonly LogLevel _logLevel; + private string _categoryName; + + public SimpleTestLogger(ITestOutputHelper outputHelper, LogLevel logLevel, string categoryName) + { + _outputHelper = outputHelper; + _logLevel = logLevel; + _categoryName = categoryName; + } + + public IDisposable? BeginScope(TState state) where TState : notnull + => new TestLoggerScope(state); + + public bool IsEnabled(LogLevel logLevel) => logLevel >= _logLevel; + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + if (logLevel < _logLevel) + return; + + _outputHelper.WriteLine($"[{logLevel}]: {formatter(state, exception)}"); + } +} \ No newline at end of file diff --git a/Frank.Testing.Logging/TestLogger.cs b/Frank.Testing.Logging/TestLogger.cs deleted file mode 100644 index 395ab20..0000000 --- a/Frank.Testing.Logging/TestLogger.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Microsoft.Extensions.Logging; - -using Xunit.Abstractions; - -namespace Frank.Testing.Logging; - -public class TestLogger : ILogger -{ - public ITestOutputHelper OutputHelper { get; } - public LogLevel LogLevel { get; } - public string? CategoryName { get; } - - public TestLogger(ITestOutputHelper outputHelper, LogLevel logLevel, string? categoryName = null) - { - OutputHelper = outputHelper; - LogLevel = logLevel; - CategoryName = categoryName; - } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) - { - if (logLevel < LogLevel) - return; - - OutputHelper.WriteLine($"{logLevel}: {formatter(state, exception)}"); - } - - public bool IsEnabled(LogLevel logLevel) - { - return logLevel >= LogLevel; - } - - public IDisposable? BeginScope(TState state) where TState : notnull - { - return new TestLoggerScope(state); - } -} \ No newline at end of file diff --git a/Frank.Testing.Logging/TestLoggerProvider.cs b/Frank.Testing.Logging/TestLoggerProvider.cs new file mode 100644 index 0000000..013a8e1 --- /dev/null +++ b/Frank.Testing.Logging/TestLoggerProvider.cs @@ -0,0 +1,23 @@ +using System.Collections.Concurrent; + +using Frank.PulseFlow; + +using Microsoft.Extensions.Logging; + +namespace Frank.Testing.Logging; + +public class TestLoggerProvider(IConduit conduit) : ILoggerProvider +{ + private readonly ConcurrentDictionary _loggers = new(); + + public ILogger CreateLogger(string categoryName) + { + if (_loggers.TryGetValue(categoryName, out var logger)) + return logger; + + var newLogger = new PulseFlowTestLogger(conduit, categoryName); + return _loggers.GetOrAdd(categoryName, newLogger); + } + + public void Dispose() => _loggers.Clear(); +} \ No newline at end of file diff --git a/Frank.Testing.Logging/TestLoggerSettings.cs b/Frank.Testing.Logging/TestLoggerSettings.cs new file mode 100644 index 0000000..307aa14 --- /dev/null +++ b/Frank.Testing.Logging/TestLoggerSettings.cs @@ -0,0 +1,8 @@ +using Microsoft.Extensions.Logging; + +namespace Frank.Testing.Logging; + +public class TestLoggerSettings +{ + public LogLevel LogLevel { get; set; } = LogLevel.Information; +} \ No newline at end of file diff --git a/Frank.Testing.Logging/TestLoggingOutputFlow.cs b/Frank.Testing.Logging/TestLoggingOutputFlow.cs index 86128ad..cd3a1a9 100644 --- a/Frank.Testing.Logging/TestLoggingOutputFlow.cs +++ b/Frank.Testing.Logging/TestLoggingOutputFlow.cs @@ -10,11 +10,14 @@ namespace Frank.Testing.Logging; /// public class TestLoggingOutputFlow(ITestOutputHelper outputHelper) : IFlow { - public Task HandleAsync(IPulse pulse, CancellationToken cancellationToken) + public async Task HandleAsync(IPulse pulse, CancellationToken cancellationToken) { - outputHelper.WriteLine(pulse.ToString()); - return Task.CompletedTask; + if (pulse is LogPulse logPulse) + { + outputHelper.WriteLine(logPulse.Message); + await Task.CompletedTask; + } } - public bool CanHandle(Type pulseType) => pulseType == typeof(LogPulse<>); + public bool CanHandle(Type pulseType) => pulseType == typeof(LogPulse); } \ No newline at end of file diff --git a/Frank.Testing.Logging/TestOutputHelperExtensions.cs b/Frank.Testing.Logging/TestOutputHelperExtensions.cs index 1bb7e86..f21ed9e 100644 --- a/Frank.Testing.Logging/TestOutputHelperExtensions.cs +++ b/Frank.Testing.Logging/TestOutputHelperExtensions.cs @@ -1,7 +1,4 @@ -using Frank.PulseFlow.Logging; -using Frank.Reflection; - -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Xunit.Abstractions; @@ -14,22 +11,8 @@ public static class TestOutputHelperExtensions /// /// The type of the class to which the created logger will be associated. /// The ITestOutputHelper instance used for logging. - /// The name of the log category. (optional) /// /// An instance of that can be used for logging tests. public static ILogger CreateTestLogger(this ITestOutputHelper outputHelper, LogLevel logLevel = LogLevel.Debug) - => new TestLogger(outputHelper, logLevel, typeof(T).GetDisplayName()); - - /// - /// Creates a test logger factory using the specified . - /// - /// The test output helper. - /// - /// A new instance of for testing purposes. - public static ILoggerFactory CreateTestLoggerFactory(this ITestOutputHelper outputHelper, LogLevel logLevel = LogLevel.Debug) - { - var factory = new LoggerFactory(); - // factory.AddProvider(new PulseFlowLoggerProvider()); - return factory; - } -} \ No newline at end of file + => new SimpleTestLogger(outputHelper, logLevel); +} diff --git a/Frank.Testing.TestOutputExtensions/Frank.Testing.TestOutputExtensions.csproj b/Frank.Testing.TestOutputExtensions/Frank.Testing.TestOutputExtensions.csproj index 8a14d3c..a438db1 100644 --- a/Frank.Testing.TestOutputExtensions/Frank.Testing.TestOutputExtensions.csproj +++ b/Frank.Testing.TestOutputExtensions/Frank.Testing.TestOutputExtensions.csproj @@ -8,7 +8,7 @@ - + diff --git a/Frank.Testing.TestOutputExtensions/TestOutputExtensions.cs b/Frank.Testing.TestOutputExtensions/TestOutputExtensions.cs new file mode 100644 index 0000000..04e96cc --- /dev/null +++ b/Frank.Testing.TestOutputExtensions/TestOutputExtensions.cs @@ -0,0 +1,15 @@ +using System.Text.Json; + +namespace Xunit.Abstractions; + +public static class TestOutputExtensions +{ + /// + /// Writes the specified object's string representation followed by the current line terminator to the output. + /// + /// The type of the object to write. + /// The ITestOutputHelper instance. + /// The object to write. + /// Thrown when unable to serialize the object. + public static void WriteLine(this ITestOutputHelper outputHelper, T? source) => outputHelper.WriteLine(JsonSerializer.Serialize(source, outputHelper.GetDefaultJsonSerializerOptions())); +} \ No newline at end of file diff --git a/Frank.Testing.TestOutputExtensions/TestOutputJsonExtensions.cs b/Frank.Testing.TestOutputExtensions/TestOutputJsonExtensions.cs index 0f331cc..8551a26 100644 --- a/Frank.Testing.TestOutputExtensions/TestOutputJsonExtensions.cs +++ b/Frank.Testing.TestOutputExtensions/TestOutputJsonExtensions.cs @@ -5,15 +5,6 @@ namespace Xunit.Abstractions; public static class TestOutputJsonExtensions { - /// - /// Writes the specified object's string representation followed by the current line terminator to the output. - /// - /// The type of the object to write. - /// The ITestOutputHelper instance. - /// The object to write. - /// Thrown when unable to serialize the object. - public static void WriteLine(this ITestOutputHelper outputHelper, T? source) => outputHelper.WriteLine(JsonSerializer.Serialize(source, JsonSerializerOptions)); - /// /// Writes a JSON representation of the specified object to the output helper. /// @@ -26,5 +17,11 @@ public static class TestOutputJsonExtensions /// public static void WriteJson(this ITestOutputHelper outputHelper, T? source, JsonSerializerOptions? options = null) => outputHelper.WriteLine(options == null ? JsonSerializer.Serialize(source, JsonSerializerOptions) : JsonSerializer.Serialize(source, options)); + /// + /// Gets the default JsonSerializerOptions. + /// + /// The default JsonSerializerOptions. + public static JsonSerializerOptions GetDefaultJsonSerializerOptions(this ITestOutputHelper _) => JsonSerializerOptions; + private static JsonSerializerOptions JsonSerializerOptions => new() { Converters = { new JsonStringEnumConverter() }, WriteIndented = true, ReferenceHandler = ReferenceHandler.IgnoreCycles, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; } \ No newline at end of file diff --git a/Frank.Testing.Tests/ApiTesting/ApiTestingTests.cs b/Frank.Testing.Tests/ApiTesting/ApiTestingTests.cs new file mode 100644 index 0000000..5d45a5e --- /dev/null +++ b/Frank.Testing.Tests/ApiTesting/ApiTestingTests.cs @@ -0,0 +1,83 @@ +using System.Net; + +using Frank.Testing.ApiTesting; + +using NSubstitute; + +using Xunit.Abstractions; + +namespace Frank.Testing.Tests.ApiTesting; + +public class ApiTestingTests +{ + private readonly ITestOutputHelper _outputHelper; + + public ApiTestingTests(ITestOutputHelper outputHelper) + { + _outputHelper = outputHelper; + } + + [Fact] + public async Task Test1() + { + var harness = new ApiTestingHarness(new MockHttpClientFactory()); + + var assertionGroup1 = new AssertionGroup + { + GroupName = "Group 1", + Assertions = new List + { + new Assertion + { + Name = "Is BRREG API alive?", + Endpoint = new Uri("https://data.brreg.no"), + Method = HttpMethod.Get, + Timeout = TimeSpan.FromSeconds(10), + ExpectedResponseCode = HttpStatusCode.OK + }, + new Assertion + { + Name = "Can I download something from the BRREG API?", + Endpoint = new Uri("https://data.brreg.no/enhetsregisteret/api/enheter"), + Method = HttpMethod.Get, + Timeout = TimeSpan.FromSeconds(10), + ExpectedResponseCode = HttpStatusCode.OK + } + } + }; + + var assertionGroup2 = new AssertionGroup + { + GroupName = "Group 2", + Assertions = new List + { + // Add more assertions... + } + }; + + harness.AssertionGroups = new List { assertionGroup1, assertionGroup2 }; + + var results = await harness.RunAssertionsAsync(); + + foreach (var result in results) + { + _outputHelper.WriteLine($"Assertion: {result.AssertionName}"); + _outputHelper.WriteLine($"Success: {result.IsSuccess}"); + _outputHelper.WriteLine($"Elapsed time: {result.ElapsedTime}"); + if (!string.IsNullOrWhiteSpace(result.ErrorMessage)) + { + _outputHelper.WriteLine($"Error Message: {result.ErrorMessage}"); + } + _outputHelper.WriteLine(""); + } + + _outputHelper.WriteLine("This is a test"); + + Assert.True(results.All(arg => arg.IsSuccess)); + } + + private class MockHttpClientFactory : IHttpClientFactory + { + public HttpClient CreateClient(string name) => new(); + } +} \ No newline at end of file diff --git a/Frank.Testing.Tests/Frank.Testing.Tests.csproj b/Frank.Testing.Tests/Frank.Testing.Tests.csproj index 136322f..d779898 100644 --- a/Frank.Testing.Tests/Frank.Testing.Tests.csproj +++ b/Frank.Testing.Tests/Frank.Testing.Tests.csproj @@ -1,10 +1,6 @@ - net8.0 - enable - enable - false true @@ -13,7 +9,12 @@ - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -25,6 +26,7 @@ + diff --git a/Frank.Testing.Tests/TestLogging/TestLoggingTests.cs b/Frank.Testing.Tests/TestLogging/TestLoggingTests.cs new file mode 100644 index 0000000..c17d3f2 --- /dev/null +++ b/Frank.Testing.Tests/TestLogging/TestLoggingTests.cs @@ -0,0 +1,47 @@ +using Frank.Testing.Logging; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +using Xunit.Abstractions; + +namespace Frank.Testing.Tests.TestLogging; + +public class TestLoggingTests(ITestOutputHelper outputHelper) +{ + [Fact] + public async Task Test1() + { + var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5)); + var host = CreateHostBuilder().Build(); + + await host.RunAsync(cancellationTokenSource.Token); + } + + private IHostBuilder CreateHostBuilder() + { + return Host.CreateDefaultBuilder() + .ConfigureLogging(logging => + { + logging.AddPulseFlowTestLoggingProvider(outputHelper, LogLevel.Information); + }) + .ConfigureServices((context, services) => + { + services.AddHostedService(); + }); + } + + private class MyService(ILogger logger) : BackgroundService + { + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + using var scope = logger.BeginScope("Titans are {Description}", "awesome"); + while (!stoppingToken.IsCancellationRequested) + { + logger.LogInformation("Hello from MyService"); + await Task.Delay(1000, stoppingToken); + } + } + } +} \ No newline at end of file diff --git a/Frank.Testing.Tests/TestLoggingTests.cs b/Frank.Testing.Tests/TestLoggingTests.cs deleted file mode 100644 index 57a0df4..0000000 --- a/Frank.Testing.Tests/TestLoggingTests.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Frank.PulseFlow; -using Frank.PulseFlow.Logging; -using Frank.Testing.Logging; - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -using Xunit.Abstractions; - -namespace Frank.Testing.Tests; - -public class TestLoggingTests -{ - private readonly ITestOutputHelper _outputHelper; - - public TestLoggingTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - - [Fact] - public void Test1() - { - var host = CreateHostBuilder().Build(); - - host.Start(); - } - - - - private IHostBuilder CreateHostBuilder() - { - return Host.CreateDefaultBuilder() - .ConfigureLogging(logging => - { - logging.ClearProviders(); - logging.AddPulseFlow(); - }) - .ConfigureServices((context, services) => - { - services.AddSingleton(_outputHelper); - services.AddPulseFlow(builder => - { - builder.AddFlow(); - }); - - services.AddHostedService(); - }); - } - - private class MyService : BackgroundService - { - private readonly ILogger _logger; - - public MyService(ILogger logger) => _logger = logger; - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - _logger.LogInformation("Hello from MyService"); - await Task.Delay(1000, stoppingToken); - } - } -} \ No newline at end of file diff --git a/Frank.Testing.Tests/TestOutputCSharpExtensionsTests.cs b/Frank.Testing.Tests/TestOutputExtensionsTests/TestOutputCSharpExtensionsTests.cs similarity index 72% rename from Frank.Testing.Tests/TestOutputCSharpExtensionsTests.cs rename to Frank.Testing.Tests/TestOutputExtensionsTests/TestOutputCSharpExtensionsTests.cs index 938dc82..7c22582 100644 --- a/Frank.Testing.Tests/TestOutputCSharpExtensionsTests.cs +++ b/Frank.Testing.Tests/TestOutputExtensionsTests/TestOutputCSharpExtensionsTests.cs @@ -1,8 +1,10 @@ -using JetBrains.Annotations; +using Frank.Testing.Tests.TestingInfrastructure; + +using JetBrains.Annotations; using Xunit.Abstractions; -namespace Frank.Testing.Tests; +namespace Frank.Testing.Tests.TestOutputExtensionsTests; [TestSubject(typeof(TestOutputCSharpExtensions))] public class TestOutputCSharpExtensionsTests(ITestOutputHelper outputHelper) diff --git a/Frank.Testing.Tests/TestOutputExtensionsTests.cs b/Frank.Testing.Tests/TestOutputExtensionsTests/TestOutputExtensionsTests.cs similarity index 92% rename from Frank.Testing.Tests/TestOutputExtensionsTests.cs rename to Frank.Testing.Tests/TestOutputExtensionsTests/TestOutputExtensionsTests.cs index a861aa8..e1cb571 100644 --- a/Frank.Testing.Tests/TestOutputExtensionsTests.cs +++ b/Frank.Testing.Tests/TestOutputExtensionsTests/TestOutputExtensionsTests.cs @@ -2,7 +2,7 @@ using Xunit.Abstractions; -namespace Frank.Testing.Tests; +namespace Frank.Testing.Tests.TestOutputExtensionsTests; [TestSubject(typeof(TestOutputJsonExtensions))] public class TestOutputExtensionsTests diff --git a/Frank.Testing.Tests/TestOutputXmlExtensionsTests.cs b/Frank.Testing.Tests/TestOutputExtensionsTests/TestOutputXmlExtensionsTests.cs similarity index 87% rename from Frank.Testing.Tests/TestOutputXmlExtensionsTests.cs rename to Frank.Testing.Tests/TestOutputExtensionsTests/TestOutputXmlExtensionsTests.cs index 8e3d3df..edd184b 100644 --- a/Frank.Testing.Tests/TestOutputXmlExtensionsTests.cs +++ b/Frank.Testing.Tests/TestOutputExtensionsTests/TestOutputXmlExtensionsTests.cs @@ -1,8 +1,10 @@ -using JetBrains.Annotations; +using Frank.Testing.Tests.TestingInfrastructure; + +using JetBrains.Annotations; using Xunit.Abstractions; -namespace Frank.Testing.Tests; +namespace Frank.Testing.Tests.TestOutputExtensionsTests; [TestSubject(typeof(TestOutputXmlExtensions))] public class TestOutputXmlExtensionsTests(ITestOutputHelper outputHelper) diff --git a/Frank.Testing.Tests/TestingInfrastructure/TestAddress.cs b/Frank.Testing.Tests/TestingInfrastructure/TestAddress.cs index 3208c74..fef89b5 100644 --- a/Frank.Testing.Tests/TestingInfrastructure/TestAddress.cs +++ b/Frank.Testing.Tests/TestingInfrastructure/TestAddress.cs @@ -1,4 +1,4 @@ -namespace Frank.Testing.Tests; +namespace Frank.Testing.Tests.TestingInfrastructure; public class TestAddress { diff --git a/Frank.Testing.Tests/TestingInfrastructure/TestPerson.cs b/Frank.Testing.Tests/TestingInfrastructure/TestPerson.cs index b0ad391..d16f6f5 100644 --- a/Frank.Testing.Tests/TestingInfrastructure/TestPerson.cs +++ b/Frank.Testing.Tests/TestingInfrastructure/TestPerson.cs @@ -1,4 +1,4 @@ -namespace Frank.Testing.Tests; +namespace Frank.Testing.Tests.TestingInfrastructure; public class TestPerson { diff --git a/Frank.Testing.sln b/Frank.Testing.sln index def1f84..0e5aa9c 100644 --- a/Frank.Testing.sln +++ b/Frank.Testing.sln @@ -20,6 +20,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Frank.Testing.Tests", "Fran EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Frank.Testing.TestOutputExtensions", "Frank.Testing.TestOutputExtensions\Frank.Testing.TestOutputExtensions.csproj", "{1C2E4123-AA8D-4A4E-B2EC-712A739F628B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Frank.Testing.ApiTesting", "Frank.Testing.ApiTesting\Frank.Testing.ApiTesting.csproj", "{3B30D75C-5B95-4E87-B862-7E8DC49038DF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,5 +43,9 @@ Global {1C2E4123-AA8D-4A4E-B2EC-712A739F628B}.Debug|Any CPU.Build.0 = Debug|Any CPU {1C2E4123-AA8D-4A4E-B2EC-712A739F628B}.Release|Any CPU.ActiveCfg = Release|Any CPU {1C2E4123-AA8D-4A4E-B2EC-712A739F628B}.Release|Any CPU.Build.0 = Release|Any CPU + {3B30D75C-5B95-4E87-B862-7E8DC49038DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B30D75C-5B95-4E87-B862-7E8DC49038DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B30D75C-5B95-4E87-B862-7E8DC49038DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B30D75C-5B95-4E87-B862-7E8DC49038DF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal