diff --git a/SchengenVisaApi/ApplicationLayer/Constants.cs b/SchengenVisaApi/ApplicationLayer/Constants.cs index 2cf42e8..aa8dc09 100644 --- a/SchengenVisaApi/ApplicationLayer/Constants.cs +++ b/SchengenVisaApi/ApplicationLayer/Constants.cs @@ -1,13 +1,12 @@ 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}$"); +} \ No newline at end of file diff --git a/SchengenVisaApi/ApplicationLayer/Services/Applicants/Models/Validation/PlaceOfWorkModelValidator.cs b/SchengenVisaApi/ApplicationLayer/Services/Applicants/Models/Validation/PlaceOfWorkModelValidator.cs index dc5449b..b1f30aa 100644 --- a/SchengenVisaApi/ApplicationLayer/Services/Applicants/Models/Validation/PlaceOfWorkModelValidator.cs +++ b/SchengenVisaApi/ApplicationLayer/Services/Applicants/Models/Validation/PlaceOfWorkModelValidator.cs @@ -60,7 +60,7 @@ public class PlaceOfWorkModelValidator : AbstractValidator .WithMessage("Building of place of work can not be empty") .Matches(Constants.EnglishPhraseRegex) .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}"); } } diff --git a/SchengenVisaApi/ApplicationLayer/Services/AuthServices/Common/AuthToken.cs b/SchengenVisaApi/ApplicationLayer/Services/AuthServices/Common/AuthToken.cs index 7c152ad..e1f661b 100644 --- a/SchengenVisaApi/ApplicationLayer/Services/AuthServices/Common/AuthToken.cs +++ b/SchengenVisaApi/ApplicationLayer/Services/AuthServices/Common/AuthToken.cs @@ -1,9 +1,8 @@ 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!; +} \ No newline at end of file diff --git a/SchengenVisaApi/ApplicationLayer/Services/Users/Models/ChangeAuthData.cs b/SchengenVisaApi/ApplicationLayer/Services/Users/Models/ChangeAuthData.cs index b0ac255..e95f137 100644 --- a/SchengenVisaApi/ApplicationLayer/Services/Users/Models/ChangeAuthData.cs +++ b/SchengenVisaApi/ApplicationLayer/Services/Users/Models/ChangeAuthData.cs @@ -1,16 +1,15 @@ using System.ComponentModel.DataAnnotations; using Domains; -namespace ApplicationLayer.Services.Users.Models -{ - /// Auth data with nullable password for making change auth data requests - public class ChangeAuthData - { - [Required] - [MaxLength(ConfigurationConstraints.EmailLength)] - public string Email { get; set; } = null!; +namespace ApplicationLayer.Services.Users.Models; - [MaxLength(ConfigurationConstraints.PasswordLength)] - public string? Password { get; set; } - } -} +/// Auth data with nullable password for making change auth data requests +public class ChangeAuthData +{ + [Required] + [MaxLength(ConfigurationConstraints.EmailLength)] + public string Email { get; set; } = null!; + + [MaxLength(ConfigurationConstraints.PasswordLength)] + public string? Password { get; set; } +} \ No newline at end of file diff --git a/SchengenVisaApi/ApplicationLayer/Services/Users/Models/UserModel.cs b/SchengenVisaApi/ApplicationLayer/Services/Users/Models/UserModel.cs index 36f87b1..3127236 100644 --- a/SchengenVisaApi/ApplicationLayer/Services/Users/Models/UserModel.cs +++ b/SchengenVisaApi/ApplicationLayer/Services/Users/Models/UserModel.cs @@ -1,16 +1,15 @@ using System.ComponentModel.DataAnnotations; using Domains; -namespace ApplicationLayer.Services.Users.Models -{ - public class UserModel - { - /// Unique Identifier of user - [Required] - public Guid Id { get; private set; } = Guid.NewGuid(); +namespace ApplicationLayer.Services.Users.Models; - [Required] - [MaxLength(ConfigurationConstraints.EmailLength)] - public string Email { get; set; } = null!; - } -} +public class UserModel +{ + /// Unique Identifier of user + [Required] + public Guid Id { get; private set; } = Guid.NewGuid(); + + [Required] + [MaxLength(ConfigurationConstraints.EmailLength)] + public string Email { get; set; } = null!; +} \ No newline at end of file diff --git a/SchengenVisaApi/ApplicationLayer/Services/Users/Requests/Validation/ChangeUserAuthDataRequestValidator.cs b/SchengenVisaApi/ApplicationLayer/Services/Users/Requests/Validation/ChangeUserAuthDataRequestValidator.cs index e824e77..bb44b86 100644 --- a/SchengenVisaApi/ApplicationLayer/Services/Users/Requests/Validation/ChangeUserAuthDataRequestValidator.cs +++ b/SchengenVisaApi/ApplicationLayer/Services/Users/Requests/Validation/ChangeUserAuthDataRequestValidator.cs @@ -1,21 +1,23 @@ using Domains; using FluentValidation; -namespace ApplicationLayer.Services.Users.Requests.Validation -{ - public class ChangeUserAuthDataRequestValidator : AbstractValidator - { - public ChangeUserAuthDataRequestValidator() - { - RuleFor(r => r.NewAuthData) - .NotEmpty(); +namespace ApplicationLayer.Services.Users.Requests.Validation; - RuleFor(r => r.NewAuthData.Email) - .NotEmpty() - .EmailAddress() - .WithMessage("Email should be valid") - .MaximumLength(ConfigurationConstraints.EmailLength) - .WithMessage($"Email address length must be less than {ConfigurationConstraints.EmailLength}"); - } +public class ChangeUserAuthDataRequestValidator : AbstractValidator +{ + public ChangeUserAuthDataRequestValidator() + { + RuleFor(r => r.UserId) + .NotEmpty(); + + RuleFor(r => r.NewAuthData) + .NotEmpty(); + + RuleFor(r => r.NewAuthData.Email) + .NotEmpty() + .EmailAddress() + .WithMessage("Email should be valid") + .MaximumLength(ConfigurationConstraints.EmailLength) + .WithMessage($"Email address length must be less than {ConfigurationConstraints.EmailLength}"); } -} +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Common/Exceptions/BlazorClientException.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Common/Exceptions/BlazorClientException.cs index 7603cb9..d40c912 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Common/Exceptions/BlazorClientException.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Common/Exceptions/BlazorClientException.cs @@ -1,4 +1,3 @@ -namespace BlazorWebAssemblyVisaApiClient.Common.Exceptions -{ - public class BlazorClientException(string message) : Exception(message); -} +namespace BlazorWebAssemblyVisaApiClient.Common.Exceptions; + +public class BlazorClientException(string message) : Exception(message); \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Common/Exceptions/NotLoggedInException.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Common/Exceptions/NotLoggedInException.cs index 130a6ec..adb6001 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Common/Exceptions/NotLoggedInException.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Common/Exceptions/NotLoggedInException.cs @@ -1,4 +1,3 @@ -namespace BlazorWebAssemblyVisaApiClient.Common.Exceptions -{ - public class NotLoggedInException() : BlazorClientException("User is not logged in."); -} +namespace BlazorWebAssemblyVisaApiClient.Common.Exceptions; + +public class NotLoggedInException() : BlazorClientException("User is not logged in."); \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Constants.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Constants.cs index 9131708..09676d9 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Constants.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Constants.cs @@ -1,20 +1,19 @@ using System.Text.RegularExpressions; 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}$"); - public readonly static MarkupString RequiredFieldMarkup = (MarkupString)"*"; + public readonly static MarkupString RequiredFieldMarkup = (MarkupString)"*"; - public const string ApplicantRole = "Applicant"; - public const string ApprovingAuthorityRole = "ApprovingAuthority"; - public const string AdminRole = "Admin"; - } -} + public const string ApplicantRole = "Applicant"; + public const string ApprovingAuthorityRole = "ApprovingAuthority"; + public const string AdminRole = "Admin"; +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/AutoMapper/Profiles/RegisterApplicantRequestProfile.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/AutoMapper/Profiles/RegisterApplicantRequestProfile.cs index e1c7d61..c395d1b 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/AutoMapper/Profiles/RegisterApplicantRequestProfile.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/AutoMapper/Profiles/RegisterApplicantRequestProfile.cs @@ -3,17 +3,16 @@ using BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models; using VisaApiClient; 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(MemberList.Destination); CreateMap(MemberList.Destination); CreateMap(MemberList.Destination); } - } -} +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/AutoMapper/Profiles/VisaApplicationCreateRequestProfile.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/AutoMapper/Profiles/VisaApplicationCreateRequestProfile.cs index 2bf617e..e2aa737 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/AutoMapper/Profiles/VisaApplicationCreateRequestProfile.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/AutoMapper/Profiles/VisaApplicationCreateRequestProfile.cs @@ -2,13 +2,12 @@ using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models; 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(MemberList.Destination); } - } -} +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Helpers/EnumExtensions.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Helpers/EnumExtensions.cs index d49ba1b..c46deb6 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Helpers/EnumExtensions.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Helpers/EnumExtensions.cs @@ -1,11 +1,11 @@ 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 member = enumMembers.First(info => info.Name == value.ToString()); var displayAttribute = (DisplayAttribute?)member @@ -14,5 +14,4 @@ namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers var displayName = displayAttribute?.Name ?? value.ToString(); return displayName; } - } -} +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Helpers/ValidationResultExtensions.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Helpers/ValidationResultExtensions.cs index d77c8a5..fbf2f27 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Helpers/ValidationResultExtensions.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Helpers/ValidationResultExtensions.cs @@ -1,15 +1,15 @@ using System.Text; using FluentValidation.Results; -namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers -{ - public static class ValidationResultExtensions - { - public static string ToErrorsString(this ValidationResult validationResult) - => ErrorsToString(validationResult.Errors.Select(e => e.ErrorMessage)); +namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers; - private static string ErrorsToString(IEnumerable errors) - { +public static class ValidationResultExtensions +{ + public static string ToErrorsString(this ValidationResult validationResult) + => ErrorsToString(validationResult.Errors.Select(e => e.ErrorMessage)); + + private static string ErrorsToString(IEnumerable errors) + { var stringBuilder = new StringBuilder(); foreach (var error in errors) { @@ -18,5 +18,4 @@ namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers return stringBuilder.ToString(); } - } -} +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/DateTimeProvider/DateTimeProvider.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/DateTimeProvider/DateTimeProvider.cs index 21952f4..8b94ddb 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/DateTimeProvider/DateTimeProvider.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/DateTimeProvider/DateTimeProvider.cs @@ -1,9 +1,8 @@ -namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider -{ - public class DateTimeProvider : IDateTimeProvider - { - public DateTime Now() => DateTime.Now; +namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider; - public string FormattedNow() => Now().ToString("yyyy-MM-dd"); - } -} +public class DateTimeProvider : IDateTimeProvider +{ + public DateTime Now() => DateTime.Now; + + public string FormattedNow() => Now().ToString("yyyy-MM-dd"); +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/DateTimeProvider/IDateTimeProvider.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/DateTimeProvider/IDateTimeProvider.cs index bcdcccd..b85ec3d 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/DateTimeProvider/IDateTimeProvider.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/DateTimeProvider/IDateTimeProvider.cs @@ -1,9 +1,8 @@ -namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider -{ - public interface IDateTimeProvider - { - DateTime Now(); +namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider; - string FormattedNow(); - } -} +public interface IDateTimeProvider +{ + DateTime Now(); + + string FormattedNow(); +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/UserDataProvider/Exceptions/UnknownRoleException.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/UserDataProvider/Exceptions/UnknownRoleException.cs index 7ece731..f28aea9 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/UserDataProvider/Exceptions/UnknownRoleException.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/UserDataProvider/Exceptions/UnknownRoleException.cs @@ -1,6 +1,5 @@ using BlazorWebAssemblyVisaApiClient.Common.Exceptions; -namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider.Exceptions -{ - public class UnknownRoleException() : BlazorClientException("Unknown user role"); -} +namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider.Exceptions; + +public class UnknownRoleException() : BlazorClientException("Unknown user role"); \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/UserDataProvider/IUserDataProvider.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/UserDataProvider/IUserDataProvider.cs index f72ad20..165ce84 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/UserDataProvider/IUserDataProvider.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/UserDataProvider/IUserDataProvider.cs @@ -1,15 +1,14 @@ 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; } - public Task GetApplicant(); + public Task GetApplicant(); - public void UpdateCurrentRole(); - } -} + public void UpdateCurrentRole(); +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/UserDataProvider/UserDataProvider.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/UserDataProvider/UserDataProvider.cs index dff20c7..7f1bf25 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/UserDataProvider/UserDataProvider.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Infrastructure/Services/UserDataProvider/UserDataProvider.cs @@ -3,23 +3,23 @@ using System.Security.Claims; using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider.Exceptions; 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(); + + public string? CurrentRole { get; private set; } + + public Action? OnRoleChanged { get; set; } + + public async Task GetApplicant() { - private readonly static JwtSecurityTokenHandler tokenHandler = new(); - - public string? CurrentRole { get; private set; } - - public Action? OnRoleChanged { get; set; } - - public async Task GetApplicant() - { return await client.GetApplicantAsync(); } - public void UpdateCurrentRole() - { + public void UpdateCurrentRole() + { var role = CurrentRole; if (client.AuthToken is null) @@ -49,5 +49,4 @@ namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvide OnRoleChanged?.Invoke(); } } - } -} +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Applicants/Models/PlaceOfWorkModel.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Applicants/Models/PlaceOfWorkModel.cs index cd6d703..fa1215e 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Applicants/Models/PlaceOfWorkModel.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Applicants/Models/PlaceOfWorkModel.cs @@ -1,21 +1,20 @@ using System.ComponentModel.DataAnnotations; 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] - [StringLength(ConfigurationConstraints.PlaceOfWorkNameLength, MinimumLength = 1)] - public string Name { get; set; } = default!; + [Required] + [StringLength(ConfigurationConstraints.PlaceOfWorkNameLength, MinimumLength = 1)] + public string Name { get; set; } = default!; - [Required] - [ValidateComplexType] - public AddressModel Address { get; set; } = new AddressModel(); + [Required] + [ValidateComplexType] + public AddressModel Address { get; set; } = new AddressModel(); - [Required] - [StringLength(ConfigurationConstraints.PhoneNumberLength, MinimumLength = ConfigurationConstraints.PhoneNumberMinLength)] - public string PhoneNum { get; set; } = default!; - } -} + [Required] + [StringLength(ConfigurationConstraints.PhoneNumberLength, MinimumLength = ConfigurationConstraints.PhoneNumberMinLength)] + public string PhoneNum { get; set; } = default!; +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Applicants/Models/RegisterApplicantRequestModel.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Applicants/Models/RegisterApplicantRequestModel.cs index d544d66..0056bbd 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Applicants/Models/RegisterApplicantRequestModel.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Applicants/Models/RegisterApplicantRequestModel.cs @@ -3,66 +3,65 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; 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] - [ValidateComplexType] - public RegisterRequestModel RegisterRequest { get; set; } = new(); + [Required] + [ValidateComplexType] + public RegisterRequestModel RegisterRequest { get; set; } = new(); - [Required] - [ValidateComplexType] - public NameModel ApplicantName { get; set; } = new(); + [Required] + [ValidateComplexType] + public NameModel ApplicantName { get; set; } = new(); - [Required] - [ValidateComplexType] - public PassportModel Passport { get; set; } = new(); + [Required] + [ValidateComplexType] + public PassportModel Passport { get; set; } = new(); - [Required(AllowEmptyStrings = true)] - public DateTimeOffset BirthDate { get; set; } + [Required(AllowEmptyStrings = true)] + public DateTimeOffset BirthDate { get; set; } - [Required] - [StringLength(70, MinimumLength = 1)] - public string CityOfBirth { get; set; } = default!; + [Required] + [StringLength(70, MinimumLength = 1)] + public string CityOfBirth { get; set; } = default!; - [Required] - [StringLength(70, MinimumLength = 1)] - public string CountryOfBirth { get; set; } = default!; + [Required] + [StringLength(70, MinimumLength = 1)] + public string CountryOfBirth { get; set; } = default!; - [Required] - [StringLength(30, MinimumLength = 1)] - public string Citizenship { get; set; } = default!; + [Required] + [StringLength(30, MinimumLength = 1)] + public string Citizenship { get; set; } = default!; - [Required] - [StringLength(30, MinimumLength = 1)] - public string CitizenshipByBirth { get; set; } = default!; + [Required] + [StringLength(30, MinimumLength = 1)] + public string CitizenshipByBirth { get; set; } = default!; - [Required(AllowEmptyStrings = true)] - [JsonConverter(typeof(StringEnumConverter))] - public Gender Gender { get; set; } + [Required(AllowEmptyStrings = true)] + [JsonConverter(typeof(StringEnumConverter))] + public Gender Gender { get; set; } - [Required(AllowEmptyStrings = true)] - [JsonConverter(typeof(StringEnumConverter))] - public MaritalStatus MaritalStatus { get; set; } + [Required(AllowEmptyStrings = true)] + [JsonConverter(typeof(StringEnumConverter))] + public MaritalStatus MaritalStatus { get; set; } - [Required] - [ValidateComplexType] - public NameModel FatherName { get; set; } = new(); + [Required] + [ValidateComplexType] + public NameModel FatherName { get; set; } = new(); - [Required] - [ValidateComplexType] - public NameModel MotherName { get; set; } = new(); + [Required] + [ValidateComplexType] + public NameModel MotherName { get; set; } = new(); - [Required] - [StringLength(50, MinimumLength = 1)] - public string JobTitle { get; set; } = default!; + [Required] + [StringLength(50, MinimumLength = 1)] + public string JobTitle { get; set; } = default!; - [Required] - [ValidateComplexType] - public PlaceOfWorkModel PlaceOfWork { get; set; } = new(); + [Required] + [ValidateComplexType] + public PlaceOfWorkModel PlaceOfWork { get; set; } = new(); - public bool IsNonResident { get; set; } - } -} + public bool IsNonResident { get; set; } +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Applicants/Models/RegisterRequestModel.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Applicants/Models/RegisterRequestModel.cs index a53b582..ce54f8d 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Applicants/Models/RegisterRequestModel.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Applicants/Models/RegisterRequestModel.cs @@ -1,13 +1,12 @@ using System.ComponentModel.DataAnnotations; 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] - [ValidateComplexType] - public AuthData AuthData { get; set; } = new AuthData(); - } -} + [Required] + [ValidateComplexType] + public AuthData AuthData { get; set; } = new AuthData(); +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Auth/ChangeUserAuthDataRequestValidator.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Auth/ChangeUserAuthDataRequestValidator.cs index ffb45ae..37462dc 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Auth/ChangeUserAuthDataRequestValidator.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/Auth/ChangeUserAuthDataRequestValidator.cs @@ -1,12 +1,12 @@ using FluentValidation; using VisaApiClient; -namespace BlazorWebAssemblyVisaApiClient.Validation.Auth +namespace BlazorWebAssemblyVisaApiClient.Validation.Auth; + +public class ChangeUserAuthDataRequestValidator : AbstractValidator { - public class ChangeUserAuthDataRequestValidator : AbstractValidator + public ChangeUserAuthDataRequestValidator() { - public ChangeUserAuthDataRequestValidator() - { RuleFor(r => r.NewAuthData) .NotEmpty(); @@ -17,5 +17,4 @@ namespace BlazorWebAssemblyVisaApiClient.Validation.Auth .MaximumLength(ConfigurationConstraints.EmailLength) .WithMessage($"Email address length must be less than {ConfigurationConstraints.EmailLength}"); } - } -} +} \ No newline at end of file diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/VisaApplications/Models/VisaApplicationCreateRequestModel.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/VisaApplications/Models/VisaApplicationCreateRequestModel.cs index a15a453..8526dbd 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/VisaApplications/Models/VisaApplicationCreateRequestModel.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Validation/VisaApplications/Models/VisaApplicationCreateRequestModel.cs @@ -1,38 +1,37 @@ using System.ComponentModel.DataAnnotations; 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] - public ReentryPermitModel? ReentryPermit { get; set; } = default!; + [ValidateComplexType] + public ReentryPermitModel? ReentryPermit { get; set; } = default!; - [Required] - [MaxLength(ConfigurationConstraints.CountryNameLength)] - public string DestinationCountry { get; set; } = default!; + [Required] + [MaxLength(ConfigurationConstraints.CountryNameLength)] + public string DestinationCountry { get; set; } = default!; - [Required] - public VisaCategory VisaCategory { get; set; } + [Required] + public VisaCategory VisaCategory { get; set; } - [Required] - public bool IsForGroup { get; set; } + [Required] + public bool IsForGroup { get; set; } - [Required] - public RequestedNumberOfEntries RequestedNumberOfEntries { get; set; } + [Required] + public RequestedNumberOfEntries RequestedNumberOfEntries { get; set; } - [Required] - [Range(0, ConfigurationConstraints.MaxValidDays)] - public int ValidDaysRequested { get; set; } + [Required] + [Range(0, ConfigurationConstraints.MaxValidDays)] + public int ValidDaysRequested { get; set; } - [ValidateComplexType] - public List PastVisas { get; set; } = []; + [ValidateComplexType] + public List PastVisas { get; set; } = []; - [ValidateComplexType] - public PermissionToDestCountryModel? PermissionToDestCountry { get; set; } = default!; + [ValidateComplexType] + public PermissionToDestCountryModel? PermissionToDestCountry { get; set; } = default!; - [ValidateComplexType] - public List PastVisits { get; set; } = []; - } -} + [ValidateComplexType] + public List PastVisits { get; set; } = []; +} \ No newline at end of file diff --git a/SchengenVisaApi/SchengenVisaApi.sln b/SchengenVisaApi/SchengenVisaApi.sln index 522c115..b1149c9 100644 --- a/SchengenVisaApi/SchengenVisaApi.sln +++ b/SchengenVisaApi/SchengenVisaApi.sln @@ -12,6 +12,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisaApiClient", "VisaApiCli EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorWebAssemblyVisaApiClient", "BlazorWebAssemblyVisaApiClient\BlazorWebAssemblyVisaApiClient.csproj", "{3DB49E3C-AAC3-452C-AEBE-F4F72D6134F8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisaApiTests", "VisaApiTests\VisaApiTests.csproj", "{4CB6FBC4-99CB-453D-8499-A498FAE9683D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution 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}.Release|Any CPU.ActiveCfg = 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 EndGlobal diff --git a/SchengenVisaApi/VisaApiClient/ClientBase.cs b/SchengenVisaApi/VisaApiClient/ClientBase.cs index b4150b8..94e19a4 100644 --- a/SchengenVisaApi/VisaApiClient/ClientBase.cs +++ b/SchengenVisaApi/VisaApiClient/ClientBase.cs @@ -1,13 +1,13 @@ using System.Text; -namespace VisaApiClient -{ - public class ClientBase - { - public AuthToken? AuthToken { get; set; } +namespace VisaApiClient; - protected Task CreateHttpRequestMessageAsync(CancellationToken cancellationToken) - { +public class ClientBase +{ + public AuthToken? AuthToken { get; set; } + + protected Task CreateHttpRequestMessageAsync(CancellationToken cancellationToken) + { var msg = new HttpRequestMessage(); msg.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", AuthToken?.Token); @@ -16,15 +16,14 @@ namespace VisaApiClient } - protected async Task PrepareRequestAsync(HttpClient client, HttpRequestMessage request, string url, CancellationToken cancellationToken) - => await Task.CompletedTask; + protected async Task PrepareRequestAsync(HttpClient client, HttpRequestMessage request, string url, CancellationToken cancellationToken) + => await Task.CompletedTask; - protected async Task PrepareRequestAsync(HttpClient client, HttpRequestMessage request, StringBuilder urlBuilder, CancellationToken cancellationToken) - { + protected async Task PrepareRequestAsync(HttpClient client, HttpRequestMessage request, StringBuilder urlBuilder, CancellationToken cancellationToken) + { await Task.CompletedTask; } - protected async Task ProcessResponseAsync(HttpClient client, HttpResponseMessage response, CancellationToken cancellationToken) - => await Task.CompletedTask; - } -} + protected async Task ProcessResponseAsync(HttpClient client, HttpResponseMessage response, CancellationToken cancellationToken) + => await Task.CompletedTask; +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Collections.cs b/SchengenVisaApi/VisaApiTests/Collections.cs new file mode 100644 index 0000000..ffa17ec --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Collections.cs @@ -0,0 +1,6 @@ +namespace VisaApi; + +public static class Collections +{ + public const string ContextUsingTestCollection = "ContextUsingTestCollection"; +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Fakers/Applicants/ApplicantFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/Applicants/ApplicantFaker.cs new file mode 100644 index 0000000..55c45da --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/Applicants/ApplicantFaker.cs @@ -0,0 +1,65 @@ +using ApplicationLayer.InfrastructureServicesInterfaces; +using Bogus; +using Domains; +using Domains.ApplicantDomain; + +namespace VisaApi.Fakers.Applicants; + +/// +/// Generates applicants +/// +public sealed class ApplicantFaker : Faker +{ + public ApplicantFaker(IDateTimeProvider dateTimeProvider) + { + RuleFor(a => a.Citizenship, f => f.Address.Country()); + + RuleFor(a => a.Gender, f => f.Random.Enum()); + + 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()); + + 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() + }); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Fakers/Applicants/Requests/NameModelFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/Applicants/Requests/NameModelFaker.cs new file mode 100644 index 0000000..bc2209c --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/Applicants/Requests/NameModelFaker.cs @@ -0,0 +1,16 @@ +using ApplicationLayer.Services.Applicants.Models; +using Bogus; + +namespace VisaApi.Fakers.Applicants.Requests; + +public sealed class NameModelFaker : Faker +{ + 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()); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Fakers/Applicants/Requests/PassportModelFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/Applicants/Requests/PassportModelFaker.cs new file mode 100644 index 0000000..0917c60 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/Applicants/Requests/PassportModelFaker.cs @@ -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 +{ + 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())); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Fakers/Applicants/Requests/PlaceOfWorkModelFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/Applicants/Requests/PlaceOfWorkModelFaker.cs new file mode 100644 index 0000000..1b42aab --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/Applicants/Requests/PlaceOfWorkModelFaker.cs @@ -0,0 +1,23 @@ +using ApplicationLayer.Services.Applicants.Models; +using Bogus; + +namespace VisaApi.Fakers.Applicants.Requests; + +public sealed class PlaceOfWorkModelFaker : Faker +{ + 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() + }); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Fakers/Auth/AuthDataFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/Auth/AuthDataFaker.cs new file mode 100644 index 0000000..3833b0e --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/Auth/AuthDataFaker.cs @@ -0,0 +1,14 @@ +using ApplicationLayer.Services.AuthServices.Common; +using Bogus; + +namespace VisaApi.Fakers.Auth; + +public sealed class AuthDataFaker : Faker +{ + public AuthDataFaker() + { + RuleFor(a => a.Email, f => f.Internet.Email()); + + RuleFor(a => a.Password, f => f.Internet.Password()); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Fakers/Auth/RegisterRequestFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/Auth/RegisterRequestFaker.cs new file mode 100644 index 0000000..eb57366 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/Auth/RegisterRequestFaker.cs @@ -0,0 +1,14 @@ +using ApplicationLayer.Services.AuthServices.Requests; +using Bogus; + +namespace VisaApi.Fakers.Auth; + +public sealed class RegisterRequestFaker : Faker +{ + private static AuthDataFaker authDataFaker = new(); + + public RegisterRequestFaker() + { + RuleFor(r => r.AuthData, () => authDataFaker.Generate()); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Fakers/Common/ChangeAuthDataFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/Common/ChangeAuthDataFaker.cs new file mode 100644 index 0000000..d51b35f --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/Common/ChangeAuthDataFaker.cs @@ -0,0 +1,14 @@ +using ApplicationLayer.Services.Users.Models; +using Bogus; + +namespace VisaApi.Fakers.Common; + +public sealed class ChangeAuthDataFaker : Faker +{ + public ChangeAuthDataFaker() + { + RuleFor(a => a.Email, f => f.Internet.Email()); + + RuleFor(a => a.Password, f => f.Internet.Password()); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Fakers/Users/Requests/ChangeUserAuthDataRequestFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/Users/Requests/ChangeUserAuthDataRequestFaker.cs new file mode 100644 index 0000000..47b283d --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/Users/Requests/ChangeUserAuthDataRequestFaker.cs @@ -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 +{ + private static ChangeAuthDataFaker changeAuthDataFaker = new(); + + public ChangeUserAuthDataRequestFaker() + { + CustomInstantiator(_ => new(Guid.NewGuid(), changeAuthDataFaker.Generate())); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Fakers/Users/UserFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/Users/UserFaker.cs new file mode 100644 index 0000000..73f485c --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/Users/UserFaker.cs @@ -0,0 +1,17 @@ +using Bogus; +using Domains.Users; + +namespace VisaApi.Fakers.Users; + +/// +/// Generates users +/// +public sealed class UserFaker : Faker +{ + public UserFaker() + { + RuleFor(u => u.Email, f => f.Internet.Email()); + + RuleFor(u => u.Password, f => f.Internet.Password()); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/PastVisaFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/PastVisaFaker.cs new file mode 100644 index 0000000..1ac6c60 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/PastVisaFaker.cs @@ -0,0 +1,28 @@ +using ApplicationLayer.InfrastructureServicesInterfaces; +using Bogus; +using Domains.VisaApplicationDomain; + +namespace VisaApi.Fakers.VisaApplications; + +/// +/// Generates past visas +/// +public sealed class PastVisaFaker : Faker +{ + 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; + } +} diff --git a/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/PastVisitFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/PastVisitFaker.cs new file mode 100644 index 0000000..96183c2 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/PastVisitFaker.cs @@ -0,0 +1,28 @@ +using ApplicationLayer.InfrastructureServicesInterfaces; +using Bogus; +using Domains.VisaApplicationDomain; + +namespace VisaApi.Fakers.VisaApplications; + +/// +/// Generates past visas +/// +public sealed class PastVisitFaker : Faker +{ + 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; + } +} diff --git a/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/PermissionToDestCountryFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/PermissionToDestCountryFaker.cs new file mode 100644 index 0000000..8ca6846 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/PermissionToDestCountryFaker.cs @@ -0,0 +1,19 @@ +using ApplicationLayer.InfrastructureServicesInterfaces; +using Bogus; +using Domains.VisaApplicationDomain; + +namespace VisaApi.Fakers.VisaApplications; + +/// +/// Generates permissions to destination Country +/// +public sealed class PermissionToDestCountryFaker : Faker +{ + public PermissionToDestCountryFaker(IDateTimeProvider dateTimeProvider) + { + RuleFor(p => p.Issuer, f => f.Company.CompanyName()); + + RuleFor(p => p.ExpirationDate, + f => f.Date.Future(4, dateTimeProvider.Now())); + } +} diff --git a/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/ReentryPermitFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/ReentryPermitFaker.cs new file mode 100644 index 0000000..ffd7fec --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/ReentryPermitFaker.cs @@ -0,0 +1,21 @@ +using ApplicationLayer.InfrastructureServicesInterfaces; +using Bogus; +using Domains; +using Domains.VisaApplicationDomain; + +namespace VisaApi.Fakers.VisaApplications; + +/// +/// Generates re-entry permissions +/// +public sealed class ReentryPermitFaker : Faker +{ + 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())); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/Requests/PastVisaModelFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/Requests/PastVisaModelFaker.cs new file mode 100644 index 0000000..1b9ff0d --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/Requests/PastVisaModelFaker.cs @@ -0,0 +1,28 @@ +using ApplicationLayer.InfrastructureServicesInterfaces; +using ApplicationLayer.Services.VisaApplications.Models; +using Bogus; + +namespace VisaApi.Fakers.VisaApplications.Requests; + +/// +/// Generates past visas +/// +public sealed class PastVisaModelFaker : Faker +{ + 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; + } +} diff --git a/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/Requests/PastVisitModelFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/Requests/PastVisitModelFaker.cs new file mode 100644 index 0000000..e975d08 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/Requests/PastVisitModelFaker.cs @@ -0,0 +1,28 @@ +using ApplicationLayer.InfrastructureServicesInterfaces; +using ApplicationLayer.Services.VisaApplications.Models; +using Bogus; + +namespace VisaApi.Fakers.VisaApplications.Requests; + +/// +/// Generates past visas +/// +public sealed class PastVisitModelFaker : Faker +{ + 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; + } +} diff --git a/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/Requests/PermissionToDestCountryModelFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/Requests/PermissionToDestCountryModelFaker.cs new file mode 100644 index 0000000..719bb49 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/Requests/PermissionToDestCountryModelFaker.cs @@ -0,0 +1,17 @@ +using ApplicationLayer.InfrastructureServicesInterfaces; +using ApplicationLayer.Services.VisaApplications.Models; +using Bogus; + +namespace VisaApi.Fakers.VisaApplications.Requests +{ + public sealed class PermissionToDestCountryModelFaker : Faker + { + public PermissionToDestCountryModelFaker(IDateTimeProvider dateTimeProvider) + { + RuleFor(p => p.Issuer, f => f.Company.CompanyName()); + + RuleFor(p => p.ExpirationDate, + f => f.Date.Future(4, dateTimeProvider.Now())); + } + } +} diff --git a/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/Requests/ReentryPermitModelFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/Requests/ReentryPermitModelFaker.cs new file mode 100644 index 0000000..4efcd47 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/Requests/ReentryPermitModelFaker.cs @@ -0,0 +1,21 @@ +using ApplicationLayer.InfrastructureServicesInterfaces; +using ApplicationLayer.Services.VisaApplications.Models; +using Bogus; +using Domains; + +namespace VisaApi.Fakers.VisaApplications.Requests; + +/// +/// Generates re-entry permissions +/// +public sealed class ReentryPermitModelFaker : Faker +{ + 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())); + } +} diff --git a/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/VisaApplicationFaker.cs b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/VisaApplicationFaker.cs new file mode 100644 index 0000000..ab931b1 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Fakers/VisaApplications/VisaApplicationFaker.cs @@ -0,0 +1,64 @@ +using ApplicationLayer.InfrastructureServicesInterfaces; +using Bogus; +using Domains; +using Domains.ApplicantDomain; +using Domains.VisaApplicationDomain; + +namespace VisaApi.Fakers.VisaApplications; + +/// +/// Generates visa applications +/// +public sealed class VisaApplicationFaker : Faker +{ + 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()); + + 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()); + + RuleFor(va => va.ForGroup, f => f.Random.Bool()); + + RuleFor(va => va.RequestedNumberOfEntries, + f => f.Random.Enum()); + + 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; + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Services/TestDateTimeProvider.cs b/SchengenVisaApi/VisaApiTests/Services/TestDateTimeProvider.cs new file mode 100644 index 0000000..d8eefce --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Services/TestDateTimeProvider.cs @@ -0,0 +1,8 @@ +using ApplicationLayer.InfrastructureServicesInterfaces; + +namespace VisaApi.Services; + +public class TestDateTimeProvider : IDateTimeProvider +{ + public DateTime Now() => DateTime.Now; +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Applicants/NameModelValidatorTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Applicants/NameModelValidatorTests.cs new file mode 100644 index 0000000..f7c72fb --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Applicants/NameModelValidatorTests.cs @@ -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 validator = new NameModelValidator(); + private readonly static NameModelFaker faker = new(); + + /// + /// Test for validator that should throw for empty first name + /// + [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); + } + + /// + /// Test for validator that should throw for empty surname + /// + [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); + } + + /// + /// Test for validator that should return no errors for empty patronymic + /// + [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(); + } + + /// + /// Test for validator that should return error for too long first name + /// + [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); + } + + /// + /// Test for validator that should return error for too long surname + /// + [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); + } + + /// + /// Test for validator that should return error for too long patronymic + /// + [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); + } + + /// + /// Test for validator that should return error for not valid firstname + /// + [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); + } + + /// + /// Test for validator that should return error for not valid surname + /// + [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); + } + + /// + /// Test for validator that should return error for not valid patronymic + /// + [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); + } + + /// + /// Test for validator that should return no errors for valid name + /// + [Fact] + private async Task ValidateForValidNameShouldReturnNoErrors() + { + var name = faker.Generate(); + + var result = await validator.ValidateAsync(name); + + result.Errors.Should().BeEmpty(); + } +} diff --git a/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Applicants/PassportModelValidatorTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Applicants/PassportModelValidatorTests.cs new file mode 100644 index 0000000..f1179d4 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Applicants/PassportModelValidatorTests.cs @@ -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 validator = new PassportModelValidator(dateTimeProvider); + private readonly static PassportModelFaker faker = new(dateTimeProvider); + + /// + /// Test for validator that should return error for empty number + /// + [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); + } + + /// + /// Test for validator that should return error for too long number + /// + [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); + } + + /// + /// Test for validator that should return error for not valid number + /// + [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); + } + + /// + /// Test for validator that should return error for empty issuer + /// + [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); + } + + /// + /// Test for validator that should return error for too long issuer + /// + [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); + } + + /// + /// Test for validator that should return error for not valid issuer + /// + [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); + } + + /// + /// Test for validator that should return error for expired passport + /// + [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); + } + + /// + /// Test for validator that should return error for passport from future + /// + [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); + } + + /// + /// Test for validator that should return error for passport that expired before issue + /// + [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); + } + + /// + /// Test for validator that should return no errors for valid passport + /// + [Fact] + private async Task ValidateForValidPassportShouldReturnNoErrors() + { + var model = faker.Generate(); + + var result = await validator.ValidateAsync(model); + + result.Errors.Should().BeEmpty(); + } +} diff --git a/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Applicants/PlaceOfWorkModelValidatorTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Applicants/PlaceOfWorkModelValidatorTests.cs new file mode 100644 index 0000000..aebd5fd --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Applicants/PlaceOfWorkModelValidatorTests.cs @@ -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 validator = new PlaceOfWorkModelValidator(); + private readonly static PlaceOfWorkModelFaker faker = new(); + + /// + /// Test for validator that should return error for empty phone num + /// + [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(); + } + + /// + /// Test for validator that should return error for long phone num + /// + [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(); + } + + /// + /// Test for validator that should return error for short phone num + /// + [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); + } + + /// + /// Test for validator that should return error for not valid phone num + /// + [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); + } + + /// + /// Test for validator that should throw exception for null address + /// + [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(); + } + + /// + /// Test for validator that should return error for empty Country + /// + [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); + } + + /// + /// Test for validator that should return error for too long Country + /// + [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); + } + + /// + /// Test for validator that should return error for not valid Country + /// + [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); + } + + /// + /// Test for validator that should return error for empty City + /// + [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); + } + + /// + /// Test for validator that should return error for too long city + /// + [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); + } + + /// + /// Test for validator that should return error for not valid city + /// + [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); + } + + /// + /// Test for validator that should return error for empty street + /// + [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); + } + + /// + /// Test for validator that should return error for too long street + /// + [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); + } + + /// + /// Test for validator that should return error for not valid street + /// + [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); + } + + /// + /// Test for validator that should return error for empty building /// + [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); + } + + /// + /// Test for validator that should return error for too long building + /// + [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); + } + + /// + /// Test for validator that should return error for not valid building + /// + [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); + } + + /// + /// Test for validator that should return error for empty name + /// + [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); + } + + /// + /// Test for validator that should return error for too long name + /// + [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); + } + + /// + /// Test for validator that should return error for not valid name + /// + [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); + } + + /// + /// Test for validator that should return no errors for valid model + /// + [Fact] + private async Task ValidateForValidShouldReturnNoErrors() + { + var model = faker.Generate(); + + var result = await validator.ValidateAsync(model); + + result.Errors.Should().BeEmpty(); + } +} diff --git a/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Auth/AuthDataValidatorTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Auth/AuthDataValidatorTests.cs new file mode 100644 index 0000000..d0c0725 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Auth/AuthDataValidatorTests.cs @@ -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 validator = new AuthDataValidator(); + private readonly static AuthDataFaker faker = new(); + + /// + /// Test for validator that should return validation error for invalid email + /// + [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)); + } + + /// + /// Test for validator that should return validation error for too long email + /// + [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)); + } + + /// + /// Test for validator that should return no errors for valid email + /// + [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(); + } + + /// + /// Test for validator that should return validation error for empty password + /// + [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)); + } + + /// + /// Test for validator that should return validation error for too long password + /// + [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)); + } + + /// + /// Test for validator that should return no errors for valid password + /// + [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(); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Auth/RegisterRequestValidatorTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Auth/RegisterRequestValidatorTests.cs new file mode 100644 index 0000000..5165e3c --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Auth/RegisterRequestValidatorTests.cs @@ -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 authDataValidator = new AuthDataValidator(); + private readonly static RegisterRequestFaker requestFaker = new(); + private readonly static UserFaker userFaker = new(); + + /// + /// Creates validator from context + /// + /// db context + /// RegisterRequest validator + private static IValidator GetValidator(DbContext context) + { + var repository = new UsersRepository(context, context); + return new RegisterRequestValidator(repository, authDataValidator); + } + + /// + /// Test for validator that should throw for empty auth data + /// + [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(); + } + + /// + /// Test for validator that should return error for used email + /// + [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); + } + + /// + /// Test for validator that should return o errors for valid requests + /// + [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(); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Users/ChangeUserAuthDataRequestValidationTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Users/ChangeUserAuthDataRequestValidationTests.cs new file mode 100644 index 0000000..0e1211b --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/Users/ChangeUserAuthDataRequestValidationTests.cs @@ -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 validator = new ChangeUserAuthDataRequestValidator(); + private readonly static ChangeUserAuthDataRequestFaker faker = new(); + + /// + /// Test for validator that should throw exception for empty auth data + /// + [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(); + } + + /// + /// Test for validator that should no errors for valid entity + /// + [Fact] + private async Task ValidateForValidShouldReturnNoErrors() + { + var request = faker.Generate(); + var result = await validator.ValidateAsync(request); + + result.IsValid.Should().BeTrue(); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/VisaApplications/PastVisaModelValidatorTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/VisaApplications/PastVisaModelValidatorTests.cs new file mode 100644 index 0000000..a40a3f2 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/VisaApplications/PastVisaModelValidatorTests.cs @@ -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 validator = new PastVisaModelValidator(dateTimeProvider); + private readonly static PastVisaModelFaker faker = new(dateTimeProvider); + + /// + /// Test for validator that should return error for empty expiration date + /// + [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 + } + + /// + /// Test for validator that should return error for expiration date less than issue date + /// + [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); + } + + /// + /// Test for validator that should return error for empty issue date + /// + [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); + } + + /// + /// Test for validator that should return error for issue date greater than current date + /// + [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); + } + + /// + /// Test for validator that should return error for empty name + /// + [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); + } + + /// + /// Test for validator that should return error for too long name + /// + [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); + } + + /// + /// Test for validator that should return error for not valid name + /// + [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); + } + + /// + /// Test for validator that should return no errors for valid model + /// + [Fact] + private async Task ValidateForValidShouldReturnNoErrors() + { + var model = faker.GenerateValid(); + + var result = await validator.ValidateAsync(model); + + result.Errors.Should().BeEmpty(); + } + } +} diff --git a/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/VisaApplications/PastVisitModelValidatorTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/VisaApplications/PastVisitModelValidatorTests.cs new file mode 100644 index 0000000..d04cbf1 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/VisaApplications/PastVisitModelValidatorTests.cs @@ -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 validator = new PastVisitModelValidator(dateTimeProvider); + private readonly static PastVisitModelFaker faker = new(dateTimeProvider); + + /// + /// Test for validator that should return error for empty start date + /// + [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); + } + + /// + /// Test for validator that should return error for start date greater than end date + /// + [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); + } + + /// + /// Test for validator that should return error for empty end date + /// + [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 + } + + /// + /// Test for validator that should return error for start date greater than current date + /// + [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); + } + + /// + /// Test for validator that should return error for empty destination Country + /// + [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); + } + + /// + /// Test for validator that should return error for too long destination Country + /// + [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); + } + + /// + /// Test for validator that should return error for not valid destination Country + /// + [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); + } + + /// + /// Test for validator that should return no errors for valid model + /// + [Fact] + private async Task ValidateForValidShouldReturnNoErrors() + { + var model = faker.GenerateValid(); + + var result = await validator.ValidateAsync(model); + + result.Errors.Should().BeEmpty(); + } + } +} diff --git a/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/VisaApplications/PermissionToDestCountryModelValidatorTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/VisaApplications/PermissionToDestCountryModelValidatorTests.cs new file mode 100644 index 0000000..37a55a2 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/VisaApplications/PermissionToDestCountryModelValidatorTests.cs @@ -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 validator = new PermissionToDestCountryModelValidator(dateTimeProvider); + private readonly static PermissionToDestCountryModelFaker faker = new(dateTimeProvider); + + /// + /// Test for validator that should return error for empty expiration date + /// + [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 + } + + /// + /// Test for validator that should return error for expiration date less than current date + /// + [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); + } + + /// + /// Test for validator that should return error for empty issuer + /// + [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); + } + + /// + /// Test for validator that should return error for not valid issuer + /// + [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); + } + + /// + /// Test for validator that should return error for too long issuer + /// + [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); + } + + /// + /// Test for validator that should return no errors for valid model + /// + [Fact] + private async Task ValidateForValidShouldReturnNoErrors() + { + var model = faker.Generate(); + + var result = await validator.ValidateAsync(model); + + result.Errors.Should().BeEmpty(); + } + } +} diff --git a/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/VisaApplications/ReentryPermitModelValidatorTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/VisaApplications/ReentryPermitModelValidatorTests.cs new file mode 100644 index 0000000..b4613cb --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Application/Validation/VisaApplications/ReentryPermitModelValidatorTests.cs @@ -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 validator = new ReentryPermitModelValidator(dateTimeProvider); + private readonly static ReentryPermitModelFaker faker = new(dateTimeProvider); + + /// + /// Test for validator that should return error for empty expiration date + /// + [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 + } + + /// + /// Test for validator that should return error for expiration date less than current date + /// + [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); + } + + /// + /// Test for validator that should return error for empty number + /// + [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); + } + + /// + /// Test for validator that should return error for not valid number + /// + [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); + } + + /// + /// Test for validator that should return error for too long number + /// + [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); + } + + /// + /// Test for validator that should return no errors for valid model + /// + [Fact] + private async Task ValidateForValidShouldReturnNoErrors() + { + var model = faker.Generate(); + + var result = await validator.ValidateAsync(model); + + result.Errors.Should().BeEmpty(); + } + } +} diff --git a/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/InMemoryContextProvider.cs b/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/InMemoryContextProvider.cs new file mode 100644 index 0000000..72953a3 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/InMemoryContextProvider.cs @@ -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 opts = new DbContextOptionsBuilder() + .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; + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/ApplicantsRepositoryTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/ApplicantsRepositoryTests.cs new file mode 100644 index 0000000..64035c0 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/ApplicantsRepositoryTests.cs @@ -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()); + + /// Returns + /// Database context + /// Repository + private static IApplicantsRepository GetRepository(DbContext context) + => new ApplicantsRepository(context, context); + + /// Returns + private static IDateTimeProvider GetDateTimeProvider() => new TestDateTimeProvider(); + + /// + /// Test for method that should throw exception for not existing entity + /// + [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(); + } + + /// + /// Test for method that should return existing entity + /// + [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); + } + + /// + /// Test for method that should throw exception for not existing entity + /// + [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(); + } + + /// + /// Test for method that should return existing entity's identifier + /// + [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); + } + + /// + /// Test for method that should throw exception for not existing entity + /// + [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(); + } + + /// + /// Test for method that should return existing entity's IsNonResident property + /// + [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); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/Generic/GenericRepositoryTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/Generic/GenericRepositoryTests.cs new file mode 100644 index 0000000..035bfc7 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/Generic/GenericRepositoryTests.cs @@ -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 +{ + /// Returns + /// Database context + /// Repository + private static GenericRepository GetRepository(DbContext context) => new TestGenericRepository(context, context); + + /// Test for method that should return empty collection if nothing added + [Fact] + public void GetAllForEmptyShouldReturnEmpty() + { + using var context = InMemoryContextProvider.GetDbContext(); + var repository = GetRepository(context); + + var result = repository.GetAllAsync(CancellationToken.None).Result; + + result.Should().BeEmpty(); + } + + /// Test for method that should return collection with added entities + [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); + } + + /// Test for method that should return existing entity + [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); + } + + /// Test for method that should throw exception for not found entity + [Fact] + public void GetByIdForNotExistingShouldThrow() + { + using var context = InMemoryContextProvider.GetDbContext(); + var repository = GetRepository(context); + + context.SaveChanges(); + + EntityNotFoundByIdException? result = null; + try + { + repository.GetByIdAsync(Guid.NewGuid(), CancellationToken.None).Wait(); + } + catch (AggregateException e) + { + result = e.InnerException as EntityNotFoundByIdException; + } + + result.Should().NotBeNull(); + } +} diff --git a/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/Generic/TestGenericRepository.cs b/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/Generic/TestGenericRepository.cs new file mode 100644 index 0000000..06cfe24 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/Generic/TestGenericRepository.cs @@ -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(reader, writer); \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/UsersRepositoryTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/UsersRepositoryTests.cs new file mode 100644 index 0000000..f41c8cd --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/UsersRepositoryTests.cs @@ -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(); + + /// Returns + /// Database context + /// Repository + private static IUsersRepository GetRepository(DbContext context) + => new UsersRepository(context, context); + + /// + /// Test for method that should return null for not existing email + /// + [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(); + } + + /// + /// Test for method that should return entity for existing email + /// + [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); + } + + /// + /// Test for method that should return empty from empty db + /// + [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(); + } + + /// + /// Test for method that should return entities from not empty db + /// + [Fact] + private async Task GetAllOfRoleForNotEmptyShouldReturnEntities() + { + await using var context = InMemoryContextProvider.GetDbContext(); + var repository = GetRepository(context); + var users = new List(); + 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); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/VisaApplicationsRepositoryTests.cs b/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/VisaApplicationsRepositoryTests.cs new file mode 100644 index 0000000..0abe709 --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/Tests/Infrastructure/Database/Repositories/VisaApplicationsRepositoryTests.cs @@ -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()); + + /// Returns + /// Database context + /// Repository + private static IVisaApplicationsRepository GetRepository(DbContext context) + => new VisaApplicationsRepository(context, context); + + /// Returns + private static IDateTimeProvider GetDateTimeProvider() => new TestDateTimeProvider(); + + /// + /// Test for method that should return empty if no applications added + /// + [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(); + } + + /// + /// Test for method that should return added entities + /// + [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(); + 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); + } + + /// + /// Test for method that should throw exception for not existing entities + /// + [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(); + } + + /// + /// Test for method that should throw exception for not existing applicant + /// + [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(); + } + + /// + /// Test for method that should throw exception for not existing application + /// + [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(); + } + + /// + /// Test for method + /// that should throw exception for not accessible application + /// + [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(); + } + + /// + /// Test for method + /// that should return application for valid identifiers + /// + [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); + } + + /// + /// Test for method that should return empty from empty db + /// + [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(); + } + + /// + /// Test for method that should return pending applications from not empty db + /// + [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); + } +} \ No newline at end of file diff --git a/SchengenVisaApi/VisaApiTests/VisaApiTests.csproj b/SchengenVisaApi/VisaApiTests/VisaApiTests.csproj new file mode 100644 index 0000000..c58d98d --- /dev/null +++ b/SchengenVisaApi/VisaApiTests/VisaApiTests.csproj @@ -0,0 +1,33 @@ + + + + net8.0 + enable + enable + + false + true + VisaApi + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + +