From 6880b8dc586fc176b6f618de261aae78e9b82221 Mon Sep 17 00:00:00 2001 From: chullybun Date: Tue, 16 Jan 2024 15:24:51 -0800 Subject: [PATCH] Upgrade dependencies to latest dotnet new beef targets .NET 8 --- CHANGELOG.md | 4 +- .../MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj | 5 +- .../MyEf.Hr.Business/MyEf.Hr.Business.csproj | 4 +- .../MyEf.Hr.CodeGen/MyEf.Hr.CodeGen.csproj | 2 +- .../MyEf.Hr.Database/MyEf.Hr.Database.csproj | 7 +- .../GlobalUsings.cs | 14 ++-- .../MyEf.Hr.Security.Subscriptions.csproj | 12 ++-- .../MyEf.Hr.Security.Subscriptions/Program.cs | 9 +++ .../SecuritySubscriberFunction.cs | 9 +-- .../MyEf.Hr.Security.Subscriptions/Startup.cs | 22 ++++--- .../MyEf.Hr.Security.Subscriptions/host.json | 3 +- .../MyEf.Hr.Security.Test.csproj | 3 +- .../SecuritySubscriberFunctionTest.cs | 4 +- .../EmployeeTerminatedSubscriberTest.cs | 16 ++--- .../MyEf.Hr/MyEf.Hr.Test/Apis/EmployeeTest.cs | 35 ++++------ .../Apis/PerformanceReviewTest.cs | 9 +-- .../MyEf.Hr.Test/Apis/ReferenceDataTest.cs | 6 +- .../MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj | 2 +- .../MyEf.Hr/docs/10-Service-Bus-Subscribe.md | 64 +++++++++---------- .../Company.AppName.Test/Apis/PersonTest.cs | 62 ++++++++++-------- .../Beef.Template.Solution.UnitTest.csproj | 2 +- .../TemplateTest.cs | 24 +++---- .../Beef.CodeGen.Core.csproj | 2 +- .../Beef.Database.Core.csproj | 2 +- tools/Beef.Database.Core/MigrationArgs.cs | 8 ++- .../Beef.Database.MySql.csproj | 2 +- .../Beef.Database.SqlServer.csproj | 2 +- 27 files changed, 169 insertions(+), 165 deletions(-) create mode 100644 samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Program.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index d51133191..6e49ffd44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,8 @@ Represents the **NuGet** versions. ## v5.9.0 -- *Fixed:* Upgraded `CoreEx` (`v3.9.0`) to include all related fixes and improvements; including dependent `UnitTestEx`. -- *Enhancement:* Updated the `dotnet new beef` template to default to `net8.0`. +- *Fixed:* Upgraded `CoreEx` ([`v3.9.0`](https://github.com/Avanade/CoreEx/blob/main/CHANGELOG.md#v390)) and `DbEx` ([`v2.4.0`](https://github.com/Avanade/DbEx/blob/main/CHANGELOG.md#v240)) to include all related fixes and improvements; including dependent `UnitTestEx` ([`v4.0.1`](https://github.com/Avanade/UnitTestEx/blob/main/CHANGELOG.md#v410)) and related `NUnit` ([`v4.0.1`](https://docs.nunit.org/articles/nunit/release-notes/Nunit4.0-MigrationGuide.html)) upgrades. +- *Enhancement:* Updated the `dotnet new beef` template to target `net8.0` and updated `NUnit`. ## v5.8.0 - *Fixed:* Upgraded `CoreEx` (`v3.8.0`) to include all related fixes and improvements; `WebApi` class supports returning value of `IActionResult` as-is. diff --git a/samples/MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj b/samples/MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj index 65d15e7ec..4e3c4d169 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj @@ -1,6 +1,6 @@  - net6.0 + net8.0 enable true True @@ -8,9 +8,8 @@ - + - diff --git a/samples/MyEf.Hr/MyEf.Hr.Business/MyEf.Hr.Business.csproj b/samples/MyEf.Hr/MyEf.Hr.Business/MyEf.Hr.Business.csproj index 87f28b701..36853e3dc 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Business/MyEf.Hr.Business.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.Business/MyEf.Hr.Business.csproj @@ -1,6 +1,6 @@  - net6.0 + net8.0 enable true @@ -9,7 +9,7 @@ - + diff --git a/samples/MyEf.Hr/MyEf.Hr.CodeGen/MyEf.Hr.CodeGen.csproj b/samples/MyEf.Hr/MyEf.Hr.CodeGen/MyEf.Hr.CodeGen.csproj index 7e3b73a8a..d85e28142 100644 --- a/samples/MyEf.Hr/MyEf.Hr.CodeGen/MyEf.Hr.CodeGen.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.CodeGen/MyEf.Hr.CodeGen.csproj @@ -1,7 +1,7 @@  Exe - net6.0 + net8.0 enable diff --git a/samples/MyEf.Hr/MyEf.Hr.Database/MyEf.Hr.Database.csproj b/samples/MyEf.Hr/MyEf.Hr.Database/MyEf.Hr.Database.csproj index cc375631f..d8b6a703d 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Database/MyEf.Hr.Database.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.Database/MyEf.Hr.Database.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 enable @@ -25,9 +25,4 @@ - - - - - diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/GlobalUsings.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/GlobalUsings.cs index aca1c87df..3e6764345 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/GlobalUsings.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/GlobalUsings.cs @@ -1,23 +1,23 @@ global using Azure.Messaging.ServiceBus; global using CoreEx; -global using CoreEx.Events; -global using CoreEx.Events.Subscribing; global using CoreEx.Azure.ServiceBus; global using CoreEx.Configuration; +global using CoreEx.Events; +global using CoreEx.Events.Subscribing; +global using CoreEx.Hosting; global using CoreEx.Http; global using CoreEx.Json; global using CoreEx.RefData; global using CoreEx.Results; global using CoreEx.Validation; -global using Microsoft.Azure.Functions.Extensions.DependencyInjection; -global using Microsoft.Azure.WebJobs; -global using Microsoft.Azure.WebJobs.ServiceBus; +global using Microsoft.Azure.Functions.Worker; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; -global using MyEf.Hr.Common.Entities; global using System; global using System.Collections.Generic; global using System.Linq; global using System.Text; -global using System.Threading.Tasks; \ No newline at end of file +global using System.Threading.Tasks; +global using MyEf.Hr.Common.Entities; \ No newline at end of file diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/MyEf.Hr.Security.Subscriptions.csproj b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/MyEf.Hr.Security.Subscriptions.csproj index c596db869..c95ac931c 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/MyEf.Hr.Security.Subscriptions.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/MyEf.Hr.Security.Subscriptions.csproj @@ -1,11 +1,10 @@ - net6.0 + net8.0 v4 true + Exe enable - - <_FunctionsSkipCleanOutput>true @@ -18,10 +17,11 @@ + + + + - - - diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Program.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Program.cs new file mode 100644 index 000000000..558d5018e --- /dev/null +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Program.cs @@ -0,0 +1,9 @@ +new HostBuilder() + .ConfigureFunctionsWorkerDefaults() + .ConfigureServices(services => + { + services.AddApplicationInsightsTelemetryWorkerService(); + services.ConfigureFunctionsApplicationInsights(); + }) + .ConfigureHostStartup() + .Build().Run(); \ No newline at end of file diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/SecuritySubscriberFunction.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/SecuritySubscriberFunction.cs index 3dab0d3c0..e6c8b98f2 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/SecuritySubscriberFunction.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/SecuritySubscriberFunction.cs @@ -1,13 +1,10 @@ namespace MyEf.Hr.Security.Subscriptions; -public class SecuritySubscriberFunction +public class SecuritySubscriberFunction(ServiceBusOrchestratedSubscriber subscriber) { - private readonly ServiceBusOrchestratedSubscriber _subscriber; + private readonly ServiceBusOrchestratedSubscriber _subscriber = subscriber.ThrowIfNull(); - public SecuritySubscriberFunction(ServiceBusOrchestratedSubscriber subscriber) => _subscriber = subscriber ?? throw new ArgumentNullException(nameof(subscriber)); - - [Singleton(Mode = SingletonMode.Function)] - [FunctionName(nameof(SecuritySubscriberFunction))] + [Function(nameof(SecuritySubscriberFunction))] public Task RunAsync([ServiceBusTrigger("%ServiceBusQueueName%", Connection = "ServiceBusConnectionString")] ServiceBusReceivedMessage message, ServiceBusMessageActions messageActions, CancellationToken cancellationToken) => _subscriber.ReceiveAsync(message, messageActions, null, cancellationToken); } \ No newline at end of file diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Startup.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Startup.cs index cd9da9a55..ea455214f 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Startup.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Startup.cs @@ -1,16 +1,20 @@ -[assembly: FunctionsStartup(typeof(MyEf.Hr.Security.Subscriptions.Startup))] +namespace MyEf.Hr.Security.Subscriptions; -namespace MyEf.Hr.Security.Subscriptions; - -public class Startup : FunctionsStartup +/// +/// The to enable testable dependency injection. +/// +public class Startup : HostStartup { - public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder) => builder.ConfigurationBuilder - .AddJsonFile(Path.Combine(builder.GetContext().ApplicationRootPath ?? "", "appsettings.json"), optional: true) - .AddEnvironmentVariables("Hr_"); + /// + public override void ConfigureAppConfiguration(HostBuilderContext context, IConfigurationBuilder config) + { + config.AddEnvironmentVariables("MyEf_Hr_"); + } - public override void Configure(IFunctionsHostBuilder builder) + /// + public override void ConfigureServices(IServiceCollection services) { - builder.Services + services .AddSettings() .AddExecutionContext() .AddJsonSerializer() diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/host.json b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/host.json index 1f75a07a1..d4760978c 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/host.json +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/host.json @@ -5,7 +5,8 @@ "samplingSettings": { "isEnabled": true, "excludedTypes": "Request" - } + }, + "enableLiveMetricsFilters": true }, "logLevel": { "default": "Warning", diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Test/MyEf.Hr.Security.Test.csproj b/samples/MyEf.Hr/MyEf.Hr.Security.Test/MyEf.Hr.Security.Test.csproj index 6b88d0c3f..e5512da1b 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Security.Test/MyEf.Hr.Security.Test.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Test/MyEf.Hr.Security.Test.csproj @@ -1,10 +1,9 @@ - net6.0 + net8.0 enable enable - false diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Test/SecuritySubscriberFunctionTest.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Test/SecuritySubscriberFunctionTest.cs index 62d3b23d7..3c0fdad6c 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Security.Test/SecuritySubscriberFunctionTest.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Test/SecuritySubscriberFunctionTest.cs @@ -7,7 +7,7 @@ public class SecuritySubscriberFunctionTest public void InvalidMessage_DeadLetter() { using var test = FunctionTester.Create(); - var actions = test.CreateWebJobsServiceBusMessageActions(); + var actions = test.CreateWorkerServiceBusMessageActions(); var message = test.CreateServiceBusMessageFromValue(null!); test.ServiceBusTrigger() @@ -21,7 +21,7 @@ public void InvalidMessage_DeadLetter() public void NotSubscribed_CompleteSilent() { using var test = FunctionTester.Create(); - var actions = test.CreateWebJobsServiceBusMessageActions(); + var actions = test.CreateWorkerServiceBusMessageActions(); var message = test.CreateServiceBusMessage(new EventData { Subject = "myef.hr.employee", Action = "updated", Source = new Uri("test", UriKind.Relative) }); test.ServiceBusTrigger() diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Test/Subscribers/EmployeeTerminatedSubscriberTest.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Test/Subscribers/EmployeeTerminatedSubscriberTest.cs index 6fe3ed648..9ded8855a 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Security.Test/Subscribers/EmployeeTerminatedSubscriberTest.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Test/Subscribers/EmployeeTerminatedSubscriberTest.cs @@ -8,7 +8,7 @@ public void ValueIsRequired_DeadLetter() { using var test = FunctionTester.Create(); var message = test.CreateServiceBusMessage(new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = null! }); - var actions = test.CreateServiceBusMessageActions(); + var actions = test.CreateWorkerServiceBusMessageActions(); test.ServiceBusTrigger() .Run(f => f.RunAsync(message, actions, default)) @@ -22,7 +22,7 @@ public void ValidationError_DeadLetter() { using var test = FunctionTester.Create(); var message = test.CreateServiceBusMessage(new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee() }); - var actions = test.CreateServiceBusMessageActions(); + var actions = test.CreateWorkerServiceBusMessageActions(); test.ServiceBusTrigger() .ExpectLogContains("A data validation error occurred") @@ -40,7 +40,7 @@ public void EmailNotFound_None_Complete() mc.Request(HttpMethod.Get, "/api/v1/users?search=profile.email eq \"bob@email.com\"").Respond.WithJson("[]", HttpStatusCode.OK); using var test = FunctionTester.Create(); - var actions = test.CreateServiceBusMessageActions(); + var actions = test.CreateWorkerServiceBusMessageActions(); var message = test.CreateServiceBusMessage( new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee { Id = 1.ToGuid(), Email = "bob@email.com", Termination = new() } }); @@ -62,7 +62,7 @@ public void EmailNotFound_Multi_Complete() mc.Request(HttpMethod.Get, "/api/v1/users?search=profile.email eq \"bob@email.com\"").Respond.WithJson("[{},{}]", HttpStatusCode.OK); using var test = FunctionTester.Create(); - var actions = test.CreateServiceBusMessageActions(); + var actions = test.CreateWorkerServiceBusMessageActions(); var message = test.CreateServiceBusMessage( new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee { Id = 1.ToGuid(), Email = "bob@email.com", Termination = new() } }); @@ -84,7 +84,7 @@ public void OktaForbidden_Retry() mc.Request(HttpMethod.Get, "/api/v1/users?search=profile.email eq \"bob@email.com\"").Respond.With(HttpStatusCode.Forbidden); using var test = FunctionTester.Create(); - var actions = test.CreateWebJobsServiceBusMessageActions(); + var actions = test.CreateWorkerServiceBusMessageActions(); var message = test.CreateServiceBusMessage( new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee { Id = 1.ToGuid(), Email = "bob@email.com", Termination = new() } }); @@ -111,7 +111,7 @@ public void OktaServiceUnavailable_Retry() }); using var test = FunctionTester.Create(); - var actions = test.CreateWebJobsServiceBusMessageActions(); + var actions = test.CreateWorkerServiceBusMessageActions(); var message = test.CreateServiceBusMessage( new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee { Id = 1.ToGuid(), Email = "bob@email.com", Termination = new() } }); @@ -134,7 +134,7 @@ public void SuccessDeactivated_Complete() mc.Request(HttpMethod.Post, "/api/v1/users/00ub0oNGTSWTBKOLGLNR/lifecycle/deactivate?sendEmail=true").Respond.With(HttpStatusCode.OK); using var test = FunctionTester.Create(); - var actions = test.CreateWebJobsServiceBusMessageActions(); + var actions = test.CreateWorkerServiceBusMessageActions(); var message = test.CreateServiceBusMessage( new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee { Id = 1.ToGuid(), Email = "bob@email.com", Termination = new() } }); @@ -155,7 +155,7 @@ public void SuccessAlreadyDeactivated_Complete() mc.Request(HttpMethod.Get, "/api/v1/users?search=profile.email eq \"bob@email.com\"").Respond.WithJson(new [] { new { id = "00ub0oNGTSWTBKOLGLNR", status = "DEACTIVATED" } }); using var test = FunctionTester.Create(); - var actions = test.CreateWebJobsServiceBusMessageActions(); + var actions = test.CreateWorkerServiceBusMessageActions(); var message = test.CreateServiceBusMessage( new EventData { Subject = "myef.hr.employee", Action = "terminated", Source = new Uri("test", UriKind.Relative), Value = new Employee { Id = 1.ToGuid(), Email = "bob@email.com", Termination = new() } }); diff --git a/samples/MyEf.Hr/MyEf.Hr.Test/Apis/EmployeeTest.cs b/samples/MyEf.Hr/MyEf.Hr.Test/Apis/EmployeeTest.cs index 50adccb41..acabfde47 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Test/Apis/EmployeeTest.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Test/Apis/EmployeeTest.cs @@ -57,7 +57,7 @@ public void A130_Get_Found_WithAddress() StartDate = new DateTime(2001, 01, 22), PhoneNo = "(428) 893 2793", Address = new Address { Street1 = "8365 851 PL NE", City = "Redmond", State = "WA", PostCode = "98052" }, - EmergencyContacts = new EmergencyContactCollection { new EmergencyContact { Id = 401.ToGuid(), FirstName = "Michael", LastName = "Manners", PhoneNo = "(234) 297 9834", Relationship = "FRD" } } + EmergencyContacts = [new EmergencyContact { Id = 401.ToGuid(), FirstName = "Michael", LastName = "Manners", PhoneNo = "(234) 297 9834", Relationship = "FRD" }] }) .Run(a => a.GetAsync(4.ToGuid())); } @@ -110,8 +110,7 @@ public void A210_GetByArgs_All() Assert.That(v, Is.Not.Null); Assert.Multiple(() => { - Assert.That(v!.Items, Is.Not.Null); - Assert.That(v.Items, Has.Count.EqualTo(3)); + Assert.That(v.Items, Is.Not.Null.And.Count.EqualTo(3)); Assert.That(v.Items.Select(x => x.LastName).ToArray(), Is.EqualTo(new string[] { "Browne", "Jones", "Smithers" })); }); } @@ -127,8 +126,7 @@ public void A220_GetByArgs_All_Paging() Assert.That(v, Is.Not.Null); Assert.Multiple(() => { - Assert.That(v!.Items, Is.Not.Null); - Assert.That(v.Items, Has.Count.EqualTo(2)); + Assert.That(v.Items, Is.Not.Null.And.Count.EqualTo(2)); Assert.That(v.Items.Select(x => x.LastName).ToArray(), Is.EqualTo(new string[] { "Jones", "Smith" })); }); @@ -148,8 +146,7 @@ public void A230_GetByArgs_FirstName() Assert.That(v, Is.Not.Null); Assert.Multiple(() => { - Assert.That(v!.Items, Is.Not.Null); - Assert.That(v.Items, Has.Count.EqualTo(2)); + Assert.That(v.Items, Is.Not.Null.And.Count.EqualTo(2)); Assert.That(v.Items.Select(x => x.LastName).ToArray(), Is.EqualTo(new string[] { "Browne", "Smithers" })); }); } @@ -164,8 +161,7 @@ public void A240_GetByArgs_LastName() Assert.That(v, Is.Not.Null); Assert.Multiple(() => { - Assert.That(v!.Items, Is.Not.Null); - Assert.That(v.Items, Has.Count.EqualTo(1)); + Assert.That(v.Items, Is.Not.Null.And.Count.EqualTo(1)); Assert.That(v.Items.Select(x => x.LastName).ToArray(), Is.EqualTo(new string[] { "Smithers" })); }); } @@ -180,8 +176,7 @@ public void A250_GetByArgs_LastName_IncludeTerminated() Assert.That(v, Is.Not.Null); Assert.Multiple(() => { - Assert.That(v!.Items, Is.Not.Null); - Assert.That(v.Items, Has.Count.EqualTo(2)); + Assert.That(v.Items, Is.Not.Null.And.Count.EqualTo(2)); Assert.That(v.Items.Select(x => x.LastName).ToArray(), Is.EqualTo(new string[] { "Smith", "Smithers" })); }); } @@ -191,13 +186,12 @@ public void A260_GetByArgs_Gender() { var v = Agent() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(new EmployeeArgs { Genders = new List { "F" } })).Value; + .Run(a => a.GetByArgsAsync(new EmployeeArgs { Genders = ["F"] })).Value; Assert.That(v, Is.Not.Null); Assert.Multiple(() => { - Assert.That(v!.Items, Is.Not.Null); - Assert.That(v.Items, Has.Count.EqualTo(2)); + Assert.That(v.Items, Is.Not.Null.And.Count.EqualTo(2)); Assert.That(v.Items.Select(x => x.LastName).ToArray(), Is.EqualTo(new string[] { "Browne", "Jones" })); }); } @@ -207,7 +201,7 @@ public void A270_GetByArgs_Empty() { Agent() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(new EmployeeArgs { LastName = "s*", FirstName = "b*", Genders = new List { "F" } })) + .Run(a => a.GetByArgsAsync(new EmployeeArgs { LastName = "s*", FirstName = "b*", Genders = ["F"] })) .AssertJson("[]"); } @@ -216,7 +210,7 @@ public void A280_GetByArgs_FieldSelection() { var r = Agent() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(new EmployeeArgs { Genders = new List { "F" } }, requestOptions: new HttpRequestOptions().Include("firstname", "lastname"))) + .Run(a => a.GetByArgsAsync(new EmployeeArgs { Genders = ["F"] }, requestOptions: new HttpRequestOptions().Include("firstname", "lastname"))) .AssertJson("[{\"firstName\":\"Rachael\",\"lastName\":\"Browne\"},{\"firstName\":\"Wendy\",\"lastName\":\"Jones\"}]"); } @@ -225,13 +219,12 @@ public void A290_GetByArgs_RefDataText() { var r = Agent() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(new EmployeeArgs { Genders = new List { "F" } }, requestOptions: new HttpRequestOptions { IncludeText = true })); + .Run(a => a.GetByArgsAsync(new EmployeeArgs { Genders = ["F"] }, requestOptions: new HttpRequestOptions { IncludeText = true })); Assert.That(r.Value, Is.Not.Null); Assert.Multiple(() => { - Assert.That(r.Value!.Items, Is.Not.Null); - Assert.That(r.Value.Items, Has.Count.EqualTo(2)); + Assert.That(r.Value.Items, Is.Not.Null.And.Count.EqualTo(2)); Assert.That(r.Value.Items.Select(x => x.LastName).ToArray(), Is.EqualTo(new string[] { "Browne", "Jones" })); Assert.That(r.Value.Items.Select(x => x.GenderText).ToArray(), Is.EqualTo(new string[] { "Female", "Female" })); }); @@ -243,7 +236,7 @@ public void A300_GetByArgs_ArgsError() Agent() .ExpectStatusCode(HttpStatusCode.BadRequest) .ExpectErrors("Genders contains one or more invalid items.") - .Run(a => a.GetByArgsAsync(new EmployeeArgs { Genders = new List { "Q" } })); + .Run(a => a.GetByArgsAsync(new EmployeeArgs { Genders = ["Q"] })); } #endregion @@ -263,7 +256,7 @@ public void B110_Create() StartDate = Cleaner.Clean(DateTime.Today, DateTimeTransform.DateOnly), PhoneNo = "(456) 789 0123", Address = new Address { Street1 = "2732 85 PL NE", City = "Bellevue", State = "WA", PostCode = "98101" }, - EmergencyContacts = new EmergencyContactCollection { new EmergencyContact { FirstName = "Danny", LastName = "Keen", PhoneNo = "(234) 297 9834", Relationship = "FRD" } } + EmergencyContacts = [new EmergencyContact { FirstName = "Danny", LastName = "Keen", PhoneNo = "(234) 297 9834", Relationship = "FRD" }] }; // Create value. diff --git a/samples/MyEf.Hr/MyEf.Hr.Test/Apis/PerformanceReviewTest.cs b/samples/MyEf.Hr/MyEf.Hr.Test/Apis/PerformanceReviewTest.cs index fc17f2ce7..03d8bece0 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Test/Apis/PerformanceReviewTest.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Test/Apis/PerformanceReviewTest.cs @@ -49,8 +49,7 @@ public void A210_GetByEmployeeId_NotFound() .Run(a => a.GetByEmployeeIdAsync(4.ToGuid())).Value!; Assert.That(v, Is.Not.Null); - Assert.That(v.Items, Is.Not.Null); - Assert.That(v.Items.Count, Is.EqualTo(0)); + Assert.That(v.Items, Is.Not.Null.And.Count.EqualTo(0)); } [Test] @@ -61,8 +60,7 @@ public void A220_GetByEmployeeId() .Run(a => a.GetByEmployeeIdAsync(2.ToGuid())).Value!; Assert.That(v, Is.Not.Null); - Assert.That(v.Items, Is.Not.Null); - Assert.That(v.Items, Has.Count.EqualTo(2)); + Assert.That(v.Items, Is.Not.Null.And.Count.EqualTo(2)); Assert.That(v.Items.Select(x => x.Notes).ToArray(), Is.EqualTo(new string[] { "Work quality low.", "Work quality below standard." })); } @@ -74,8 +72,7 @@ public void A220_GetByEmployeeId_Last() .Run(a => a.GetByEmployeeIdAsync(2.ToGuid(), PagingArgs.CreateSkipAndTake(0, 1))).Value!; Assert.That(v, Is.Not.Null); - Assert.That(v.Items, Is.Not.Null); - Assert.That(v.Items, Has.Count.EqualTo(1)); + Assert.That(v.Items, Is.Not.Null.And.Count.EqualTo(1)); Assert.That(v.Items.Select(x => x.Notes).ToArray(), Is.EqualTo(new string[] { "Work quality low." })); } diff --git a/samples/MyEf.Hr/MyEf.Hr.Test/Apis/ReferenceDataTest.cs b/samples/MyEf.Hr/MyEf.Hr.Test/Apis/ReferenceDataTest.cs index d9ca8033e..a8e24f26f 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Test/Apis/ReferenceDataTest.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Test/Apis/ReferenceDataTest.cs @@ -21,7 +21,7 @@ public void A110_GendersAll() public void A120_GendersFilter() { Agent() - .Run(a => a.GenderGetAllAsync(new ReferenceDataFilter { Codes = new string[] { "F" } })) + .Run(a => a.GenderGetAllAsync(new ReferenceDataFilter { Codes = ["F"] })) .AssertOK() .AssertJsonFromResource("RefDataGendersFilter_Response.json", "id", "etag"); } @@ -30,8 +30,8 @@ public void A120_GendersFilter() public void A130_GetNamed() { Agent() - .Run(a => a.GetNamedAsync(new string[] { "Gender" })) + .Run(a => a.GetNamedAsync(["Gender"])) .AssertOK() - .AssertJsonFromResource("RefDataGetNamed_Response.json", "gender.id", "gender.etag", "gender.entitykey"); + .AssertJsonFromResource("RefDataGetNamed_Response.json", "gender.id", "gender.etag"); } } \ No newline at end of file diff --git a/samples/MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj b/samples/MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj index f9aff2bbc..21c63c6e7 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 false enable enable diff --git a/samples/MyEf.Hr/docs/10-Service-Bus-Subscribe.md b/samples/MyEf.Hr/docs/10-Service-Bus-Subscribe.md index 837d235ed..28a2ca981 100644 --- a/samples/MyEf.Hr/docs/10-Service-Bus-Subscribe.md +++ b/samples/MyEf.Hr/docs/10-Service-Bus-Subscribe.md @@ -2,7 +2,9 @@ At a high-level this represents the subscribing of events/messages from Azure Service Bus to meet the stated requirements of when an Employee is _terminated_ that the Employee's User Account will be automatically _deactivated_ within OKTA. -This article contains an end-to-end walkthrough to build the requisite Azure Function. +This article contains an end-to-end walkthrough to build the requisite Azure Function. + +> _Note:_ as of `Beef` (`v5.9.0`), and corresponding `CoreEx` ([`v3.9.0`](https://github.com/Avanade/CoreEx/blob/main/CHANGELOG.md#v390)), [isolated](https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide) Azure Function support was added. This documentation and example have been updated accordingly.
@@ -37,7 +39,7 @@ Class | Description ## Service Bus subscribing -The [`CoreEx.Azure.ServiceBus`](https://github.com/Avanade/CoreEx/tree/main/src/CoreEx.Azure/ServiceBus) capabilities will be leveraged to manage the underlying processing once triggered. This has the advantage of enabling standardized, consistent, and tested, functionality to further industrailize event/messaging subscription services. +The [`CoreEx.Azure.ServiceBus`](https://github.com/Avanade/CoreEx/tree/main/src/CoreEx.Azure/ServiceBus) capabilities will be leveraged to manage the underlying processing once triggered. This has the advantage of enabling standardized, consistent, and tested, functionality to further industrialize event/messaging subscription services. There are two Azure Service Bus subscribing capabilities that both inherit from the aforementioned `EventSubscriberBase`. These each also leverage the [`ServiceBusSubscriberInvoker`](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.Azure/ServiceBus/ServiceBusSubscriberInvoker.cs) that ensures consistency of logging, exception handling, associated [`ServiceBusMessageActions`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.webjobs.servicebus.servicebusmessageactions) management to perform the corresponding `CompleteMessageAsync` or `DeadLetterMessageAsync`, and finally message bubbling to enable retries where the error is considered transient in nature. @@ -60,11 +62,14 @@ This sample has used a basic queue for simplicity. ## Create Azure Function project -From Visual Studio, add a new Project named `MyEf.Hr.Security.Subscriptions` (within the existing `MyEf.Hr` solution) leveraging the _Azure Functions_ project template. On the additional information page of the wizard, enter the following, then _Create_ for the new `SecuritySubscriberFunction` function. +From Visual Studio, add a new Project named `MyEf.Hr.Security.Subscriptions` (within the existing `MyEf.Hr` solution) leveraging the _Azure Functions_ project template. + +On the additional information page of the wizard, enter the following, then _Create_ for the new `SecuritySubscriberFunction` function. Property | Value -|- -Functions worker | `.NET 6.0 (Long Term Support)` +Functions worker | `.NET 8.0 Isolated (Long Term Support)` +Function | `Service Bus Queue Trigger` Connection string setting name | `ServiceBusConnectionString` Queue name | `%ServiceBusQueueName%` @@ -76,10 +81,8 @@ Then complete the following house cleaning tasks within the newly created projec Update project dependencies as follows. -1. Update the `Microsoft.Azure.WebJobs.Extensions.ServiceBus` NuGet package dependency to latest `5.x.x` version. -2. Add the `Microsoft.Azure.Functions.Extensions` NuGet package as a dependency to enable Dependency Injection (DI). -3. Add the `CoreEx.Azure` and `CoreEx.Validation` NuGet packages as dependencies. -4. Add `MyHr.Ef.Common` as a project reference dependency (within a real implemenation the `*.Common` assemblies should be published as internal packages for reuse across domains; that is largely their purpose). +1. Add the `CoreEx.Azure` and `CoreEx.Validation` NuGet packages as dependencies. +2. Add `MyHr.Ef.Common` as a project reference dependency (within a real implemenation the `*.Common` assemblies should be published as internal packages for reuse across domains; that is largely their purpose).
@@ -87,20 +90,20 @@ Update project dependencies as follows. Open the `host.json` file and replace with the contents from [`host.json`](../MyEf.Hr.Security.Subscriptions/host.json). -The [configuration](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-service-bus) within will ensure that _only_ a _single_ message can be processed at a time, and that there is also _no_ automatic completion of messages. +The bindings configuration [documentation](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-service-bus) will assist with understanding and further configuring.
### Local settings -Open the `local.settings.json` file and replace `Values` JSON with the following. +Open the `local.settings.json` file and replace `Values` JSON with the following; including your actual connection string secret. ``` json { "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "FUNCTIONS_WORKER_RUNTIME": "dotnet", + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", "ServiceBusConnectionString": "get-your-secret-and-paste-here", "ServiceBusQueueName": "event-stream" } @@ -109,27 +112,21 @@ Open the `local.settings.json` file and replace `Values` JSON with the following
-### Project JSON - -Open the `MyEf.Hr.Security.Subscriptions` project XML file and add the following within the `` XML element. This is needed to resolve an issue related to the usage of `System.Memory.Data v6.0.0`. +### Global usings -``` xml - -<_FunctionsSkipCleanOutput>true -enable -``` +Create a new `GlobalUsings.cs` file, then copy in the contents from [`GlobalUsings`](../MyEf.Hr.Security.Subscriptions/GlobalUsings.cs) replacing existing.
-### Global usings +### Program host -Create a new `GlobalUsings.cs` file, then copy in the contents from [`GlobalUsings`](../MyEf.Hr.Security.Subscriptions/GlobalUsings.cs) replacing existing. +Open the `Program.cs` file and replace with the contents from [`Program.cs`](../MyEf.Hr.Security.Subscriptions/Program.cs). This is essentially the same as the default; however, a `Startup` class is now referenced using `ConfigureHostStartup`. This is a `CoreEx.Hosting` extension method that will enable the requisite [Dependency Injection](#Dependency_injection) to also be used within unit testing.
### Dependency injection -[Dependency Injection](https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection) needs to be added to the Project as this is not enabled by default. To enable, create a new `Startup.cs` file, then copy in the contents from [`Startup`](../MyEf.Hr.Security.Subscriptions/Startup.cs) replacing existing. For the most part the required `CoreEx` services are being registered. +Dependency Injection needs to be added, which is also accessible from unit tests. To enable, create a new `Startup.cs` file, then copy in the contents from [`Startup`](../MyEf.Hr.Security.Subscriptions/Startup.cs). For the most part the required `CoreEx` services are being registered. The Service Bus subscribing requires the following additional services registered. @@ -161,23 +158,22 @@ The aforementioned services registration code of interest is as follows. This represents the Azure Service Bus trigger subscription entry point; i.e. what is the registered function logic to be executed by the Azure Function runtime fabric. This requires the use of Dependency Injection to access the registered `ServiceBusOrchestratedSubscriber` to orchestrate the underlying subscribers. -The function method signature must include the [ServiceBusTrigger](https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.webjobs.servicebustriggerattribute) attribute to specify the queue or topic/subscription related properties, sessions support, as well as the Service Bus connection string name. Finally, for the `ServiceBusOrchestratedSubscriber` to function correctly the `ServiceBusReceivedMessage` and `ServiceBusMessageActions` parameters must be specified and passed into the `ServiceBusOrchestratedSubscriber.ReceiveAsync`. Note: do _not_ under any circumstances use `AutoCompleteMessages` as completion is managed internally. +The function method signature must include the [ServiceBusTrigger](https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.webjobs.servicebustriggerattribute) attribute to specify the queue or topic/subscription related properties, sessions support, as well as the Service Bus connection string name. Finally, for the `ServiceBusOrchestratedSubscriber` to function correctly the `ServiceBusReceivedMessage` and `ServiceBusMessageActions` parameters must be specified and passed into the `ServiceBusOrchestratedSubscriber.ReceiveAsync`. + +> Note: do **not** under any circumstances use `AutoCompleteMessages` as completion is managed internally. Rename the `Function1.cs` to `SecuritySubscriberFunction.cs` where this did not occur correctly. Copy and replace the contents from the following. ``` csharp namespace MyEf.Hr.Security.Subscriptions; -public class SecuritySubscriberFunction +public class SecuritySubscriberFunction(ServiceBusOrchestratedSubscriber subscriber) { - private readonly ServiceBusOrchestratedSubscriber _subscriber; - - public SecuritySubscriberFunction(ServiceBusOrchestratedSubscriber subscriber) => _subscriber = subscriber ?? throw new ArgumentNullException(nameof(subscriber)); + private readonly ServiceBusOrchestratedSubscriber _subscriber = subscriber.ThrowIfNull(); - [Singleton(Mode = SingletonMode.Function)] - [FunctionName(nameof(SecuritySubscriberFunction))] + [Function(nameof(SecuritySubscriberFunction))] public Task RunAsync([ServiceBusTrigger("%ServiceBusQueueName%", Connection = "ServiceBusConnectionString")] ServiceBusReceivedMessage message, ServiceBusMessageActions messageActions, CancellationToken cancellationToken) - => _subscriber.ReceiveAsync(message, messageActions, cancellationToken); + => _subscriber.ReceiveAsync(message, messageActions, null, cancellationToken); } ``` @@ -348,8 +344,8 @@ public class EmployeeTerminatedSubcriber : SubscriberBase public override Task ReceiveAsync(EventData @event, EventSubscriberArgs args, CancellationToken cancellationToken) => Result.GoAsync(_okta.GetUserAsync(@event.Value.Id, @event.Value.Email!)) - .When(user => !user.IsDeactivatable, user => _logger.LogWarning("Employee {EmployeeId} with email {Email} has User status of {UserStatus} and is therefore unable to be deactivated.", @event.Value.Id, @event.Value.Email, user.Status)) - .WhenAsAsync(user => user.IsDeactivatable, user => _okta.DeactivateUserAsync(user.Id!)); + .When(user => !user.IsDeactivatable, user => _logger.LogWarning("Employee {EmployeeId} with email {Email} has User status of {UserStatus} and is therefore unable to be deactivated.", @event.Value.Id, @event.Value.Email, user.Status)) + .WhenAsAsync(user => user.IsDeactivatable, user => _okta.DeactivateUserAsync(user.Id!)); } ``` @@ -359,7 +355,7 @@ public class EmployeeTerminatedSubcriber : SubscriberBase There is generally no specific provision for the unit testing of Azure Functions, and related Azure Service Bus trigger, as it requires a dependent messaging subsystem, being Azure Service Bus. Also, at time of writing, there is not a standard means of hosting the Azure Function in process to verify in a unit testing context; especially where the likes of Dependency Injection (DI) is being leveraged. -However, given the complexity of logic that typically resides within there should be _strong_ desire to verfiy via unit tests. To enable Avanade has created _UnitTestEx_ which supports the unit testing of [Service Bus-trigger Azure Functions](https://github.com/Avanade/unittestex#service-bus-trigger-azure-function). This capability assumes that Dependency Injection (DI) is being leveraged, and will create the underlying host and enable a `ServiceBusReceivedMessage` to be sent simulating the Azure Function runtime capability. +However, given the complexity of logic that typically resides within there should be a _strong_ desire to verfiy via unit tests. To enable Avanade has created _UnitTestEx_ which supports the unit testing of [Service Bus-trigger Azure Functions](https://github.com/Avanade/unittestex#service-bus-trigger-azure-function). This capability assumes that Dependency Injection (DI) is being leveraged, and will create the underlying host and enable a `ServiceBusReceivedMessage` to be sent simulating the Azure Function runtime capability. As the implemented subscriber is invoking OKTA, _UnitTestEx_ also easily enables the [mocking of HTTP requests/responses](https://github.com/Avanade/unittestex#http-client-mocking) to verify both success and failure scenarios. @@ -453,4 +449,4 @@ The new _Security_ domain that performs a [Service Bus Subscribe](./Service-Bus- ## Next Step -Next we will [wrap up](./../README.md#Conclusion) the sample - we are done! +Next we will [wrap up](./../README.md#Conclusion) the sample - we are done - BOOM! diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Test/Apis/PersonTest.cs b/templates/Beef.Template.Solution/content/Company.AppName.Test/Apis/PersonTest.cs index 1da816984..d92d59782 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Test/Apis/PersonTest.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Test/Apis/PersonTest.cs @@ -7,7 +7,7 @@ public class PersonTest : UsingApiTester { #if (!implement_httpagent) [OneTimeSetUp] - public void OneTimeSetUp() => Assert.IsTrue(TestSetUp.Default.SetUp()); + public void OneTimeSetUp() => Assert.That(TestSetUp.Default.SetUp(), Is.True); #endif #region Get @@ -85,7 +85,7 @@ public void A120_Get_Modified_NotModified() .Run(a => a.GetAsync(1, new HttpRequestOptions { ETag = TestSetUp.Default.ConcurrencyErrorETag })).Value!; #endif - Assert.NotNull(v); + Assert.That(v, Is.Not.Null); Agent() .ExpectStatusCode(HttpStatusCode.NotModified) @@ -108,10 +108,12 @@ public void A210_GetByArgs_All() .ExpectStatusCode(HttpStatusCode.OK) .Run(a => a.GetByArgsAsync(null)).Value!; - Assert.IsNotNull(v); - Assert.IsNotNull(v.Items); - Assert.AreEqual(4, v.Items.Count); - Assert.AreEqual(new string[] { "Browne", "Jones", "Smith", "Smithers" }, v.Items.Select(x => x.LastName).ToArray()); + Assert.That(v, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(v!.Items, Is.Not.Null.And.Count.EqualTo(4)); + Assert.That(v.Items.Select(x => x.LastName).ToArray(), Is.EqualTo(new string[] { "Browne", "Jones", "Smith", "Smithers" })); + }); } [Test] @@ -121,10 +123,12 @@ public void A220_GetByArgs_Paging() .ExpectStatusCode(HttpStatusCode.OK) .Run(a => a.GetByArgsAsync(null, PagingArgs.CreateSkipAndTake(1, 2))).Value!; - Assert.IsNotNull(v); - Assert.IsNotNull(v.Items); - Assert.AreEqual(2, v.Items.Count); - Assert.AreEqual(new string[] { "Jones", "Smith" }, v.Items.Select(x => x.LastName).ToArray()); + Assert.That(v, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(v!.Items, Is.Not.Null.And.Count.EqualTo(2)); + Assert.That(v.Items.Select(x => x.LastName).ToArray(), Is.EqualTo(new string[] { "Jones", "Smith" })); + }); } [Test] @@ -134,10 +138,12 @@ public void A230_GetByArgs_FirstName() .ExpectStatusCode(HttpStatusCode.OK) .Run(a => a.GetByArgsAsync(new PersonArgs { FirstName = "*a*" })).Value!; - Assert.IsNotNull(v); - Assert.IsNotNull(v.Items); - Assert.AreEqual(3, v.Items.Count); - Assert.AreEqual(new string[] { "Browne", "Smith", "Smithers" }, v.Items.Select(x => x.LastName).ToArray()); + Assert.That(v, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(v!.Items, Is.Not.Null.And.Count.EqualTo(3)); + Assert.That(v.Items.Select(x => x.LastName).ToArray(), Is.EqualTo(new string[] { "Browne", "Smith", "Smithers" })); + }); } [Test] @@ -147,10 +153,12 @@ public void A240_GetByArgs_LastName() .ExpectStatusCode(HttpStatusCode.OK) .Run(a => a.GetByArgsAsync(new PersonArgs { LastName = "s*" })).Value!; - Assert.IsNotNull(v); - Assert.IsNotNull(v.Items); - Assert.AreEqual(2, v.Items.Count); - Assert.AreEqual(new string[] { "Smith", "Smithers" }, v.Items.Select(x => x.LastName).ToArray()); + Assert.That(v, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(v!.Items, Is.Not.Null.And.Count.EqualTo(2)); + Assert.That(v.Items.Select(x => x.LastName).ToArray(), Is.EqualTo(new string[] { "Smith", "Smithers" })); + }); } [Test] @@ -158,12 +166,14 @@ public void A250_GetByArgs_Gender() { var v = Agent() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(new PersonArgs { Genders = new List { "F" } })).Value!; + .Run(a => a.GetByArgsAsync(new PersonArgs { Genders = ["F"] })).Value!; - Assert.IsNotNull(v); - Assert.IsNotNull(v.Items); - Assert.AreEqual(2, v.Items.Count); - Assert.AreEqual(new string[] { "Browne", "Jones" }, v.Items.Select(x => x.LastName).ToArray()); + Assert.That(v, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(v!.Items, Is.Not.Null.And.Count.EqualTo(2)); + Assert.That(v.Items.Select(x => x.LastName).ToArray(), Is.EqualTo(new string[] { "Browne", "Jones" })); + }); } [Test] @@ -171,7 +181,7 @@ public void A260_GetByArgs_Empty() { Agent() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(new PersonArgs { LastName = "s*", FirstName = "b*", Genders = new List { "F" } })) + .Run(a => a.GetByArgsAsync(new PersonArgs { LastName = "s*", FirstName = "b*", Genders = ["F"] })) .AssertJson("[]"); } @@ -180,7 +190,7 @@ public void A270_GetByArgs_FieldSelection() { Agent() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(new PersonArgs { Genders = new List { "F" } }, requestOptions: new HttpRequestOptions().Include("firstname", "lastname"))) + .Run(a => a.GetByArgsAsync(new PersonArgs { Genders = ["F"] }, requestOptions: new HttpRequestOptions().Include("firstname", "lastname"))) .AssertJson("[{\"firstName\":\"Rachael\",\"lastName\":\"Browne\"},{\"firstName\":\"Wendy\",\"lastName\":\"Jones\"}]"); } @@ -189,7 +199,7 @@ public void A280_GetByArgs_RefDataText() { var r = Agent() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(new PersonArgs { Genders = new List { "F" } }, requestOptions: new HttpRequestOptions { IncludeText = true })) + .Run(a => a.GetByArgsAsync(new PersonArgs { Genders = ["F"] }, requestOptions: new HttpRequestOptions { IncludeText = true })) .AssertJsonFromResource("A280_GetByArgs_RefDataText-Response.json", "etag", "changeLog"); } diff --git a/tests/Beef.Template.Solution.UnitTest/Beef.Template.Solution.UnitTest.csproj b/tests/Beef.Template.Solution.UnitTest/Beef.Template.Solution.UnitTest.csproj index 49609008c..cad66f6e5 100644 --- a/tests/Beef.Template.Solution.UnitTest/Beef.Template.Solution.UnitTest.csproj +++ b/tests/Beef.Template.Solution.UnitTest/Beef.Template.Solution.UnitTest.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net8.0 false diff --git a/tests/Beef.Template.Solution.UnitTest/TemplateTest.cs b/tests/Beef.Template.Solution.UnitTest/TemplateTest.cs index 1acc10dd9..1e4a8540f 100644 --- a/tests/Beef.Template.Solution.UnitTest/TemplateTest.cs +++ b/tests/Beef.Template.Solution.UnitTest/TemplateTest.cs @@ -57,7 +57,7 @@ private static (int exitCode, string stdOut) ExecuteCommand(string filename, str return (process.ExitCode, sb.ToString()); } - public void OneTimeSetUp() + private static void OneTimeSetUp() { if (!_firstTime) return; @@ -81,28 +81,28 @@ public void OneTimeSetUp() // Remove existing cached Beef nuget packages. var (exitCode, stdOut) = ExecuteCommand("dotnet", "nuget locals global-packages --list"); - Assert.GreaterOrEqual(0, exitCode); + Assert.That(exitCode, Is.AtLeast(0)); var nugets = new DirectoryInfo(stdOut.Replace("info : global-packages: ", string.Empty).Replace("global-packages: ", string.Empty)); - Assert.IsTrue(nugets.Exists); + Assert.That(nugets.Exists, Is.True); foreach (var di in nugets.EnumerateDirectories().Where(x => x.Name.StartsWith("beef."))) { di.Delete(true); } // Build Beef and package (nuget) - only local package, no deployment. - Assert.GreaterOrEqual(0, ExecuteCommand("powershell", $"{Path.Combine(_rootDir.FullName, "nuget-publish.ps1")} -configuration 'Debug' -IncludeSymbols -IncludeSource").exitCode, "nuget publish"); + Assert.That(ExecuteCommand("powershell", $"{Path.Combine(_rootDir.FullName, "nuget-publish.ps1")} -configuration 'Debug' -IncludeSymbols -IncludeSource").exitCode, Is.AtLeast(0), "nuget publish"); // Uninstall any previous beef templates (failure is ok here) ExecuteCommand("dotnet", "new uninstall beef.template.solution"); // Determine the "actual" version to publish so we are explicit. var pf = Directory.GetFiles(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "nuget-publish"), "Beef.Template.Solution.*.nupkg").FirstOrDefault(); - Assert.IsNotNull(pf, "Beef.Template.Solution.*.nupkg could not be found."); + Assert.That(pf, Is.Not.Null, "Beef.Template.Solution.*.nupkg could not be found."); // Install the Beef template solution from local package. // dotnet new -i beef.template.solution --nuget-source https://api.nuget.org/v3/index.json - Assert.GreaterOrEqual(0, ExecuteCommand("dotnet", $"new install beef.template.solution::{new FileInfo(pf).Name[23..^6]} --nuget-source {nugets}").exitCode, "install beef.template.solution"); + Assert.That(ExecuteCommand("dotnet", $"new install beef.template.solution::{new FileInfo(pf).Name[23..^6]} --nuget-source {nugets}").exitCode, Is.AtLeast(0), "install beef.template.solution"); } [Test] @@ -145,24 +145,24 @@ private static void SolutionCreateGenerateTest(string company, string appName, s // Mkdir and create solution from template. var dir = Path.Combine(_unitTests.FullName, $"{company}.{appName}"); Directory.CreateDirectory(dir); - Assert.Zero(ExecuteCommand("dotnet", $"new beef --company {company} --appname {appName} --datasource {datasource}", dir).exitCode, "dotnet new beef"); + Assert.That(ExecuteCommand("dotnet", $"new beef --company {company} --appname {appName} --datasource {datasource}", dir).exitCode, Is.Zero, "dotnet new beef"); // Restore nuget packages from our repository. var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "nuget-publish"); - Assert.Zero(ExecuteCommand("dotnet", $"restore -s {path}", dir).exitCode, "dotnet restore"); + Assert.That(ExecuteCommand("dotnet", $"restore -s {path}", dir).exitCode, Is.Zero, "dotnet restore"); // CodeGen: Execute code-generation. - Assert.Zero(ExecuteCommand("dotnet", "run all", Path.Combine(dir, $"{company}.{appName}.CodeGen")).exitCode, "dotnet run all [entity]"); + Assert.That(ExecuteCommand("dotnet", "run all", Path.Combine(dir, $"{company}.{appName}.CodeGen")).exitCode, Is.Zero, "dotnet run all [entity]"); // Database: Execute code-generation. if (datasource == "SqlServerProcs" || datasource == "SqlServer" || datasource == "MySQL") { - Assert.Zero(ExecuteCommand("dotnet", "run drop --accept-prompts", Path.Combine(dir, $"{company}.{appName}.Database")).exitCode, "dotnet run drop [database]"); - Assert.Zero(ExecuteCommand("dotnet", "run all", Path.Combine(dir, $"{company}.{appName}.Database")).exitCode, "dotnet run all [database]"); + Assert.That(ExecuteCommand("dotnet", "run drop --accept-prompts", Path.Combine(dir, $"{company}.{appName}.Database")).exitCode, Is.Zero, "dotnet run drop [database]"); + Assert.That(ExecuteCommand("dotnet", "run all", Path.Combine(dir, $"{company}.{appName}.Database")).exitCode, Is.Zero, "dotnet run all [database]"); } // Run the intra-integration tests. - Assert.Zero(ExecuteCommand("dotnet", $"test {company}.{ appName}.Test.csproj -v n", Path.Combine(dir, $"{company}.{appName}.Test")).exitCode, "dotnet test"); + Assert.That(ExecuteCommand("dotnet", $"test {company}.{ appName}.Test.csproj -v n", Path.Combine(dir, $"{company}.{appName}.Test")).exitCode, Is.Zero, "dotnet test"); } } } \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Beef.CodeGen.Core.csproj b/tools/Beef.CodeGen.Core/Beef.CodeGen.Core.csproj index 6cbd19938..6e25c41d4 100644 --- a/tools/Beef.CodeGen.Core/Beef.CodeGen.Core.csproj +++ b/tools/Beef.CodeGen.Core/Beef.CodeGen.Core.csproj @@ -32,7 +32,7 @@ - + diff --git a/tools/Beef.Database.Core/Beef.Database.Core.csproj b/tools/Beef.Database.Core/Beef.Database.Core.csproj index 8fe1880ef..07a8b6605 100644 --- a/tools/Beef.Database.Core/Beef.Database.Core.csproj +++ b/tools/Beef.Database.Core/Beef.Database.Core.csproj @@ -15,7 +15,7 @@ - + diff --git a/tools/Beef.Database.Core/MigrationArgs.cs b/tools/Beef.Database.Core/MigrationArgs.cs index 5e1b06faa..970789cd4 100644 --- a/tools/Beef.Database.Core/MigrationArgs.cs +++ b/tools/Beef.Database.Core/MigrationArgs.cs @@ -4,6 +4,7 @@ using OnRamp; using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; namespace Beef.Database @@ -49,6 +50,9 @@ public MigrationArgs(MigrationCommand migrationCommand, string? connectionString /// This depends on the underlying database provider as to whether there are Beef schema objects to use. public bool BeefSchema { get; set; } + /// + List ICodeGeneratorArgs.Assemblies => Assemblies.Select(x => x.Assembly).ToList(); + /// /// Indicates whether to use the standard Beef schema objects. /// @@ -113,8 +117,8 @@ public void CopyFrom(MigrationArgs args) ExpectNoChanges = args.ExpectNoChanges; IsSimulation = args.IsSimulation; - Assemblies.Clear(); - Assemblies.AddRange(args.Assemblies); + ClearAssemblies(); + AddAssembly(args.Assemblies.ToArray()); Parameters.Clear(); if (args.Parameters != null) diff --git a/tools/Beef.Database.MySql/Beef.Database.MySql.csproj b/tools/Beef.Database.MySql/Beef.Database.MySql.csproj index 631f78741..52b077dfe 100644 --- a/tools/Beef.Database.MySql/Beef.Database.MySql.csproj +++ b/tools/Beef.Database.MySql/Beef.Database.MySql.csproj @@ -15,7 +15,7 @@ - + diff --git a/tools/Beef.Database.SqlServer/Beef.Database.SqlServer.csproj b/tools/Beef.Database.SqlServer/Beef.Database.SqlServer.csproj index d1846459c..26d37a636 100644 --- a/tools/Beef.Database.SqlServer/Beef.Database.SqlServer.csproj +++ b/tools/Beef.Database.SqlServer/Beef.Database.SqlServer.csproj @@ -15,7 +15,7 @@ - +