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.
146 lines
5.5 KiB
C#
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__";
|
|
}
|
|
}
|