Introduce Yavsc.Interfaces.ISmtpClient and a recording test fake

Narrow ISmtpClient to the four operations MailSender actually uses,
behind a Yavsc.Interfaces.ISmtpClientFactory. Production wires
MailKitSmtpClient (SmtpClientFactory); tests wire a recording fake
(RecordingSmtpClientFactory). The fake is pre-registered in
WebServerFixture so SMTP calls are short-circuited; the EMailling
test now asserts the Connect -> Authenticate -> Send -> Disconnect
sequence. Yavsc.Abstract stays free of MailKit/MimeKit.
This commit is contained in:
Paul Schneider 2026-06-22 01:44:24 +01:00
commit 2dec799d71
13 changed files with 264 additions and 10 deletions

View file

@ -0,0 +1,25 @@
using System.Threading;
using System.Threading.Tasks;
using MimeKit;
namespace Yavsc.Interfaces
{
/// <summary>
/// Yavsc's own SMTP client surface. Narrower than
/// <c>MailKit.Net.Smtp.ISmtpClient</c>: only the calls that
/// <c>MailSender</c> actually makes. Production wires
/// <c>MailKitSmtpClient</c>; tests wire a recording fake.
/// </summary>
public interface ISmtpClient : IDisposable
{
int Timeout { get; set; }
void Connect(string host, int port, MailKit.Security.SecureSocketOptions options);
void Authenticate(string userName, string password);
Task SendAsync(MimeMessage message, CancellationToken cancellationToken = default);
void Disconnect(bool quit);
}
}

View file

@ -0,0 +1,13 @@
namespace Yavsc.Interfaces
{
/// <summary>
/// Builds fresh <see cref="ISmtpClient"/> instances on demand.
/// Per-call construction is required because <c>MailSender</c>
/// owns its client with a <c>using</c> block: a singleton or
/// scoped client would race across concurrent sends.
/// </summary>
public interface ISmtpClientFactory
{
ISmtpClient CreateClient();
}
}