diff --git "a/Homeworks/04 \320\224\320\276\320\261\320\260\320\262\320\273\320\265\320\275\320\270\320\265 \321\216\320\275\320\270\321\202 \321\202\320\265\321\201\321\202\320\276\320\262/src/PromoCodeFactory.UnitTests/PromoCodeFactory.UnitTests.csproj" "b/Homeworks/04 \320\224\320\276\320\261\320\260\320\262\320\273\320\265\320\275\320\270\320\265 \321\216\320\275\320\270\321\202 \321\202\320\265\321\201\321\202\320\276\320\262/src/PromoCodeFactory.UnitTests/PromoCodeFactory.UnitTests.csproj"
index 3e205e938..59a2e3622 100644
--- "a/Homeworks/04 \320\224\320\276\320\261\320\260\320\262\320\273\320\265\320\275\320\270\320\265 \321\216\320\275\320\270\321\202 \321\202\320\265\321\201\321\202\320\276\320\262/src/PromoCodeFactory.UnitTests/PromoCodeFactory.UnitTests.csproj"
+++ "b/Homeworks/04 \320\224\320\276\320\261\320\260\320\262\320\273\320\265\320\275\320\270\320\265 \321\216\320\275\320\270\321\202 \321\202\320\265\321\201\321\202\320\276\320\262/src/PromoCodeFactory.UnitTests/PromoCodeFactory.UnitTests.csproj"
@@ -14,10 +14,14 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
-
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
diff --git "a/Homeworks/04 \320\224\320\276\320\261\320\260\320\262\320\273\320\265\320\275\320\270\320\265 \321\216\320\275\320\270\321\202 \321\202\320\265\321\201\321\202\320\276\320\262/src/PromoCodeFactory.UnitTests/WebHost/Controllers/Partners/SetLimitTests.cs" "b/Homeworks/04 \320\224\320\276\320\261\320\260\320\262\320\273\320\265\320\275\320\270\320\265 \321\216\320\275\320\270\321\202 \321\202\320\265\321\201\321\202\320\276\320\262/src/PromoCodeFactory.UnitTests/WebHost/Controllers/Partners/SetLimitTests.cs"
index c84e0d8ac..f6b25bf61 100644
--- "a/Homeworks/04 \320\224\320\276\320\261\320\260\320\262\320\273\320\265\320\275\320\270\320\265 \321\216\320\275\320\270\321\202 \321\202\320\265\321\201\321\202\320\276\320\262/src/PromoCodeFactory.UnitTests/WebHost/Controllers/Partners/SetLimitTests.cs"
+++ "b/Homeworks/04 \320\224\320\276\320\261\320\260\320\262\320\273\320\265\320\275\320\270\320\265 \321\216\320\275\320\270\321\202 \321\202\320\265\321\201\321\202\320\276\320\262/src/PromoCodeFactory.UnitTests/WebHost/Controllers/Partners/SetLimitTests.cs"
@@ -1,29 +1,201 @@
+using AwesomeAssertions;
+using Microsoft.AspNetCore.Mvc;
+using Moq;
+using PromoCodeFactory.Core.Abstractions.Repositories;
+using PromoCodeFactory.Core.Domain.Administration;
+using PromoCodeFactory.Core.Domain.PromoCodeManagement;
+using PromoCodeFactory.Core.Exceptions;
+using PromoCodeFactory.WebHost.Controllers;
+using PromoCodeFactory.WebHost.Models.Partners;
+using Soenneker.Utils.AutoBogus;
+
namespace PromoCodeFactory.UnitTests.WebHost.Controllers.Partners;
public class SetLimitTests
{
+ private readonly Mock> _partnersRepositoryMock;
+ private readonly Mock> _partnerLimitsRepositoryMock;
+ private readonly PartnersController _partnersController;
+
+ public SetLimitTests()
+ {
+ _partnersRepositoryMock = new Mock>();
+ _partnerLimitsRepositoryMock = new Mock>();
+
+ _partnersController = new PartnersController(_partnersRepositoryMock.Object, _partnerLimitsRepositoryMock.Object);
+ }
+
[Fact]
public async Task CreateLimit_WhenPartnerNotFound_ReturnsNotFound()
{
+ //Arrange
+ var partnerId = Guid.NewGuid();
+ var request = new PartnerPromoCodeLimitCreateRequest(DateTimeOffset.UtcNow.AddDays(1), 100);
+ _partnersRepositoryMock.Setup(x => x.GetById(partnerId, true, It.IsAny()))
+ .ReturnsAsync((Partner?)null);
+
+ //Act
+ var result = await _partnersController.CreateLimit(partnerId, request, CancellationToken.None);
+
+ //Assert
+ var notFoundResult = result.Result.Should().BeOfType().Which;
+ notFoundResult.Value.Should().BeOfType();
+ var problemDetails = notFoundResult.Value as ProblemDetails;
+
+ problemDetails!.Title.Should().Be("Partner not found");
+ problemDetails.Detail.Should().Be($"Partner with Id {partnerId} not found.");
}
[Fact]
public async Task CreateLimit_WhenPartnerBlocked_ReturnsUnprocessableEntity()
{
+ //Arrange
+ var partnerId = Guid.NewGuid();
+ var partner = CreatePartner(partnerId, false);
+ var request = new PartnerPromoCodeLimitCreateRequest(DateTimeOffset.UtcNow.AddDays(1), 100);
+ _partnersRepositoryMock.Setup(x => x.GetById(partnerId, true, It.IsAny()))
+ .ReturnsAsync(partner);
+
+ //Act
+ var result = await _partnersController.CreateLimit(partnerId, request, CancellationToken.None);
+
+ //Assert
+ var unprocessableResult = result.Result.Should().BeOfType().Which;
+ unprocessableResult.Value.Should().BeOfType();
+ var problemDetails = unprocessableResult.Value as ProblemDetails;
+
+ problemDetails!.Title.Should().Be("Partner blocked");
+ problemDetails.Detail.Should().Be($"Cannot create limit for a blocked partner.");
}
[Fact]
public async Task CreateLimit_WhenValidRequest_ReturnsCreatedAndAddsLimit()
{
+ //Arrange
+ const int PROMO_CODE_LIMIT = 100;
+
+ var partnerId = Guid.NewGuid();
+ var partner = CreatePartner(partnerId, true);
+ var request = new PartnerPromoCodeLimitCreateRequest(DateTimeOffset.UtcNow.AddDays(1), PROMO_CODE_LIMIT);
+ _partnersRepositoryMock.Setup(x => x.GetById(partnerId, true, It.IsAny()))
+ .ReturnsAsync(partner);
+
+ //Act
+ var result = await _partnersController.CreateLimit(partnerId, request, CancellationToken.None);
+
+ //Assert
+ var createdResult = result.Result.Should().BeOfType().Which;
+ createdResult.Value.Should().BeOfType();
+ var response = createdResult.Value as PartnerPromoCodeLimitResponse;
+
+ response!.Limit.Should().Be(100);
+ response.CreatedAt.Should().BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromSeconds(5));
+ _partnerLimitsRepositoryMock.Verify(
+ x => x.Add(It.Is(l => l.Limit == PROMO_CODE_LIMIT && l.Partner.Id == partnerId), It.IsAny()),
+ Times.Once);
}
[Fact]
public async Task CreateLimit_WhenValidRequestWithActiveLimits_CancelsOldLimitsAndAddsNew()
{
+ //Arrange
+ const int PROMO_CODE_LIMIT = 100;
+
+ var partnerId = Guid.NewGuid();
+ var limitId = Guid.NewGuid();
+ var partner = CreatePartnerWithLimit(partnerId, limitId, isActive: true);
+
+ var request = new PartnerPromoCodeLimitCreateRequest(DateTimeOffset.UtcNow.AddDays(1), PROMO_CODE_LIMIT);
+ _partnersRepositoryMock.Setup(x => x.GetById(partnerId, true, It.IsAny()))
+ .ReturnsAsync(partner);
+
+ var oldLimit = partner.PartnerLimits.First();
+
+ //Act
+ var result = await _partnersController.CreateLimit(partnerId, request, CancellationToken.None);
+
+ //Assert
+ var createdResult = result.Result.Should().BeOfType().Which;
+ createdResult.Value.Should().BeOfType();
+ var response = createdResult.Value as PartnerPromoCodeLimitResponse;
+
+ oldLimit.CanceledAt.Should().NotBeNull();
+ var newLimit = result.Value;
+
+ _partnersRepositoryMock.Verify(
+ x => x.Update(It.Is(p => p.PartnerLimits.Any(l => l.Id == oldLimit.Id)), It.IsAny()),
+ Times.Once);
+ _partnerLimitsRepositoryMock.Verify(
+ x => x.Add(It.Is(l => l.Id == response.Id), It.IsAny()),
+ Times.Once);
}
[Fact]
public async Task CreateLimit_WhenUpdateThrowsEntityNotFoundException_ReturnsNotFound()
{
+ //Arrange
+ var partnerId = Guid.NewGuid();
+ var partner = CreatePartnerWithLimit(partnerId, Guid.NewGuid(), isActive: true);
+ var request = new PartnerPromoCodeLimitCreateRequest(DateTimeOffset.UtcNow.AddDays(1), 100);
+ _partnersRepositoryMock.Setup(x => x.GetById(partnerId, true, It.IsAny()))
+ .ReturnsAsync(partner);
+ _partnersRepositoryMock.Setup(x => x.Update(It.IsAny(), It.IsAny()))
+ .ThrowsAsync(new EntityNotFoundException(typeof(Partner), partnerId));
+
+ //Act
+ var result = await _partnersController.CreateLimit(partnerId, request, CancellationToken.None);
+
+ //Assert
+ result.Result.Should().BeOfType();
+ }
+
+ private static Partner CreatePartner(
+ Guid partnerId,
+ bool isActive)
+ {
+ var partner = new AutoFaker()
+ .RuleFor(p => p.Id, _ => partnerId)
+ .RuleFor(p => p.IsActive, _ => isActive)
+ .RuleFor(p => p.PartnerLimits, _ => [])
+ .Generate();
+ partner.Manager = new AutoFaker().Generate();
+ return partner;
+ }
+
+ private static Partner CreatePartnerWithLimit(
+ Guid partnerId,
+ Guid limitId,
+ bool isActive,
+ DateTimeOffset? canceledAt = null)
+ {
+ var role = new AutoFaker()
+ .RuleFor(r => r.Id, _ => Guid.NewGuid())
+ .Generate();
+
+ var employee = new AutoFaker()
+ .RuleFor(e => e.Id, _ => Guid.NewGuid())
+ .RuleFor(e => e.Role, role)
+ .Generate();
+
+ var limits = new List();
+
+ var partner = new AutoFaker()
+ .RuleFor(p => p.Id, _ => partnerId)
+ .RuleFor(p => p.IsActive, _ => isActive)
+ .RuleFor(p => p.Manager, employee)
+ .RuleFor(p => p.PartnerLimits, limits)
+ .Generate();
+
+ var limit = new AutoFaker()
+ .RuleFor(l => l.Id, _ => limitId)
+ .RuleFor(l => l.Partner, partner)
+ .RuleFor(l => l.CanceledAt, _ => canceledAt)
+ .RuleFor(l => l.CreatedAt, _ => DateTimeOffset.UtcNow.AddDays(-1))
+ .RuleFor(l => l.EndAt, _ => DateTimeOffset.UtcNow.AddDays(30))
+ .RuleFor(l => l.Limit, 2)
+ .Generate();
+
+ limits.Add(limit);
+ return partner;
}
}
diff --git "a/Homeworks/04 \320\224\320\276\320\261\320\260\320\262\320\273\320\265\320\275\320\270\320\265 \321\216\320\275\320\270\321\202 \321\202\320\265\321\201\321\202\320\276\320\262/src/PromoCodeFactory.UnitTests/WebHost/Controllers/PromoCodes/CreateTests.cs" "b/Homeworks/04 \320\224\320\276\320\261\320\260\320\262\320\273\320\265\320\275\320\270\320\265 \321\216\320\275\320\270\321\202 \321\202\320\265\321\201\321\202\320\276\320\262/src/PromoCodeFactory.UnitTests/WebHost/Controllers/PromoCodes/CreateTests.cs"
index 8f66ae5c3..45f01459d 100644
--- "a/Homeworks/04 \320\224\320\276\320\261\320\260\320\262\320\273\320\265\320\275\320\270\320\265 \321\216\320\275\320\270\321\202 \321\202\320\265\321\201\321\202\320\276\320\262/src/PromoCodeFactory.UnitTests/WebHost/Controllers/PromoCodes/CreateTests.cs"
+++ "b/Homeworks/04 \320\224\320\276\320\261\320\260\320\262\320\273\320\265\320\275\320\270\320\265 \321\216\320\275\320\270\321\202 \321\202\320\265\321\201\321\202\320\276\320\262/src/PromoCodeFactory.UnitTests/WebHost/Controllers/PromoCodes/CreateTests.cs"
@@ -1,29 +1,201 @@
+using System.Linq.Expressions;
+using AwesomeAssertions;
+using Microsoft.AspNetCore.Mvc;
+using Moq;
+using PromoCodeFactory.Core.Abstractions.Repositories;
+using PromoCodeFactory.Core.Domain.Administration;
+using PromoCodeFactory.Core.Domain.PromoCodeManagement;
+using PromoCodeFactory.WebHost.Controllers;
+using PromoCodeFactory.WebHost.Models.PromoCodes;
+using Soenneker.Utils.AutoBogus;
+
namespace PromoCodeFactory.UnitTests.WebHost.Controllers.PromoCodes;
public class CreateTests
{
+ private readonly Mock> _partnersRepositoryMock;
+ private readonly Mock> _promoCodeRepositoryMock;
+ private readonly Mock> _customerRepositoryMock;
+ private readonly Mock> _preferenceRepositoryMock;
+ private readonly Mock> _customerPromoCodeRepositoryMock;
+ private readonly PromoCodesController _promoCodesController;
+
+ public CreateTests()
+ {
+ _partnersRepositoryMock = new Mock>();
+ _promoCodeRepositoryMock = new Mock>();
+ _customerRepositoryMock = new Mock>();
+ _preferenceRepositoryMock= new Mock>();
+ _customerPromoCodeRepositoryMock = new Mock>();
+
+ _promoCodesController = new PromoCodesController(_promoCodeRepositoryMock.Object, _customerRepositoryMock.Object, _customerPromoCodeRepositoryMock.Object, _partnersRepositoryMock.Object, _preferenceRepositoryMock.Object);
+ }
+
+
[Fact]
public async Task Create_WhenPartnerNotFound_ReturnsNotFound()
{
+ //Arrange
+ var partnerId = Guid.NewGuid();
+ var request = new PromoCodeCreateRequest("Code", "ServiceInfo", partnerId, DateTime.Now, DateTime.Now, new Guid());
+ _partnersRepositoryMock.Setup(x => x.GetById(partnerId, true, It.IsAny()))
+ .ReturnsAsync((Partner?)null);
+
+ //Act
+ var result = await _promoCodesController.Create(request, CancellationToken.None);
+
+ //Assert
+ var notFoundResult = result.Result.Should().BeOfType().Which;
+ notFoundResult.Value.Should().BeOfType();
+ var problemDetails = notFoundResult.Value as ProblemDetails;
+
+ problemDetails!.Title.Should().Be("Partner not found");
+ problemDetails.Detail.Should().Be($"Partner with Id {partnerId} not found.");
}
[Fact]
public async Task Create_WhenPreferenceNotFound_ReturnsNotFound()
{
+ //Arrange
+ var partnerId = Guid.NewGuid();
+ var preferenceId = Guid.NewGuid();
+ var partner = CreatePartner(partnerId, true);
+ var request = new PromoCodeCreateRequest("CODE", "ServiceInfo", partnerId, DateTime.UtcNow.AddDays(1), DateTime.UtcNow.AddDays(30), preferenceId);
+ _partnersRepositoryMock.Setup(x => x.GetById(partnerId, true, It.IsAny()))
+ .ReturnsAsync(partner);
+ _preferenceRepositoryMock.Setup(x => x.GetById(preferenceId, false, It.IsAny()))
+ .ReturnsAsync((Preference?)null);
+
+ //Act
+ var result = await _promoCodesController.Create(request, CancellationToken.None);
+
+ //Assert
+ var notFoundResult = result.Result.Should().BeOfType().Which;
+ notFoundResult.Value.Should().BeOfType();
+ var problemDetails = notFoundResult.Value as ProblemDetails;
+ problemDetails!.Title.Should().Be("Preference not found");
+ problemDetails.Detail.Should().Be($"Preference with Id {preferenceId} not found.");
}
[Fact]
public async Task Create_WhenNoActiveLimit_ReturnsUnprocessableEntity()
{
+ //Arrange
+ var partnerId = Guid.NewGuid();
+ var preferenceId = Guid.NewGuid();
+ var partner = CreatePartner(partnerId, true);
+ var preference = CreatePreference(preferenceId);
+ var request = new PromoCodeCreateRequest("CODE", "ServiceInfo", partnerId, DateTime.UtcNow.AddDays(1), DateTime.UtcNow.AddDays(30), preferenceId);
+ _partnersRepositoryMock.Setup(x => x.GetById(partnerId, true, It.IsAny()))
+ .ReturnsAsync(partner);
+ _preferenceRepositoryMock.Setup(x => x.GetById(preferenceId, false, It.IsAny()))
+ .ReturnsAsync(preference);
+
+ //Act
+ var result = await _promoCodesController.Create(request, CancellationToken.None);
+
+ //Assert
+ var unprocessableResult = result.Result.Should().BeOfType().Which;
+ unprocessableResult.StatusCode.Should().Be(422);
+ unprocessableResult.Value.Should().BeOfType();
+ var problemDetails = unprocessableResult.Value as ProblemDetails;
+ problemDetails!.Title.Should().Be("No active limit");
+ problemDetails.Detail.Should().Be("Partner has no active promo code limit.");
}
[Fact]
public async Task Create_WhenLimitExceeded_ReturnsUnprocessableEntity()
{
+ //Arrange
+ var partnerId = Guid.NewGuid();
+ var preferenceId = Guid.NewGuid();
+ var partner = CreatePartner(partnerId, true, limit: 1, issuedCount: 1);
+ var preference = CreatePreference(preferenceId);
+ var request = new PromoCodeCreateRequest("CODE", "ServiceInfo", partnerId, DateTime.UtcNow.AddDays(1), DateTime.UtcNow.AddDays(30), preferenceId);
+ _partnersRepositoryMock.Setup(x => x.GetById(partnerId, true, It.IsAny()))
+ .ReturnsAsync(partner);
+ _preferenceRepositoryMock.Setup(x => x.GetById(preferenceId, false, It.IsAny()))
+ .ReturnsAsync(preference);
+
+ //Act
+ var result = await _promoCodesController.Create(request, CancellationToken.None);
+
+ //Assert
+ var unprocessableResult = result.Result.Should().BeOfType().Which;
+ unprocessableResult.StatusCode.Should().Be(422);
+ unprocessableResult.Value.Should().BeOfType();
+ var problemDetails = unprocessableResult.Value as ProblemDetails;
+ problemDetails!.Title.Should().Be("Limit exceeded");
+ problemDetails.Detail.Should().Be($"Cannot create promo code. Limit would be exceeded (current: 1/1).");
}
[Fact]
public async Task Create_WhenValidRequest_ReturnsCreatedAndIncrementsIssuedCount()
{
+ //Arrange
+ var partnerId = Guid.NewGuid();
+ var preferenceId = Guid.NewGuid();
+ var partner = CreatePartner(partnerId, true, limit: 100, issuedCount: 0);
+ var preference = CreatePreference(preferenceId);
+ var request = new PromoCodeCreateRequest("CODE", "ServiceInfo", partnerId, DateTime.UtcNow.AddDays(1), DateTime.UtcNow.AddDays(30), preferenceId);
+ _partnersRepositoryMock.Setup(x => x.GetById(partnerId, true, It.IsAny()))
+ .ReturnsAsync(partner);
+ _preferenceRepositoryMock.Setup(x => x.GetById(preferenceId, false, It.IsAny()))
+ .ReturnsAsync(preference);
+ _customerRepositoryMock.Setup(x => x.GetWhere(It.IsAny>>(), It.IsAny(), default))
+ .ReturnsAsync(new List());
+
+ //Act
+ var result = await _promoCodesController.Create(request, CancellationToken.None);
+
+ //Assert
+ var createdResult = result.Result.Should().BeOfType().Which;
+ createdResult.Value.Should().BeOfType();
+ var response = createdResult.Value as PromoCodeShortResponse;
+ response!.Code.Should().Be("CODE");
+
+ _promoCodeRepositoryMock.Verify(
+ x => x.Add(It.IsAny(), It.IsAny()),
+ Times.Once);
+ _partnersRepositoryMock.Verify(
+ x => x.Update(It.Is(p => p.PartnerLimits.Any(l => l.IssuedCount == 1)), It.IsAny()),
+ Times.Once);
+ }
+
+ private static Partner CreatePartner(
+ Guid partnerId,
+ bool isActive,
+ int limit = 0,
+ int issuedCount = 0)
+ {
+ var partner = new AutoFaker()
+ .RuleFor(p => p.Id, _ => partnerId)
+ .RuleFor(p => p.IsActive, _ => isActive)
+ .RuleFor(p => p.PartnerLimits, _ => [])
+ .Generate();
+ partner.Manager = new AutoFaker().Generate();
+
+ if (limit > 0)
+ {
+ var partnerLimit = new PartnerPromoCodeLimit
+ {
+ Id = Guid.NewGuid(),
+ Partner = partner,
+ Limit = limit,
+ IssuedCount = issuedCount,
+ CreatedAt = DateTimeOffset.UtcNow.AddDays(-1),
+ EndAt = DateTimeOffset.UtcNow.AddDays(30)
+ };
+ partner.PartnerLimits.Add(partnerLimit);
+ }
+
+ return partner;
+ }
+
+ private static Preference CreatePreference(Guid preferenceId)
+ {
+ return new AutoFaker()
+ .RuleFor(p => p.Id, _ => preferenceId)
+ .Generate();
}
}