Merge pull request #15

tests
This commit is contained in:
prtsie
2024-09-22 19:54:51 +03:00
committed by GitHub
62 changed files with 2834 additions and 265 deletions

View File

@@ -1,13 +1,12 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace ApplicationLayer namespace ApplicationLayer;
public static class Constants
{ {
public static class Constants
{
public readonly static Regex EnglishWordRegex = new("^[a-zA-Z]*$"); public readonly static Regex EnglishWordRegex = new("^[a-zA-Z]*$");
public readonly static Regex EnglishPhraseRegex = new(@"^[a-zA-Z№0-9?><;,{}[\]\-_+=!@#$%\^&*|']*$"); public readonly static Regex EnglishPhraseRegex = new(@"^[a-zA-Z №0-9;,\-_+=#*']*$");
public readonly static Regex PhoneNumRegex = new(@"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$"); public readonly static Regex PhoneNumRegex = new(@"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$");
}
} }

View File

@@ -60,7 +60,7 @@ public class PlaceOfWorkModelValidator : AbstractValidator<PlaceOfWorkModel>
.WithMessage("Building of place of work can not be empty") .WithMessage("Building of place of work can not be empty")
.Matches(Constants.EnglishPhraseRegex) .Matches(Constants.EnglishPhraseRegex)
.WithMessage("Place of work building field can contain only english letters, digits and special symbols") .WithMessage("Place of work building field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CountryNameLength) .MaximumLength(ConfigurationConstraints.BuildingNumberLength)
.WithMessage($"Building of place of work length must be less than {ConfigurationConstraints.BuildingNumberLength}"); .WithMessage($"Building of place of work length must be less than {ConfigurationConstraints.BuildingNumberLength}");
} }
} }

View File

@@ -1,9 +1,8 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace ApplicationLayer.Services.AuthServices.Common namespace ApplicationLayer.Services.AuthServices.Common;
public class AuthToken
{ {
public class AuthToken
{
[Required] public string Token { get; set; } = null!; [Required] public string Token { get; set; } = null!;
}
} }

View File

@@ -1,16 +1,15 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Domains; using Domains;
namespace ApplicationLayer.Services.Users.Models namespace ApplicationLayer.Services.Users.Models;
/// Auth data with nullable password for making change auth data requests
public class ChangeAuthData
{ {
/// Auth data with nullable password for making change auth data requests
public class ChangeAuthData
{
[Required] [Required]
[MaxLength(ConfigurationConstraints.EmailLength)] [MaxLength(ConfigurationConstraints.EmailLength)]
public string Email { get; set; } = null!; public string Email { get; set; } = null!;
[MaxLength(ConfigurationConstraints.PasswordLength)] [MaxLength(ConfigurationConstraints.PasswordLength)]
public string? Password { get; set; } public string? Password { get; set; }
}
} }

View File

@@ -1,10 +1,10 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Domains; using Domains;
namespace ApplicationLayer.Services.Users.Models namespace ApplicationLayer.Services.Users.Models;
public class UserModel
{ {
public class UserModel
{
/// Unique Identifier of user /// Unique Identifier of user
[Required] [Required]
public Guid Id { get; private set; } = Guid.NewGuid(); public Guid Id { get; private set; } = Guid.NewGuid();
@@ -12,5 +12,4 @@ namespace ApplicationLayer.Services.Users.Models
[Required] [Required]
[MaxLength(ConfigurationConstraints.EmailLength)] [MaxLength(ConfigurationConstraints.EmailLength)]
public string Email { get; set; } = null!; public string Email { get; set; } = null!;
}
} }

View File

@@ -1,12 +1,15 @@
using Domains; using Domains;
using FluentValidation; using FluentValidation;
namespace ApplicationLayer.Services.Users.Requests.Validation namespace ApplicationLayer.Services.Users.Requests.Validation;
public class ChangeUserAuthDataRequestValidator : AbstractValidator<ChangeUserAuthDataRequest>
{ {
public class ChangeUserAuthDataRequestValidator : AbstractValidator<ChangeUserAuthDataRequest>
{
public ChangeUserAuthDataRequestValidator() public ChangeUserAuthDataRequestValidator()
{ {
RuleFor(r => r.UserId)
.NotEmpty();
RuleFor(r => r.NewAuthData) RuleFor(r => r.NewAuthData)
.NotEmpty(); .NotEmpty();
@@ -17,5 +20,4 @@ namespace ApplicationLayer.Services.Users.Requests.Validation
.MaximumLength(ConfigurationConstraints.EmailLength) .MaximumLength(ConfigurationConstraints.EmailLength)
.WithMessage($"Email address length must be less than {ConfigurationConstraints.EmailLength}"); .WithMessage($"Email address length must be less than {ConfigurationConstraints.EmailLength}");
} }
}
} }

View File

@@ -1,4 +1,3 @@
namespace BlazorWebAssemblyVisaApiClient.Common.Exceptions namespace BlazorWebAssemblyVisaApiClient.Common.Exceptions;
{
public class BlazorClientException(string message) : Exception(message); public class BlazorClientException(string message) : Exception(message);
}

View File

@@ -1,4 +1,3 @@
namespace BlazorWebAssemblyVisaApiClient.Common.Exceptions namespace BlazorWebAssemblyVisaApiClient.Common.Exceptions;
{
public class NotLoggedInException() : BlazorClientException("User is not logged in."); public class NotLoggedInException() : BlazorClientException("User is not logged in.");
}

View File

@@ -1,13 +1,13 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
namespace BlazorWebAssemblyVisaApiClient namespace BlazorWebAssemblyVisaApiClient;
public static class Constants
{ {
public static class Constants
{
public readonly static Regex EnglishWordRegex = new("^[a-zA-Z]*$"); public readonly static Regex EnglishWordRegex = new("^[a-zA-Z]*$");
public readonly static Regex EnglishPhraseRegex = new(@"^[a-zA-Z№0-9?><;,{}[\]\-_+=!@#$%\^&*|']*$"); public readonly static Regex EnglishPhraseRegex = new(@"^[a-z A-Z№0-9?><;,{}[\]\-_+=!@#$%\^&*|']*$");
public readonly static Regex PhoneNumRegex = new(@"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$"); public readonly static Regex PhoneNumRegex = new(@"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$");
@@ -16,5 +16,4 @@ namespace BlazorWebAssemblyVisaApiClient
public const string ApplicantRole = "Applicant"; public const string ApplicantRole = "Applicant";
public const string ApprovingAuthorityRole = "ApprovingAuthority"; public const string ApprovingAuthorityRole = "ApprovingAuthority";
public const string AdminRole = "Admin"; public const string AdminRole = "Admin";
}
} }

View File

@@ -3,10 +3,10 @@ using BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models;
using VisaApiClient; using VisaApiClient;
using PlaceOfWorkModel = BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models.PlaceOfWorkModel; using PlaceOfWorkModel = BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models.PlaceOfWorkModel;
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.AutoMapper.Profiles namespace BlazorWebAssemblyVisaApiClient.Infrastructure.AutoMapper.Profiles;
public class RegisterApplicantRequestProfile : Profile
{ {
public class RegisterApplicantRequestProfile : Profile
{
public RegisterApplicantRequestProfile() public RegisterApplicantRequestProfile()
{ {
CreateMap<RegisterApplicantRequestModel, RegisterApplicantRequest>(MemberList.Destination); CreateMap<RegisterApplicantRequestModel, RegisterApplicantRequest>(MemberList.Destination);
@@ -15,5 +15,4 @@ namespace BlazorWebAssemblyVisaApiClient.Infrastructure.AutoMapper.Profiles
CreateMap<PlaceOfWorkModel, VisaApiClient.PlaceOfWorkModel>(MemberList.Destination); CreateMap<PlaceOfWorkModel, VisaApiClient.PlaceOfWorkModel>(MemberList.Destination);
} }
}
} }

View File

@@ -2,13 +2,12 @@
using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models; using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models;
using VisaApiClient; using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.AutoMapper.Profiles namespace BlazorWebAssemblyVisaApiClient.Infrastructure.AutoMapper.Profiles;
public class VisaApplicationCreateRequestProfile : Profile
{ {
public class VisaApplicationCreateRequestProfile : Profile
{
public VisaApplicationCreateRequestProfile() public VisaApplicationCreateRequestProfile()
{ {
CreateMap<VisaApplicationCreateRequestModel, VisaApplicationCreateRequest>(MemberList.Destination); CreateMap<VisaApplicationCreateRequestModel, VisaApplicationCreateRequest>(MemberList.Destination);
} }
}
} }

View File

@@ -1,9 +1,9 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers;
public static class EnumExtensions
{ {
public static class EnumExtensions
{
public static string GetDisplayName(this Enum value) public static string GetDisplayName(this Enum value)
{ {
var enumMembers = value.GetType().GetMembers(); var enumMembers = value.GetType().GetMembers();
@@ -14,5 +14,4 @@ namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
var displayName = displayAttribute?.Name ?? value.ToString(); var displayName = displayAttribute?.Name ?? value.ToString();
return displayName; return displayName;
} }
}
} }

View File

@@ -1,10 +1,10 @@
using System.Text; using System.Text;
using FluentValidation.Results; using FluentValidation.Results;
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers;
public static class ValidationResultExtensions
{ {
public static class ValidationResultExtensions
{
public static string ToErrorsString(this ValidationResult validationResult) public static string ToErrorsString(this ValidationResult validationResult)
=> ErrorsToString(validationResult.Errors.Select(e => e.ErrorMessage)); => ErrorsToString(validationResult.Errors.Select(e => e.ErrorMessage));
@@ -18,5 +18,4 @@ namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
return stringBuilder.ToString(); return stringBuilder.ToString();
} }
}
} }

View File

@@ -1,9 +1,8 @@
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider;
public class DateTimeProvider : IDateTimeProvider
{ {
public class DateTimeProvider : IDateTimeProvider
{
public DateTime Now() => DateTime.Now; public DateTime Now() => DateTime.Now;
public string FormattedNow() => Now().ToString("yyyy-MM-dd"); public string FormattedNow() => Now().ToString("yyyy-MM-dd");
}
} }

View File

@@ -1,9 +1,8 @@
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider;
public interface IDateTimeProvider
{ {
public interface IDateTimeProvider
{
DateTime Now(); DateTime Now();
string FormattedNow(); string FormattedNow();
}
} }

View File

@@ -1,6 +1,5 @@
using BlazorWebAssemblyVisaApiClient.Common.Exceptions; using BlazorWebAssemblyVisaApiClient.Common.Exceptions;
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider.Exceptions namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider.Exceptions;
{
public class UnknownRoleException() : BlazorClientException("Unknown user role"); public class UnknownRoleException() : BlazorClientException("Unknown user role");
}

View File

@@ -1,9 +1,9 @@
using VisaApiClient; using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider;
public interface IUserDataProvider
{ {
public interface IUserDataProvider
{
public string? CurrentRole { get; } public string? CurrentRole { get; }
public Action? OnRoleChanged { get; set; } public Action? OnRoleChanged { get; set; }
@@ -11,5 +11,4 @@ namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvide
public Task<ApplicantModel> GetApplicant(); public Task<ApplicantModel> GetApplicant();
public void UpdateCurrentRole(); public void UpdateCurrentRole();
}
} }

View File

@@ -3,10 +3,10 @@ using System.Security.Claims;
using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider.Exceptions; using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider.Exceptions;
using VisaApiClient; using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider;
public class UserDataProvider(Client client) : IUserDataProvider
{ {
public class UserDataProvider(Client client) : IUserDataProvider
{
private readonly static JwtSecurityTokenHandler tokenHandler = new(); private readonly static JwtSecurityTokenHandler tokenHandler = new();
public string? CurrentRole { get; private set; } public string? CurrentRole { get; private set; }
@@ -49,5 +49,4 @@ namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvide
OnRoleChanged?.Invoke(); OnRoleChanged?.Invoke();
} }
} }
}
} }

View File

@@ -1,11 +1,11 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using VisaApiClient; using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models namespace BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models;
/// Model of place of work with attributes required for validation to work
public class PlaceOfWorkModel
{ {
/// Model of place of work with attributes required for validation to work
public class PlaceOfWorkModel
{
[Required] [Required]
[StringLength(ConfigurationConstraints.PlaceOfWorkNameLength, MinimumLength = 1)] [StringLength(ConfigurationConstraints.PlaceOfWorkNameLength, MinimumLength = 1)]
public string Name { get; set; } = default!; public string Name { get; set; } = default!;
@@ -17,5 +17,4 @@ namespace BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models
[Required] [Required]
[StringLength(ConfigurationConstraints.PhoneNumberLength, MinimumLength = ConfigurationConstraints.PhoneNumberMinLength)] [StringLength(ConfigurationConstraints.PhoneNumberLength, MinimumLength = ConfigurationConstraints.PhoneNumberMinLength)]
public string PhoneNum { get; set; } = default!; public string PhoneNum { get; set; } = default!;
}
} }

View File

@@ -3,11 +3,11 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using VisaApiClient; using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models namespace BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models;
/// Model of request with attributes required for validation to work
public class RegisterApplicantRequestModel
{ {
/// Model of request with attributes required for validation to work
public class RegisterApplicantRequestModel
{
[Required] [Required]
[ValidateComplexType] [ValidateComplexType]
public RegisterRequestModel RegisterRequest { get; set; } = new(); public RegisterRequestModel RegisterRequest { get; set; } = new();
@@ -64,5 +64,4 @@ namespace BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models
public PlaceOfWorkModel PlaceOfWork { get; set; } = new(); public PlaceOfWorkModel PlaceOfWork { get; set; } = new();
public bool IsNonResident { get; set; } public bool IsNonResident { get; set; }
}
} }

View File

@@ -1,13 +1,12 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using VisaApiClient; using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models namespace BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models;
/// Model of request with attributes required for validation to work
public class RegisterRequestModel
{ {
/// Model of request with attributes required for validation to work
public class RegisterRequestModel
{
[Required] [Required]
[ValidateComplexType] [ValidateComplexType]
public AuthData AuthData { get; set; } = new AuthData(); public AuthData AuthData { get; set; } = new AuthData();
}
} }

View File

@@ -1,10 +1,10 @@
using FluentValidation; using FluentValidation;
using VisaApiClient; using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Validation.Auth namespace BlazorWebAssemblyVisaApiClient.Validation.Auth;
public class ChangeUserAuthDataRequestValidator : AbstractValidator<ChangeUserAuthDataRequest>
{ {
public class ChangeUserAuthDataRequestValidator : AbstractValidator<ChangeUserAuthDataRequest>
{
public ChangeUserAuthDataRequestValidator() public ChangeUserAuthDataRequestValidator()
{ {
RuleFor(r => r.NewAuthData) RuleFor(r => r.NewAuthData)
@@ -17,5 +17,4 @@ namespace BlazorWebAssemblyVisaApiClient.Validation.Auth
.MaximumLength(ConfigurationConstraints.EmailLength) .MaximumLength(ConfigurationConstraints.EmailLength)
.WithMessage($"Email address length must be less than {ConfigurationConstraints.EmailLength}"); .WithMessage($"Email address length must be less than {ConfigurationConstraints.EmailLength}");
} }
}
} }

View File

@@ -1,11 +1,11 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using VisaApiClient; using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models;
/// Model for request for data annotations validation to work
public class VisaApplicationCreateRequestModel
{ {
/// Model for request for data annotations validation to work
public class VisaApplicationCreateRequestModel
{
[ValidateComplexType] [ValidateComplexType]
public ReentryPermitModel? ReentryPermit { get; set; } = default!; public ReentryPermitModel? ReentryPermit { get; set; } = default!;
@@ -34,5 +34,4 @@ namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models
[ValidateComplexType] [ValidateComplexType]
public List<PastVisitModel> PastVisits { get; set; } = []; public List<PastVisitModel> PastVisits { get; set; } = [];
}
} }

View File

@@ -12,6 +12,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisaApiClient", "VisaApiCli
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorWebAssemblyVisaApiClient", "BlazorWebAssemblyVisaApiClient\BlazorWebAssemblyVisaApiClient.csproj", "{3DB49E3C-AAC3-452C-AEBE-F4F72D6134F8}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorWebAssemblyVisaApiClient", "BlazorWebAssemblyVisaApiClient\BlazorWebAssemblyVisaApiClient.csproj", "{3DB49E3C-AAC3-452C-AEBE-F4F72D6134F8}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisaApiTests", "VisaApiTests\VisaApiTests.csproj", "{4CB6FBC4-99CB-453D-8499-A498FAE9683D}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -42,5 +44,9 @@ Global
{3DB49E3C-AAC3-452C-AEBE-F4F72D6134F8}.Debug|Any CPU.Build.0 = Debug|Any CPU {3DB49E3C-AAC3-452C-AEBE-F4F72D6134F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DB49E3C-AAC3-452C-AEBE-F4F72D6134F8}.Release|Any CPU.ActiveCfg = Release|Any CPU {3DB49E3C-AAC3-452C-AEBE-F4F72D6134F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DB49E3C-AAC3-452C-AEBE-F4F72D6134F8}.Release|Any CPU.Build.0 = Release|Any CPU {3DB49E3C-AAC3-452C-AEBE-F4F72D6134F8}.Release|Any CPU.Build.0 = Release|Any CPU
{4CB6FBC4-99CB-453D-8499-A498FAE9683D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4CB6FBC4-99CB-453D-8499-A498FAE9683D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4CB6FBC4-99CB-453D-8499-A498FAE9683D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4CB6FBC4-99CB-453D-8499-A498FAE9683D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@@ -1,9 +1,9 @@
using System.Text; using System.Text;
namespace VisaApiClient namespace VisaApiClient;
public class ClientBase
{ {
public class ClientBase
{
public AuthToken? AuthToken { get; set; } public AuthToken? AuthToken { get; set; }
protected Task<HttpRequestMessage> CreateHttpRequestMessageAsync(CancellationToken cancellationToken) protected Task<HttpRequestMessage> CreateHttpRequestMessageAsync(CancellationToken cancellationToken)
@@ -26,5 +26,4 @@ namespace VisaApiClient
protected async Task ProcessResponseAsync(HttpClient client, HttpResponseMessage response, CancellationToken cancellationToken) protected async Task ProcessResponseAsync(HttpClient client, HttpResponseMessage response, CancellationToken cancellationToken)
=> await Task.CompletedTask; => await Task.CompletedTask;
}
} }

View File

@@ -0,0 +1,6 @@
namespace VisaApi;
public static class Collections
{
public const string ContextUsingTestCollection = "ContextUsingTestCollection";
}

View File

@@ -0,0 +1,65 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using Bogus;
using Domains;
using Domains.ApplicantDomain;
namespace VisaApi.Fakers.Applicants;
/// <summary>
/// Generates applicants
/// </summary>
public sealed class ApplicantFaker : Faker<Applicant>
{
public ApplicantFaker(IDateTimeProvider dateTimeProvider)
{
RuleFor(a => a.Citizenship, f => f.Address.Country());
RuleFor(a => a.Gender, f => f.Random.Enum<Gender>());
RuleForType(typeof(Name), f
=> new Name
{
FirstName = f.Name.LastName(),
Surname = f.Name.LastName(),
Patronymic = f.Name.FirstName()
});
RuleFor(a => a.BirthDate,
f => f.Date.Past(60, dateTimeProvider.Now()));
RuleFor(a => a.Passport, f
=> new Passport
{
Issuer = f.Company.CompanyName(),
Number = f.Random.String(ConfigurationConstraints.PasswordLength, 'a', 'z'),
ExpirationDate = f.Date.Future(4, dateTimeProvider.Now()),
IssueDate = f.Date.Past(4, dateTimeProvider.Now())
});
RuleFor(a => a.JobTitle, f => f.Name.JobTitle());
RuleFor(a => a.MaritalStatus, f => f.Random.Enum<MaritalStatus>());
RuleFor(a => a.CitizenshipByBirth, f => f.Address.Country());
RuleFor(a => a.CityOfBirth, f => f.Address.City());
RuleFor(a => a.CountryOfBirth, f => f.Address.Country());
RuleFor(a => a.IsNonResident, f => f.Random.Bool());
RuleFor(a => a.PlaceOfWork, f
=> new PlaceOfWork
{
Address = new Address
{
Country = f.Address.Country(),
City = f.Address.City(),
Street = f.Address.StreetName(),
Building = f.Address.BuildingNumber()
},
Name = f.Company.CompanyName(),
PhoneNum = f.Phone.PhoneNumber()
});
}
}

View File

@@ -0,0 +1,16 @@
using ApplicationLayer.Services.Applicants.Models;
using Bogus;
namespace VisaApi.Fakers.Applicants.Requests;
public sealed class NameModelFaker : Faker<NameModel>
{
public NameModelFaker()
{
RuleFor(m => m.FirstName, f => f.Name.FirstName());
RuleFor(m => m.Surname, f => f.Name.LastName());
RuleFor(m => m.Patronymic, f => f.Name.FirstName());
}
}

View File

@@ -0,0 +1,23 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.Applicants.Models;
using Bogus;
using Domains;
namespace VisaApi.Fakers.Applicants.Requests;
public sealed class PassportModelFaker : Faker<PassportModel>
{
public PassportModelFaker(IDateTimeProvider dateTimeProvider)
{
RuleFor(m => m.Issuer, f => f.Company.CompanyName());
RuleFor(m => m.Number,
f => f.Random.String(ConfigurationConstraints.PassportNumberLength, 'a', 'z'));
RuleFor(m => m.ExpirationDate,
f => f.Date.Future(4, dateTimeProvider.Now()));
RuleFor(m => m.IssueDate,
f => f.Date.Past(4, dateTimeProvider.Now()));
}
}

View File

@@ -0,0 +1,23 @@
using ApplicationLayer.Services.Applicants.Models;
using Bogus;
namespace VisaApi.Fakers.Applicants.Requests;
public sealed class PlaceOfWorkModelFaker : Faker<PlaceOfWorkModel>
{
public PlaceOfWorkModelFaker()
{
RuleFor(m => m.Name, f => f.Company.CompanyName());
RuleFor(m => m.PhoneNum, f => f.Phone.PhoneNumber("###########"));
RuleFor(m => m.Address,
f => new AddressModel
{
Country = f.Address.Country(),
City = f.Address.City(),
Street = f.Address.StreetName(),
Building = f.Address.BuildingNumber()
});
}
}

View File

@@ -0,0 +1,14 @@
using ApplicationLayer.Services.AuthServices.Common;
using Bogus;
namespace VisaApi.Fakers.Auth;
public sealed class AuthDataFaker : Faker<AuthData>
{
public AuthDataFaker()
{
RuleFor(a => a.Email, f => f.Internet.Email());
RuleFor(a => a.Password, f => f.Internet.Password());
}
}

View File

@@ -0,0 +1,14 @@
using ApplicationLayer.Services.AuthServices.Requests;
using Bogus;
namespace VisaApi.Fakers.Auth;
public sealed class RegisterRequestFaker : Faker<RegisterRequest>
{
private static AuthDataFaker authDataFaker = new();
public RegisterRequestFaker()
{
RuleFor(r => r.AuthData, () => authDataFaker.Generate());
}
}

View File

@@ -0,0 +1,14 @@
using ApplicationLayer.Services.Users.Models;
using Bogus;
namespace VisaApi.Fakers.Common;
public sealed class ChangeAuthDataFaker : Faker<ChangeAuthData>
{
public ChangeAuthDataFaker()
{
RuleFor(a => a.Email, f => f.Internet.Email());
RuleFor(a => a.Password, f => f.Internet.Password());
}
}

View File

@@ -0,0 +1,15 @@
using ApplicationLayer.Services.Users.Requests;
using Bogus;
using VisaApi.Fakers.Common;
namespace VisaApi.Fakers.Users.Requests;
public sealed class ChangeUserAuthDataRequestFaker : Faker<ChangeUserAuthDataRequest>
{
private static ChangeAuthDataFaker changeAuthDataFaker = new();
public ChangeUserAuthDataRequestFaker()
{
CustomInstantiator(_ => new(Guid.NewGuid(), changeAuthDataFaker.Generate()));
}
}

View File

@@ -0,0 +1,17 @@
using Bogus;
using Domains.Users;
namespace VisaApi.Fakers.Users;
/// <summary>
/// Generates users
/// </summary>
public sealed class UserFaker : Faker<User>
{
public UserFaker()
{
RuleFor(u => u.Email, f => f.Internet.Email());
RuleFor(u => u.Password, f => f.Internet.Password());
}
}

View File

@@ -0,0 +1,28 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using Bogus;
using Domains.VisaApplicationDomain;
namespace VisaApi.Fakers.VisaApplications;
/// <summary>
/// Generates past visas
/// </summary>
public sealed class PastVisaFaker : Faker<PastVisa>
{
private IDateTimeProvider dateTimeProvider;
public PastVisaFaker(IDateTimeProvider dateTimeProvider)
{
this.dateTimeProvider = dateTimeProvider;
RuleFor(pv => pv.Name, f => f.Random.Words());
}
public PastVisa GenerateValid()
{
var result = Generate();
result.IssueDate = dateTimeProvider.Now().AddDays(-Random.Shared.Next(11, 900));
result.ExpirationDate = result.IssueDate.AddDays(Random.Shared.Next(1, 11));
return result;
}
}

View File

@@ -0,0 +1,28 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using Bogus;
using Domains.VisaApplicationDomain;
namespace VisaApi.Fakers.VisaApplications;
/// <summary>
/// Generates past visas
/// </summary>
public sealed class PastVisitFaker : Faker<PastVisit>
{
private IDateTimeProvider dateTimeProvider;
public PastVisitFaker(IDateTimeProvider dateTimeProvider)
{
this.dateTimeProvider = dateTimeProvider;
RuleFor(pv => pv.DestinationCountry, f => f.Address.Country());
}
public PastVisit GenerateValid()
{
var result = Generate();
result.StartDate = dateTimeProvider.Now().AddDays(-Random.Shared.Next(11, 900));
result.EndDate = result.StartDate.AddDays(Random.Shared.Next(1, 11));
return result;
}
}

View File

@@ -0,0 +1,19 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using Bogus;
using Domains.VisaApplicationDomain;
namespace VisaApi.Fakers.VisaApplications;
/// <summary>
/// Generates permissions to destination Country
/// </summary>
public sealed class PermissionToDestCountryFaker : Faker<PermissionToDestCountry>
{
public PermissionToDestCountryFaker(IDateTimeProvider dateTimeProvider)
{
RuleFor(p => p.Issuer, f => f.Company.CompanyName());
RuleFor(p => p.ExpirationDate,
f => f.Date.Future(4, dateTimeProvider.Now()));
}
}

View File

@@ -0,0 +1,21 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using Bogus;
using Domains;
using Domains.VisaApplicationDomain;
namespace VisaApi.Fakers.VisaApplications;
/// <summary>
/// Generates re-entry permissions
/// </summary>
public sealed class ReentryPermitFaker : Faker<ReentryPermit>
{
public ReentryPermitFaker(IDateTimeProvider dateTimeProvider)
{
RuleFor(p => p.Number,
f => f.Random.String(ConfigurationConstraints.ReentryPermitNumberLength, 'a', 'z'));
RuleFor(p => p.ExpirationDate,
f => f.Date.Future(4, dateTimeProvider.Now()));
}
}

View File

@@ -0,0 +1,28 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.VisaApplications.Models;
using Bogus;
namespace VisaApi.Fakers.VisaApplications.Requests;
/// <summary>
/// Generates past visas
/// </summary>
public sealed class PastVisaModelFaker : Faker<PastVisaModel>
{
private IDateTimeProvider dateTimeProvider;
public PastVisaModelFaker(IDateTimeProvider dateTimeProvider)
{
this.dateTimeProvider = dateTimeProvider;
RuleFor(pv => pv.Name, f => f.Random.Words());
}
public PastVisaModel GenerateValid()
{
var result = Generate();
result.IssueDate = dateTimeProvider.Now().AddDays(-Random.Shared.Next(11, 900));
result.ExpirationDate = result.IssueDate.AddDays(Random.Shared.Next(1, 11));
return result;
}
}

View File

@@ -0,0 +1,28 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.VisaApplications.Models;
using Bogus;
namespace VisaApi.Fakers.VisaApplications.Requests;
/// <summary>
/// Generates past visas
/// </summary>
public sealed class PastVisitModelFaker : Faker<PastVisitModel>
{
private IDateTimeProvider dateTimeProvider;
public PastVisitModelFaker(IDateTimeProvider dateTimeProvider)
{
this.dateTimeProvider = dateTimeProvider;
RuleFor(pv => pv.DestinationCountry, f => f.Address.Country());
}
public PastVisitModel GenerateValid()
{
var result = Generate();
result.StartDate = dateTimeProvider.Now().AddDays(-Random.Shared.Next(11, 900));
result.EndDate = result.StartDate.AddDays(Random.Shared.Next(1, 11));
return result;
}
}

View File

@@ -0,0 +1,17 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.VisaApplications.Models;
using Bogus;
namespace VisaApi.Fakers.VisaApplications.Requests
{
public sealed class PermissionToDestCountryModelFaker : Faker<PermissionToDestCountryModel>
{
public PermissionToDestCountryModelFaker(IDateTimeProvider dateTimeProvider)
{
RuleFor(p => p.Issuer, f => f.Company.CompanyName());
RuleFor(p => p.ExpirationDate,
f => f.Date.Future(4, dateTimeProvider.Now()));
}
}
}

View File

@@ -0,0 +1,21 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.VisaApplications.Models;
using Bogus;
using Domains;
namespace VisaApi.Fakers.VisaApplications.Requests;
/// <summary>
/// Generates re-entry permissions
/// </summary>
public sealed class ReentryPermitModelFaker : Faker<ReentryPermitModel>
{
public ReentryPermitModelFaker(IDateTimeProvider dateTimeProvider)
{
RuleFor(p => p.Number,
f => f.Random.String(ConfigurationConstraints.ReentryPermitNumberLength, 'a', 'z'));
RuleFor(p => p.ExpirationDate,
f => f.Date.Future(4, dateTimeProvider.Now()));
}
}

View File

@@ -0,0 +1,64 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using Bogus;
using Domains;
using Domains.ApplicantDomain;
using Domains.VisaApplicationDomain;
namespace VisaApi.Fakers.VisaApplications;
/// <summary>
/// Generates visa applications
/// </summary>
public sealed class VisaApplicationFaker : Faker<VisaApplication>
{
private static ReentryPermitFaker reentryPermitFaker = null!;
private static PermissionToDestCountryFaker permissionToDestCountryFaker = null!;
public VisaApplicationFaker(IDateTimeProvider dateTimeProvider)
{
reentryPermitFaker = new(dateTimeProvider);
permissionToDestCountryFaker = new(dateTimeProvider);
var pastVisaFaker = new PastVisaFaker(dateTimeProvider);
var pastVisitFaker = new PastVisitFaker(dateTimeProvider);
RuleFor(va => va.Status, f => f.Random.Enum<ApplicationStatus>());
RuleFor(va => va.DestinationCountry, f => f.Address.Country());
RuleFor(va => va.PastVisas,
f => f.PickRandom(pastVisaFaker.Generate(3), f.Random.Int(0, 3)).ToList());
RuleFor(va => va.PastVisits,
f => f.PickRandom(pastVisitFaker.Generate(3), f.Random.Int(0, 3)).ToList());
RuleFor(va => va.VisaCategory, f => f.Random.Enum<VisaCategory>());
RuleFor(va => va.ForGroup, f => f.Random.Bool());
RuleFor(va => va.RequestedNumberOfEntries,
f => f.Random.Enum<RequestedNumberOfEntries>());
RuleFor(va => va.RequestDate, dateTimeProvider.Now);
RuleFor(va => va.ValidDaysRequested,
f => f.Random.Int(1, ConfigurationConstraints.MaxValidDays));
}
public VisaApplication GenerateValid(Applicant applicant)
{
var result = Generate();
result.ApplicantId = applicant.Id;
if (applicant.IsNonResident)
{
result.ReentryPermit = reentryPermitFaker.Generate();
}
if (result.VisaCategory is VisaCategory.Transit)
{
result.PermissionToDestCountry = permissionToDestCountryFaker.Generate();
}
return result;
}
}

View File

@@ -0,0 +1,8 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
namespace VisaApi.Services;
public class TestDateTimeProvider : IDateTimeProvider
{
public DateTime Now() => DateTime.Now;
}

View File

@@ -0,0 +1,170 @@
using System.Text;
using ApplicationLayer.Services.Applicants.Models;
using ApplicationLayer.Services.Applicants.Models.Validation;
using Domains;
using FluentAssertions;
using FluentValidation;
using VisaApi.Fakers.Applicants.Requests;
using Xunit;
namespace VisaApi.Tests.Application.Validation.Applicants;
public class NameModelValidatorTests
{
private readonly static IValidator<NameModel> validator = new NameModelValidator();
private readonly static NameModelFaker faker = new();
/// <summary>
/// Test for <see cref="NameModel"/> validator that should throw for empty first name
/// </summary>
[Fact]
private async Task ValidateForEmptyFirstNameShouldThrow()
{
var name = faker.Generate();
name.FirstName = null!;
var result = await validator.ValidateAsync(name);
result.Errors.Where(error => error.PropertyName == nameof(name.FirstName))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="NameModel"/> validator that should throw for empty surname
/// </summary>
[Fact]
private async Task ValidateForEmptySurnameShouldThrow()
{
var name = faker.Generate();
name.Surname = null!;
var result = await validator.ValidateAsync(name);
result.Errors.Where(error => error.PropertyName == nameof(name.Surname))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="NameModel"/> validator that should return no errors for empty patronymic
/// </summary>
[Fact]
private async Task ValidateForEmptyPatronymicShouldReturnNoErrors()
{
var name = faker.Generate();
name.Patronymic = null;
var result = await validator.ValidateAsync(name);
result.Errors.Where(error => error.PropertyName == nameof(name.Patronymic))
.Should().BeEmpty();
}
/// <summary>
/// Test for <see cref="NameModel"/> validator that should return error for too long first name
/// </summary>
[Fact]
private async Task ValidateForLongFirstNameShouldReturnError()
{
var name = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('h', ConfigurationConstraints.NameLength + 1);
name.FirstName = stringBuilder.ToString();
var result = await validator.ValidateAsync(name);
result.Errors.Where(error => error.PropertyName == nameof(name.FirstName))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="NameModel"/> validator that should return error for too long surname
/// </summary>
[Fact]
private async Task ValidateForLongSurnameShouldReturnError()
{
var name = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('h', ConfigurationConstraints.NameLength + 1);
name.Surname = stringBuilder.ToString();
var result = await validator.ValidateAsync(name);
result.Errors.Where(error => error.PropertyName == nameof(name.Surname))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="NameModel"/> validator that should return error for too long patronymic
/// </summary>
[Fact]
private async Task ValidateForLongPatronymicShouldReturnError()
{
var name = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('h', ConfigurationConstraints.NameLength + 1);
name.Patronymic = stringBuilder.ToString();
var result = await validator.ValidateAsync(name);
result.Errors.Where(error => error.PropertyName == nameof(name.Patronymic))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="NameModel"/> validator that should return error for not valid firstname
/// </summary>
[Fact]
private async Task ValidateForNotValidFirstNameShouldReturnError()
{
var name = faker.Generate();
name.FirstName = "&&7!**|";
var result = await validator.ValidateAsync(name);
result.Errors.Where(error => error.PropertyName == nameof(name.FirstName))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="NameModel"/> validator that should return error for not valid surname
/// </summary>
[Fact]
private async Task ValidateForNotValidSurnameShouldReturnError()
{
var name = faker.Generate();
name.Surname = "&&7!**|";
var result = await validator.ValidateAsync(name);
result.Errors.Where(error => error.PropertyName == nameof(name.Surname))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="NameModel"/> validator that should return error for not valid patronymic
/// </summary>
[Fact]
private async Task ValidateForNotValidPatronymicShouldReturnError()
{
var name = faker.Generate();
name.Patronymic = "&&7!**|";
var result = await validator.ValidateAsync(name);
result.Errors.Where(error => error.PropertyName == nameof(name.Patronymic))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="NameModel"/> validator that should return no errors for valid name
/// </summary>
[Fact]
private async Task ValidateForValidNameShouldReturnNoErrors()
{
var name = faker.Generate();
var result = await validator.ValidateAsync(name);
result.Errors.Should().BeEmpty();
}
}

View File

@@ -0,0 +1,174 @@
using System.Text;
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.Applicants.Models;
using ApplicationLayer.Services.Applicants.Models.Validation;
using Domains;
using FluentAssertions;
using FluentValidation;
using VisaApi.Fakers.Applicants.Requests;
using VisaApi.Services;
using Xunit;
namespace VisaApi.Tests.Application.Validation.Applicants;
public class PassportModelValidatorTests
{
private readonly static IDateTimeProvider dateTimeProvider = new TestDateTimeProvider();
private readonly static IValidator<PassportModel> validator = new PassportModelValidator(dateTimeProvider);
private readonly static PassportModelFaker faker = new(dateTimeProvider);
/// <summary>
/// Test for <see cref="PassportModel"/> validator that should return error for empty number
/// </summary>
[Fact]
private async Task ValidateForEmptyNumberShouldReturnError()
{
var model = faker.Generate();
model.Number = string.Empty;
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Number))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PassportModel"/> validator that should return error for too long number
/// </summary>
[Fact]
private async Task ValidateForLongNumberShouldReturnError()
{
var model = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('d', ConfigurationConstraints.PassportNumberLength + 1);
model.Number = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Number))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PassportModel"/> validator that should return error for not valid number
/// </summary>
[Fact]
private async Task ValidateForNotValidNumberShouldReturnError()
{
var model = faker.Generate();
model.Number = "&?%$24asd\\]|";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Number))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PassportModel"/> validator that should return error for empty issuer
/// </summary>
[Fact]
private async Task ValidateForEmptyIssuerShouldReturnError()
{
var model = faker.Generate();
model.Issuer = string.Empty;
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Issuer))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PassportModel"/> validator that should return error for too long issuer
/// </summary>
[Fact]
private async Task ValidateForLongIssuerShouldReturnError()
{
var model = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('d', ConfigurationConstraints.IssuerNameLength + 1);
model.Issuer = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Issuer))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PassportModel"/> validator that should return error for not valid issuer
/// </summary>
[Fact]
private async Task ValidateForNotValidIssuerShouldReturnError()
{
var model = faker.Generate();
model.Issuer = "&?%$24asd\\]|";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Issuer))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PassportModel"/> validator that should return error for expired passport
/// </summary>
[Fact]
private async Task ValidateForExpiredPassportShouldReturnError()
{
var model = faker.Generate();
model.ExpirationDate = dateTimeProvider.Now().AddDays(-10);
model.IssueDate = model.ExpirationDate.AddDays(-10);
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.ExpirationDate))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PassportModel"/> validator that should return error for passport from future
/// </summary>
[Fact]
private async Task ValidateForPassportFromFutureShouldReturnError()
{
var model = faker.Generate();
model.ExpirationDate = dateTimeProvider.Now().AddDays(10);
model.IssueDate = model.ExpirationDate.AddDays(-3);
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.IssueDate))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PassportModel"/> validator that should return error for passport that expired before issue
/// </summary>
[Fact]
private async Task ValidateForPassportExpiredBeforeIssueShouldReturnError()
{
var model = faker.Generate();
model.ExpirationDate = dateTimeProvider.Now().AddDays(10);
model.IssueDate = model.ExpirationDate.AddDays(3);
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.IssueDate))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PassportModel"/> validator that should return no errors for valid passport
/// </summary>
[Fact]
private async Task ValidateForValidPassportShouldReturnNoErrors()
{
var model = faker.Generate();
var result = await validator.ValidateAsync(model);
result.Errors.Should().BeEmpty();
}
}

View File

@@ -0,0 +1,353 @@
using System.Text;
using ApplicationLayer.Services.Applicants.Models;
using ApplicationLayer.Services.Applicants.Models.Validation;
using Domains;
using FluentAssertions;
using FluentValidation;
using VisaApi.Fakers.Applicants.Requests;
using Xunit;
namespace VisaApi.Tests.Application.Validation.Applicants;
public class PlaceOfWorkModelValidatorTests
{
private readonly static IValidator<PlaceOfWorkModel> validator = new PlaceOfWorkModelValidator();
private readonly static PlaceOfWorkModelFaker faker = new();
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for empty phone num
/// </summary>
[Fact]
private async Task ValidateForEmptyPhoneNumShouldReturnError()
{
var model = faker.Generate();
model.PhoneNum = string.Empty;
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.PhoneNum))
.Should().NotBeEmpty();
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for long phone num
/// </summary>
[Fact]
private async Task ValidateForLongPhoneNumShouldReturnError()
{
var model = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('8', ConfigurationConstraints.PhoneNumberLength + 1);
model.PhoneNum = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.PhoneNum))
.Should().NotBeEmpty();
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for short phone num
/// </summary>
[Fact]
private async Task ValidateForShortPhoneNumShouldReturnError()
{
var model = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('8', ConfigurationConstraints.PhoneNumberMinLength - 1);
model.PhoneNum = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.PhoneNum))
.Should()
.HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for not valid phone num
/// </summary>
[Fact]
private async Task ValidateForNotValidPhoneNumShouldReturnError()
{
var model = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('a', ConfigurationConstraints.PhoneNumberMinLength);
model.PhoneNum = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.PhoneNum))
.Should()
.HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should throw exception for null address
/// </summary>
[Fact]
private async Task ValidateForEmptyAddressShouldThrow()
{
var model = faker.Generate();
model.Address = null!;
NullReferenceException? result = null;
try
{
await validator.ValidateAsync(model);
}
catch (Exception e)
{
result = e as NullReferenceException;
}
result.Should().NotBeNull();
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for empty Country
/// </summary>
[Fact]
private async Task ValidateForEmptyCountryShouldReturnError()
{
var model = faker.Generate();
model.Address.Country = "";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == "Address.Country")
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for too long Country
/// </summary>
[Fact]
private async Task ValidateForLongCountryShouldReturnError()
{
var model = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('a', ConfigurationConstraints.CountryNameLength + 1);
model.Address.Country = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == "Address.Country")
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for not valid Country
/// </summary>
[Fact]
private async Task ValidateForNotValidCountryShouldReturnError()
{
var model = faker.Generate();
model.Address.Country = "|&%";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == "Address.Country")
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for empty City
/// </summary>
[Fact]
private async Task ValidateForEmptyCityShouldReturnError()
{
var model = faker.Generate();
model.Address.City = "";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == "Address.City")
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for too long city
/// </summary>
[Fact]
private async Task ValidateForLongCityShouldReturnError()
{
var model = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('a', ConfigurationConstraints.CityNameLength + 1);
model.Address.City = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == "Address.City")
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for not valid city
/// </summary>
[Fact]
private async Task ValidateForNotValidCityShouldReturnError()
{
var model = faker.Generate();
model.Address.City = "|&%";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == "Address.City")
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for empty street
/// </summary>
[Fact]
private async Task ValidateForEmptyStreetShouldReturnError()
{
var model = faker.Generate();
model.Address.Street = "";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == "Address.Street")
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for too long street
/// </summary>
[Fact]
private async Task ValidateForLongStreetShouldReturnError()
{
var model = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('a', ConfigurationConstraints.StreetNameLength + 1);
model.Address.Street = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == "Address.Street")
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for not valid street
/// </summary>
[Fact]
private async Task ValidateForNotValidStreetShouldReturnError()
{
var model = faker.Generate();
model.Address.Street = "|&%";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == "Address.Street")
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for empty building /// </summary>
[Fact]
private async Task ValidateForEmptyBuildingShouldReturnError()
{
var model = faker.Generate();
model.Address.Building = "";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == "Address.Building")
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for too long building
/// </summary>
[Fact]
private async Task ValidateForLongBuildingShouldReturnError()
{
var model = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('a', ConfigurationConstraints.BuildingNumberLength + 1);
model.Address.Building = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == "Address.Building")
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for not valid building
/// </summary>
[Fact]
private async Task ValidateForNotValidBuildingShouldReturnError()
{
var model = faker.Generate();
model.Address.Building = "|&%";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == "Address.Building")
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for empty name
/// </summary>
[Fact]
private async Task ValidateForEmptyNameShouldReturnError()
{
var model = faker.Generate();
model.Name = "";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Name))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for too long name
/// </summary>
[Fact]
private async Task ValidateForTooLongNameShouldReturnError()
{
var model = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('g', ConfigurationConstraints.PlaceOfWorkNameLength + 1);
model.Name = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Name))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return error for not valid name
/// </summary>
[Fact]
private async Task ValidateForNotValidNameShouldReturnError()
{
var model = faker.Generate();
model.Name = "@$%&|";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Name))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PlaceOfWorkModel"/> validator that should return no errors for valid model
/// </summary>
[Fact]
private async Task ValidateForValidShouldReturnNoErrors()
{
var model = faker.Generate();
var result = await validator.ValidateAsync(model);
result.Errors.Should().BeEmpty();
}
}

View File

@@ -0,0 +1,113 @@
using System.Text;
using ApplicationLayer.Services.AuthServices.Common;
using ApplicationLayer.Services.AuthServices.Requests.Validation;
using Domains;
using FluentAssertions;
using FluentValidation;
using VisaApi.Fakers.Auth;
using Xunit;
namespace VisaApi.Tests.Application.Validation.Auth;
public class AuthDataValidatorTests
{
private readonly static IValidator<AuthData> validator = new AuthDataValidator();
private readonly static AuthDataFaker faker = new();
/// <summary>
/// Test for <see cref="AuthData"/> validator that should return validation error for invalid email
/// </summary>
[Fact]
private async Task ValidateForInvalidEmailShouldReturnError()
{
var authData = faker.Generate();
authData.Email = "alsdas'dsa";
var result = await validator.ValidateAsync(authData);
result.Errors.Should()
.HaveCount(1)
.And.Contain(error => error.PropertyName == nameof(authData.Email));
}
/// <summary>
/// Test for <see cref="AuthData"/> validator that should return validation error for too long email
/// </summary>
[Fact]
private async Task ValidateForLongEmailShouldReturnError()
{
var authData = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('d', ConfigurationConstraints.EmailLength);
stringBuilder.Append("@mail.ru");
authData.Email = stringBuilder.ToString();
var result = await validator.ValidateAsync(authData);
result.Errors.Should()
.HaveCount(1)
.And.Contain(error => error.PropertyName == nameof(authData.Email));
}
/// <summary>
/// Test for <see cref="AuthData"/> validator that should return no errors for valid email
/// </summary>
[Fact]
private async Task ValidateForValidEmailShouldReturnNoError()
{
var authData = faker.Generate();
var result = await validator.ValidateAsync(authData);
result.Errors.Where(error => error.PropertyName == nameof(authData.Email))
.Should().BeEmpty();
}
/// <summary>
/// Test for <see cref="AuthData"/> validator that should return validation error for empty password
/// </summary>
[Fact]
private async Task ValidateForEmptyPasswordShouldReturnError()
{
var authData = faker.Generate();
authData.Password = string.Empty;
var result = await validator.ValidateAsync(authData);
result.Errors.Should()
.HaveCount(1)
.And.Contain(error => error.PropertyName == nameof(authData.Password));
}
/// <summary>
/// Test for <see cref="AuthData"/> validator that should return validation error for too long password
/// </summary>
[Fact]
private async Task ValidateForLongPasswordShouldReturnError()
{
var authData = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('d', ConfigurationConstraints.PasswordLength + 1);
authData.Password = stringBuilder.ToString();
var result = await validator.ValidateAsync(authData);
result.Errors.Should()
.HaveCount(1)
.And.Contain(error => error.PropertyName == nameof(authData.Password));
}
/// <summary>
/// Test for <see cref="AuthData"/> validator that should return no errors for valid password
/// </summary>
[Fact]
private async Task ValidateForValidPasswordShouldReturnNoError()
{
var authData = faker.Generate();
var result = await validator.ValidateAsync(authData);
result.Errors.Where(error => error.PropertyName == nameof(authData.Password))
.Should().BeEmpty();
}
}

View File

@@ -0,0 +1,92 @@
using ApplicationLayer.Services.AuthServices.Common;
using ApplicationLayer.Services.AuthServices.Requests;
using ApplicationLayer.Services.AuthServices.Requests.Validation;
using FluentAssertions;
using FluentValidation;
using Infrastructure.Database;
using Infrastructure.Database.Users.Repositories;
using VisaApi.Fakers.Auth;
using VisaApi.Fakers.Users;
using VisaApi.Tests.Infrastructure.Database;
using Xunit;
namespace VisaApi.Tests.Application.Validation.Auth;
[Collection(Collections.ContextUsingTestCollection)]
public class RegisterRequestValidatorTests
{
private readonly static IValidator<AuthData> authDataValidator = new AuthDataValidator();
private readonly static RegisterRequestFaker requestFaker = new();
private readonly static UserFaker userFaker = new();
/// <summary>
/// Creates validator from context
/// </summary>
/// <param name="context">db context</param>
/// <returns>RegisterRequest validator</returns>
private static IValidator<RegisterRequest> GetValidator(DbContext context)
{
var repository = new UsersRepository(context, context);
return new RegisterRequestValidator(repository, authDataValidator);
}
/// <summary>
/// Test for <see cref="RegisterRequest"/> validator that should throw for empty auth data
/// </summary>
[Fact]
private async Task ValidateForEmptyAuthDataShouldThrow()
{
var context = InMemoryContextProvider.GetDbContext();
var validator = GetValidator(context);
var request = requestFaker.Generate();
request.AuthData = null!;
NullReferenceException? result = null;
try
{
await validator.ValidateAsync(request);
}
catch (Exception e)
{
result = e as NullReferenceException;
}
result.Should().NotBeNull();
}
/// <summary>
/// Test for <see cref="RegisterRequest"/> validator that should return error for used email
/// </summary>
[Fact]
private async Task ValidateForUsedEmailShouldReturnError()
{
var context = InMemoryContextProvider.GetDbContext();
var validator = GetValidator(context);
var user = userFaker.Generate();
await context.AddAsync(user);
await context.SaveChangesAsync();
var request = requestFaker.Generate();
request.AuthData.Email = user.Email;
var result = await validator.ValidateAsync(request);
result.Errors.Should()
.Contain(error => error.PropertyName == nameof(request.AuthData))
.And.HaveCount(1);
}
/// <summary>
/// Test for <see cref="RegisterRequest"/> validator that should return o errors for valid requests
/// </summary>
[Fact]
private async Task ValidateForValidRequestShouldReturnNoErrors()
{
var context = InMemoryContextProvider.GetDbContext();
var validator = GetValidator(context);
var request = requestFaker.Generate();
var result = await validator.ValidateAsync(request);
result.Errors.Should().BeEmpty();
}
}

View File

@@ -0,0 +1,48 @@
using ApplicationLayer.Services.Users.Requests;
using ApplicationLayer.Services.Users.Requests.Validation;
using FluentAssertions;
using FluentValidation;
using VisaApi.Fakers.Users.Requests;
using Xunit;
namespace VisaApi.Tests.Application.Validation.Users;
public class ChangeUserAuthDataRequestValidationTests
{
private readonly static IValidator<ChangeUserAuthDataRequest> validator = new ChangeUserAuthDataRequestValidator();
private readonly static ChangeUserAuthDataRequestFaker faker = new();
/// <summary>
/// Test for <see cref="ChangeUserAuthDataRequest"/> validator that should throw exception for empty auth data
/// </summary>
[Fact]
private async Task ValidateForEmptyAuthDataShouldThrow()
{
var request = faker.Generate();
request.NewAuthData = null!;
NullReferenceException? result = null;
try
{
await validator.ValidateAsync(request);
}
catch (Exception e)
{
result = e as NullReferenceException;
}
result.Should().NotBeNull();
}
/// <summary>
/// Test for <see cref="ChangeUserAuthDataRequest"/> validator that should no errors for valid entity
/// </summary>
[Fact]
private async Task ValidateForValidShouldReturnNoErrors()
{
var request = faker.Generate();
var result = await validator.ValidateAsync(request);
result.IsValid.Should().BeTrue();
}
}

View File

@@ -0,0 +1,140 @@
using System.Text;
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.VisaApplications.Models;
using ApplicationLayer.Services.VisaApplications.Models.Validation;
using Domains;
using FluentAssertions;
using FluentValidation;
using VisaApi.Fakers.VisaApplications.Requests;
using VisaApi.Services;
using Xunit;
namespace VisaApi.Tests.Application.Validation.VisaApplications
{
public class PastVisaModelValidatorTests
{
private readonly static IDateTimeProvider dateTimeProvider = new TestDateTimeProvider();
private readonly static IValidator<PastVisaModel> validator = new PastVisaModelValidator(dateTimeProvider);
private readonly static PastVisaModelFaker faker = new(dateTimeProvider);
/// <summary>
/// Test for <see cref="PastVisaModel"/> validator that should return error for empty expiration date
/// </summary>
[Fact]
private async Task ValidateForEmptyExpirationDateShouldReturnError()
{
var model = faker.GenerateValid();
model.ExpirationDate = new();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.ExpirationDate))
.Should().HaveCount(2); //expected error + error because of expiration date less than issue date
}
/// <summary>
/// Test for <see cref="PastVisaModel"/> validator that should return error for expiration date less than issue date
/// </summary>
[Fact]
private async Task ValidateForExpirationDateLessThanIssueDateShouldReturnError()
{
var model = faker.GenerateValid();
model.ExpirationDate = model.IssueDate.AddDays(-4);
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.ExpirationDate))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PastVisaModel"/> validator that should return error for empty issue date
/// </summary>
[Fact]
private async Task ValidateForEmptyIssueDateShouldReturnError()
{
var model = faker.GenerateValid();
model.IssueDate = new();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.IssueDate))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PastVisaModel"/> validator that should return error for issue date greater than current date
/// </summary>
[Fact]
private async Task ValidateForIssueDateGreaterThanCurrentDateShouldReturnError()
{
var model = faker.GenerateValid();
model.IssueDate = dateTimeProvider.Now().AddDays(4);
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.IssueDate))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PastVisaModel"/> validator that should return error for empty name
/// </summary>
[Fact]
private async Task ValidateForEmptyNameShouldReturnError()
{
var model = faker.GenerateValid();
model.Name = string.Empty;
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Name))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PastVisaModel"/> validator that should return error for too long name
/// </summary>
[Fact]
private async Task ValidateForTooLongNameShouldReturnError()
{
var model = faker.GenerateValid();
var stringBuilder = new StringBuilder();
stringBuilder.Append('d', ConfigurationConstraints.VisaNameLength + 1);
model.Name = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Name))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PastVisaModel"/> validator that should return error for not valid name
/// </summary>
[Fact]
private async Task ValidateForNotValidNameShouldReturnError()
{
var model = faker.GenerateValid();
model.Name = "|}{%^&";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Name))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PastVisaModel"/> validator that should return no errors for valid model
/// </summary>
[Fact]
private async Task ValidateForValidShouldReturnNoErrors()
{
var model = faker.GenerateValid();
var result = await validator.ValidateAsync(model);
result.Errors.Should().BeEmpty();
}
}
}

View File

@@ -0,0 +1,142 @@
using System.Text;
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.VisaApplications.Models;
using ApplicationLayer.Services.VisaApplications.Models.Validation;
using Domains;
using FluentAssertions;
using FluentValidation;
using VisaApi.Fakers.VisaApplications.Requests;
using VisaApi.Services;
using Xunit;
namespace VisaApi.Tests.Application.Validation.VisaApplications
{
public class PastVisitModelValidatorTests
{
private readonly static IDateTimeProvider dateTimeProvider = new TestDateTimeProvider();
private readonly static IValidator<PastVisitModel> validator = new PastVisitModelValidator(dateTimeProvider);
private readonly static PastVisitModelFaker faker = new(dateTimeProvider);
/// <summary>
/// Test for <see cref="PastVisitModel"/> validator that should return error for empty start date
/// </summary>
[Fact]
private async Task ValidateForEmptyStartDateShouldReturnError()
{
var model = faker.GenerateValid();
model.StartDate = new();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.StartDate))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PastVisitModel"/> validator that should return error for start date greater than end date
/// </summary>
[Fact]
private async Task ValidateForStartDateGreaterThanEndDateShouldReturnError()
{
var model = faker.GenerateValid();
model.EndDate = dateTimeProvider.Now().AddDays(-10);
model.StartDate = model.EndDate.AddDays(4);
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.StartDate))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PastVisitModel"/> validator that should return error for empty end date
/// </summary>
[Fact]
private async Task ValidateForEmptyEndDateShouldReturnError()
{
var model = faker.GenerateValid();
model.EndDate = new();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.EndDate))
.Should().HaveCount(1); //expected error + error because of end date less than start date
}
/// <summary>
/// Test for <see cref="PastVisitModel"/> validator that should return error for start date greater than current date
/// </summary>
[Fact]
private async Task ValidateForStartDateGreaterThanCurrentDateShouldReturnError()
{
var model = faker.GenerateValid();
model.StartDate = dateTimeProvider.Now().AddDays(4);
model.EndDate = model.StartDate.AddDays(4);
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.StartDate))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PastVisitModel"/> validator that should return error for empty destination Country
/// </summary>
[Fact]
private async Task ValidateForEmptyDestinationCountryShouldReturnError()
{
var model = faker.GenerateValid();
model.DestinationCountry = string.Empty;
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.DestinationCountry))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PastVisitModel"/> validator that should return error for too long destination Country
/// </summary>
[Fact]
private async Task ValidateForTooLongDestinationCountryShouldReturnError()
{
var model = faker.GenerateValid();
var stringBuilder = new StringBuilder();
stringBuilder.Append('d', ConfigurationConstraints.CountryNameLength + 1);
model.DestinationCountry = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.DestinationCountry))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PastVisitModel"/> validator that should return error for not valid destination Country
/// </summary>
[Fact]
private async Task ValidateForNotValidDestinationCountryShouldReturnError()
{
var model = faker.GenerateValid();
model.DestinationCountry = "|}{%^&";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.DestinationCountry))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PastVisitModel"/> validator that should return no errors for valid model
/// </summary>
[Fact]
private async Task ValidateForValidShouldReturnNoErrors()
{
var model = faker.GenerateValid();
var result = await validator.ValidateAsync(model);
result.Errors.Should().BeEmpty();
}
}
}

View File

@@ -0,0 +1,110 @@
using System.Text;
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.VisaApplications.Models;
using ApplicationLayer.Services.VisaApplications.Models.Validation;
using Domains;
using FluentAssertions;
using FluentValidation;
using VisaApi.Fakers.VisaApplications.Requests;
using VisaApi.Services;
using Xunit;
namespace VisaApi.Tests.Application.Validation.VisaApplications
{
public class PermissionToDestCountryModelValidatorTests
{
private readonly static IDateTimeProvider dateTimeProvider = new TestDateTimeProvider();
private readonly static IValidator<PermissionToDestCountryModel> validator = new PermissionToDestCountryModelValidator(dateTimeProvider);
private readonly static PermissionToDestCountryModelFaker faker = new(dateTimeProvider);
/// <summary>
/// Test for <see cref="PermissionToDestCountryModel"/> validator that should return error for empty expiration date
/// </summary>
[Fact]
private async Task ValidateForEmptyExpirationDateShouldReturnError()
{
var model = faker.Generate();
model.ExpirationDate = new();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.ExpirationDate))
.Should().HaveCount(2); //expected error + error because of expired
}
/// <summary>
/// Test for <see cref="PermissionToDestCountryModel"/> validator that should return error for expiration date less than current date
/// </summary>
[Fact]
private async Task ValidateForExpirationDateLessThanCurrentDateShouldReturnError()
{
var model = faker.Generate();
model.ExpirationDate = dateTimeProvider.Now().AddDays(-1);
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.ExpirationDate))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PermissionToDestCountryModel"/> validator that should return error for empty issuer
/// </summary>
[Fact]
private async Task ValidateForEmptyIssuerShouldReturnError()
{
var model = faker.Generate();
model.Issuer = string.Empty;
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Issuer))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PermissionToDestCountryModel"/> validator that should return error for not valid issuer
/// </summary>
[Fact]
private async Task ValidateForNotValidIssuerShouldReturnError()
{
var model = faker.Generate();
model.Issuer = "}{)(*&*^%#!#!:";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Issuer))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PermissionToDestCountryModel"/> validator that should return error for too long issuer
/// </summary>
[Fact]
private async Task ValidateForTooLongIssuerShouldReturnError()
{
var model = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('g', ConfigurationConstraints.IssuerNameLength + 1);
model.Issuer = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Issuer))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="PermissionToDestCountryModel"/> validator that should return no errors for valid model
/// </summary>
[Fact]
private async Task ValidateForValidShouldReturnNoErrors()
{
var model = faker.Generate();
var result = await validator.ValidateAsync(model);
result.Errors.Should().BeEmpty();
}
}
}

View File

@@ -0,0 +1,110 @@
using System.Text;
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.VisaApplications.Models;
using ApplicationLayer.Services.VisaApplications.Models.Validation;
using Domains;
using FluentAssertions;
using FluentValidation;
using VisaApi.Fakers.VisaApplications.Requests;
using VisaApi.Services;
using Xunit;
namespace VisaApi.Tests.Application.Validation.VisaApplications
{
public class ReentryPermitModelValidatorTests
{
private readonly static IDateTimeProvider dateTimeProvider = new TestDateTimeProvider();
private readonly static IValidator<ReentryPermitModel> validator = new ReentryPermitModelValidator(dateTimeProvider);
private readonly static ReentryPermitModelFaker faker = new(dateTimeProvider);
/// <summary>
/// Test for <see cref="ReentryPermitModel"/> validator that should return error for empty expiration date
/// </summary>
[Fact]
private async Task ValidateForEmptyExpirationDateShouldReturnError()
{
var model = faker.Generate();
model.ExpirationDate = new();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.ExpirationDate))
.Should().HaveCount(2); //expected error + error because of expired
}
/// <summary>
/// Test for <see cref="ReentryPermitModel"/> validator that should return error for expiration date less than current date
/// </summary>
[Fact]
private async Task ValidateForExpirationDateLessThanCurrentDateShouldReturnError()
{
var model = faker.Generate();
model.ExpirationDate = dateTimeProvider.Now().AddDays(-1);
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.ExpirationDate))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="ReentryPermitModel"/> validator that should return error for empty number
/// </summary>
[Fact]
private async Task ValidateForEmptyNumberShouldReturnError()
{
var model = faker.Generate();
model.Number = string.Empty;
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Number))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="ReentryPermitModel"/> validator that should return error for not valid number
/// </summary>
[Fact]
private async Task ValidateForNotValidNumberShouldReturnError()
{
var model = faker.Generate();
model.Number = "}{)(*&*^%#!#!:";
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Number))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="ReentryPermitModel"/> validator that should return error for too long number
/// </summary>
[Fact]
private async Task ValidateForTooLongNumberShouldReturnError()
{
var model = faker.Generate();
var stringBuilder = new StringBuilder();
stringBuilder.Append('g', ConfigurationConstraints.ReentryPermitNumberLength + 1);
model.Number = stringBuilder.ToString();
var result = await validator.ValidateAsync(model);
result.Errors.Where(error => error.PropertyName == nameof(model.Number))
.Should().HaveCount(1);
}
/// <summary>
/// Test for <see cref="ReentryPermitModel"/> validator that should return no errors for valid model
/// </summary>
[Fact]
private async Task ValidateForValidShouldReturnNoErrors()
{
var model = faker.Generate();
var result = await validator.ValidateAsync(model);
result.Errors.Should().BeEmpty();
}
}
}

View File

@@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using DbContext = Infrastructure.Database.DbContext;
namespace VisaApi.Tests.Infrastructure.Database;
public static class InMemoryContextProvider
{
private static DbContextOptions<DbContext> opts = new DbContextOptionsBuilder<DbContext>()
.UseInMemoryDatabase("VisaApiDB")
.ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning))
.Options;
public static DbContext GetDbContext()
{
var result = new DbContext(opts);
result.Database.EnsureDeleted();
result.Database.EnsureCreated();
return result;
}
}

View File

@@ -0,0 +1,154 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.Applicants.NeededServices;
using FluentAssertions;
using Infrastructure.Database;
using Infrastructure.Database.Applicants.Repositories;
using Infrastructure.Database.Applicants.Repositories.Exceptions;
using VisaApi.Fakers.Applicants;
using VisaApi.Fakers.Users;
using VisaApi.Services;
using Xunit;
namespace VisaApi.Tests.Infrastructure.Database.Repositories;
[Collection(Collections.ContextUsingTestCollection)]
public class ApplicantsRepositoryTests
{
private readonly static UserFaker userFaker = new();
private readonly static ApplicantFaker applicantFaker = new(GetDateTimeProvider());
/// <summary> Returns <see cref="IApplicantsRepository"/> </summary>
/// <param name="context"> Database context </param>
/// <returns>Repository</returns>
private static IApplicantsRepository GetRepository(DbContext context)
=> new ApplicantsRepository(context, context);
/// <summary> Returns <see cref="IDateTimeProvider"/> </summary>
private static IDateTimeProvider GetDateTimeProvider() => new TestDateTimeProvider();
/// <summary>
/// Test for <see cref="IApplicantsRepository.FindByUserIdAsync"/> method that should throw exception for not existing entity
/// </summary>
[Fact]
private async Task FindByUserIdForNotExistingShouldThrow()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
ApplicantNotFoundByUserIdException? result = null;
try
{
await repository.FindByUserIdAsync(Guid.NewGuid(), CancellationToken.None);
}
catch (Exception e)
{
result = e as ApplicantNotFoundByUserIdException;
}
result.Should().NotBeNull();
}
/// <summary>
/// Test for <see cref="IApplicantsRepository.FindByUserIdAsync"/> method that should return existing entity
/// </summary>
[Fact]
private async Task FindByUserIdForExistingShouldReturnApplicant()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var user = userFaker.Generate();
var applicant = applicantFaker.Generate();
applicant.UserId = user.Id;
await context.AddAsync(user);
await repository.AddAsync(applicant, CancellationToken.None);
await context.SaveChangesAsync();
var result = await repository.FindByUserIdAsync(user.Id, CancellationToken.None);
result.Should().BeEquivalentTo(applicant);
}
/// <summary>
/// Test for <see cref="IApplicantsRepository.GetApplicantIdByUserId"/> method that should throw exception for not existing entity
/// </summary>
[Fact]
private async Task GetApplicantIdByUserIdForNotExistingShouldThrow()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
ApplicantNotFoundByUserIdException? result = null;
try
{
await repository.GetApplicantIdByUserId(Guid.NewGuid(), CancellationToken.None);
}
catch (Exception e)
{
result = e as ApplicantNotFoundByUserIdException;
}
result.Should().NotBeNull();
}
/// <summary>
/// Test for <see cref="IApplicantsRepository.GetApplicantIdByUserId"/> method that should return existing entity's identifier
/// </summary>
[Fact]
private async Task GetApplicantIdByUserIdForExistingShouldReturnApplicant()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var user = userFaker.Generate();
var applicant = applicantFaker.Generate();
applicant.UserId = user.Id;
await context.AddAsync(user);
await repository.AddAsync(applicant, CancellationToken.None);
await context.SaveChangesAsync();
var result = await repository.GetApplicantIdByUserId(user.Id, CancellationToken.None);
result.Should().Be(applicant.Id);
}
/// <summary>
/// Test for <see cref="IApplicantsRepository.IsApplicantNonResidentByUserId"/> method that should throw exception for not existing entity
/// </summary>
[Fact]
private async Task IsApplicantNonResidentByUserIdForNotExistingShouldThrow()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
ApplicantNotFoundByUserIdException? result = null;
try
{
await repository.IsApplicantNonResidentByUserId(Guid.NewGuid(), CancellationToken.None);
}
catch (Exception e)
{
result = e as ApplicantNotFoundByUserIdException;
}
result.Should().NotBeNull();
}
/// <summary>
/// Test for <see cref="IApplicantsRepository.IsApplicantNonResidentByUserId"/> method that should return existing entity's IsNonResident property
/// </summary>
[Fact]
private async Task IsApplicantNonResidentByUserIdForExistingShouldReturnApplicant()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var user = userFaker.Generate();
var applicant = new ApplicantFaker(GetDateTimeProvider()).Generate();
applicant.UserId = user.Id;
await context.AddAsync(user);
await repository.AddAsync(applicant, CancellationToken.None);
await context.SaveChangesAsync();
var result = await repository.IsApplicantNonResidentByUserId(user.Id, CancellationToken.None);
result.Should().Be(applicant.IsNonResident);
}
}

View File

@@ -0,0 +1,90 @@
using ApplicationLayer.GeneralExceptions;
using Domains.Users;
using FluentAssertions;
using Infrastructure.Database;
using Infrastructure.Database.Generic;
using Xunit;
namespace VisaApi.Tests.Infrastructure.Database.Repositories.Generic;
[Collection(Collections.ContextUsingTestCollection)]
public class GenericRepositoryTests
{
/// <summary> Returns <see cref="GenericRepository{T}"/> </summary>
/// <param name="context"> Database context </param>
/// <returns>Repository</returns>
private static GenericRepository<User> GetRepository(DbContext context) => new TestGenericRepository(context, context);
/// <summary> Test for <see cref="GenericRepository{T}.GetAllAsync"/> method that should return empty collection if nothing added </summary>
[Fact]
public void GetAllForEmptyShouldReturnEmpty()
{
using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var result = repository.GetAllAsync(CancellationToken.None).Result;
result.Should().BeEmpty();
}
/// <summary> Test for <see cref="GenericRepository{T}.GetAllAsync"/> method that should return collection with added entities </summary>
[Fact]
public void GetAllForNotEmptyShouldReturnEntities()
{
using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
User[] users =
[
new() { Email = "nasrudin@mail.ru", Password = "12345", Role = Role.Admin },
new() { Email = "bruh@mail.ru", Password = "123", Role = Role.Applicant }
];
foreach (var user in users)
{
repository.AddAsync(user, CancellationToken.None).Wait();
}
context.SaveChanges();
var result = repository.GetAllAsync(CancellationToken.None).Result;
result.Should().Contain(users).And.HaveSameCount(users);
}
/// <summary> Test for <see cref="GenericRepository{T}.GetByIdAsync"/> method that should return existing entity </summary>
[Fact]
public void GetByIdForExistingShouldReturnEntity()
{
using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var user = new User { Email = "nasrudin@mail.ru", Password = "12345", Role = Role.Admin };
repository.AddAsync(user, CancellationToken.None).Wait();
context.SaveChanges();
var result = repository.GetByIdAsync(user.Id, CancellationToken.None).Result;
result.Should().Be(user);
}
/// <summary> Test for <see cref="GenericRepository{T}.GetByIdAsync"/> method that should throw exception for not found entity </summary>
[Fact]
public void GetByIdForNotExistingShouldThrow()
{
using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
context.SaveChanges();
EntityNotFoundByIdException<User>? result = null;
try
{
repository.GetByIdAsync(Guid.NewGuid(), CancellationToken.None).Wait();
}
catch (AggregateException e)
{
result = e.InnerException as EntityNotFoundByIdException<User>;
}
result.Should().NotBeNull();
}
}

View File

@@ -0,0 +1,6 @@
using Domains.Users;
using Infrastructure.Database.Generic;
namespace VisaApi.Tests.Infrastructure.Database.Repositories.Generic;
public class TestGenericRepository(IGenericReader reader, IGenericWriter writer) : GenericRepository<User>(reader, writer);

View File

@@ -0,0 +1,91 @@
using ApplicationLayer.Services.AuthServices.NeededServices;
using ApplicationLayer.Services.VisaApplications.NeededServices;
using Domains.Users;
using FluentAssertions;
using Infrastructure.Database;
using Infrastructure.Database.Users.Repositories;
using VisaApi.Fakers.Users;
using Xunit;
namespace VisaApi.Tests.Infrastructure.Database.Repositories;
[Collection(Collections.ContextUsingTestCollection)]
public class UsersRepositoryTests
{
private readonly static UserFaker userFaker = new();
/// <summary> Returns <see cref="IVisaApplicationsRepository"/> </summary>
/// <param name="context"> Database context </param>
/// <returns>Repository</returns>
private static IUsersRepository GetRepository(DbContext context)
=> new UsersRepository(context, context);
/// <summary>
/// Test for <see cref="IUsersRepository.FindByEmailAsync"/> method that should return null for not existing email
/// </summary>
[Fact]
private async Task FindByEmailForNotExistingShouldReturnNull()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var result = await repository.FindByEmailAsync("email@email.ru", CancellationToken.None);
result.Should().BeNull();
}
/// <summary>
/// Test for <see cref="IUsersRepository.FindByEmailAsync"/> method that should return entity for existing email
/// </summary>
[Fact]
private async Task FindByEmailForExistingShouldReturnEntity()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var user = userFaker.Generate();
await repository.AddAsync(user, CancellationToken.None);
await context.SaveChangesAsync();
var result = await repository.FindByEmailAsync(user.Email, CancellationToken.None);
result.Should().Be(user);
}
/// <summary>
/// Test for <see cref="IUsersRepository.GetAllOfRoleAsync"/> method that should return empty from empty db
/// </summary>
[Fact]
private async Task GetAllOfRoleForEmptyShouldReturnEmpty()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var result = await repository.GetAllOfRoleAsync(Role.ApprovingAuthority, CancellationToken.None);
result.Should().BeEmpty();
}
/// <summary>
/// Test for <see cref="IUsersRepository.GetAllOfRoleAsync"/> method that should return entities from not empty db
/// </summary>
[Fact]
private async Task GetAllOfRoleForNotEmptyShouldReturnEntities()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var users = new List<User>();
for (var i = 0; i < 3; i++)
{
var user = userFaker.Generate();
user.Role = Role.ApprovingAuthority;
users.Add(user);
await repository.AddAsync(user, CancellationToken.None);
}
await context.SaveChangesAsync();
var result = await repository.GetAllOfRoleAsync(Role.ApprovingAuthority, CancellationToken.None);
result.Should().Contain(users).And.HaveSameCount(users);
}
}

View File

@@ -0,0 +1,265 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.VisaApplications.NeededServices;
using Domains.VisaApplicationDomain;
using FluentAssertions;
using Infrastructure.Database;
using Infrastructure.Database.VisaApplications.Repositories;
using Infrastructure.Database.VisaApplications.Repositories.Exceptions;
using VisaApi.Fakers.Applicants;
using VisaApi.Fakers.Users;
using VisaApi.Fakers.VisaApplications;
using VisaApi.Services;
using Xunit;
namespace VisaApi.Tests.Infrastructure.Database.Repositories;
[Collection(Collections.ContextUsingTestCollection)]
public class VisaApplicationsRepositoryTests
{
private readonly static UserFaker userFaker = new();
private readonly static ApplicantFaker applicantFaker = new(GetDateTimeProvider());
private readonly static VisaApplicationFaker applicationFaker = new(GetDateTimeProvider());
/// <summary> Returns <see cref="IVisaApplicationsRepository"/> </summary>
/// <param name="context"> Database context </param>
/// <returns>Repository</returns>
private static IVisaApplicationsRepository GetRepository(DbContext context)
=> new VisaApplicationsRepository(context, context);
/// <summary> Returns <see cref="IDateTimeProvider"/> </summary>
private static IDateTimeProvider GetDateTimeProvider() => new TestDateTimeProvider();
/// <summary>
/// Test for <see cref="IVisaApplicationsRepository.GetOfApplicantAsync"/> method that should return empty if no applications added
/// </summary>
[Fact]
private async Task GetOfApplicantForEmptyShouldReturnEmpty()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var user = userFaker.Generate();
var applicant = applicantFaker.Generate();
applicant.UserId = user.Id;
await context.AddAsync(user);
await context.AddAsync(applicant);
await context.SaveChangesAsync();
var result = await repository.GetOfApplicantAsync(applicant.Id, CancellationToken.None);
result.Should().BeEmpty();
}
/// <summary>
/// Test for <see cref="IVisaApplicationsRepository.GetOfApplicantAsync"/> method that should return added entities
/// </summary>
[Fact]
private async Task GetOfApplicantForExistingShouldReturnEntities()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var user = userFaker.Generate();
var applicant = applicantFaker.Generate();
applicant.UserId = user.Id;
await context.AddAsync(user);
await context.AddAsync(applicant);
var applications = new List<VisaApplication>();
for (var i = 0; i < 5; i++)
{
var application = applicationFaker.GenerateValid(applicant);
applications.Add(application);
await context.AddAsync(application);
}
await context.SaveChangesAsync();
var result = await repository.GetOfApplicantAsync(applicant.Id, CancellationToken.None);
result.Should().Contain(applications).And.HaveSameCount(applications);
}
/// <summary>
/// Test for <see cref="IVisaApplicationsRepository.GetByApplicantAndApplicationIdAsync"/> method that should throw exception for not existing entities
/// </summary>
[Fact]
private async Task GetApplicantIdByUserIdForNotExistingShouldThrow()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
ApplicationNotFoundByApplicantAndApplicationIdException? result = null;
await context.SaveChangesAsync();
try
{
await repository.GetByApplicantAndApplicationIdAsync(Guid.NewGuid(), Guid.NewGuid(), CancellationToken.None);
}
catch (Exception e)
{
result = e as ApplicationNotFoundByApplicantAndApplicationIdException;
}
result.Should().NotBeNull();
}
/// <summary>
/// Test for <see cref="IVisaApplicationsRepository.GetByApplicantAndApplicationIdAsync"/> method that should throw exception for not existing applicant
/// </summary>
[Fact]
private async Task GetApplicantIdByUserIdForNotExistingApplicantShouldThrow()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var user = userFaker.Generate();
var applicant = applicantFaker.Generate();
applicant.UserId = user.Id;
var application = applicationFaker.GenerateValid(applicant);
await context.AddAsync(user);
await context.AddAsync(applicant);
await context.AddAsync(application);
ApplicationNotFoundByApplicantAndApplicationIdException? result = null;
await context.SaveChangesAsync();
try
{
await repository.GetByApplicantAndApplicationIdAsync(Guid.NewGuid(), application.Id, CancellationToken.None);
}
catch (Exception e)
{
result = e as ApplicationNotFoundByApplicantAndApplicationIdException;
}
result.Should().NotBeNull();
}
/// <summary>
/// Test for <see cref="IVisaApplicationsRepository.GetByApplicantAndApplicationIdAsync"/> method that should throw exception for not existing application
/// </summary>
[Fact]
private async Task GetApplicantIdByUserIdForNotExistingApplicationShouldThrow()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var user = userFaker.Generate();
var applicant = applicantFaker.Generate();
applicant.UserId = user.Id;
await context.AddAsync(user);
await context.AddAsync(applicant);
ApplicationNotFoundByApplicantAndApplicationIdException? result = null;
await context.SaveChangesAsync();
try
{
await repository.GetByApplicantAndApplicationIdAsync(applicant.Id, Guid.NewGuid(), CancellationToken.None);
}
catch (Exception e)
{
result = e as ApplicationNotFoundByApplicantAndApplicationIdException;
}
result.Should().NotBeNull();
}
/// <summary>
/// Test for <see cref="IVisaApplicationsRepository.GetByApplicantAndApplicationIdAsync"/> method
/// that should throw exception for not accessible application
/// </summary>
[Fact]
private async Task GetApplicantIdByUserIdForNotAccessibleApplicationShouldThrow()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var user = userFaker.Generate();
var applicant = applicantFaker.Generate();
applicant.UserId = user.Id;
var otherUser = userFaker.Generate();
var otherApplicant = applicantFaker.Generate();
otherApplicant.UserId = user.Id;
var notAccessibleApplication = applicationFaker.GenerateValid(otherApplicant);
await context.AddAsync(user);
await context.AddAsync(applicant);
await context.AddAsync(otherUser);
await context.AddAsync(otherApplicant);
await context.AddAsync(notAccessibleApplication);
ApplicationNotFoundByApplicantAndApplicationIdException? result = null;
await context.SaveChangesAsync();
try
{
await repository.GetByApplicantAndApplicationIdAsync(applicant.Id, notAccessibleApplication.Id, CancellationToken.None);
}
catch (Exception e)
{
result = e as ApplicationNotFoundByApplicantAndApplicationIdException;
}
result.Should().NotBeNull();
}
/// <summary>
/// Test for <see cref="IVisaApplicationsRepository.GetByApplicantAndApplicationIdAsync"/> method
/// that should return application for valid identifiers
/// </summary>
[Fact]
private async Task GetApplicantIdByUserIdForValidIdsShouldReturnApplication()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var user = userFaker.Generate();
var applicant = applicantFaker.Generate();
applicant.UserId = user.Id;
var application = applicationFaker.GenerateValid(applicant);
await context.AddAsync(user);
await context.AddAsync(applicant);
await context.AddAsync(application);
await context.SaveChangesAsync();
var result = await repository.GetByApplicantAndApplicationIdAsync(applicant.Id, application.Id, CancellationToken.None);
result.Should().Be(application);
}
/// <summary>
/// Test for <see cref="IVisaApplicationsRepository.GetPendingApplicationsAsync"/> method that should return empty from empty db
/// </summary>
[Fact]
private async Task GetPendingApplicationsForEmptyShouldReturnEmpty()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var result = await repository.GetPendingApplicationsAsync(CancellationToken.None);
result.Should().BeEmpty();
}
/// <summary>
/// Test for <see cref="IVisaApplicationsRepository.GetPendingApplicationsAsync"/> method that should return pending applications from not empty db
/// </summary>
[Fact]
private async Task GetPendingApplicationsForExistingShouldReturnExistingPending()
{
await using var context = InMemoryContextProvider.GetDbContext();
var repository = GetRepository(context);
var user = userFaker.Generate();
var applicant = applicantFaker.Generate();
applicant.UserId = user.Id;
var applicationPending = applicationFaker.GenerateValid(applicant);
applicationPending.Status = ApplicationStatus.Pending;
var applicationNotPending = applicationFaker.GenerateValid(applicant);
applicationNotPending.Status = ApplicationStatus.Approved;
await context.AddAsync(user);
await context.AddAsync(applicant);
await context.AddAsync(applicationPending);
await context.AddAsync(applicationNotPending);
await context.SaveChangesAsync();
var result = await repository.GetPendingApplicationsAsync(CancellationToken.None);
result.Should().Contain(applicationPending).And.HaveCount(1);
}
}

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<RootNamespace>VisaApi</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bogus" Version="35.6.1" />
<PackageReference Include="FluentAssertions" Version="6.12.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.8" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SchengenVisaApi\SchengenVisaApi.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Tests\Infrastructure\" />
</ItemGroup>
</Project>