test(sms): regression tests for code-review fixes
Lock the behaviors changed by the review-fix commit + the security invariants: - ManagementActorTests: UpdateSms/SmtpConfig now require Administrator (updated the existing success cases from Designer); + UpdateSmsConfig_WithDesignerRole_Returns Unauthorized and _WithEmptyAuthToken_PreservesExistingToken regression tests. - SecretEncryptionTests: SmsConfiguration.AuthToken stored-encrypted round-trip + null round-trip (AccountSid stays plaintext) — guards ApplySecretColumnEncryption. - ArtifactDiffTests: CompareSmsConfiguration New/Identical/Modified + the secret presence-only invariant (value never echoed, presence-flip shows <present> only). - UpdateCommandContractTests: notification sms update core fields Required, --auth-token optional. - NotificationListsPageTests: SMS recipient badge shows phone, not "Name <>". - NotificationOutboxActorDispatchTests: SMS-typed notification routes to the SMS adapter (StubAdapter.Type made configurable), not the Email adapter. - NotificationRecipientTests (new): ForEmail/ForSms + public-ctor invariants.
This commit is contained in:
+33
-2
@@ -52,15 +52,17 @@ public class NotificationOutboxActorDispatchTests : TestKit
|
||||
private readonly Func<DeliveryOutcome> _outcome;
|
||||
private readonly TimeSpan _delay;
|
||||
|
||||
public StubAdapter(Func<DeliveryOutcome> outcome, TimeSpan? delay = null)
|
||||
public StubAdapter(Func<DeliveryOutcome> outcome, TimeSpan? delay = null,
|
||||
NotificationType type = NotificationType.Email)
|
||||
{
|
||||
_outcome = outcome;
|
||||
_delay = delay ?? TimeSpan.Zero;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public int CallCount;
|
||||
|
||||
public NotificationType Type => NotificationType.Email;
|
||||
public NotificationType Type { get; }
|
||||
|
||||
public async Task<DeliveryOutcome> DeliverAsync(
|
||||
Notification notification, CancellationToken cancellationToken = default)
|
||||
@@ -151,6 +153,35 @@ public class NotificationOutboxActorDispatchTests : TestKit
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dispatch_SmsTypedNotification_RoutesToSmsAdapter_NotEmailAdapter()
|
||||
{
|
||||
// NotificationOutbox-NNN: the Type-keyed adapter selection must land an SMS-typed
|
||||
// notification on the SMS adapter (not the Email one) and apply its outcome.
|
||||
SetupSmtpRetryPolicy(maxRetries: 5, retryDelay: TimeSpan.FromMinutes(1));
|
||||
var notification = MakeNotification(type: NotificationType.Sms);
|
||||
_outboxRepository.GetDueAsync(Arg.Any<DateTimeOffset>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||
.Returns(new[] { notification });
|
||||
var emailAdapter = new StubAdapter(() => DeliveryOutcome.Success("ops@example.com"),
|
||||
type: NotificationType.Email);
|
||||
var smsAdapter = new StubAdapter(() => DeliveryOutcome.Success("+15551234567"),
|
||||
type: NotificationType.Sms);
|
||||
var actor = CreateActor([emailAdapter, smsAdapter]);
|
||||
|
||||
actor.Tell(InternalMessages.DispatchTick.Instance);
|
||||
|
||||
AwaitAssert(() =>
|
||||
{
|
||||
Assert.Equal(1, smsAdapter.CallCount);
|
||||
Assert.Equal(0, emailAdapter.CallCount);
|
||||
_outboxRepository.Received(1).UpdateAsync(
|
||||
Arg.Is<Notification>(n =>
|
||||
n.Status == NotificationStatus.Delivered &&
|
||||
n.ResolvedTargets == "+15551234567"),
|
||||
Arg.Any<CancellationToken>());
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Success_MarksNotificationDelivered_WithResolvedTargets()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user