diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..40df522 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dotnet-test-explorer.testProjectPath": "**/*Tests.csproj" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index d71b4ae..1e6b35f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,7 +7,7 @@ "type": "process", "args": [ "build", - "${workspaceFolder}/src/COVIDScreeningApi/COVIDScreeningApi.csproj", + "${workspaceFolder}/COVIDScreeningApi.sln", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], diff --git a/src/COVIDScreeningApi.sln b/COVIDScreeningApi.sln similarity index 52% rename from src/COVIDScreeningApi.sln rename to COVIDScreeningApi.sln index 3af13a6..71bd184 100644 --- a/src/COVIDScreeningApi.sln +++ b/COVIDScreeningApi.sln @@ -3,7 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30014.187 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "COVIDScreeningApi", "COVIDScreeningApi\COVIDScreeningApi.csproj", "{B2A6F07E-1C41-4F1B-8DB9-50CEC1D730BC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "COVIDScreeningApi", "src\COVIDScreeningApi\COVIDScreeningApi.csproj", "{B2A6F07E-1C41-4F1B-8DB9-50CEC1D730BC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{CAEFA69E-FAAE-4366-8B00-6F9BA7ACA7CC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "COVIDScreeningApi.Tests", "tests\COVIDScreeningApi.Tests\COVIDScreeningApi.Tests.csproj", "{C7B963D3-AF0C-44EE-B061-0089F49025B5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +19,10 @@ Global {B2A6F07E-1C41-4F1B-8DB9-50CEC1D730BC}.Debug|Any CPU.Build.0 = Debug|Any CPU {B2A6F07E-1C41-4F1B-8DB9-50CEC1D730BC}.Release|Any CPU.ActiveCfg = Release|Any CPU {B2A6F07E-1C41-4F1B-8DB9-50CEC1D730BC}.Release|Any CPU.Build.0 = Release|Any CPU + {C7B963D3-AF0C-44EE-B061-0089F49025B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7B963D3-AF0C-44EE-B061-0089F49025B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7B963D3-AF0C-44EE-B061-0089F49025B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7B963D3-AF0C-44EE-B061-0089F49025B5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -22,4 +30,7 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A9CE1A19-0B11-4E36-9772-C540EE3EE609} EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {C7B963D3-AF0C-44EE-B061-0089F49025B5} = {CAEFA69E-FAAE-4366-8B00-6F9BA7ACA7CC} + EndGlobalSection EndGlobal diff --git a/src/COVIDScreeningApi/Controllers/PortsOfEntryController.cs b/src/COVIDScreeningApi/Controllers/PortsOfEntryController.cs index 2968824..76db619 100644 --- a/src/COVIDScreeningApi/Controllers/PortsOfEntryController.cs +++ b/src/COVIDScreeningApi/Controllers/PortsOfEntryController.cs @@ -41,7 +41,7 @@ public PortsOfEntry Get(Guid id) // POST api/ [HttpPost] - public ActionResult Post([FromBody] PortsOfEntry value) + public ActionResult Post([FromBody] PortsOfEntry value) { var dataObject = PortOfEntry.FromApiModel(value); Guid newId = Guid.NewGuid(); diff --git a/src/COVIDScreeningApi/Controllers/RepresentativeDataController.cs b/src/COVIDScreeningApi/Controllers/RepresentativeDataController.cs index 7cd1c98..281a72d 100644 --- a/src/COVIDScreeningApi/Controllers/RepresentativeDataController.cs +++ b/src/COVIDScreeningApi/Controllers/RepresentativeDataController.cs @@ -37,7 +37,7 @@ public RepresentativeData Get (Guid id) { // POST api/ [HttpPost] - public ActionResult Post ([FromBody] RepresentativeData value) { + public ActionResult Post ([FromBody] RepresentativeData value) { var dataObject = Representative.FromApiModel(value); Guid newId = Guid.NewGuid(); dataObject.Id = newId; diff --git a/src/COVIDScreeningApi/Controllers/ScreeningDataTableController.cs b/src/COVIDScreeningApi/Controllers/ScreeningDataTableController.cs index 2fe6ba1..3ddaa6b 100644 --- a/src/COVIDScreeningApi/Controllers/ScreeningDataTableController.cs +++ b/src/COVIDScreeningApi/Controllers/ScreeningDataTableController.cs @@ -37,7 +37,7 @@ public ScreeningDataTable Get (Guid id) { // POST api/ [HttpPost] - public ActionResult Post ([FromBody] ScreeningDataTable value) { + public ActionResult Post ([FromBody] ScreeningDataTable value) { var dataObject = Screening.FromApiModel(value); Guid newId = Guid.NewGuid(); dataObject.Id = newId; diff --git a/src/COVIDScreeningApi/Infrastructure/DbContextOptionsBuilderExtensions.cs b/src/COVIDScreeningApi/Infrastructure/DbContextOptionsBuilderExtensions.cs index fb65fd8..2aaaefb 100644 --- a/src/COVIDScreeningApi/Infrastructure/DbContextOptionsBuilderExtensions.cs +++ b/src/COVIDScreeningApi/Infrastructure/DbContextOptionsBuilderExtensions.cs @@ -2,9 +2,9 @@ namespace COVIDScreeningApi { - internal static class DbContextOptionsBuilderExtensions + public static class DbContextOptionsBuilderExtensions { - internal static DbContextOptionsBuilder UseCosmos(this DbContextOptionsBuilder builder, + public static DbContextOptionsBuilder UseCosmos(this DbContextOptionsBuilder builder, string connectionString, string databaseName) { diff --git a/tests/COVIDScreeningApi.Tests/COVIDScreeningApi.Tests.csproj b/tests/COVIDScreeningApi.Tests/COVIDScreeningApi.Tests.csproj new file mode 100644 index 0000000..ee3e34e --- /dev/null +++ b/tests/COVIDScreeningApi.Tests/COVIDScreeningApi.Tests.csproj @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + netcoreapp3.1 + + + + + Always + + + + diff --git a/tests/COVIDScreeningApi.Tests/DataPersistenceTests.cs b/tests/COVIDScreeningApi.Tests/DataPersistenceTests.cs new file mode 100644 index 0000000..28a9ee8 --- /dev/null +++ b/tests/COVIDScreeningApi.Tests/DataPersistenceTests.cs @@ -0,0 +1,128 @@ +using System; +using System.Text.Json; +using Bogus; +using COVIDScreeningApi.Controllers; +using COVIDScreeningApi.Data; +using COVIDScreeningApi.Models; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Xunit; +using Microsoft.AspNetCore.Mvc; +using System.Linq; + +namespace COVIDScreeningApi.Tests +{ + public class DataPersistenceTests + { + private readonly DataContext _dataContext; + public RepresentativeDataController RepresentativeDataController { get; } + public PortsOfEntryController PortsOfEntryController { get; } + public ScreeningDataTableController ScreeningDataTableController { get; } + + private readonly IConfiguration _configuration; + public DataPersistenceTests(DataContext dataContext, IConfiguration configuration) + { + _configuration = configuration; + _dataContext = dataContext; + + RepresentativeDataController = new RepresentativeDataController(_dataContext, _configuration); + PortsOfEntryController = new PortsOfEntryController(_dataContext, _configuration); + ScreeningDataTableController = new ScreeningDataTableController(_dataContext, _configuration); + } + + [Fact] + public void RepresentativeCanBeSavedToDatabase() + { + var rep = CreateRandomRepresentative(); + var postResult = RepresentativeDataController.Post(rep); + var createdReult = postResult.Result as CreatedResult; + (createdReult.Value as RepresentativeData).Should().NotBeNull(); + (createdReult.Value as RepresentativeData).Id.Should().NotBe(Guid.Empty); + } + + [Fact] + public void CanGetRandomExistingRepresentatives() + { + var rep = GetRandomRepresentative(); + rep.Should().NotBeNull(); + } + + [Fact] + public void ScreeningDataCanBeSavedToDatabase() + { + var rep = CreateRandomRepresentative(); + rep.Should().NotBeNull(); + var screen = CreateRandomScreeningData(rep.RepName); + var postResult = ScreeningDataTableController.Post(screen); + var createdReult = postResult.Result as CreatedResult; + (createdReult.Value as ScreeningDataTable).Should().NotBeNull(); + (createdReult.Value as ScreeningDataTable).Id.Should().NotBe(Guid.Empty); + } + + [Fact] + public void PortsOfEntryCanBeSavedToDatabase() + { + var portOfEntry = CreateRandomPortOfEntry(); + portOfEntry.Should().NotBeNull(); + var postResult = PortsOfEntryController.Post(portOfEntry); + var createdReult = postResult.Result as CreatedResult; + (createdReult.Value as PortsOfEntry).Should().NotBeNull(); + (createdReult.Value as PortsOfEntry).Id.Should().NotBe(Guid.Empty); + } + + RepresentativeData GetRandomRepresentative() + { + var reps = RepresentativeDataController.Get(); + return reps.ElementAt(new Random().Next(0, (reps.Count() - 1))); + } + + RepresentativeData CreateRandomRepresentative() + { + return new Faker() + .RuleFor(x => x.Id, f => Guid.Empty) + .RuleFor(x => x.RepContact, f => f.Phone.PhoneNumber()) + .RuleFor(x => x.RepLocation, f => f.Address.City()) + .RuleFor(x => x.RepName, f => f.Name.FullName()) + .FinishWith((f, u) => + { + u.RepEmail = f.Internet.Email(u.RepName); + }) + .Generate(); + } + + ScreeningDataTable CreateRandomScreeningData(string repName) + { + return new Faker() + .RuleFor(u => u.Bodyache, f => f.System.Random.Bool()) + .RuleFor(u => u.DryCough, f => f.System.Random.Bool()) + .RuleFor(u => u.Fatigue, f => f.System.Random.Bool()) + .RuleFor(u => u.Fever, f => f.System.Random.Bool()) + .RuleFor(u => u.Headache, f => f.System.Random.Bool()) + .RuleFor(u => u.RunnyNose, f => f.System.Random.Bool()) + .RuleFor(u => u.ShortnessOfBreath, f => f.System.Random.Bool()) + .RuleFor(u => u.SoreThroat, f => f.System.Random.Bool()) + .RuleFor(u => u.TraveledOutsideTheUS, f => f.System.Random.Bool()) + .RuleFor(u => u.Id, f => Guid.Empty) + .RuleFor(u => u.InContactWithCOVID, f => f.System.Random.Bool()) + .RuleFor(u => u.Location, f => f.Address.City()) + .RuleFor(u => u.ContactNumber, f => f.Phone.PhoneNumber()) + .RuleFor(u => u.DateOfScreening, f => f.Date.Recent()) + .RuleFor(u => u.Nationality, "United States") + .RuleFor(u => u.Passport, f => f.Finance.CreditCardNumber()) + .RuleFor(u => u.ScreeningRepName, repName) + .RuleFor(u => u.VisitorName, f => f.Name.FullName()) + .Generate(); + } + + PortsOfEntry CreateRandomPortOfEntry() + { + return new Faker() + .RuleFor(u => u.Id, f => Guid.Empty) + .RuleFor(u => u.ItemsLabels, f => f.Address.City()) + .RuleFor(u => u.ItemsLatitudes, f => f.Address.Latitude(47.3022815, 47.5517946)) + .RuleFor(u => u.ItemsLongitudes, f => f.Address.Longitude(-122.7553483, -121.881312)) + .Generate(); + } + } +} \ No newline at end of file diff --git a/tests/COVIDScreeningApi.Tests/Startup.cs b/tests/COVIDScreeningApi.Tests/Startup.cs new file mode 100644 index 0000000..c50171f --- /dev/null +++ b/tests/COVIDScreeningApi.Tests/Startup.cs @@ -0,0 +1,40 @@ +using System.Configuration; +using System.Reflection; +using COVIDScreeningApi.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; +using Microsoft.Extensions.Configuration; +using COVIDScreeningApi; + +[assembly: TestFramework("COVIDScreeningApi.Tests.Startup", "COVIDScreeningApi.Tests")] + +namespace COVIDScreeningApi.Tests +{ + public class Startup : DependencyInjectionTestFramework + { + IConfiguration Configuration = + new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); + + public Startup(IMessageSink messageSink) : base(messageSink) { } + + protected void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(Configuration); + + services.AddDbContext(optionsBuilder => + { + optionsBuilder.UseCosmos( + Configuration.GetConnectionString("CosmosDbConnectionString"), + "COVIDScreeningDb"); + }); + } + + protected override IHostBuilder CreateHostBuilder(AssemblyName assemblyName) => + base.CreateHostBuilder(assemblyName) + .ConfigureServices(ConfigureServices); + } +} \ No newline at end of file diff --git a/tests/COVIDScreeningApi.Tests/appsettings.json b/tests/COVIDScreeningApi.Tests/appsettings.json new file mode 100644 index 0000000..2873804 --- /dev/null +++ b/tests/COVIDScreeningApi.Tests/appsettings.json @@ -0,0 +1,14 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "SwaggerBaseUrl": "https://localhost:5001", + "ConnectionStrings": { + "CosmosDbConnectionString" : "" + } +}