diff --git a/Blazorcrud.Client/Properties/launchSettings.json b/Blazorcrud.Client/Properties/launchSettings.json
index 8f173be..63c8722 100644
--- a/Blazorcrud.Client/Properties/launchSettings.json
+++ b/Blazorcrud.Client/Properties/launchSettings.json
@@ -13,7 +13,7 @@
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
- "applicationUrl": "https://localhost:5001;http://localhost:5000",
+ "applicationUrl": "https://localhost:5002;http://localhost:5003",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
diff --git a/Blazorcrud.Server.IntegrationTests/Blazorcrud.Server.IntegrationTests.csproj b/Blazorcrud.Server.IntegrationTests/Blazorcrud.Server.IntegrationTests.csproj
new file mode 100644
index 0000000..6d8d145
--- /dev/null
+++ b/Blazorcrud.Server.IntegrationTests/Blazorcrud.Server.IntegrationTests.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/Blazorcrud.Server.IntegrationTests/GlobalUsings.cs b/Blazorcrud.Server.IntegrationTests/GlobalUsings.cs
new file mode 100644
index 0000000..8c927eb
--- /dev/null
+++ b/Blazorcrud.Server.IntegrationTests/GlobalUsings.cs
@@ -0,0 +1 @@
+global using Xunit;
\ No newline at end of file
diff --git a/Blazorcrud.Server.IntegrationTests/PersonControllerTests.cs b/Blazorcrud.Server.IntegrationTests/PersonControllerTests.cs
new file mode 100644
index 0000000..1687824
--- /dev/null
+++ b/Blazorcrud.Server.IntegrationTests/PersonControllerTests.cs
@@ -0,0 +1,144 @@
+using System.Net;
+using System.Net.Http.Json;
+using System.Threading.Tasks;
+using Blazorcrud.Server;
+using Blazorcrud.Shared.Data;
+using Blazorcrud.Shared.Models;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Xunit;
+
+namespace Blazorcrud.Server.IntegrationTests
+{
+ public class PersonControllerTests : IClassFixture>
+ {
+ private readonly WebApplicationFactory _factory;
+
+ public PersonControllerTests(WebApplicationFactory factory)
+ {
+ _factory = factory;
+ }
+
+ [Fact]
+ public async Task Get_People_Returns_Success_And_Content()
+ {
+ // Arrange
+ var client = _factory.CreateClient();
+
+ // Act
+ var response = await client.GetAsync("/api/person?page=1");
+
+ // Assert
+ response.EnsureSuccessStatusCode();
+ var pagedResult = await response.Content.ReadFromJsonAsync>();
+ Assert.NotNull(pagedResult);
+ Assert.IsType>(pagedResult);
+ }
+
+ [Fact]
+ public async Task Get_Person_By_Id_Returns_Success_And_Content()
+ {
+ // Arrange
+ var client = _factory.CreateClient();
+
+ // Act
+ var response = await client.GetAsync("/api/person/1");
+
+ // Assert
+ response.EnsureSuccessStatusCode();
+ var person = await response.Content.ReadFromJsonAsync();
+ Assert.NotNull(person);
+ Assert.Equal(1, person.PersonId);
+ }
+
+ [Fact]
+ public async Task Add_Person_Returns_Success()
+ {
+ // Arrange
+ var client = _factory.CreateClient();
+ var token = await Utilities.GetJwtAsync(client);
+ client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
+ var newPerson = new Person
+ {
+ FirstName = "John",
+ LastName = "Doe",
+ Gender = Gender.Male,
+ PhoneNumber = "1234567890",
+ Addresses = new()
+ {
+ new Address { Street = "123 Main St", City = "Anytown", State = "CA", ZipCode = "12345" }
+ }
+ };
+
+ // Act
+ var response = await client.PostAsJsonAsync("/api/person", newPerson);
+
+ // Assert
+ response.EnsureSuccessStatusCode();
+ var person = await response.Content.ReadFromJsonAsync();
+ Assert.NotNull(person);
+ Assert.Equal("John", person.FirstName);
+ }
+
+ [Fact]
+ public async Task Update_Person_Returns_Success()
+ {
+ // Arrange
+ var client = _factory.CreateClient();
+ var token = await Utilities.GetJwtAsync(client);
+ client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
+ var newPerson = new Person
+ {
+ FirstName = "Jane",
+ LastName = "Doe",
+ Gender = Gender.Female,
+ PhoneNumber = "1234567890",
+ Addresses = new()
+ {
+ new Address { Street = "123 Main St", City = "Anytown", State = "CA", ZipCode = "12345" }
+ }
+ };
+ var response = await client.PostAsJsonAsync("/api/person", newPerson);
+ response.EnsureSuccessStatusCode();
+ var person = await response.Content.ReadFromJsonAsync();
+
+ // Act
+ person.FirstName = "Janet";
+ response = await client.PutAsJsonAsync("/api/person", person);
+
+ // Assert
+ response.EnsureSuccessStatusCode();
+ var updatedPerson = await response.Content.ReadFromJsonAsync();
+ Assert.NotNull(updatedPerson);
+ Assert.Equal("Janet", updatedPerson.FirstName);
+ }
+
+ [Fact]
+ public async Task Delete_Person_Returns_Success()
+ {
+ // Arrange
+ var client = _factory.CreateClient();
+ var token = await Utilities.GetJwtAsync(client);
+ client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
+ var newPerson = new Person
+ {
+ FirstName = "Jim",
+ LastName = "Doe",
+ Gender = Gender.Male,
+ PhoneNumber = "1234567890",
+ Addresses = new()
+ {
+ new Address { Street = "123 Main St", City = "Anytown", State = "CA", ZipCode = "12345" }
+ }
+ };
+ var response = await client.PostAsJsonAsync("/api/person", newPerson);
+ response.EnsureSuccessStatusCode();
+ var person = await response.Content.ReadFromJsonAsync();
+
+ // Act
+ response = await client.DeleteAsync($"/api/person/{person.PersonId}");
+
+ // Assert
+ response.EnsureSuccessStatusCode();
+ }
+ }
+}
diff --git a/Blazorcrud.Server.IntegrationTests/UnitTest1.cs b/Blazorcrud.Server.IntegrationTests/UnitTest1.cs
new file mode 100644
index 0000000..3cfd8ef
--- /dev/null
+++ b/Blazorcrud.Server.IntegrationTests/UnitTest1.cs
@@ -0,0 +1,10 @@
+namespace Blazorcrud.Server.IntegrationTests;
+
+public class UnitTest1
+{
+ [Fact]
+ public void Test1()
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/Blazorcrud.Server.IntegrationTests/Utilities.cs b/Blazorcrud.Server.IntegrationTests/Utilities.cs
new file mode 100644
index 0000000..ea2c60d
--- /dev/null
+++ b/Blazorcrud.Server.IntegrationTests/Utilities.cs
@@ -0,0 +1,25 @@
+using System.Net.Http;
+using System.Net.Http.Json;
+using System.Threading.Tasks;
+using Blazorcrud.Server.Authorization;
+using Blazorcrud.Shared.Models;
+using Microsoft.AspNetCore.Mvc.Testing;
+
+namespace Blazorcrud.Server.IntegrationTests
+{
+ public static class Utilities
+ {
+ public static async Task GetJwtAsync(HttpClient client)
+ {
+ var login = new AuthenticateRequest
+ {
+ Username = "admin",
+ Password = "admin"
+ };
+ var response = await client.PostAsJsonAsync("/api/user/authenticate", login);
+ response.EnsureSuccessStatusCode();
+ var authResponse = await response.Content.ReadFromJsonAsync();
+ return authResponse.Token;
+ }
+ }
+}
diff --git a/Blazorcrud.Server.Tests/Blazorcrud.Server.Tests.csproj b/Blazorcrud.Server.Tests/Blazorcrud.Server.Tests.csproj
new file mode 100644
index 0000000..9df6e81
--- /dev/null
+++ b/Blazorcrud.Server.Tests/Blazorcrud.Server.Tests.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/Blazorcrud.Server.Tests/GlobalUsings.cs b/Blazorcrud.Server.Tests/GlobalUsings.cs
new file mode 100644
index 0000000..8c927eb
--- /dev/null
+++ b/Blazorcrud.Server.Tests/GlobalUsings.cs
@@ -0,0 +1 @@
+global using Xunit;
\ No newline at end of file
diff --git a/Blazorcrud.Server.Tests/PersonControllerTests.cs b/Blazorcrud.Server.Tests/PersonControllerTests.cs
new file mode 100644
index 0000000..679f698
--- /dev/null
+++ b/Blazorcrud.Server.Tests/PersonControllerTests.cs
@@ -0,0 +1,113 @@
+using Xunit;
+using Moq;
+using Blazorcrud.Server.Controllers;
+using Blazorcrud.Server.Models;
+using Blazorcrud.Shared.Models;
+using Microsoft.AspNetCore.Mvc;
+using System.Threading.Tasks;
+using Blazorcrud.Shared.Data;
+using System.Collections.Generic;
+
+namespace Blazorcrud.Server.Tests
+{
+ public class PersonControllerTests
+ {
+ private readonly Mock _mockRepo;
+ private readonly PersonController _controller;
+
+ public PersonControllerTests()
+ {
+ _mockRepo = new Mock();
+ _controller = new PersonController(_mockRepo.Object);
+ }
+
+ [Fact]
+ public void GetPeople_Returns_OkResult_With_PagedResult_Of_Person()
+ {
+ // Arrange
+ var name = "Test";
+ var page = 1;
+ var pagedResult = new PagedResult { Results = new List { new Person { PersonId = 1, FirstName = "Test" } } };
+ _mockRepo.Setup(repo => repo.GetPeople(name, page))
+ .Returns(pagedResult);
+
+ // Act
+ var result = _controller.GetPeople(name, page);
+
+ // Assert
+ var okResult = Assert.IsType(result);
+ var returnValue = Assert.IsType>(okResult.Value);
+ Assert.Single(returnValue.Results);
+ }
+
+ [Fact]
+ public async Task GetPerson_Returns_OkResult_With_Person()
+ {
+ // Arrange
+ var id = 1;
+ var person = new Person { PersonId = id, FirstName = "Test" };
+ _mockRepo.Setup(repo => repo.GetPerson(id))
+ .ReturnsAsync(person);
+
+ // Act
+ var result = await _controller.GetPerson(id);
+
+ // Assert
+ var okResult = Assert.IsType(result);
+ var returnValue = Assert.IsType(okResult.Value);
+ Assert.Equal(id, returnValue.PersonId);
+ }
+
+ [Fact]
+ public async Task AddPerson_Returns_OkResult_With_Person()
+ {
+ // Arrange
+ var person = new Person { PersonId = 1, FirstName = "Test" };
+ _mockRepo.Setup(repo => repo.AddPerson(person))
+ .ReturnsAsync(person);
+
+ // Act
+ var result = await _controller.AddPerson(person);
+
+ // Assert
+ var okResult = Assert.IsType(result);
+ var returnValue = Assert.IsType(okResult.Value);
+ Assert.Equal(person.PersonId, returnValue.PersonId);
+ }
+
+ [Fact]
+ public async Task UpdatePerson_Returns_OkResult_With_Person()
+ {
+ // Arrange
+ var person = new Person { PersonId = 1, FirstName = "Test" };
+ _mockRepo.Setup(repo => repo.UpdatePerson(person))
+ .ReturnsAsync(person);
+
+ // Act
+ var result = await _controller.UpdatePerson(person);
+
+ // Assert
+ var okResult = Assert.IsType(result);
+ var returnValue = Assert.IsType(okResult.Value);
+ Assert.Equal(person.PersonId, returnValue.PersonId);
+ }
+
+ [Fact]
+ public async Task DeletePerson_Returns_OkResult_With_Person()
+ {
+ // Arrange
+ var id = 1;
+ var person = new Person { PersonId = id, FirstName = "Test" };
+ _mockRepo.Setup(repo => repo.DeletePerson(id))
+ .ReturnsAsync(person);
+
+ // Act
+ var result = await _controller.DeletePerson(id);
+
+ // Assert
+ var okResult = Assert.IsType(result);
+ var returnValue = Assert.IsType(okResult.Value);
+ Assert.Equal(id, returnValue.PersonId);
+ }
+ }
+}
diff --git a/Blazorcrud.Server.Tests/UnitTest1.cs b/Blazorcrud.Server.Tests/UnitTest1.cs
new file mode 100644
index 0000000..b69cfce
--- /dev/null
+++ b/Blazorcrud.Server.Tests/UnitTest1.cs
@@ -0,0 +1,10 @@
+namespace Blazorcrud.Server.Tests;
+
+public class UnitTest1
+{
+ [Fact]
+ public void Test1()
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/Blazorcrud.Server/Blazorcrud.Server.csproj b/Blazorcrud.Server/Blazorcrud.Server.csproj
index 323606c..0f1eaac 100644
--- a/Blazorcrud.Server/Blazorcrud.Server.csproj
+++ b/Blazorcrud.Server/Blazorcrud.Server.csproj
@@ -23,4 +23,7 @@
+
+
+
\ No newline at end of file
diff --git a/Blazorcrud.Server/PublicProgram.cs b/Blazorcrud.Server/PublicProgram.cs
new file mode 100644
index 0000000..9a19438
--- /dev/null
+++ b/Blazorcrud.Server/PublicProgram.cs
@@ -0,0 +1 @@
+public partial class Program { }
diff --git a/Blazorcrud.sln b/Blazorcrud.sln
index 1d2c739..a41ca43 100644
--- a/Blazorcrud.sln
+++ b/Blazorcrud.sln
@@ -9,6 +9,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazorcrud.Client", "Blazor
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazorcrud.Server", "Blazorcrud.Server\Blazorcrud.Server.csproj", "{75348867-CC83-4724-8011-5D5CB07010BE}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazorcrud.Server.Tests", "Blazorcrud.Server.Tests\Blazorcrud.Server.Tests.csproj", "{15AADED1-998B-44CB-83BE-4E206F496847}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazorcrud.Server.IntegrationTests", "Blazorcrud.Server.IntegrationTests\Blazorcrud.Server.IntegrationTests.csproj", "{6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -58,5 +62,29 @@ Global
{75348867-CC83-4724-8011-5D5CB07010BE}.Release|x64.Build.0 = Release|Any CPU
{75348867-CC83-4724-8011-5D5CB07010BE}.Release|x86.ActiveCfg = Release|Any CPU
{75348867-CC83-4724-8011-5D5CB07010BE}.Release|x86.Build.0 = Release|Any CPU
+ {15AADED1-998B-44CB-83BE-4E206F496847}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {15AADED1-998B-44CB-83BE-4E206F496847}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {15AADED1-998B-44CB-83BE-4E206F496847}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {15AADED1-998B-44CB-83BE-4E206F496847}.Debug|x64.Build.0 = Debug|Any CPU
+ {15AADED1-998B-44CB-83BE-4E206F496847}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {15AADED1-998B-44CB-83BE-4E206F496847}.Debug|x86.Build.0 = Debug|Any CPU
+ {15AADED1-998B-44CB-83BE-4E206F496847}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {15AADED1-998B-44CB-83BE-4E206F496847}.Release|Any CPU.Build.0 = Release|Any CPU
+ {15AADED1-998B-44CB-83BE-4E206F496847}.Release|x64.ActiveCfg = Release|Any CPU
+ {15AADED1-998B-44CB-83BE-4E206F496847}.Release|x64.Build.0 = Release|Any CPU
+ {15AADED1-998B-44CB-83BE-4E206F496847}.Release|x86.ActiveCfg = Release|Any CPU
+ {15AADED1-998B-44CB-83BE-4E206F496847}.Release|x86.Build.0 = Release|Any CPU
+ {6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}.Debug|x64.Build.0 = Debug|Any CPU
+ {6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}.Debug|x86.Build.0 = Debug|Any CPU
+ {6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}.Release|x64.ActiveCfg = Release|Any CPU
+ {6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}.Release|x64.Build.0 = Release|Any CPU
+ {6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}.Release|x86.ActiveCfg = Release|Any CPU
+ {6525ED9C-1BD3-49B1-B04D-03CA6A068A3C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal