add tests

This commit is contained in:
Aaron Po
2026-06-19 23:14:10 -04:00
parent 86a0c50f6e
commit 4554fff534
2 changed files with 224 additions and 53 deletions

View File

@@ -1,69 +1,239 @@
using FluentAssertions;
using Xunit;
using Service.Breweries;
using API.Core.Contracts.Breweries;
using Domain.Entities; using Domain.Entities;
using FluentAssertions;
using Infrastructure.Repository.Breweries;
using Moq;
namespace Service.Breweries.Tests; namespace Service.Breweries.Tests;
public class BreweryServiceTests public class BreweryServiceTests
{ {
private class FakeRepo : IBreweryRepository private readonly Mock<IBreweryRepository> _repoMock;
{ private readonly BreweryService _service;
public BreweryPost? Created;
public Task<BreweryPost?> GetByIdAsync(Guid id) => Task.FromResult<BreweryPost?>(null); public BreweryServiceTests()
public Task<IEnumerable<BreweryPost>> GetAllAsync(int? limit, int? offset) => Task.FromResult<IEnumerable<BreweryPost>>(Array.Empty<BreweryPost>()); {
public Task UpdateAsync(BreweryPost brewery) { Created = brewery; return Task.CompletedTask; } _repoMock = new Mock<IBreweryRepository>();
public Task DeleteAsync(Guid id) => Task.CompletedTask; _service = new BreweryService(_repoMock.Object);
public Task CreateAsync(BreweryPost brewery) { Created = brewery; return Task.CompletedTask; } }
private static BreweryLocationCreateRequest ValidLocationCreateRequest() =>
new(
CityId: Guid.NewGuid(),
AddressLine1: "123 Main St",
AddressLine2: null,
PostalCode: "12345",
Coordinates: null
);
[Fact]
public async Task GetByIdAsync_ReturnsBrewery_WhenFound()
{
var brewery = new BreweryPost { BreweryPostId = Guid.NewGuid() };
_repoMock.Setup(r => r.GetByIdAsync(brewery.BreweryPostId))
.ReturnsAsync(brewery);
var result = await _service.GetByIdAsync(brewery.BreweryPostId);
result.Should().Be(brewery);
} }
[Fact] [Fact]
public async Task CreateAsync_ReturnsFailure_WhenLocationMissing() public async Task GetByIdAsync_ReturnsNull_WhenNotFound()
{ {
var repo = new FakeRepo(); var id = Guid.NewGuid();
var svc = new BreweryService(repo); _repoMock.Setup(r => r.GetByIdAsync(id))
.ReturnsAsync((BreweryPost?)null);
var dto = new BreweryCreateDto var result = await _service.GetByIdAsync(id);
{
PostedById = Guid.NewGuid(),
BreweryName = "X",
Description = "Y",
Location = null!
};
var result = await svc.CreateAsync(dto); result.Should().BeNull();
result.Success.Should().BeFalse();
result.Message.Should().Contain("Location");
} }
[Fact] [Fact]
public async Task CreateAsync_ReturnsSuccess_AndPersistsEntity() public async Task GetAllAsync_PassesLimitAndOffset_ToRepository()
{ {
var repo = new FakeRepo(); _repoMock.Setup(r => r.GetAllAsync(10, 5))
var svc = new BreweryService(repo); .ReturnsAsync(Array.Empty<BreweryPost>());
var loc = new BreweryLocationCreateDto var result = await _service.GetAllAsync(10, 5);
result.Should().BeEmpty();
_repoMock.Verify(r => r.GetAllAsync(10, 5), Times.Once);
}
[Fact]
public async Task GetAllAsync_ReturnsAllBreweries_FromRepository()
{ {
CityId = Guid.NewGuid(), var breweries = new[]
AddressLine1 = "123 Main", {
PostalCode = "12345" new BreweryPost { BreweryPostId = Guid.NewGuid() },
new BreweryPost { BreweryPostId = Guid.NewGuid() },
}; };
_repoMock.Setup(r => r.GetAllAsync(null, null))
.ReturnsAsync(breweries);
var dto = new BreweryCreateDto var result = await _service.GetAllAsync();
result.Should().BeEquivalentTo(breweries);
}
[Fact]
public async Task CreateAsync_PersistsEntity_WithNewIdsAndCreatedAt()
{ {
PostedById = Guid.NewGuid(), var request = new BreweryCreateRequest(
BreweryName = "MyBrew", PostedById: Guid.NewGuid(),
Description = "Desc", BreweryName: "MyBrew",
Location = loc Description: "Desc",
}; Location: ValidLocationCreateRequest()
);
var result = await svc.CreateAsync(dto); BreweryPost? persisted = null;
_repoMock
.Setup(r => r.CreateAsync(It.IsAny<BreweryPost>()))
.Callback<BreweryPost>(b => persisted = b)
.Returns(Task.CompletedTask);
var before = DateTime.UtcNow;
var result = await _service.CreateAsync(request);
var after = DateTime.UtcNow;
result.Success.Should().BeTrue();
persisted.Should().NotBeNull();
persisted!.BreweryPostId.Should().NotBe(Guid.Empty);
persisted.PostedById.Should().Be(request.PostedById);
persisted.BreweryName.Should().Be("MyBrew");
persisted.Description.Should().Be("Desc");
persisted.CreatedAt.Should().BeOnOrAfter(before).And.BeOnOrBefore(after);
persisted.UpdatedAt.Should().BeNull();
persisted.Location.Should().NotBeNull();
persisted.Location!.BreweryPostLocationId.Should().NotBe(Guid.Empty);
persisted.Location.BreweryPostLocationId.Should().NotBe(persisted.BreweryPostId);
persisted.Location.CityId.Should().Be(request.Location.CityId);
persisted.Location.AddressLine1.Should().Be(request.Location.AddressLine1);
persisted.Location.PostalCode.Should().Be(request.Location.PostalCode);
_repoMock.Verify(r => r.CreateAsync(It.IsAny<BreweryPost>()), Times.Once);
}
[Fact]
public async Task CreateAsync_ReturnsServiceReturn_WrappingTheCreatedEntity()
{
var request = new BreweryCreateRequest(
PostedById: Guid.NewGuid(),
BreweryName: "MyBrew",
Description: "Desc",
Location: ValidLocationCreateRequest()
);
_repoMock
.Setup(r => r.CreateAsync(It.IsAny<BreweryPost>()))
.Returns(Task.CompletedTask);
var result = await _service.CreateAsync(request);
result.Success.Should().BeTrue(); result.Success.Should().BeTrue();
repo.Created.Should().NotBeNull();
repo.Created!.BreweryName.Should().Be("MyBrew");
result.Brewery.BreweryName.Should().Be("MyBrew"); result.Brewery.BreweryName.Should().Be("MyBrew");
result.Brewery.Location.Should().NotBeNull();
}
[Fact]
public async Task UpdateAsync_UpdatesNameDescription_AndSetsUpdatedAt()
{
var request = new BreweryUpdateRequest(
BreweryPostId: Guid.NewGuid(),
PostedById: Guid.NewGuid(),
BreweryName: "Renamed",
Description: "New description",
Location: null
);
BreweryPost? persisted = null;
_repoMock
.Setup(r => r.UpdateAsync(It.IsAny<BreweryPost>()))
.Callback<BreweryPost>(b => persisted = b)
.Returns(Task.CompletedTask);
var before = DateTime.UtcNow;
var result = await _service.UpdateAsync(request);
var after = DateTime.UtcNow;
result.Success.Should().BeTrue();
persisted.Should().NotBeNull();
persisted!.BreweryPostId.Should().Be(request.BreweryPostId);
persisted.PostedById.Should().Be(request.PostedById);
persisted.BreweryName.Should().Be("Renamed");
persisted.Description.Should().Be("New description");
persisted.UpdatedAt.Should().NotBeNull();
persisted.UpdatedAt!.Value.Should().BeOnOrAfter(before).And.BeOnOrBefore(after);
}
[Fact]
public async Task UpdateAsync_ClearsLocation_WhenRequestLocationIsNull()
{
var request = new BreweryUpdateRequest(
BreweryPostId: Guid.NewGuid(),
PostedById: Guid.NewGuid(),
BreweryName: "Name",
Description: "Description",
Location: null
);
BreweryPost? persisted = null;
_repoMock
.Setup(r => r.UpdateAsync(It.IsAny<BreweryPost>()))
.Callback<BreweryPost>(b => persisted = b)
.Returns(Task.CompletedTask);
await _service.UpdateAsync(request);
persisted!.Location.Should().BeNull();
}
[Fact]
public async Task UpdateAsync_SetsLocation_WhenRequestLocationProvided()
{
var locationRequest = new BreweryLocationUpdateRequest(
BreweryPostLocationId: Guid.NewGuid(),
CityId: Guid.NewGuid(),
AddressLine1: "456 Oak Ave",
AddressLine2: "Suite 2",
PostalCode: "54321",
Coordinates: null
);
var request = new BreweryUpdateRequest(
BreweryPostId: Guid.NewGuid(),
PostedById: Guid.NewGuid(),
BreweryName: "Name",
Description: "Description",
Location: locationRequest
);
BreweryPost? persisted = null;
_repoMock
.Setup(r => r.UpdateAsync(It.IsAny<BreweryPost>()))
.Callback<BreweryPost>(b => persisted = b)
.Returns(Task.CompletedTask);
await _service.UpdateAsync(request);
persisted!.Location.Should().NotBeNull();
persisted.Location!.BreweryPostLocationId.Should().Be(locationRequest.BreweryPostLocationId);
persisted.Location.BreweryPostId.Should().Be(request.BreweryPostId);
persisted.Location.CityId.Should().Be(locationRequest.CityId);
persisted.Location.AddressLine1.Should().Be(locationRequest.AddressLine1);
persisted.Location.AddressLine2.Should().Be(locationRequest.AddressLine2);
persisted.Location.PostalCode.Should().Be(locationRequest.PostalCode);
}
[Fact]
public async Task DeleteAsync_DelegatesToRepository()
{
var id = Guid.NewGuid();
_repoMock.Setup(r => r.DeleteAsync(id)).Returns(Task.CompletedTask);
await _service.DeleteAsync(id);
_repoMock.Verify(r => r.DeleteAsync(id), Times.Once);
} }
} }

View File

@@ -11,6 +11,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="xunit" Version="2.9.2" /> <PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="FluentAssertions" Version="6.9.0" /> <PackageReference Include="FluentAssertions" Version="6.9.0" />
</ItemGroup> </ItemGroup>