Skip to content

Commit

Permalink
feat: apply fixes after review
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilbaczek committed Oct 26, 2024
1 parent b29e8cb commit 08934db
Show file tree
Hide file tree
Showing 23 changed files with 88 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace EvolutionaryArchitecture.Fitnet.Contracts.Api.UnitTests.SignContract;

public sealed class SignContractRequestValidatorTests
{
private const string ValidSignatureText = "John Doe";
private const string ValidSignature = "John Doe";
private const int SignatureCharacterLimit = 100;
private readonly SignContractRequestValidator _validator = new();
private readonly DateTimeOffset _fakeNow = new Faker().Date.RecentOffset();
Expand All @@ -14,7 +14,7 @@ public sealed class SignContractRequestValidatorTests
internal void Given_sign_contract_request_validation_When_request_is_valid_Then_result_should_have_no_errors()
{
// Arrange
var request = new SignContractRequest(_fakeNow, ValidSignatureText);
var request = new SignContractRequest(_fakeNow, ValidSignature);

// Act
var result = _validator.TestValidate(request);
Expand All @@ -27,7 +27,7 @@ internal void Given_sign_contract_request_validation_When_request_is_valid_Then_
internal void Given_sign_contract_request_validation_When_signed_at_not_provided_Then_result_should_have_error()
{
// Arrange
var request = new SignContractRequest(default, ValidSignatureText);
var request = new SignContractRequest(default, ValidSignature);

// Act
var result = _validator.TestValidate(request);
Expand All @@ -38,11 +38,11 @@ internal void Given_sign_contract_request_validation_When_signed_at_not_provided


[Fact]
internal void Given_sign_contract_request_validation_When_signature_text_is_to_long_Then_result_should_have_error()
internal void Given_sign_contract_request_validation_When_signature_is_to_long_Then_result_should_have_error()
{
// Arrange
var tooLongSignatureText = GenerateTooLongSignatureText();
var request = new SignContractRequest(default, tooLongSignatureText);
var tooLongSignature = GenerateTooLongSignature();
var request = new SignContractRequest(default, tooLongSignature);

// Act
var result = _validator.TestValidate(request);
Expand All @@ -51,5 +51,5 @@ internal void Given_sign_contract_request_validation_When_signature_text_is_to_l
result.ShouldHaveValidationErrorFor(signContractRequest => signContractRequest.SignedAt);
}

private static string GenerateTooLongSignatureText() => new('a', SignatureCharacterLimit + 1);
private static string GenerateTooLongSignature() => new('a', SignatureCharacterLimit + 1);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,38 @@
using Core.SignContract.Signatures.Exceptions;
using FluentAssertions;

public sealed class SignatureTests
public sealed class DigitalSignatureTests
{
[Theory]
[InlineData("John Doe")]
[InlineData("Kamil Baczek")]
[InlineData("Maciej Jedrzejewski")]
[InlineData("John David Smith")]
internal void Given_create_signature_When_signature_is_valid_Then_should_not_throw(string signatureText)
internal void Given_create_signature_When_signature_is_valid_Then_should_not_throw(string signature)
{
// Arrange
var dateTimeOffset = DateTimeOffset.Now;
var now = DateTimeOffset.Now;

// Act
var signature = Signature.From(dateTimeOffset, signatureText);
var digitalSignature = DigitalSignature.From(now, signature);

// Assert
signature.Text.Should().Be(signatureText);
signature.Date.Should().Be(dateTimeOffset);
digitalSignature.Signature.Should().Be(signature);
digitalSignature.Date.Should().Be(now);
}

[Theory]
[InlineData("invalidSignature!")]
[InlineData("invalid@Signature")]
internal void Given_create_signature_When_signature_has_forbidden_characters_Then_should_throw_exception(string signature)
{
// Arrange
var now = DateTimeOffset.Now;

// Act
Action act = () => Signature.From(DateTimeOffset.Now, signature);
Action act = () => DigitalSignature.From(now, signature);

// Assert
act.Should().Throw<SignatureNotValidException>();
act.Should().Throw<DigitalSignatureNotValidException>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ namespace EvolutionaryArchitecture.Fitnet.Contracts.Api.SignContract;

using EvolutionaryArchitecture.Fitnet.Contracts.Application.SignContract;

internal sealed record SignContractRequest(DateTimeOffset SignedAt, string SignatureText)
internal sealed record SignContractRequest(DateTimeOffset SignedAt, string Signature)
{
internal SignContractCommand ToCommand(Guid id) =>
new(id, SignatureText, SignedAt);
new(id, Signature, SignedAt);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ namespace EvolutionaryArchitecture.Fitnet.Contracts.Api.SignContract;

internal sealed class SignContractRequestValidator : AbstractValidator<SignContractRequest>
{
private const int SignatureMaximumLength = 100;

public SignContractRequestValidator()
{
RuleFor(signContractRequest => signContractRequest.SignatureText)
RuleFor(signContractRequest => signContractRequest.Signature)
.NotEmpty()
.MaximumLength(100);
.MaximumLength(SignatureMaximumLength);

RuleFor(signContractRequest => signContractRequest.SignedAt)
.NotEmpty();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace EvolutionaryArchitecture.Fitnet.Contracts.Application.SignContract;

public sealed record SignContractCommand(Guid Id, string SignatureText, DateTimeOffset SignedAt) : ICommand<ErrorOr<Guid>>;
public sealed record SignContractCommand(Guid Id, string Signature, DateTimeOffset SignedAt) : ICommand<ErrorOr<Guid>>;
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal sealed class SignContractCommandHandler(
{
public async Task<ErrorOr<Guid>> Handle(SignContractCommand command, CancellationToken cancellationToken) =>
await contractsRepository.GetByIdAsync(command.Id, cancellationToken)
.ThenAsync(async contract => await contract.Sign(Signature.From(command.SignedAt, command.SignatureText), timeProvider.GetUtcNow())
.ThenAsync(async contract => await contract.Sign(DigitalSignature.From(command.SignedAt, command.Signature), timeProvider.GetUtcNow())
.ThenAsync(async bindingContract =>
{
await bindingContractsRepository.AddAsync(bindingContract, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public SignContractBuilder SignedOn(DateTimeOffset signDay, DateTimeOffset fakeT

private BindingContract Build()
{
var signature = Signature.From(_signDay, "John Doe");
var signature = DigitalSignature.From(_signDay, "John Doe");
var bindingContract = parentBuilder.Sign(signature, _fakeToday);

return bindingContract.Value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace EvolutionaryArchitecture.Fitnet.Contracts.Core.UnitTests.SignContract;

public sealed class SignContractTests
{
private const string SignatureText = "John Doe";
private const string Signature = "John Doe";

[Theory]
[ClassData(typeof(SignContractTestData))]
Expand All @@ -21,7 +21,7 @@ internal void Given_sign_contract_Then_expiration_date_is_set_to_contract_durati
Contract contract = ContractBuilder
.Prepared()
.PreparedAt(preparedAt);
var signature = Signature.From(signedAt, SignatureText);
var signature = DigitalSignature.From(signedAt, Signature);

// Act
var signResult = contract.Sign(signature, fakeNow);
Expand All @@ -40,10 +40,10 @@ internal void Given_sign_contract_Then_contracts_becomes_binding_contract()
// Arrange
Contract contract = ContractBuilder
.Prepared();
var signature = Signature.From(SignedAt, SignatureText);
var digitalSignature = DigitalSignature.From(SignedAt, Signature);

// Act
var signResult = contract.Sign(signature, FakeNow);
var signResult = contract.Sign(digitalSignature, FakeNow);

// Assert
var @event = signResult.Value.GetPublishedEvent<BindingContractStartedEvent>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
namespace EvolutionaryArchitecture.Fitnet.Contracts.Core.UnitTests.SignContract;

using Common;
using Core.SignContract.Signatures;

internal sealed class SignedContractBuilder(Contract parentBuilder)
{
private const string SignatureText = "John Doe";
private const string Signature = "John Doe";
private DateTimeOffset? _signDay;
private DateTimeOffset? _fakeToday;

Expand All @@ -21,7 +20,7 @@ private BindingContract Build()
{
var signDay = _signDay ?? FakeContractDates.SignDay;
var fakeToday = _fakeToday ?? FakeContractDates.SignDay;
var signature = Signature.From(signDay, SignatureText);
var signature = Core.SignContract.Signatures.DigitalSignature.From(signDay, Signature);
var bindingContract = parentBuilder.Sign(signature, fakeToday).Value;

return bindingContract;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public sealed class Contract : Entity
public DateTimeOffset PreparedAt { get; init; }
public TimeSpan Duration { get; init; }

public Signature? Signature { get; private set; }
public DigitalSignature? Signature { get; private set; }
public DateTimeOffset? ExpiringAt { get; private set; }

public bool IsSigned => Signature is not null;
Expand Down Expand Up @@ -52,13 +52,13 @@ public static ErrorOr<Contract> Prepare(
new PreviousContractHasToBeSignedRule(isPreviousContractSigned))
.Then<Contract>(_ => new Contract(customerId, preparedAt, StandardDuration));

public ErrorOr<BindingContract> Sign(Signature signature, DateTimeOffset now) =>
public ErrorOr<BindingContract> Sign(DigitalSignature digitalSignature, DateTimeOffset now) =>
BusinessRuleValidator.Validate(
new ContractMustNotBeAlreadySignedRule(IsSigned),
new ContractCanOnlyBeSignedWithin30DaysFromPreparationRule(PreparedAt, signature.Date))
new ContractCanOnlyBeSignedWithin30DaysFromPreparationRule(PreparedAt, digitalSignature.Date))
.Then(_ =>
{
Signature = signature;
Signature = digitalSignature;
ExpiringAt = now.Add(Duration);
var bindingContract = BindingContract.Start(Id, CustomerId, Duration, now, ExpiringAt.Value);

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
global using ErrorOr;
global using System.Text.RegularExpressions;
global using ErrorOr;
global using EvolutionaryArchitecture.Fitnet.Contracts.Core.SignContract.Signatures.Exceptions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace EvolutionaryArchitecture.Fitnet.Contracts.Core.SignContract.Signatures;

public sealed partial class DigitalSignature
{
private static readonly Regex SignaturePattern = SignatureAllowedCharacters();
public DateTimeOffset Date { get; }
public string Signature { get; }

private DigitalSignature(DateTimeOffset date, string signature)
{
Date = date;
if (!SignaturePattern.IsMatch(signature))
{
throw new DigitalSignatureNotValidException(signature);
}

Signature = signature;
}

public static DigitalSignature From(DateTimeOffset signedAt, string signature) =>
new(signedAt, signature);

[GeneratedRegex(@"^[A-Za-z\s]+$")]
private static partial Regex SignatureAllowedCharacters();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace EvolutionaryArchitecture.Fitnet.Contracts.Core.SignContract.Signatures.Exceptions;

public sealed class DigitalSignatureNotValidException(string signatureText) : InvalidOperationException($"Signature: '{signatureText}' contains invalid characters.");

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ public void Configure(EntityTypeBuilder<Contract> builder)
value => new ContractId(value))
.ValueGeneratedOnAdd();
builder.Property(contract => contract.PreparedAt).IsRequired();
builder.OwnsOne<Signature>("Signature", signatureBuilder =>
builder.OwnsOne<DigitalSignature>("Signature", signatureBuilder =>
{
signatureBuilder.Property(signature => signature.Date).IsRequired();
signatureBuilder.Property(signature => signature.Text).IsRequired().HasMaxLength(100);
signatureBuilder.Property(signature => signature.Signature).IsRequired().HasMaxLength(100);
});
}
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace EvolutionaryArchitecture.Fitnet.Contracts.Infrastructure.Database.Migr
using Microsoft.EntityFrameworkCore.Migrations;

/// <inheritdoc />
public partial class AddSignatureText : Migration
public partial class AddSignature : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
Expand All @@ -16,7 +16,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
newName: "Signature_Date");

migrationBuilder.AddColumn<string>(
name: "Signature_Text",
name: "Signature_Signature",
schema: "Contracts",
table: "Contracts",
type: "character varying(100)",
Expand All @@ -28,7 +28,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Signature_Text",
name: "Signature_Signature",
schema: "Contracts",
table: "Contracts");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@ protected override void BuildModel(ModelBuilder modelBuilder)

modelBuilder.Entity("EvolutionaryArchitecture.Fitnet.Contracts.Core.Contract", b =>
{
b.OwnsOne("EvolutionaryArchitecture.Fitnet.Contracts.Core.Signature", "Signature", b1 =>
b.OwnsOne("EvolutionaryArchitecture.Fitnet.Contracts.Core.SignContract.Signatures.DigitalSignature", "Signature", b1 =>
{
b1.Property<Guid>("ContractId")
.HasColumnType("uuid");

b1.Property<DateTimeOffset>("Date")
.HasColumnType("timestamp with time zone");

b1.Property<string>("Text")
b1.Property<string>("Signature")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
<PackageReference Include="MediatR" Version="12.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.6" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Loading

0 comments on commit 08934db

Please sign in to comment.