Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions TDDLesson.Orders.Tests/NotificationServiceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System.Runtime.InteropServices.JavaScript;
using FluentAssertions;
using TDDLesson;

namespace TestProject1;

[TestClass]
public class NotificationServiceTests
{
[TestMethod]
public void ShouldReturnNull_WhenEmployeesNotSaved()
{
var notificationRequest = new NotificationRequest(
false,
99,
new DateTime(2024, 07, 01),
"[email protected]");

var emailDto = NotificationService.CreateNotification(notificationRequest);

emailDto.Should().BeNull();
}

[TestMethod]
public void ShouldReturnNull_WhenDataNotInInterval()
{
var notificationRequest = new NotificationRequest(
true,
101,
new DateTime(1990, 07, 01),
"[email protected]");

var emailDto = NotificationService.CreateNotification(notificationRequest);

emailDto.Should().BeNull();
}

[TestMethod]
public void ShouldReturnEmailDto_WhenCorrectData()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не совсем бизнесовый нейминг: бизнес не знает, что такое дто. Бизнес не знает, что значит Return.
Представьте, что вы название теста транслируете вашему ПМ-у или какому-нибудь менеджеру из m1/m2. Поймут ли они перевод?

А вот если будет, например,
ShouldCreateNotification_When.. - как будто уже и понятнее
или
ShouldNotCreateNotification
ShouldFail
ShouldCreateNothing

в общем, приветствуются термины бизнеса, а не термины из языка программирования (Return/False/True/Null/etc)

{
var notificationRequest = new NotificationRequest(
true,
101,
new DateTime(2024, 07, 01),
"[email protected]");

var emailDto = NotificationService.CreateNotification(notificationRequest);

emailDto.Should().NotBeNull();
Comment on lines +47 to +49
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ну тут ассертик недостаточный, но думаю, вы просто углы срезали :)

}

[TestMethod]
public void ShouldReturnEmailDtoWithoutInvite_WhenCorrectDataAndLess500Employee()
{
var notificationRequest = new NotificationRequest(
true,
150,
new DateTime(2024, 07, 01),
"[email protected]");

var emailDto = NotificationService.CreateNotification(notificationRequest);

emailDto.Should().NotBeNull();
emailDto.MailTo.Should().Be("[email protected]");
emailDto.Subject.Should().Be(NotificationService.Subject);
emailDto.Body.Should().Be(NotificationService.ProcessedMessage);

}

[TestMethod]
public void ShouldReturnEmailDtoWithInvite_WhenCorrectDataAndMore500Employee()
{
var notificationRequest = new NotificationRequest(
true,
550,
new DateTime(2024, 07, 01),
"[email protected]");

var emailDto = NotificationService.CreateNotification(notificationRequest);

emailDto.Should().NotBeNull();
emailDto.MailTo.Should().Be("[email protected]");
emailDto.Subject.Should().Be(NotificationService.Subject);
emailDto.Body.Should().Be(NotificationService.ProcessedMessage + " " + NotificationService.InviteMessage);

}
}
42 changes: 42 additions & 0 deletions TDDLesson.Orders.Tests/ProposalValidatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using FluentAssertions;
using TDDLesson;

namespace TestProject1;

[TestClass]
public class ProposalValidatorTests
{
[TestMethod]
public void ShouldBeValid_WhenEmployeesAndRevenueInCorrectInterval()
{
var request = new ProposalValidationRequest(101, 31);

var result = ProposalValidator.IsProposalValid(request);

result.Should().BeTrue();
}

[TestMethod]
[DataRow(100)]
[DataRow(50)]
public void ShouldBeInvalid_WhenEmployeesNumberLessThan100(int employeeAmount)
{
var request = new ProposalValidationRequest(employeeAmount, 31);

var result = ProposalValidator.IsProposalValid(request);

result.Should().BeFalse();
}

[TestMethod]
[DataRow(30)]
[DataRow(15)]
public void ShouldBeInvalid_WhenRevenueAmountLessThan30(int itRevenuePercent)
{
var request = new ProposalValidationRequest(101, itRevenuePercent);

var result = ProposalValidator.IsProposalValid(request);

result.Should().BeFalse();
}
}
4 changes: 4 additions & 0 deletions TDDLesson.Orders.Tests/TDDLesson.Orders.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\TDDLesson.Orders\TDDLesson.Orders.csproj" />
</ItemGroup>

</Project>
46 changes: 45 additions & 1 deletion TDDLesson.Orders/AccreditationProposalProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,59 @@ public class AccreditationProposalProcessor
{
private readonly IRevenueService _revenueService;
private readonly IRepository _orderRepository;
private readonly IEmailClient _emailClient;

public AccreditationProposalProcessor(IRevenueService revenueService, IRepository orderRepository)
public AccreditationProposalProcessor(IRevenueService revenueService, IRepository orderRepository, IEmailClient emailClient)
{
_revenueService = revenueService;
_orderRepository = orderRepository;
_emailClient = emailClient;
}

public async Task HandleProposal(ProposalDto dto)
{
var itRevenuePercent = _revenueService.GetRevenuePercent(dto.CompanyNumber);
var proposalValidationRequest = new ProposalValidationRequest(
EmployeesAmount: dto.EmployeesAmount,
ItRevenuePercent: itRevenuePercent
);

var valid = ProposalValidator.IsProposalValid(proposalValidationRequest);

if (!valid)
return;

var saved = false;
try
{
await _orderRepository.SaveAsync(new ProposalData
{
CompanyEmail = dto.CompanyEmail,
CompanyName = dto.CompanyName,
CompanyNumber = dto.CompanyNumber,
EmployeesAmount = dto.EmployeesAmount
});

saved = true;
}
catch (Exception e)
{
}
Comment on lines +29 to +44
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вы тут немного перемудрили с try-catch и saved. Лучше отказы инфраструктуры в бизнес-логику не встраивать (как это у вас в CreateNotification сделано), а делать их "вокруг" бизнес-процесса. Т.е. в идеальной ситуации весь этот хэндлер будет обернут в трай-кетч с какой-нибудь ретрай-политикой и его инфраструктурные операторы будут идемпотентными.

При этом в самом коде будет чистое-бизнесовое:

  • Если валидно - сохраняем заявку
  • Если подходят условия для отправки - отправляем письмо



var notificationRequest = new NotificationRequest(
Saved: saved,
EmployeesNumber: dto.EmployeesAmount,
HandleDate: DateTime.UtcNow,
MailTo: dto.CompanyEmail);

var emailDto = NotificationService.CreateNotification(notificationRequest);

if (emailDto != null)
await _emailClient.SendEmail(
mailTo: emailDto.MailTo,
subject: emailDto.Subject,
body: emailDto.Body);

}
}
3 changes: 3 additions & 0 deletions TDDLesson.Orders/EmailDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace TDDLesson;

public record EmailDto(string MailTo, string Subject, string Body);
3 changes: 3 additions & 0 deletions TDDLesson.Orders/NotificationRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace TDDLesson;

public record NotificationRequest(bool Saved, int EmployeesNumber, DateTime HandleDate, string MailTo);
24 changes: 24 additions & 0 deletions TDDLesson.Orders/NotificationService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace TDDLesson;

public static class NotificationService
{
public const string Subject = "Proposal info.";
public const string ProcessedMessage = "Proposal processed.";
public const string InviteMessage = "We invite you to the forum on the development of the IT industry.";

public static EmailDto? CreateNotification(NotificationRequest request)
{
if (request.HandleDate < new DateTime(2024, 06, 01) || request.HandleDate > new DateTime(2024, 09, 01))
{
return null;
}
if (request.EmployeesNumber <= 100)
{
return null;
}

var body = request.EmployeesNumber <= 500 ? ProcessedMessage : ProcessedMessage + " " + InviteMessage;
return new EmailDto(request.MailTo, Subject, body);
}
}

12 changes: 12 additions & 0 deletions TDDLesson.Orders/ProposalData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace TDDLesson;

public class ProposalData
{
public required int CompanyNumber { get; set; }

public required string CompanyName { get; set; }

public required string CompanyEmail { get; set; }

public required int EmployeesAmount { get; set; }
}
3 changes: 3 additions & 0 deletions TDDLesson.Orders/ProposalValidationRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace TDDLesson;

public record ProposalValidationRequest(int EmployeesAmount, float ItRevenuePercent);
15 changes: 15 additions & 0 deletions TDDLesson.Orders/ProposalValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace TDDLesson;

public static class ProposalValidator
{
public static bool IsProposalValid(ProposalValidationRequest proposalValidationRequest)
{
if (proposalValidationRequest.EmployeesAmount <= 100)
return false;

if (proposalValidationRequest.ItRevenuePercent <= 30)
return false;

return true;
}
}
7 changes: 4 additions & 3 deletions TDDLesson.sln.DotSettings.user
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Int64 x:Key="/Default/Environment/Hierarchy/Build/BuildTool/MsbuildVersion/@EntryValue">4294967293</s:Int64>
<s:String x:Key="/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue">/usr/local/share/dotnet/sdk/8.0.203/MSBuild.dll</s:String>
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=46c725ae_002Dcc42_002D4ccd_002Db699_002Df44f04a3f20a/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;Solution /&gt;&#xD;
&lt;/SessionState&gt;</s:String></wpf:ResourceDictionary>



</wpf:ResourceDictionary>