Initial code commit.

This commit is contained in:
2026-02-03 10:44:31 +08:00
parent 8927c5ae0e
commit d69fe2cc1f
99 changed files with 10839 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
using AutoMapper;
using Microsoft.Extensions.Logging;
using Moq;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Repositories;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Services;
using Sufi.Demo.PeopleDirectory.Application.Features.Contacts.Commands;
using Sufi.Demo.PeopleDirectory.Domain.Entities.Misc;
namespace Sufi.Demo.PeopleDirectory.UnitTests.Contacts
{
public class AddEditContactCommandHandlerTests
{
private readonly Mock<IMapper> _mapperMock;
private readonly Mock<IUnitOfWork<int>> _unitOfWorkMock;
private readonly Mock<ILogger<AddEditContactCommandHandler>> _loggerMock = new();
private readonly Mock<IAppCache> _appCacheMock = new();
private readonly AddEditContactCommandHandler _handler;
public AddEditContactCommandHandlerTests()
{
_mapperMock = new Mock<IMapper>();
_unitOfWorkMock = new Mock<IUnitOfWork<int>>();
_handler = new AddEditContactCommandHandler(_mapperMock.Object, _unitOfWorkMock.Object, _loggerMock.Object, _appCacheMock.Object);
}
[Fact]
public async Task Handle_ShouldAddNewContact_WhenMaxCountNotExceeded()
{
// Arrange
var command = new AddEditContactCommand
{
Id = 0,
UserName = "JohnDoe",
Email = "john@example.com",
Phone = "1234567890",
SkillSets = "C#, SQL",
Hobby = "Reading"
};
_unitOfWorkMock.Setup(u => u.Repository<Contact>().CountAsync()).ReturnsAsync(50);
_mapperMock.Setup(m => m.Map<Contact>(command)).Returns(new Contact());
_unitOfWorkMock.Setup(u => u.Repository<Contact>().AddAsync(It.IsAny<Contact>())).Returns(Task.FromResult(new Contact()));
_unitOfWorkMock.Setup(u => u.Commit(It.IsAny<CancellationToken>())).ReturnsAsync(1);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
Assert.True(result.Succeeded);
Assert.Equal("New contact saved.", result.Messages[0]);
}
[Fact]
public async Task Handle_ShouldFailToAddNewContact_WhenMaxCountExceeded()
{
// Arrange
var command = new AddEditContactCommand { Id = 0 };
_unitOfWorkMock.Setup(u => u.Repository<Contact>().CountAsync()).ReturnsAsync(101);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
Assert.False(result.Succeeded);
Assert.Equal("Max item count reached. Please delete some first.", result.Messages[0]);
}
[Fact]
public async Task Handle_ShouldUpdateContact_WhenContactExists()
{
// Arrange
var command = new AddEditContactCommand
{
Id = 1,
UserName = "JaneDoe",
Email = "jane@example.com",
Phone = "0987654321",
SkillSets = "Java, Python",
Hobby = "Traveling"
};
var existingContact = new Contact { Id = 1 };
_unitOfWorkMock.Setup(u => u.Repository<Contact>().GetByIdAsync(1)).ReturnsAsync(existingContact);
_unitOfWorkMock.Setup(u => u.Repository<Contact>().UpdateAsync(existingContact)).Returns(Task.CompletedTask);
_unitOfWorkMock.Setup(u => u.Commit(It.IsAny<CancellationToken>())).ReturnsAsync(1);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
Assert.True(result.Succeeded);
Assert.Equal("Contact updated.", result.Messages[0]);
}
[Fact]
public async Task Handle_ShouldFailToUpdateContact_WhenContactDoesNotExist()
{
// Arrange
var command = new AddEditContactCommand { Id = 1 };
Contact? existingContact = null;
_unitOfWorkMock.Setup(u => u.Repository<Contact>().GetByIdAsync(1)).ReturnsAsync(existingContact);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
Assert.False(result.Succeeded);
Assert.Equal("Contact not found!", result.Messages[0]);
}
}
}

View File

@@ -0,0 +1,57 @@
using Microsoft.Extensions.Logging;
using Moq;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Repositories;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Services;
using Sufi.Demo.PeopleDirectory.Application.Features.Contacts.Commands;
using Sufi.Demo.PeopleDirectory.Domain.Entities.Misc;
namespace Sufi.Demo.PeopleDirectory.UnitTests.Contacts
{
public class DeleteContactCommandHandlerTests
{
private readonly Mock<IUnitOfWork<int>> _unitOfWorkMock;
private readonly Mock<ILogger<DeleteContactCommandHandler>> _loggerMock = new();
private readonly Mock<IAppCache> _appCacheMock = new();
private readonly DeleteContactCommandHandler _handler;
public DeleteContactCommandHandlerTests()
{
_unitOfWorkMock = new Mock<IUnitOfWork<int>>();
_handler = new DeleteContactCommandHandler(_unitOfWorkMock.Object, _loggerMock.Object, _appCacheMock.Object);
}
[Fact]
public async Task Handle_ShouldDeleteContact_WhenContactExists()
{
// Arrange
var command = new DeleteContactCommand { Id = 1 };
var existingContact = new Contact { Id = 1 };
_unitOfWorkMock.Setup(u => u.Repository<Contact>().GetByIdAsync(1)).ReturnsAsync(existingContact);
_unitOfWorkMock.Setup(u => u.Repository<Contact>().DeleteAsync(existingContact)).Returns(Task.CompletedTask);
_unitOfWorkMock.Setup(u => u.Commit(It.IsAny<CancellationToken>())).ReturnsAsync(1);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
Assert.True(result.Succeeded);
Assert.Empty(result.Messages); // Success messages are empty in this implementation
}
[Fact]
public async Task Handle_ShouldFailToDeleteContact_WhenContactDoesNotExist()
{
// Arrange
var command = new DeleteContactCommand { Id = 1 };
Contact? existingContact = null;
_unitOfWorkMock.Setup(u => u.Repository<Contact>().GetByIdAsync(1)).ReturnsAsync(existingContact);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
Assert.False(result.Succeeded);
Assert.Equal("No data to delete.", result.Messages[0]);
}
}
}

View File

@@ -0,0 +1,84 @@
using AutoMapper;
using Moq;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Repositories;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Services;
using Sufi.Demo.PeopleDirectory.Application.Features.Contacts.Queries.GetById;
using Sufi.Demo.PeopleDirectory.Domain.Entities.Misc;
namespace Sufi.Demo.PeopleDirectory.UnitTests.Contacts
{
public class GetContactByIdQueryHandlerTests
{
private readonly Mock<IUnitOfWork<int>> _unitOfWorkMock;
private readonly Mock<IMapper> _mapperMock;
private readonly Mock<IAppCache> _appCacheMock = new();
private readonly GetContactByIdQueryHandler _handler;
public GetContactByIdQueryHandlerTests()
{
_unitOfWorkMock = new Mock<IUnitOfWork<int>>();
_mapperMock = new Mock<IMapper>();
_handler = new GetContactByIdQueryHandler(_unitOfWorkMock.Object, _mapperMock.Object, _appCacheMock.Object);
}
[Fact]
public async Task Handle_ReturnsMappedContact_WhenContactExists()
{
// Arrange
var contact = new Contact
{
Id = 1,
UserName = "User1",
Phone = "123",
Email = "user1@example.com",
SkillSets = "C#",
Hobby = "Reading"
};
var mappedContact = new GetContactByIdResponse
{
Id = 1,
UserName = "User1",
Phone = "123",
Email = "user1@example.com",
SkillSets = "C#",
Hobby = "Reading"
};
_unitOfWorkMock.Setup(u => u.Repository<Contact>().GetByIdAsync(1)).ReturnsAsync(contact);
_mapperMock.Setup(m => m.Map<GetContactByIdResponse>(contact)).Returns(mappedContact);
_appCacheMock.Setup(c => c.GetOrAddAsync(It.IsAny<string>(), It.IsAny<Func<CancellationToken, ValueTask<Contact>>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<TimeSpan?>()))
.ReturnsAsync(contact);
var query = new GetContactByIdQuery { Id = 1 };
// Act
var result = await _handler.Handle(query, CancellationToken.None);
// Assert
Assert.True(result.Succeeded);
Assert.NotNull(result.Data);
Assert.Equal(1, result.Data.Id);
Assert.Equal("User1", result.Data.UserName);
}
[Fact]
public async Task Handle_ReturnsNull_WhenContactDoesNotExist()
{
// Arrange
Contact? contact = null;
GetContactByIdResponse? mappedContact = null;
_unitOfWorkMock.Setup(u => u.Repository<Contact>().GetByIdAsync(99)).ReturnsAsync(contact);
_mapperMock.Setup(m => m.Map<GetContactByIdResponse>(contact)).Returns(mappedContact);
var query = new GetContactByIdQuery { Id = 99 };
// Act
var result = await _handler.Handle(query, CancellationToken.None);
// Assert
Assert.True(result.Succeeded);
Assert.Null(result.Data);
}
}
}

View File

@@ -0,0 +1,100 @@
using AutoMapper;
using Moq;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Repositories;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Services;
using Sufi.Demo.PeopleDirectory.Application.Features.Contacts.Queries.GetAll;
using Sufi.Demo.PeopleDirectory.Application.Features.Contacts.Queries.GetById;
using Sufi.Demo.PeopleDirectory.Domain.Entities.Misc;
namespace Sufi.Demo.PeopleDirectory.UnitTests.Contacts
{
public class GetContactsQueriesHandlerTests
{
private readonly Mock<IUnitOfWork<int>> _unitOfWorkMock;
private readonly Mock<IMapper> _mapperMock;
private readonly Mock<IAppCache> _appCacheMock = new();
public GetContactsQueriesHandlerTests()
{
_unitOfWorkMock = new Mock<IUnitOfWork<int>>();
_mapperMock = new Mock<IMapper>();
}
[Fact]
public async Task GetAllContactsQueryHandler_ReturnsMappedContacts()
{
// Arrange
var contacts = new List<Contact>
{
new() { Id = 1, UserName = "User1", Phone = "123", Email = "user1@example.com", SkillSets = "C#", Hobby = "Reading" },
new() { Id = 2, UserName = "User2", Phone = "456", Email = "user2@example.com", SkillSets = "Java", Hobby = "Swimming" }
};
var mappedContacts = new List<GetAllContactsResponse>
{
new() { Id = 1, UserName = "User1", Phone = "123", Email = "user1@example.com", SkillSets = "C#", Hobby = "Reading" },
new() { Id = 2, UserName = "User2", Phone = "456", Email = "user2@example.com", SkillSets = "Java", Hobby = "Swimming" }
};
_unitOfWorkMock.Setup(u => u.Repository<Contact>().GetAllAsync()).ReturnsAsync(contacts);
_mapperMock.Setup(m => m.Map<List<GetAllContactsResponse>>(contacts)).Returns(mappedContacts);
_appCacheMock.Setup(c => c.GetOrAddAsync(It.IsAny<string>(), It.IsAny<Func<CancellationToken, ValueTask<List<Contact>>>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<TimeSpan?>()))
.ReturnsAsync(contacts);
var handler = new GetAllContactsQueryHandler(_unitOfWorkMock.Object, _mapperMock.Object, _appCacheMock.Object);
// Act
var result = await handler.Handle(new GetAllContactsQuery(), CancellationToken.None);
// Assert
Assert.True(result.Succeeded);
Assert.NotNull(result.Data);
Assert.Equal(2, result.Data.Count);
Assert.Equal("User1", result.Data[0].UserName);
Assert.Equal("User2", result.Data[1].UserName);
}
[Fact]
public async Task GetContactByIdQueryHandler_ReturnsMappedContact_WhenContactExists()
{
// Arrange
var contact = new Contact { Id = 1, UserName = "User1", Phone = "123", Email = "user1@example.com", SkillSets = "C#", Hobby = "Reading" };
var mappedContact = new GetContactByIdResponse { Id = 1, UserName = "User1", Phone = "123", Email = "user1@example.com", SkillSets = "C#", Hobby = "Reading" };
_unitOfWorkMock.Setup(u => u.Repository<Contact>().GetByIdAsync(1)).ReturnsAsync(contact);
_mapperMock.Setup(m => m.Map<GetContactByIdResponse>(contact)).Returns(mappedContact);
_appCacheMock.Setup(c => c.GetOrAddAsync(It.IsAny<string>(), It.IsAny<Func<CancellationToken, ValueTask<Contact>>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<TimeSpan?>()))
.ReturnsAsync(contact);
var handler = new GetContactByIdQueryHandler(_unitOfWorkMock.Object, _mapperMock.Object, _appCacheMock.Object);
// Act
var result = await handler.Handle(new GetContactByIdQuery { Id = 1 }, CancellationToken.None);
// Assert
Assert.True(result.Succeeded);
Assert.NotNull(result.Data);
Assert.Equal(1, result.Data.Id);
Assert.Equal("User1", result.Data.UserName);
}
[Fact]
public async Task GetContactByIdQueryHandler_ReturnsNullData_WhenContactDoesNotExist()
{
// Arrange
Contact? contact = null;
GetContactByIdResponse? mappedContact = null;
_unitOfWorkMock.Setup(u => u.Repository<Contact>().GetByIdAsync(99)).ReturnsAsync(contact);
_mapperMock.Setup(m => m.Map<GetContactByIdResponse>(contact)).Returns(mappedContact);
var handler = new GetContactByIdQueryHandler(_unitOfWorkMock.Object, _mapperMock.Object, _appCacheMock.Object);
// Act
var result = await handler.Handle(new GetContactByIdQuery { Id = 99 }, CancellationToken.None);
// Assert
Assert.True(result.Succeeded);
Assert.Null(result.Data);
}
}
}

View File

@@ -0,0 +1,35 @@
<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="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Sufi.Demo.PeopleDirectory.Application\Sufi.Demo.PeopleDirectory.Application.csproj" />
<ProjectReference Include="..\Sufi.Demo.PeopleDirectory.Shared\Sufi.Demo.PeopleDirectory.Shared.csproj" />
<ProjectReference Include="..\ui\Sufi.Demo.PeopleDirectory.UI\Server\Sufi.Demo.PeopleDirectory.UI.Server.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
</Project>