test(auditlog): assert ExecutionId threading hops; defensive Guid parse on S&F read
This commit is contained in:
@@ -396,6 +396,46 @@ public class StoreAndForwardStorageTests : IAsyncLifetime, IDisposable
|
||||
Assert.Null(retrieved.SourceScript);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MalformedExecutionId_ReadsBackAsNull_DoesNotAbortRetrySweep()
|
||||
{
|
||||
// Defensive read path: a corrupt (non-null, non-GUID) execution_id must
|
||||
// be treated as "no execution id" rather than throwing FormatException
|
||||
// — a single bad row must not abort the whole GetMessagesForRetryAsync
|
||||
// sweep, which reads many rows. Persist two due rows, then corrupt the
|
||||
// execution_id of one directly in the DB.
|
||||
var goodId = Guid.NewGuid();
|
||||
var good = CreateMessage("good1", StoreAndForwardCategory.ExternalSystem);
|
||||
good.ExecutionId = goodId;
|
||||
good.LastAttemptAt = null; // due immediately
|
||||
await _storage.EnqueueAsync(good);
|
||||
|
||||
var bad = CreateMessage("bad1", StoreAndForwardCategory.ExternalSystem);
|
||||
bad.ExecutionId = Guid.NewGuid();
|
||||
bad.LastAttemptAt = null; // due immediately
|
||||
await _storage.EnqueueAsync(bad);
|
||||
|
||||
await using (var conn = new SqliteConnection($"Data Source={_dbName};Mode=Memory;Cache=Shared"))
|
||||
{
|
||||
await conn.OpenAsync();
|
||||
await using var corrupt = conn.CreateCommand();
|
||||
corrupt.CommandText =
|
||||
"UPDATE sf_messages SET execution_id = 'not-a-guid' WHERE id = 'bad1';";
|
||||
await corrupt.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
// The sweep must not throw; the corrupt row reads back with a null
|
||||
// ExecutionId, the well-formed row keeps its value.
|
||||
var due = await _storage.GetMessagesForRetryAsync();
|
||||
Assert.Null(Assert.Single(due, m => m.Id == "bad1").ExecutionId);
|
||||
Assert.Equal(goodId, Assert.Single(due, m => m.Id == "good1").ExecutionId);
|
||||
|
||||
// The single-row read path is equally defensive.
|
||||
var retrieved = await _storage.GetMessageByIdAsync("bad1");
|
||||
Assert.NotNull(retrieved);
|
||||
Assert.Null(retrieved!.ExecutionId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InitializeAsync_IsIdempotent_WhenColumnsAlreadyExist()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user