Migrate Breweries to a vertical slice (Features.Breweries)

Moves brewery CRUD from the Service.Breweries/Infrastructure.Repository
layered split into a single Features.Breweries project: MediatR
commands/queries replace BreweryService, and the repository moves in
alongside its own controller. Extracts Infrastructure.Sql out of
Infrastructure.Repository (just the generic ADO.NET connection/base-repo
plumbing) since Breweries no longer needs the rest of that project, while
Auth and UserAccount repos still do until their own migration.

Also fixes the API.Core and Infrastructure.Repository.Tests Dockerfiles,
which were restoring against COPY destinations that didn't match the
projects' actual relative paths (silently working only because of the
COPY . . that followed); both now restore against Core.slnx with correctly
nested paths, verified with a real docker build.
This commit is contained in:
Aaron Po
2026-06-19 23:54:25 -04:00
parent 62d682472e
commit a8c1b17095
46 changed files with 803 additions and 789 deletions

View File

@@ -0,0 +1,106 @@
using Domain.Entities;
using FluentAssertions;
using Features.Breweries.Commands.UpdateBrewery;
using Features.Breweries.Repository;
using Moq;
namespace Features.Breweries.Tests.Commands;
public class UpdateBreweryHandlerTests
{
private readonly Mock<IBreweryRepository> _repoMock = new();
private readonly UpdateBreweryHandler _handler;
public UpdateBreweryHandlerTests()
{
_handler = new UpdateBreweryHandler(_repoMock.Object);
}
[Fact]
public async Task Handle_UpdatesNameDescription_AndSetsUpdatedAt()
{
var command = new UpdateBreweryCommand(
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;
await _handler.Handle(command, CancellationToken.None);
var after = DateTime.UtcNow;
persisted.Should().NotBeNull();
persisted!.BreweryPostId.Should().Be(command.BreweryPostId);
persisted.PostedById.Should().Be(command.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 Handle_ClearsLocation_WhenCommandLocationIsNull()
{
var command = new UpdateBreweryCommand(
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 _handler.Handle(command, CancellationToken.None);
persisted!.Location.Should().BeNull();
}
[Fact]
public async Task Handle_SetsLocation_WhenCommandLocationProvided()
{
var locationCommand = new UpdateBreweryLocation(
BreweryPostLocationId: Guid.NewGuid(),
CityId: Guid.NewGuid(),
AddressLine1: "456 Oak Ave",
AddressLine2: "Suite 2",
PostalCode: "54321",
Coordinates: null
);
var command = new UpdateBreweryCommand(
BreweryPostId: Guid.NewGuid(),
PostedById: Guid.NewGuid(),
BreweryName: "Name",
Description: "Description",
Location: locationCommand
);
BreweryPost? persisted = null;
_repoMock
.Setup(r => r.UpdateAsync(It.IsAny<BreweryPost>()))
.Callback<BreweryPost>(b => persisted = b)
.Returns(Task.CompletedTask);
await _handler.Handle(command, CancellationToken.None);
persisted!.Location.Should().NotBeNull();
persisted.Location!.BreweryPostLocationId.Should().Be(locationCommand.BreweryPostLocationId);
persisted.Location.BreweryPostId.Should().Be(command.BreweryPostId);
persisted.Location.CityId.Should().Be(locationCommand.CityId);
persisted.Location.AddressLine1.Should().Be(locationCommand.AddressLine1);
persisted.Location.AddressLine2.Should().Be(locationCommand.AddressLine2);
persisted.Location.PostalCode.Should().Be(locationCommand.PostalCode);
}
}