Files
EnvelopeGenerator/EnvelopeGenerator.Tests/Fake.cs
TekH 9cfdd16970 Refactor test DB config to support SQL Server and fix seeding
Refactored Fake.cs to configure both in-memory and SQL Server
database contexts for testing, using the "Default" connection
string from configuration. Added detailed EF logging and SQL
executor setup. In TestBase.cs, fixed Setup to use the correct
repository instance for seeding email templates.
2026-01-19 15:51:09 +01:00

311 lines
11 KiB
C#

using Bogus;
using CommandDotNet;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.UserManager.Domain.Entities;
using EnvelopeGenerator.Application;
using EnvelopeGenerator.Application.Common.Configurations;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.EnvelopeReceivers.Commands;
using EnvelopeGenerator.Application.Envelopes.Commands;
using EnvelopeGenerator.Application.Histories.Commands;
using EnvelopeGenerator.Application.Receivers.Commands;
using EnvelopeGenerator.Application.Users.Commands;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
namespace EnvelopeGenerator.Tests;
public class Fake
{
public static readonly Faker Provider = new("de");
public static Host CreateHost(Action<IServiceCollection>? configureServices = null) => Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((context, config) =>
{
// add appsettings.json
config.SetBasePath(Directory.GetCurrentDirectory());
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
})
.ConfigureServices((context, services) =>
{
IConfiguration configuration = context.Configuration;
// add Application and Infrastructure services
#pragma warning disable CS0618
services.AddEnvelopeGeneratorServices(configuration);
var cnnStrName = "Default";
var connStr = context.Configuration.GetConnectionString(cnnStrName)
?? throw new InvalidOperationException($"Connection string '{cnnStrName}' is missing in the application configuration.");
services.AddEnvelopeGeneratorInfrastructureServices(opt =>
{
opt.AddDbContext(dbCtxOpt => dbCtxOpt.UseInMemoryDatabase("EnvelopeGeneratorTestDb"));
opt.AddDbTriggerParams(context.Configuration);
opt.AddDbContext((provider, options) =>
{
var logger = provider.GetRequiredService<ILogger<EGDbContext>>();
options.UseSqlServer(connStr)
.LogTo(log => logger.LogInformation("{log}", log), Microsoft.Extensions.Logging.LogLevel.Trace)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
});
opt.AddSQLExecutor(executor => executor.ConnectionString = connStr);
});
var prodCnnStr = context.Configuration.GetConnectionString("Default");
services.AddDbContext<EGDbContext2Prod>(opt => opt.UseSqlServer(prodCnnStr));
// add custom services
configureServices?.Invoke(services);
#pragma warning restore CS0618
})
.Build()
.ToFake();
public class Host : IHost
{
#region Root
private readonly IHost _root;
public Host(IHost root)
{
_root = root;
}
public IServiceProvider Services => _root.Services;
public void Dispose()
{
_root.Dispose();
}
public Task StartAsync(CancellationToken cancel = default)
{
return _root.StartAsync(cancel);
}
public Task StopAsync(CancellationToken cancel = default)
{
return _root.StopAsync(cancel);
}
#endregion
#region Shortcuts
public IMediator Mediator => Services.GetRequiredService<IMediator>();
public IRepository<TEntity> GetRepository<TEntity>() => Services.GetRequiredService<IRepository<TEntity>>();
public async Task<Host> AddSamples()
{
await AddSampleReceivers();
await AddSampleUser();
return this;
}
#endregion
#region Sample Receivers
public List<(int Id, string EmailAddress)> _sampleReceivers = new();
public IEnumerable<(int Id, string EmailAddress)> SampleReceivers
=> _sampleReceivers.Any()
? _sampleReceivers
: throw new InvalidOperationException(
"No sample receivers have been initialized. Call AddSampleReceivers() before accessing this property."
);
public async Task<Host> AddSampleReceivers()
{
var mediator = Mediator;
foreach (var cmd in Provider.CreateReceiverCommands())
{
var (receiver, _) = await mediator.Send(cmd);
_sampleReceivers.Add((receiver.Id, cmd.EmailAddress));
}
return this;
}
#endregion
#region Sample User
private User? _user;
public User User => _user ?? throw new InvalidOperationException(
"The 'User' instance has not been initialized. Call AddSampleUser() before accessing this property.");
public async Task<Host> AddSampleUser()
{
var repo = GetRepository<User>();
var cmd = Provider.CreateUserCommand();
_user = await repo.CreateAsync(cmd);
return this;
}
#endregion
}
/// <summary>
/// Represents a mock database context that inherits from <see cref="EGDbContext"/>.
/// This context is intended for testing purposes while connecting to the production database.
/// </summary>
public class EGDbContext2Prod : EGDbContextBase
{
public EGDbContext2Prod(DbContextOptions<EGDbContext2Prod> options, IOptions<DbTriggerParams> triggerParamOptions, ILogger<EGDbContext>? logger = null) : base(options, triggerParamOptions, logger)
{
}
}
}
public static class Extensions
{
public static Fake.Host ToFake(this IHost host) => new(host);
public static T PickEnum<T>(this Faker faker) where T : struct, Enum
{
var values = Enum.GetValues(typeof(T));
var index = faker.Random.Int(0, values.Length - 1);
return (T)values.GetValue(index)!;
}
#region Receiver Command
public static CreateReceiverCommand CreateReceiverCommand(this Faker fake, string? emailAddress = null) => new()
{
EmailAddress = emailAddress ?? fake.Internet.Email(),
};
public static List<CreateReceiverCommand> CreateReceiverCommands(this Faker fake, int minCount = 10, int maxCount = 20)
=> Enumerable.Range(0, fake.Random.Number(minCount, maxCount))
.Select(_ => fake.CreateReceiverCommand())
.ToList();
#endregion
#region Envelope
public static CreateEnvelopeCommand CreateEnvelopeCommand(this Faker fake, int userId) => new()
{
Message = fake.Lorem.Paragraph(fake.Random.Number(2, 5)),
Title = fake.Lorem.Words(fake.Random.Number(3, 4)).Join(" "),
UserId = userId,
UseSQLExecutor = false
};
public static List<CreateEnvelopeCommand> CreateEnvelopeCommands(this Faker fake, params int[] userIDs)
=> Enumerable.Range(0, userIDs.Length)
.Select(fake.CreateEnvelopeCommand)
.ToList();
public static Envelope CreateEnvelope(this Faker faker, int userId, bool tfaEnabled = false) => new()
{
Id = faker.Random.Number(1, 1000),
UserId = userId,
Status = EnvelopeStatus.EnvelopeCreated,
Uuid = Guid.NewGuid().ToString(),
Title = faker.Lorem.Paragraph(faker.Random.Number(1, 2)),
Message = faker.Lorem.Paragraph(faker.Random.Number(2, 5)),
TfaEnabled = tfaEnabled,
AddedWhen = DateTime.UtcNow,
CertificationType = (int)CertificationType.AdvancedElectronicSignature,
UseAccessCode = false,
ContractType = (int)ContractType.Contract,
Language = "de",
SendReminderEmails = false,
Comment = faker.Lorem.Sentence(10),
DocResult = faker.CreatePdfAsByte()
};
#endregion
#region Document
public static byte[] CreatePdfAsByte(this Faker faker)
{
string name = faker.Name.FullName();
string address = faker.Address.FullAddress();
string lorem = faker.Lorem.Paragraphs(2);
QuestPDF.Settings.License = LicenseType.Community;
var document = QuestPDF.Fluent.Document.Create(container =>
{
container.Page(page =>
{
page.Margin(50);
page.Header().Text("Random PDF").FontSize(20).Bold();
page.Content().Column(col =>
{
col.Item().Text($"Vor- und Nachname: {name}");
col.Item().Text($"Adresse: {address}");
col.Item().Text(lorem);
});
});
});
using var ms = new MemoryStream();
document.GeneratePdf(ms);
return ms.ToArray();
}
public static string CreatePdfAsBase64(this Faker faker) => Convert.ToBase64String(faker.CreatePdfAsByte());
public static DocumentCreateCommand CreateDocumentCommand(this Faker faker) => new()
{
DataAsBase64 = faker.CreatePdfAsBase64()
};
public static List<DocumentCreateCommand> CreateDocumentCommands(this Faker fake, int minCount = 10, int maxCount = 20)
=> Enumerable.Range(0, fake.Random.Number(minCount, maxCount))
.Select(_ => fake.CreateDocumentCommand())
.ToList();
#endregion
#region Envelope Receiver
public static EnvelopeReceiver CreateEnvelopeReceiver(this Faker faker, int envelopeId, int receiverId) => new()
{
EnvelopeId = envelopeId,
ReceiverId = receiverId,
Status = ReceiverStatus.Unsigned,
AddedWhen = DateTime.UtcNow,
AccessCode = faker.Random.Number(1000, 9999).ToString(),
ChangedWhen = DateTime.UtcNow,
CompanyName = faker.Company.CompanyName(),
JobTitle = faker.Name.JobTitle(),
Name = faker.Name.FullName(),
};
#endregion
#region History
public static CreateHistoryCommand CreateHistoryCommand(this Faker fake, string key, EnvelopeStatus? status = null)
{
return new()
{
Status = status ?? fake.PickEnum<EnvelopeStatus>(),
Comment = fake.Lorem.Sentence(),
Key = key,
};
}
#endregion
#region User Command
public static CreateUserCommand CreateUserCommand(this Faker fake) => new()
{
Prename = fake.Name.FirstName(),
Name = fake.Name.LastName(),
Username = fake.Internet.UserName(),
Shortname = fake.Random.String2(3, 8),
Email = fake.Internet.Email()
};
public static List<CreateUserCommand> CreateUserCommands(this Faker fake, int minCount = 10, int maxCount = 20)
=> Enumerable.Range(0, fake.Random.Number(minCount, maxCount))
.Select(_ => fake.CreateUserCommand())
.ToList();
#endregion
}