mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-06 13:32:13 +00:00
Add starter unit tests for validators, DTO mappers, and PagedResult (#9)
This commit is contained in:
parent
1f3972ae49
commit
b2aeb35a55
5 changed files with 241 additions and 0 deletions
34
SeaHaven.Services.Tests/PagedResultTests.cs
Normal file
34
SeaHaven.Services.Tests/PagedResultTests.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
using FluentAssertions;
|
||||||
|
using SeaHaven.Services.DTOs;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace SeaHaven.Services.Tests;
|
||||||
|
|
||||||
|
public class PagedResultTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 10, 0)]
|
||||||
|
[InlineData(10, 10, 1)]
|
||||||
|
[InlineData(25, 10, 3)]
|
||||||
|
[InlineData(100, 25, 4)]
|
||||||
|
public void TotalPages_IsCeilingOfCountOverPageSize(int totalCount, int pageSize, int expectedPages)
|
||||||
|
{
|
||||||
|
var result = new PagedResult<string> { TotalCount = totalCount, PageSize = pageSize, Page = 1 };
|
||||||
|
|
||||||
|
result.TotalPages.Should().Be(expectedPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void HasPrevious_IsFalseOnFirstPage_TrueAfterward()
|
||||||
|
{
|
||||||
|
new PagedResult<string> { Page = 1, PageSize = 10, TotalCount = 30 }.HasPrevious.Should().BeFalse();
|
||||||
|
new PagedResult<string> { Page = 2, PageSize = 10, TotalCount = 30 }.HasPrevious.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void HasNext_IsTrueBeforeLastPage_FalseOnLastPage()
|
||||||
|
{
|
||||||
|
new PagedResult<string> { Page = 2, PageSize = 10, TotalCount = 30 }.HasNext.Should().BeTrue();
|
||||||
|
new PagedResult<string> { Page = 3, PageSize = 10, TotalCount = 30 }.HasNext.Should().BeFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
23
SeaHaven.Services.Tests/SeaHaven.Services.Tests.csproj
Normal file
23
SeaHaven.Services.Tests/SeaHaven.Services.Tests.csproj
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||||
|
<PackageReference Include="xunit" Version="2.9.2" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||||
|
<PackageReference Include="FluentAssertions" Version="6.12.1" />
|
||||||
|
<PackageReference Include="FluentValidation" Version="12.1.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\SeaHaven.Services\SeaHaven.Services.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
104
SeaHaven.Services.Tests/ValidationTests.cs
Normal file
104
SeaHaven.Services.Tests/ValidationTests.cs
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
using FluentAssertions;
|
||||||
|
using SeaHaven.Services.DTOs;
|
||||||
|
using SeaHaven.Services.Validation;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace SeaHaven.Services.Tests;
|
||||||
|
|
||||||
|
public class CreateAssetValidationTests
|
||||||
|
{
|
||||||
|
private readonly CreateAssetValidation _validator = new();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_WhenNameEmpty_Fails()
|
||||||
|
{
|
||||||
|
var result = _validator.Validate(new CreateAssetDTO { Name = "" });
|
||||||
|
|
||||||
|
result.IsValid.Should().BeFalse();
|
||||||
|
result.Errors.Should().Contain(e => e.PropertyName == nameof(CreateAssetDTO.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_WhenNameTooLong_Fails()
|
||||||
|
{
|
||||||
|
var result = _validator.Validate(new CreateAssetDTO { Name = new string('a', 201) });
|
||||||
|
|
||||||
|
result.IsValid.Should().BeFalse();
|
||||||
|
result.Errors.Should().Contain(e => e.PropertyName == nameof(CreateAssetDTO.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_WhenLocationIdZero_Fails()
|
||||||
|
{
|
||||||
|
var result = _validator.Validate(new CreateAssetDTO { Name = "Pump", LocationId = 0 });
|
||||||
|
|
||||||
|
result.IsValid.Should().BeFalse();
|
||||||
|
result.Errors.Should().Contain(e => e.PropertyName == nameof(CreateAssetDTO.LocationId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_WhenValid_Passes()
|
||||||
|
{
|
||||||
|
var result = _validator.Validate(new CreateAssetDTO { Name = "Pump", LocationId = 5 });
|
||||||
|
|
||||||
|
result.IsValid.Should().BeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreateContactValidationTests
|
||||||
|
{
|
||||||
|
private readonly CreateContactValidation _validator = new();
|
||||||
|
|
||||||
|
private static CreateContactDTO Valid() => new()
|
||||||
|
{
|
||||||
|
FirstName = "Ada",
|
||||||
|
LastName = "Lovelace",
|
||||||
|
Email = "ada@example.com"
|
||||||
|
};
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_WhenNamesMissing_Fails()
|
||||||
|
{
|
||||||
|
var dto = Valid();
|
||||||
|
dto.FirstName = "";
|
||||||
|
dto.LastName = "";
|
||||||
|
|
||||||
|
var result = _validator.Validate(dto);
|
||||||
|
|
||||||
|
result.IsValid.Should().BeFalse();
|
||||||
|
result.Errors.Should().Contain(e => e.PropertyName == nameof(CreateContactDTO.FirstName));
|
||||||
|
result.Errors.Should().Contain(e => e.PropertyName == nameof(CreateContactDTO.LastName));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_WhenEmailMalformed_Fails()
|
||||||
|
{
|
||||||
|
var dto = Valid();
|
||||||
|
dto.Email = "not-an-email";
|
||||||
|
|
||||||
|
var result = _validator.Validate(dto);
|
||||||
|
|
||||||
|
result.IsValid.Should().BeFalse();
|
||||||
|
result.Errors.Should().Contain(e => e.PropertyName == nameof(CreateContactDTO.Email));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_WhenPhoneTooLong_Fails()
|
||||||
|
{
|
||||||
|
var dto = Valid();
|
||||||
|
dto.Phone = new string('1', 21);
|
||||||
|
|
||||||
|
var result = _validator.Validate(dto);
|
||||||
|
|
||||||
|
result.IsValid.Should().BeFalse();
|
||||||
|
result.Errors.Should().Contain(e => e.PropertyName == nameof(CreateContactDTO.Phone));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_WhenValid_Passes()
|
||||||
|
{
|
||||||
|
var result = _validator.Validate(Valid());
|
||||||
|
|
||||||
|
result.IsValid.Should().BeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
74
SeaHaven.Services.Tests/WorkOrderMappingTests.cs
Normal file
74
SeaHaven.Services.Tests/WorkOrderMappingTests.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
using Data.SeaHavenIndustries;
|
||||||
|
using Data.SeaHavenIndustries.Enums;
|
||||||
|
using FluentAssertions;
|
||||||
|
using SeaHaven.Services.DTOs;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace SeaHaven.Services.Tests;
|
||||||
|
|
||||||
|
public class WorkOrderMappingTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void MapToModel_WithValidEnumStrings_ParsesStatusAndPriority()
|
||||||
|
{
|
||||||
|
var entity = new WorkOrder
|
||||||
|
{
|
||||||
|
Id = 7,
|
||||||
|
WorkerOrderTitle = "Fix pump",
|
||||||
|
Status = "InProgress",
|
||||||
|
Priority = "High"
|
||||||
|
};
|
||||||
|
|
||||||
|
var dto = WorkOrderDTO.MapToModel(entity);
|
||||||
|
|
||||||
|
dto.Id.Should().Be(7);
|
||||||
|
dto.WorkerOrderTitle.Should().Be("Fix pump");
|
||||||
|
dto.Status.Should().Be(WorkOrderStatus.InProgress);
|
||||||
|
dto.Priority.Should().Be(WorkOrderPriority.High);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapToModel_WithUnparseableEnumStrings_FallsBackToDefaults()
|
||||||
|
{
|
||||||
|
var entity = new WorkOrder
|
||||||
|
{
|
||||||
|
WorkerOrderTitle = "Garbage",
|
||||||
|
Status = "not-a-status",
|
||||||
|
Priority = "not-a-priority"
|
||||||
|
};
|
||||||
|
|
||||||
|
var dto = WorkOrderDTO.MapToModel(entity);
|
||||||
|
|
||||||
|
dto.Status.Should().Be(WorkOrderStatus.Open);
|
||||||
|
dto.Priority.Should().Be(WorkOrderPriority.Medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapToEntity_SerializesEnumsToStrings()
|
||||||
|
{
|
||||||
|
var dto = new WorkOrderDTO
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
WorkerOrderTitle = "Inspect",
|
||||||
|
Status = WorkOrderStatus.Completed,
|
||||||
|
Priority = WorkOrderPriority.Critical
|
||||||
|
};
|
||||||
|
|
||||||
|
var entity = WorkOrderDTO.MapToEntity(dto);
|
||||||
|
|
||||||
|
entity.Status.Should().Be("Completed");
|
||||||
|
entity.Priority.Should().Be("Critical");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateMapToEntity_WhenStatusAndPriorityNull_AppliesDefaults()
|
||||||
|
{
|
||||||
|
var dto = new CreateWorkOrderDTO { WorkerOrderTitle = "New" };
|
||||||
|
|
||||||
|
var entity = CreateWorkOrderDTO.MapToEntity(dto);
|
||||||
|
|
||||||
|
entity.Status.Should().Be("Open");
|
||||||
|
entity.Priority.Should().Be("Medium");
|
||||||
|
entity.CreatedDate.Should().NotBeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SeaHaven.DataServices", "Se
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.SeaHavenIndustries.Tests", "Api.SeaHavenIndustries.Tests\Api.SeaHavenIndustries.Tests.csproj", "{1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.SeaHavenIndustries.Tests", "Api.SeaHavenIndustries.Tests\Api.SeaHavenIndustries.Tests.csproj", "{1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SeaHaven.Services.Tests", "SeaHaven.Services.Tests\SeaHaven.Services.Tests.csproj", "{97455198-C479-47F2-8BC8-601B335DFFAB}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -45,6 +47,10 @@ Global
|
||||||
{1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}.Release|Any CPU.Build.0 = Release|Any CPU
|
{1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{97455198-C479-47F2-8BC8-601B335DFFAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{97455198-C479-47F2-8BC8-601B335DFFAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{97455198-C479-47F2-8BC8-601B335DFFAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{97455198-C479-47F2-8BC8-601B335DFFAB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue