test(playwright): generalize NotificationDataSeeder for status/created-at (Wave 4 prep)
Add InsertNotificationAsync with explicit status/createdAt parameters so tests
can seed back-dated Retrying rows that satisfy the IsStuck derived property
(Status ∈ {Pending,Retrying} && CreatedAt < now − 10 min). Refactor
InsertParkedNotificationAsync to delegate to it, preserving its exact public
signature and producing identical SQL for existing callers.
This commit is contained in:
+64
-14
@@ -38,27 +38,50 @@ internal static class NotificationDataSeeder
|
||||
public static string ConnectionString => PlaywrightDbConnection.ConnectionString;
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a single <c>Parked</c> row into the central <c>Notifications</c> table.
|
||||
/// Inserts one notification row with explicit <paramref name="status"/> and
|
||||
/// <paramref name="createdAt"/> (for stuck/age edge-case tests).
|
||||
///
|
||||
/// <para>
|
||||
/// Populates every NOT NULL column (NotificationId, Type, ListName, Subject, Body,
|
||||
/// Status, RetryCount, SourceSiteId, SiteEnqueuedAt, CreatedAt) and stamps the row with
|
||||
/// the unique <paramref name="listNameMarker"/> so the test can filter to it and the
|
||||
/// teardown can delete by it. <c>Status</c> is fixed to <c>Parked</c> — the only status
|
||||
/// from which the page exposes Retry/Discard. Timestamps are pinned to "now" so the
|
||||
/// page's default unconstrained query window sees the row.
|
||||
/// Status, RetryCount, SourceSiteId, SiteEnqueuedAt, CreatedAt) and stamps the row
|
||||
/// with the unique <paramref name="listNameMarker"/> so the test can filter to it and
|
||||
/// the teardown can delete by it. <c>SiteEnqueuedAt</c> is set equal to
|
||||
/// <paramref name="createdAt"/>. All nullable provenance columns (SourceNode,
|
||||
/// OriginExecutionId, …) are left to default to NULL, which the page renders as an
|
||||
/// em-dash.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// To seed a genuinely stuck row set <paramref name="status"/> to <c>Retrying</c> (or
|
||||
/// <c>Pending</c>) and <paramref name="createdAt"/> to more than 10 minutes in the past —
|
||||
/// <c>IsStuck</c> is derived as <c>Status ∈ {Pending, Retrying} && CreatedAt <
|
||||
/// now − 10 min</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="notificationId">GUID primary key (stored as its 36-char string form).</param>
|
||||
/// <param name="listNameMarker">Unique per-run marker stored in <c>ListName</c>.</param>
|
||||
/// <param name="subject">Subject text (searchable via the page's subject keyword box).</param>
|
||||
/// <param name="status">
|
||||
/// Status value stored as varchar (HasConversion<string>()): e.g. <c>Pending</c>,
|
||||
/// <c>Retrying</c>, <c>Parked</c>, <c>Delivered</c>, <c>Discarded</c>.
|
||||
/// </param>
|
||||
/// <param name="createdAt">
|
||||
/// Timestamp written to both <c>CreatedAt</c> and <c>SiteEnqueuedAt</c>. Pass a
|
||||
/// back-dated value (e.g. <c>DateTimeOffset.UtcNow.AddMinutes(-15)</c>) to produce a
|
||||
/// stuck row.
|
||||
/// </param>
|
||||
/// <param name="sourceSite">Originating site identifier (e.g. <c>site-a</c>).</param>
|
||||
/// <param name="retryCount">Retry count to display on the row.</param>
|
||||
/// <param name="lastError">Optional last-error text shown beneath the subject.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
public static async Task InsertParkedNotificationAsync(
|
||||
public static async Task InsertNotificationAsync(
|
||||
Guid notificationId,
|
||||
string listNameMarker,
|
||||
string subject,
|
||||
string sourceSite,
|
||||
int retryCount = 3,
|
||||
string status,
|
||||
DateTimeOffset createdAt,
|
||||
string sourceSite = "site-a",
|
||||
int retryCount = 0,
|
||||
string? lastError = "SMTP 451 transient failure (seeded)",
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
@@ -74,8 +97,6 @@ VALUES
|
||||
(@id, @type, @listName, @subject, @body, @status, @retryCount,
|
||||
@lastError, @sourceSite, @siteEnqueuedAt, @createdAt);";
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
|
||||
await using var connection = new SqlConnection(ConnectionString);
|
||||
await connection.OpenAsync(ct);
|
||||
await using var cmd = connection.CreateCommand();
|
||||
@@ -85,16 +106,45 @@ VALUES
|
||||
cmd.Parameters.AddWithValue("@listName", listNameMarker);
|
||||
cmd.Parameters.AddWithValue("@subject", subject);
|
||||
cmd.Parameters.AddWithValue("@body", "Seeded notification body for Playwright E2E.");
|
||||
cmd.Parameters.AddWithValue("@status", "Parked");
|
||||
cmd.Parameters.AddWithValue("@status", status);
|
||||
cmd.Parameters.AddWithValue("@retryCount", retryCount);
|
||||
cmd.Parameters.AddWithValue("@lastError", (object?)lastError ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@sourceSite", sourceSite);
|
||||
cmd.Parameters.AddWithValue("@siteEnqueuedAt", now);
|
||||
cmd.Parameters.AddWithValue("@createdAt", now);
|
||||
cmd.Parameters.AddWithValue("@siteEnqueuedAt", createdAt);
|
||||
cmd.Parameters.AddWithValue("@createdAt", createdAt);
|
||||
|
||||
await cmd.ExecuteNonQueryAsync(ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a single <c>Parked</c> row into the central <c>Notifications</c> table.
|
||||
/// Populates every NOT NULL column (NotificationId, Type, ListName, Subject, Body,
|
||||
/// Status, RetryCount, SourceSiteId, SiteEnqueuedAt, CreatedAt) and stamps the row with
|
||||
/// the unique <paramref name="listNameMarker"/> so the test can filter to it and the
|
||||
/// teardown can delete by it. <c>Status</c> is fixed to <c>Parked</c> — the only status
|
||||
/// from which the page exposes Retry/Discard. Timestamps are pinned to "now" so the
|
||||
/// page's default unconstrained query window sees the row.
|
||||
/// </summary>
|
||||
/// <param name="notificationId">GUID primary key (stored as its 36-char string form).</param>
|
||||
/// <param name="listNameMarker">Unique per-run marker stored in <c>ListName</c>.</param>
|
||||
/// <param name="subject">Subject text (searchable via the page's subject keyword box).</param>
|
||||
/// <param name="sourceSite">Originating site identifier (e.g. <c>site-a</c>).</param>
|
||||
/// <param name="retryCount">Retry count to display on the row.</param>
|
||||
/// <param name="lastError">Optional last-error text shown beneath the subject.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
public static Task InsertParkedNotificationAsync(
|
||||
Guid notificationId,
|
||||
string listNameMarker,
|
||||
string subject,
|
||||
string sourceSite,
|
||||
int retryCount = 3,
|
||||
string? lastError = "SMTP 451 transient failure (seeded)",
|
||||
CancellationToken ct = default)
|
||||
=> InsertNotificationAsync(
|
||||
notificationId, listNameMarker, subject,
|
||||
status: "Parked", createdAt: DateTimeOffset.UtcNow,
|
||||
sourceSite: sourceSite, retryCount: retryCount, lastError: lastError, ct: ct);
|
||||
|
||||
/// <summary>
|
||||
/// Best-effort cleanup. Deletes every <c>Notifications</c> row whose <c>ListName</c>
|
||||
/// equals <paramref name="listNameMarker"/>. Swallows all errors — the marker carries a
|
||||
|
||||
Reference in New Issue
Block a user