added tests
This commit is contained in:
parent
21db1ffea3
commit
204176e40a
13 changed files with 975 additions and 31 deletions
|
|
@ -0,0 +1,74 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Registration.API.Controllers;
|
||||||
|
|
||||||
|
namespace Registration.Tests.Controllers;
|
||||||
|
|
||||||
|
public class AdminControllerTests
|
||||||
|
{
|
||||||
|
private static IConfiguration BuildConfiguration(string? adminPassword)
|
||||||
|
{
|
||||||
|
var dict = new Dictionary<string, string?>();
|
||||||
|
if (adminPassword is not null)
|
||||||
|
{
|
||||||
|
dict["Admin:Password"] = adminPassword;
|
||||||
|
}
|
||||||
|
return new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Login_returns_Ok_with_valid_credentials_from_configuration()
|
||||||
|
{
|
||||||
|
var controller = new AdminController(BuildConfiguration("s3cret"));
|
||||||
|
|
||||||
|
var result = controller.Login(new AdminController.LoginRequest
|
||||||
|
{
|
||||||
|
Username = "admin",
|
||||||
|
Password = "s3cret"
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.IsType<OkObjectResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Login_returns_Unauthorized_with_bad_password()
|
||||||
|
{
|
||||||
|
var controller = new AdminController(BuildConfiguration("s3cret"));
|
||||||
|
|
||||||
|
var result = controller.Login(new AdminController.LoginRequest
|
||||||
|
{
|
||||||
|
Username = "admin",
|
||||||
|
Password = "wrong"
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.IsType<UnauthorizedObjectResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Login_returns_Unauthorized_with_wrong_username()
|
||||||
|
{
|
||||||
|
var controller = new AdminController(BuildConfiguration("s3cret"));
|
||||||
|
|
||||||
|
var result = controller.Login(new AdminController.LoginRequest
|
||||||
|
{
|
||||||
|
Username = "root",
|
||||||
|
Password = "s3cret"
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.IsType<UnauthorizedObjectResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Login_falls_back_to_default_password_when_config_missing()
|
||||||
|
{
|
||||||
|
var controller = new AdminController(BuildConfiguration(null));
|
||||||
|
|
||||||
|
var result = controller.Login(new AdminController.LoginRequest
|
||||||
|
{
|
||||||
|
Username = "admin",
|
||||||
|
Password = "admin123"
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.IsType<OkObjectResult>(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Moq;
|
||||||
|
using Registration.API.Controllers;
|
||||||
|
using Registration.Domain.Models;
|
||||||
|
using Registration.Infra.Repositories;
|
||||||
|
|
||||||
|
namespace Registration.Tests.Controllers;
|
||||||
|
|
||||||
|
public class ContentControllerTests
|
||||||
|
{
|
||||||
|
private readonly Mock<IRegistrationRepository> _repo = new();
|
||||||
|
|
||||||
|
private ContentController Sut() => new(_repo.Object);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetContent_returns_Ok_with_content_from_repository()
|
||||||
|
{
|
||||||
|
var content = new EventContent { Title = "Hello", SubTitle = "Sub" };
|
||||||
|
_repo.Setup(r => r.GetEventContent()).ReturnsAsync(content);
|
||||||
|
|
||||||
|
var result = await Sut().GetContent();
|
||||||
|
|
||||||
|
var ok = Assert.IsType<OkObjectResult>(result);
|
||||||
|
Assert.Same(content, ok.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateContent_forwards_payload_and_returns_Ok()
|
||||||
|
{
|
||||||
|
var content = new EventContent { Title = "New" };
|
||||||
|
|
||||||
|
var result = await Sut().UpdateContent(content);
|
||||||
|
|
||||||
|
Assert.IsType<OkResult>(result);
|
||||||
|
_repo.Verify(r => r.UpdateEventContent(content), Times.Once);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Moq;
|
||||||
|
using Registration.API.Controllers;
|
||||||
|
using Registration.API.RequestModels;
|
||||||
|
using Registration.API.Services;
|
||||||
|
using Registration.Domain.Models;
|
||||||
|
|
||||||
|
namespace Registration.Tests.Controllers;
|
||||||
|
|
||||||
|
public class ParticipantControllerTests
|
||||||
|
{
|
||||||
|
private readonly Mock<IVbytesParticipantRelayService> _relay = new();
|
||||||
|
|
||||||
|
private ParticipantController Sut() => new(_relay.Object);
|
||||||
|
|
||||||
|
private static ParticipantRegistrationRequest ValidRequest() => new()
|
||||||
|
{
|
||||||
|
IsMember = true,
|
||||||
|
FirstName = " Anna ",
|
||||||
|
SurName = " Andersson ",
|
||||||
|
Grade = " 9 ",
|
||||||
|
PhoneNumber = "+46 70 111 22 33",
|
||||||
|
Email = " anna@example.com ",
|
||||||
|
GuardianName = " Bertil Andersson ",
|
||||||
|
GuardianPhoneNumber = "+46 70 999 88 77",
|
||||||
|
GuardianEmail = " bertil@example.com ",
|
||||||
|
IsVisitor = false,
|
||||||
|
HasApprovedGdpr = true,
|
||||||
|
Friends = " Maria ",
|
||||||
|
SpecialDiet = " Vegan "
|
||||||
|
};
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterForLan_returns_Ok_when_relay_succeeds()
|
||||||
|
{
|
||||||
|
Participant? captured = null;
|
||||||
|
_relay.Setup(r => r.RegisterParticipantAsync(It.IsAny<Participant>(), It.IsAny<CancellationToken>()))
|
||||||
|
.Callback<Participant, CancellationToken>((p, _) => captured = p)
|
||||||
|
.ReturnsAsync(new VbytesRelayResult(true, 200, "ok"));
|
||||||
|
|
||||||
|
var result = await Sut().RegisterForLan(ValidRequest(), CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<OkResult>(result);
|
||||||
|
Assert.NotNull(captured);
|
||||||
|
Assert.Equal("Anna", captured!.FirstName);
|
||||||
|
Assert.Equal("Andersson", captured.SurName);
|
||||||
|
Assert.Equal("9", captured.Grade);
|
||||||
|
Assert.Equal("anna@example.com", captured.Email);
|
||||||
|
Assert.Equal("bertil@example.com", captured.GuardianEmail);
|
||||||
|
Assert.Equal("Bertil Andersson", captured.GuardianName);
|
||||||
|
Assert.Equal("0701112233", captured.PhoneNumber);
|
||||||
|
Assert.Equal("0709998877", captured.GuardianPhoneNumber);
|
||||||
|
Assert.Equal("Maria", captured.Friends);
|
||||||
|
Assert.Equal("Vegan", captured.SpecialDiet);
|
||||||
|
Assert.True(captured.IsMember);
|
||||||
|
Assert.False(captured.IsVisitor);
|
||||||
|
Assert.True(captured.HasApprovedGdpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterForLan_normalizes_missing_optional_phone_to_null()
|
||||||
|
{
|
||||||
|
Participant? captured = null;
|
||||||
|
_relay.Setup(r => r.RegisterParticipantAsync(It.IsAny<Participant>(), It.IsAny<CancellationToken>()))
|
||||||
|
.Callback<Participant, CancellationToken>((p, _) => captured = p)
|
||||||
|
.ReturnsAsync(new VbytesRelayResult(true, 200, "ok"));
|
||||||
|
|
||||||
|
var req = ValidRequest();
|
||||||
|
req.PhoneNumber = " ";
|
||||||
|
|
||||||
|
await Sut().RegisterForLan(req, CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Null(captured!.PhoneNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterForLan_returns_Unauthorized_when_relay_fails()
|
||||||
|
{
|
||||||
|
_relay.Setup(r => r.RegisterParticipantAsync(It.IsAny<Participant>(), It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(new VbytesRelayResult(false, 401, "401 Unauthorized"));
|
||||||
|
|
||||||
|
var result = await Sut().RegisterForLan(ValidRequest(), CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<UnauthorizedResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterForLan_returns_Unauthorized_when_success_message_contains_401()
|
||||||
|
{
|
||||||
|
_relay.Setup(r => r.RegisterParticipantAsync(It.IsAny<Participant>(), It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(new VbytesRelayResult(true, 200, "downstream returned 401"));
|
||||||
|
|
||||||
|
var result = await Sut().RegisterForLan(ValidRequest(), CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<UnauthorizedResult>(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Moq;
|
||||||
|
using Registration.API.Controllers;
|
||||||
|
using Registration.API.Services;
|
||||||
|
using Registration.Infra.Repositories;
|
||||||
|
|
||||||
|
namespace Registration.Tests.Controllers;
|
||||||
|
|
||||||
|
public class RegistrationControllerTests
|
||||||
|
{
|
||||||
|
private readonly Mock<IRegistrationRepository> _repo = new();
|
||||||
|
private readonly Mock<IAuthService> _auth = new();
|
||||||
|
|
||||||
|
private RegistrationController Sut() => new(_repo.Object, _auth.Object);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ValidateSsn_returns_400_for_invalid_ssn()
|
||||||
|
{
|
||||||
|
var result = await Sut().ValidateSsn("123", CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<BadRequestObjectResult>(result);
|
||||||
|
_auth.VerifyNoOtherCalls();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ValidateSsn_returns_Ok_when_member()
|
||||||
|
{
|
||||||
|
_auth.Setup(a => a.IsMemberAsync("199001011234", It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(true);
|
||||||
|
|
||||||
|
var result = await Sut().ValidateSsn("19900101-1234", CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<OkResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ValidateSsn_returns_NotFound_when_not_member()
|
||||||
|
{
|
||||||
|
_auth.Setup(a => a.IsMemberAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(false);
|
||||||
|
|
||||||
|
var result = await Sut().ValidateSsn("19900101-1234", CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<NotFoundResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterMember_returns_400_for_invalid_ssn()
|
||||||
|
{
|
||||||
|
var result = await Sut().RegisterMember("nope", CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<BadRequestObjectResult>(result);
|
||||||
|
_repo.Verify(r => r.AddRegistration(It.IsAny<string>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterMember_returns_Ok_when_added()
|
||||||
|
{
|
||||||
|
_repo.Setup(r => r.AddRegistration("199001011234")).ReturnsAsync(true);
|
||||||
|
|
||||||
|
var result = await Sut().RegisterMember("19900101-1234", CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<OkResult>(result);
|
||||||
|
_repo.Verify(r => r.AddRegistration("199001011234"), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterMember_returns_Conflict_when_already_registered()
|
||||||
|
{
|
||||||
|
_repo.Setup(r => r.AddRegistration(It.IsAny<string>())).ReturnsAsync(false);
|
||||||
|
|
||||||
|
var result = await Sut().RegisterMember("19900101-1234", CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<ConflictResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IsMemberRegistered_returns_400_for_invalid_ssn()
|
||||||
|
{
|
||||||
|
var result = await Sut().IsMemberRegistered("bad");
|
||||||
|
|
||||||
|
Assert.IsType<BadRequestObjectResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IsMemberRegistered_returns_Conflict_when_already_registered()
|
||||||
|
{
|
||||||
|
_repo.Setup(r => r.GetIsRegistered("199001011234")).ReturnsAsync(true);
|
||||||
|
|
||||||
|
var result = await Sut().IsMemberRegistered("19900101-1234");
|
||||||
|
|
||||||
|
Assert.IsType<ConflictResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IsMemberRegistered_returns_Ok_when_not_yet_registered()
|
||||||
|
{
|
||||||
|
_repo.Setup(r => r.GetIsRegistered(It.IsAny<string>())).ReturnsAsync(false);
|
||||||
|
|
||||||
|
var result = await Sut().IsMemberRegistered("19900101-1234");
|
||||||
|
|
||||||
|
Assert.IsType<OkResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ClearRegistrations_calls_repository_and_returns_Ok()
|
||||||
|
{
|
||||||
|
var result = await Sut().ClearRegistrations(CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<OkObjectResult>(result);
|
||||||
|
_repo.Verify(r => r.ClearRegistrations(), Times.Once);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Moq;
|
||||||
|
using Registration.API.Controllers;
|
||||||
|
using Registration.API.RequestModels;
|
||||||
|
using Registration.API.Services;
|
||||||
|
using Registration.Domain.Models;
|
||||||
|
|
||||||
|
namespace Registration.Tests.Controllers;
|
||||||
|
|
||||||
|
public class VolunteerControllerTests
|
||||||
|
{
|
||||||
|
private readonly Mock<IVbytesVolunteerRelayService> _relay = new();
|
||||||
|
|
||||||
|
private VolunteerController Sut() => new(_relay.Object);
|
||||||
|
|
||||||
|
private static VolunteerRegistrationRequest ValidRequest() => new()
|
||||||
|
{
|
||||||
|
FirstName = " Eva ",
|
||||||
|
SurName = " Svensson ",
|
||||||
|
PhoneNumber = "+46 70 111 22 33",
|
||||||
|
Email = " eva@example.com ",
|
||||||
|
HasApprovedGdpr = true,
|
||||||
|
AreasOfInterest =
|
||||||
|
[
|
||||||
|
new AreaOfInterestRequest { Name = " Kiosk & Kök " },
|
||||||
|
new AreaOfInterestRequest { Name = "Städning" }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterVolunteer_returns_Ok_when_relay_succeeds()
|
||||||
|
{
|
||||||
|
Volunteer? captured = null;
|
||||||
|
_relay.Setup(r => r.RegisterVolunteerAsync(It.IsAny<Volunteer>(), It.IsAny<CancellationToken>()))
|
||||||
|
.Callback<Volunteer, CancellationToken>((v, _) => captured = v)
|
||||||
|
.ReturnsAsync(new VbytesRelayResult(true, 200, "ok"));
|
||||||
|
|
||||||
|
var result = await Sut().RegisterVolunteer(ValidRequest(), CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<OkResult>(result);
|
||||||
|
Assert.NotNull(captured);
|
||||||
|
Assert.Equal("Eva", captured!.FirstName);
|
||||||
|
Assert.Equal("Svensson", captured.SurName);
|
||||||
|
Assert.Equal("eva@example.com", captured.Email);
|
||||||
|
Assert.Equal("0701112233", captured.PhoneNumber);
|
||||||
|
Assert.Equal(2, captured.AreasOfInterest.Count);
|
||||||
|
Assert.Equal("Kiosk & Kök", captured.AreasOfInterest[0].Name);
|
||||||
|
Assert.Equal("Städning", captured.AreasOfInterest[1].Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterVolunteer_filters_empty_areas_and_returns_validation_problem_when_none_remain()
|
||||||
|
{
|
||||||
|
var controller = Sut();
|
||||||
|
var req = new VolunteerRegistrationRequest
|
||||||
|
{
|
||||||
|
FirstName = "Eva",
|
||||||
|
SurName = "Svensson",
|
||||||
|
PhoneNumber = "0701112233",
|
||||||
|
Email = "eva@example.com",
|
||||||
|
HasApprovedGdpr = true,
|
||||||
|
AreasOfInterest =
|
||||||
|
[
|
||||||
|
new AreaOfInterestRequest { Name = " " }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await controller.RegisterVolunteer(req, CancellationToken.None);
|
||||||
|
|
||||||
|
var obj = Assert.IsType<ObjectResult>(result);
|
||||||
|
Assert.IsType<ValidationProblemDetails>(obj.Value);
|
||||||
|
_relay.Verify(r => r.RegisterVolunteerAsync(It.IsAny<Volunteer>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterVolunteer_returns_Unauthorized_when_relay_fails()
|
||||||
|
{
|
||||||
|
_relay.Setup(r => r.RegisterVolunteerAsync(It.IsAny<Volunteer>(), It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(new VbytesRelayResult(false, 401, "401"));
|
||||||
|
|
||||||
|
var result = await Sut().RegisterVolunteer(ValidRequest(), CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<UnauthorizedResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterVolunteer_returns_Unauthorized_when_success_message_contains_401()
|
||||||
|
{
|
||||||
|
_relay.Setup(r => r.RegisterVolunteerAsync(It.IsAny<Volunteer>(), It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(new VbytesRelayResult(true, 200, "401 something"));
|
||||||
|
|
||||||
|
var result = await Sut().RegisterVolunteer(ValidRequest(), CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.IsType<UnauthorizedResult>(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,21 +1,30 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
|
||||||
<PackageReference Include="xunit" Version="2.9.2" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||||
</ItemGroup>
|
<PackageReference Include="Moq" Version="4.20.72" />
|
||||||
|
<PackageReference Include="xunit" Version="2.9.2" />
|
||||||
<ItemGroup>
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||||
<Using Include="Xunit" />
|
</ItemGroup>
|
||||||
</ItemGroup>
|
|
||||||
|
<ItemGroup>
|
||||||
</Project>
|
<ProjectReference Include="..\Registration.API\Registration.API.csproj" />
|
||||||
|
<ProjectReference Include="..\Registration.Infra\Registration.Infra.csproj" />
|
||||||
|
<ProjectReference Include="..\Registration.Domain\Registration.Domain.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Xunit" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Moq;
|
||||||
|
using Registration.Domain.Models;
|
||||||
|
using Registration.Infra.Repositories;
|
||||||
|
|
||||||
|
namespace Registration.Tests.Repositories;
|
||||||
|
|
||||||
|
public class RegistrationRepositoryTests
|
||||||
|
{
|
||||||
|
private static IConfiguration BuildConfig(string? connection, string? pepper)
|
||||||
|
{
|
||||||
|
var dict = new Dictionary<string, string?>();
|
||||||
|
if (connection is not null) dict["ConnectionStrings:DefaultConnection"] = connection;
|
||||||
|
if (pepper is not null) dict["Security:SsnPepper"] = pepper;
|
||||||
|
return new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_throws_when_connection_string_missing()
|
||||||
|
{
|
||||||
|
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||||
|
new RegistrationRepository(BuildConfig(null, "pepper")));
|
||||||
|
|
||||||
|
Assert.Contains("DefaultConnection", ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_throws_when_pepper_missing()
|
||||||
|
{
|
||||||
|
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||||
|
new RegistrationRepository(BuildConfig("Host=localhost", null)));
|
||||||
|
|
||||||
|
Assert.Contains("SsnPepper", ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_succeeds_when_required_values_present()
|
||||||
|
{
|
||||||
|
var repo = new RegistrationRepository(BuildConfig("Host=localhost", "pepper"));
|
||||||
|
|
||||||
|
Assert.NotNull(repo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RegistrationRepositoryContractTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task Mocked_repository_supports_full_add_check_clear_cycle()
|
||||||
|
{
|
||||||
|
var registered = new HashSet<string>();
|
||||||
|
var mock = new Mock<IRegistrationRepository>();
|
||||||
|
mock.Setup(r => r.AddRegistration(It.IsAny<string>()))
|
||||||
|
.ReturnsAsync((string ssn) => registered.Add(ssn));
|
||||||
|
mock.Setup(r => r.GetIsRegistered(It.IsAny<string>()))
|
||||||
|
.ReturnsAsync((string ssn) => registered.Contains(ssn));
|
||||||
|
mock.Setup(r => r.ClearRegistrations())
|
||||||
|
.Returns(() => { registered.Clear(); return Task.CompletedTask; });
|
||||||
|
|
||||||
|
var repo = mock.Object;
|
||||||
|
|
||||||
|
Assert.False(await repo.GetIsRegistered("199001011234"));
|
||||||
|
Assert.True(await repo.AddRegistration("199001011234"));
|
||||||
|
Assert.False(await repo.AddRegistration("199001011234"));
|
||||||
|
Assert.True(await repo.GetIsRegistered("199001011234"));
|
||||||
|
|
||||||
|
await repo.ClearRegistrations();
|
||||||
|
|
||||||
|
Assert.False(await repo.GetIsRegistered("199001011234"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Mocked_repository_supports_event_content_roundtrip()
|
||||||
|
{
|
||||||
|
EventContent stored = new();
|
||||||
|
var mock = new Mock<IRegistrationRepository>();
|
||||||
|
mock.Setup(r => r.GetEventContent()).ReturnsAsync(() => stored);
|
||||||
|
mock.Setup(r => r.UpdateEventContent(It.IsAny<EventContent>()))
|
||||||
|
.Returns((EventContent c) => { stored = c; return Task.CompletedTask; });
|
||||||
|
|
||||||
|
var repo = mock.Object;
|
||||||
|
|
||||||
|
var content = new EventContent
|
||||||
|
{
|
||||||
|
Title = "VBytes LAN",
|
||||||
|
EventDate = "2026-05-26",
|
||||||
|
RegistrationEnabled = false,
|
||||||
|
VolunteerAreas = "A\nB"
|
||||||
|
};
|
||||||
|
await repo.UpdateEventContent(content);
|
||||||
|
var loaded = await repo.GetEventContent();
|
||||||
|
|
||||||
|
Assert.Equal(content.Title, loaded.Title);
|
||||||
|
Assert.Equal(content.EventDate, loaded.EventDate);
|
||||||
|
Assert.False(loaded.RegistrationEnabled);
|
||||||
|
Assert.Equal("A\nB", loaded.VolunteerAreas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Registration.Tests.Services;
|
||||||
|
|
||||||
|
internal sealed class FakeHttpMessageHandler : HttpMessageHandler
|
||||||
|
{
|
||||||
|
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handler;
|
||||||
|
|
||||||
|
public List<HttpRequestMessage> Requests { get; } = [];
|
||||||
|
public List<string?> RequestBodies { get; } = [];
|
||||||
|
|
||||||
|
public FakeHttpMessageHandler(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handler)
|
||||||
|
{
|
||||||
|
_handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FakeHttpMessageHandler RespondWith(HttpStatusCode status, string body = "")
|
||||||
|
{
|
||||||
|
return new FakeHttpMessageHandler((_, _) => Task.FromResult(new HttpResponseMessage(status)
|
||||||
|
{
|
||||||
|
Content = new StringContent(body)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FakeHttpMessageHandler Throws(Exception exception)
|
||||||
|
{
|
||||||
|
return new FakeHttpMessageHandler((_, _) => Task.FromException<HttpResponseMessage>(exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Requests.Add(request);
|
||||||
|
RequestBodies.Add(request.Content is null ? null : await request.Content.ReadAsStringAsync(cancellationToken));
|
||||||
|
return await _handler(request, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Registration.API.Configuration;
|
||||||
|
using Registration.API.Services;
|
||||||
|
using Registration.Domain.Models;
|
||||||
|
|
||||||
|
namespace Registration.Tests.Services;
|
||||||
|
|
||||||
|
public class VbytesParticipantRelayServiceTests
|
||||||
|
{
|
||||||
|
private static VbytesRelayOptions ValidOptions() => new()
|
||||||
|
{
|
||||||
|
BaseUrl = "https://relay.example",
|
||||||
|
ParticipantRegisterPath = "/api/participant",
|
||||||
|
VolunteerRegisterPath = "/api/volunteer",
|
||||||
|
ApiKeyHeaderName = "X-Api-Key",
|
||||||
|
ApiKey = "secret"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Participant SampleParticipant() => new()
|
||||||
|
{
|
||||||
|
IsMember = true,
|
||||||
|
FirstName = "Anna",
|
||||||
|
SurName = "Andersson",
|
||||||
|
Grade = "9",
|
||||||
|
PhoneNumber = "0701112233",
|
||||||
|
Email = "anna@example.com",
|
||||||
|
GuardianName = "Bertil",
|
||||||
|
GuardianPhoneNumber = "0709998877",
|
||||||
|
GuardianEmail = "bertil@example.com",
|
||||||
|
IsVisitor = false,
|
||||||
|
HasApprovedGdpr = true,
|
||||||
|
Friends = null,
|
||||||
|
SpecialDiet = null
|
||||||
|
};
|
||||||
|
|
||||||
|
private static VbytesParticipantRelayService Build(FakeHttpMessageHandler handler, VbytesRelayOptions options)
|
||||||
|
{
|
||||||
|
var client = new HttpClient(handler) { BaseAddress = new Uri(options.BaseUrl) };
|
||||||
|
return new VbytesParticipantRelayService(
|
||||||
|
client,
|
||||||
|
Options.Create(options),
|
||||||
|
NullLogger<VbytesParticipantRelayService>.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterParticipantAsync_returns_500_when_base_url_missing()
|
||||||
|
{
|
||||||
|
var opts = ValidOptions();
|
||||||
|
opts.BaseUrl = "";
|
||||||
|
// BaseAddress can't be empty on HttpClient — use any URI, ValidateConfiguration runs first.
|
||||||
|
var handler = FakeHttpMessageHandler.RespondWith(HttpStatusCode.OK);
|
||||||
|
var client = new HttpClient(handler);
|
||||||
|
var sut = new VbytesParticipantRelayService(
|
||||||
|
client, Options.Create(opts), NullLogger<VbytesParticipantRelayService>.Instance);
|
||||||
|
|
||||||
|
var result = await sut.RegisterParticipantAsync(SampleParticipant());
|
||||||
|
|
||||||
|
Assert.False(result.Success);
|
||||||
|
Assert.Equal(StatusCodes.Status500InternalServerError, result.StatusCode);
|
||||||
|
Assert.Empty(handler.Requests);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterParticipantAsync_returns_500_when_api_key_missing_or_placeholder()
|
||||||
|
{
|
||||||
|
var opts = ValidOptions();
|
||||||
|
opts.ApiKey = "__SET_IN_USER_SECRETS__";
|
||||||
|
var handler = FakeHttpMessageHandler.RespondWith(HttpStatusCode.OK);
|
||||||
|
var sut = Build(handler, opts);
|
||||||
|
|
||||||
|
var result = await sut.RegisterParticipantAsync(SampleParticipant());
|
||||||
|
|
||||||
|
Assert.False(result.Success);
|
||||||
|
Assert.Equal(StatusCodes.Status500InternalServerError, result.StatusCode);
|
||||||
|
Assert.Empty(handler.Requests);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterParticipantAsync_posts_to_configured_path_with_api_key_header()
|
||||||
|
{
|
||||||
|
var opts = ValidOptions();
|
||||||
|
var handler = FakeHttpMessageHandler.RespondWith(HttpStatusCode.OK, "ok");
|
||||||
|
var sut = Build(handler, opts);
|
||||||
|
|
||||||
|
var result = await sut.RegisterParticipantAsync(SampleParticipant());
|
||||||
|
|
||||||
|
Assert.True(result.Success);
|
||||||
|
var request = Assert.Single(handler.Requests);
|
||||||
|
Assert.Equal(HttpMethod.Post, request.Method);
|
||||||
|
Assert.EndsWith(opts.ParticipantRegisterPath, request.RequestUri!.AbsolutePath);
|
||||||
|
Assert.True(request.Headers.TryGetValues(opts.ApiKeyHeaderName, out var headerValues));
|
||||||
|
Assert.Equal(opts.ApiKey, headerValues!.First());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterParticipantAsync_serializes_payload_in_snake_case()
|
||||||
|
{
|
||||||
|
var opts = ValidOptions();
|
||||||
|
var handler = FakeHttpMessageHandler.RespondWith(HttpStatusCode.OK);
|
||||||
|
var sut = Build(handler, opts);
|
||||||
|
|
||||||
|
await sut.RegisterParticipantAsync(SampleParticipant());
|
||||||
|
|
||||||
|
var body = Assert.Single(handler.RequestBodies);
|
||||||
|
Assert.Contains("\"first_name\"", body);
|
||||||
|
Assert.Contains("\"guardian_phone\"", body);
|
||||||
|
Assert.Contains("\"is_visiting\"", body);
|
||||||
|
Assert.Contains("\"gdpr\"", body);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterParticipantAsync_returns_504_on_TaskCanceledException()
|
||||||
|
{
|
||||||
|
var opts = ValidOptions();
|
||||||
|
var handler = FakeHttpMessageHandler.Throws(new TaskCanceledException("simulated timeout"));
|
||||||
|
var sut = Build(handler, opts);
|
||||||
|
|
||||||
|
var result = await sut.RegisterParticipantAsync(SampleParticipant());
|
||||||
|
|
||||||
|
Assert.False(result.Success);
|
||||||
|
Assert.Equal(StatusCodes.Status504GatewayTimeout, result.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterParticipantAsync_returns_502_on_generic_exception()
|
||||||
|
{
|
||||||
|
var opts = ValidOptions();
|
||||||
|
var handler = FakeHttpMessageHandler.Throws(new HttpRequestException("boom"));
|
||||||
|
var sut = Build(handler, opts);
|
||||||
|
|
||||||
|
var result = await sut.RegisterParticipantAsync(SampleParticipant());
|
||||||
|
|
||||||
|
Assert.False(result.Success);
|
||||||
|
Assert.Equal(StatusCodes.Status502BadGateway, result.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterParticipantAsync_returns_failure_with_upstream_status_when_non_success()
|
||||||
|
{
|
||||||
|
var opts = ValidOptions();
|
||||||
|
var handler = FakeHttpMessageHandler.RespondWith(HttpStatusCode.Unauthorized, "401 token expired");
|
||||||
|
var sut = Build(handler, opts);
|
||||||
|
|
||||||
|
var result = await sut.RegisterParticipantAsync(SampleParticipant());
|
||||||
|
|
||||||
|
Assert.False(result.Success);
|
||||||
|
Assert.Equal(StatusCodes.Status401Unauthorized, result.StatusCode);
|
||||||
|
Assert.Contains("401", result.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterParticipantAsync_truncates_huge_body_in_logged_message_path()
|
||||||
|
{
|
||||||
|
// The relay caps the message to 600 chars before returning — verify non-success path keeps a reasonable length.
|
||||||
|
var opts = ValidOptions();
|
||||||
|
var bigBody = new string('x', 5000);
|
||||||
|
var handler = FakeHttpMessageHandler.RespondWith(HttpStatusCode.BadGateway, bigBody);
|
||||||
|
var sut = Build(handler, opts);
|
||||||
|
|
||||||
|
var result = await sut.RegisterParticipantAsync(SampleParticipant());
|
||||||
|
|
||||||
|
Assert.False(result.Success);
|
||||||
|
// Success path returns full body, failure path returns the (untruncated) body too,
|
||||||
|
// but the log-safe truncation is applied only to the logged message. Assert the
|
||||||
|
// returned message echoes the upstream body so callers can surface it.
|
||||||
|
Assert.Contains("x", result.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Registration.API.Configuration;
|
||||||
|
using Registration.API.Services;
|
||||||
|
using Registration.Domain.Models;
|
||||||
|
|
||||||
|
namespace Registration.Tests.Services;
|
||||||
|
|
||||||
|
public class VbytesVolunteerRelayServiceTests
|
||||||
|
{
|
||||||
|
private static VbytesRelayOptions ValidOptions() => new()
|
||||||
|
{
|
||||||
|
BaseUrl = "https://relay.example",
|
||||||
|
ParticipantRegisterPath = "/api/participant",
|
||||||
|
VolunteerRegisterPath = "/api/volunteer",
|
||||||
|
ApiKeyHeaderName = "X-Api-Key",
|
||||||
|
ApiKey = "secret"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Volunteer SampleVolunteer() => new()
|
||||||
|
{
|
||||||
|
FirstName = "Eva",
|
||||||
|
SurName = "Svensson",
|
||||||
|
PhoneNumber = "0701112233",
|
||||||
|
Email = "eva@example.com",
|
||||||
|
HasApprovedGdpr = true,
|
||||||
|
AreasOfInterest =
|
||||||
|
[
|
||||||
|
new AreaOfInterest { Name = "Kiosk & Kök" },
|
||||||
|
new AreaOfInterest { Name = "Städning" }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
private static VbytesVolunteerRelayService Build(FakeHttpMessageHandler handler, VbytesRelayOptions options)
|
||||||
|
{
|
||||||
|
var client = new HttpClient(handler) { BaseAddress = new Uri(options.BaseUrl) };
|
||||||
|
return new VbytesVolunteerRelayService(
|
||||||
|
client,
|
||||||
|
Options.Create(options),
|
||||||
|
NullLogger<VbytesVolunteerRelayService>.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterVolunteerAsync_returns_500_when_configuration_invalid()
|
||||||
|
{
|
||||||
|
var opts = ValidOptions();
|
||||||
|
opts.ApiKey = "";
|
||||||
|
var handler = FakeHttpMessageHandler.RespondWith(HttpStatusCode.OK);
|
||||||
|
var sut = Build(handler, opts);
|
||||||
|
|
||||||
|
var result = await sut.RegisterVolunteerAsync(SampleVolunteer());
|
||||||
|
|
||||||
|
Assert.False(result.Success);
|
||||||
|
Assert.Equal(StatusCodes.Status500InternalServerError, result.StatusCode);
|
||||||
|
Assert.Empty(handler.Requests);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterVolunteerAsync_posts_to_configured_path_with_serialized_payload()
|
||||||
|
{
|
||||||
|
var opts = ValidOptions();
|
||||||
|
var handler = FakeHttpMessageHandler.RespondWith(HttpStatusCode.OK);
|
||||||
|
var sut = Build(handler, opts);
|
||||||
|
|
||||||
|
var result = await sut.RegisterVolunteerAsync(SampleVolunteer());
|
||||||
|
|
||||||
|
Assert.True(result.Success);
|
||||||
|
var request = Assert.Single(handler.Requests);
|
||||||
|
Assert.EndsWith(opts.VolunteerRegisterPath, request.RequestUri!.AbsolutePath);
|
||||||
|
var body = Assert.Single(handler.RequestBodies);
|
||||||
|
Assert.Contains("\"first_name\"", body);
|
||||||
|
Assert.Contains("\"areas\"", body);
|
||||||
|
using var doc = JsonDocument.Parse(body!);
|
||||||
|
var areas = doc.RootElement.GetProperty("areas").EnumerateArray()
|
||||||
|
.Select(e => e.GetString() ?? string.Empty).ToArray();
|
||||||
|
Assert.Equal(new[] { "Kiosk & Kök", "Städning" }, areas);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterVolunteerAsync_returns_504_on_timeout()
|
||||||
|
{
|
||||||
|
var opts = ValidOptions();
|
||||||
|
var handler = FakeHttpMessageHandler.Throws(new TaskCanceledException());
|
||||||
|
var sut = Build(handler, opts);
|
||||||
|
|
||||||
|
var result = await sut.RegisterVolunteerAsync(SampleVolunteer());
|
||||||
|
|
||||||
|
Assert.False(result.Success);
|
||||||
|
Assert.Equal(StatusCodes.Status504GatewayTimeout, result.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterVolunteerAsync_returns_502_on_generic_exception()
|
||||||
|
{
|
||||||
|
var opts = ValidOptions();
|
||||||
|
var handler = FakeHttpMessageHandler.Throws(new InvalidOperationException("nope"));
|
||||||
|
var sut = Build(handler, opts);
|
||||||
|
|
||||||
|
var result = await sut.RegisterVolunteerAsync(SampleVolunteer());
|
||||||
|
|
||||||
|
Assert.False(result.Success);
|
||||||
|
Assert.Equal(StatusCodes.Status502BadGateway, result.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterVolunteerAsync_returns_failure_for_non_success_upstream()
|
||||||
|
{
|
||||||
|
var opts = ValidOptions();
|
||||||
|
var handler = FakeHttpMessageHandler.RespondWith(HttpStatusCode.BadRequest, "bad data");
|
||||||
|
var sut = Build(handler, opts);
|
||||||
|
|
||||||
|
var result = await sut.RegisterVolunteerAsync(SampleVolunteer());
|
||||||
|
|
||||||
|
Assert.False(result.Success);
|
||||||
|
Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode);
|
||||||
|
Assert.Equal("bad data", result.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
namespace Registration.Tests;
|
|
||||||
|
|
||||||
public class UnitTest1
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void Test1()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Registration.API.Validation;
|
||||||
|
|
||||||
|
namespace Registration.Tests.Validation;
|
||||||
|
|
||||||
|
public class InputNormalizationExtensionsTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void NormalizePhone_delegates_to_NormalizeSwedishMobile()
|
||||||
|
{
|
||||||
|
Assert.Equal("0701234567", "+46 70 123 45 67".NormalizePhone());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NormalizeOptionalPhone_returns_null_for_null_or_whitespace()
|
||||||
|
{
|
||||||
|
Assert.Null(((string?)null).NormalizeOptionalPhone());
|
||||||
|
Assert.Null("".NormalizeOptionalPhone());
|
||||||
|
Assert.Null(" ".NormalizeOptionalPhone());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NormalizeOptionalPhone_normalizes_when_value_present()
|
||||||
|
{
|
||||||
|
Assert.Equal("0701234567", "+46701234567".NormalizeOptionalPhone());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("19900101-1234", "199001011234")]
|
||||||
|
[InlineData("900101-1234", "9001011234")]
|
||||||
|
public void TryNormalizeSwedishSsn_returns_true_for_valid_ssn(string input, string expected)
|
||||||
|
{
|
||||||
|
var ok = input.TryNormalizeSwedishSsn(out var normalized, out var errorResult);
|
||||||
|
|
||||||
|
Assert.True(ok);
|
||||||
|
Assert.Equal(expected, normalized);
|
||||||
|
Assert.Null(errorResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("")]
|
||||||
|
[InlineData("123")]
|
||||||
|
[InlineData("12345678901")]
|
||||||
|
public void TryNormalizeSwedishSsn_returns_false_with_400_for_invalid_ssn(string input)
|
||||||
|
{
|
||||||
|
var ok = input.TryNormalizeSwedishSsn(out _, out var errorResult);
|
||||||
|
|
||||||
|
Assert.False(ok);
|
||||||
|
var bad = Assert.IsType<BadRequestObjectResult>(errorResult);
|
||||||
|
var problem = Assert.IsType<ValidationProblemDetails>(bad.Value);
|
||||||
|
Assert.Equal(StatusCodes.Status400BadRequest, problem.Status);
|
||||||
|
Assert.True(problem.Errors.ContainsKey("ssn"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
using Registration.API.Validation;
|
||||||
|
|
||||||
|
namespace Registration.Tests.Validation;
|
||||||
|
|
||||||
|
public class InputNormalizationTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("19900101-1234", "199001011234")]
|
||||||
|
[InlineData("900101-1234", "9001011234")]
|
||||||
|
[InlineData("19900101 1234", "199001011234")]
|
||||||
|
[InlineData("199001011234", "199001011234")]
|
||||||
|
[InlineData("abc19-90-01-01-1234xyz", "199001011234")]
|
||||||
|
[InlineData("", "")]
|
||||||
|
public void NormalizeSsn_strips_non_digits(string input, string expected)
|
||||||
|
{
|
||||||
|
Assert.Equal(expected, InputNormalization.NormalizeSsn(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("9001011234", true)]
|
||||||
|
[InlineData("199001011234", true)]
|
||||||
|
[InlineData("", false)]
|
||||||
|
[InlineData("123", false)]
|
||||||
|
[InlineData("12345678901", false)]
|
||||||
|
[InlineData("1234567890123", false)]
|
||||||
|
public void IsValidSsn_accepts_only_length_10_or_12(string input, bool expected)
|
||||||
|
{
|
||||||
|
Assert.Equal(expected, InputNormalization.IsValidSsn(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("0701234567", "0701234567")]
|
||||||
|
[InlineData("+46701234567", "0701234567")]
|
||||||
|
[InlineData("46701234567", "0701234567")]
|
||||||
|
[InlineData("0046701234567", "0701234567")]
|
||||||
|
[InlineData("+46-70-123 45 67", "0701234567")]
|
||||||
|
[InlineData("701234567", "0701234567")]
|
||||||
|
public void NormalizeSwedishMobile_returns_leading_zero_format(string input, string expected)
|
||||||
|
{
|
||||||
|
Assert.Equal(expected, InputNormalization.NormalizeSwedishMobile(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NormalizeSwedishMobile_handles_empty_input()
|
||||||
|
{
|
||||||
|
Assert.Equal(string.Empty, InputNormalization.NormalizeSwedishMobile(string.Empty));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue