Files
EnvelopeGenerator/EnvelopeGenerator.Tests/Infrastructure/MailTemplatePlaceholderDbTests.cs
TekH d913b949e0 Add DTOs and tests for placeholder replacement
Added new record types `CreateEnvelopeDocumentDto`, `CreateEnvelopeFieldDto`, and `CreateEnvelopeReceiverDto` with XML documentation in `CreateEnvelopeCommand.cs`. Marked `ConfigService` as obsolete in `CreateEnvelopeCommandHandler.cs`. Modified `CertificationType` usage in `Fake.cs` to use the enum directly. Updated `EnvelopeGenerator.Tests.csproj` with a new project reference and compile directives. Introduced `MailTemplatePlaceholderDbTests` and `ReplacePlaceholderExtensionsTests` to verify placeholder replacement functionality. Added supporting model classes for testing. These changes improve code functionality and test coverage.
2026-10-29 09:53:52 +01:00

146 lines
5.5 KiB
C#

using System.Text.Json;
using System.Text.RegularExpressions;
using EnvelopeGenerator.Infrastructure.Notification;
using Microsoft.Data.SqlClient;
namespace EnvelopeGenerator.Tests.Infrastructure;
[TestFixture]
public class MailTemplatePlaceholderDbTests
{
private static readonly Regex PlaceholderPattern = new(@"\[[A-Z0-9_]+\]", RegexOptions.Compiled);
private static IEnumerable<TestCaseData> DbTemplateCases()
{
foreach (var template in ReadTemplates())
{
yield return new TestCaseData(template)
{
TestName = $"Template_{template.Name}_{template.LangCode}_{template.Guid}"
};
}
}
[Test]
public void Database_contains_templates()
{
Assert.That(ReadTemplates(), Is.Not.Empty);
}
[TestCaseSource(nameof(DbTemplateCases))]
public void Every_placeholder_in_every_db_template_is_replaced(TemplateRow template)
{
var data = new TemplatePlaceholders();
var subject = template.Subject.ReplacePlaceholders(data);
var body = template.Body.ReplacePlaceholders(data);
var originalTokens = PlaceholderPattern
.Matches(template.Subject + template.Body)
.Select(m => m.Value)
.Distinct()
.ToArray();
Assert.Multiple(() =>
{
Assert.That(Unresolved(subject), Is.Empty,
$"'{template.Name}' subject has unresolved placeholders.");
Assert.That(Unresolved(body), Is.Empty,
$"'{template.Name}' body has unresolved placeholders.");
foreach (var token in originalTokens)
{
Assert.That(subject + body, Does.Contain(Expected(token)),
$"'{template.Name}' placeholder {token} was not replaced with its value.");
}
});
}
private static string[] Unresolved(string text)
=> PlaceholderPattern.Matches(text)
.Select(m => m.Value)
.Distinct()
.ToArray();
private static string Expected(string token) => $"__{token.Trim('[', ']')}__";
private static List<TemplateRow> ReadTemplates()
{
const string sql = "SELECT GUID, NAME, SUBJECT, BODY, LANG_CODE FROM TBSIG_EMAIL_TEMPLATE";
using var connection = new SqlConnection(ReadConnectionString());
connection.Open();
using var command = new SqlCommand(sql, connection);
using var reader = command.ExecuteReader();
var templates = new List<TemplateRow>();
while (reader.Read())
{
templates.Add(new TemplateRow(
reader.GetInt32(0),
reader.GetString(1),
reader.IsDBNull(2) ? string.Empty : reader.GetString(2),
reader.IsDBNull(3) ? string.Empty : reader.GetString(3),
reader.IsDBNull(4) ? string.Empty : reader.GetString(4)));
}
return templates;
}
private static string ReadConnectionString()
{
var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "EnvelopeGenerator.sln")))
directory = directory.Parent;
if (directory is null)
throw new InvalidOperationException("Solution root (EnvelopeGenerator.sln) was not found.");
var settingsPath = Path.Combine(
directory.FullName,
"EnvelopeGenerator.Server",
"EnvelopeGenerator.Server",
"appsettings.json");
using var stream = File.OpenRead(settingsPath);
using var document = JsonDocument.Parse(stream, new JsonDocumentOptions
{
CommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true
});
return document.RootElement
.GetProperty("ConnectionStrings")
.GetProperty("Default")
.GetString()
?? throw new InvalidOperationException($"ConnectionStrings:Default is missing in '{settingsPath}'.");
}
public sealed record TemplateRow(int Guid, string Name, string Subject, string Body, string LangCode);
/// <summary>
/// One property per placeholder used by any row in TBSIG_EMAIL_TEMPLATE.
/// The sentinel value equals the placeholder name, so a wrong mapping is detected.
/// </summary>
private sealed class TemplatePlaceholders
{
public string DocumentProcess { get; set; } = "__DOCUMENT_PROCESS__";
public string DocumentTitle { get; set; } = "__DOCUMENT_TITLE__";
public string LinkToDocumentText { get; set; } = "__LINK_TO_DOCUMENT_TEXT__";
public string LinkToDocument { get; set; } = "__LINK_TO_DOCUMENT__";
public string Message { get; set; } = "__MESSAGE__";
public string NamePortal { get; set; } = "__NAME_PORTAL__";
public string NameReceiver { get; set; } = "__NAME_RECEIVER__";
public string NameSender { get; set; } = "__NAME_SENDER__";
public string SignatureType { get; set; } = "__SIGNATURE_TYPE__";
public string Reason { get; set; } = "__REASON__";
public string ReceiverAction { get; set; } = "__RECEIVER_ACTION__";
public string FinalAction { get; set; } = "__FINAL_ACTION__";
public string FinalStatus { get; set; } = "__FINAL_STATUS__";
public string DocumentAccessCode { get; set; } = "__DOCUMENT_ACCESS_CODE__";
public string TfaExpiration { get; set; } = "__TFA_EXPIRATION__";
public string TfaQrCode { get; set; } = "__TFA_QR_CODE__";
}
}