513fa1aa16
Comment/XML-doc changes only: - add summaries to ~10k test methods, fixtures, fakes and helpers (+ <param> on theories, <returns> on async Task methods, <inheritdoc/> on interface implementations the checker can resolve) - strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N, #NN, Component-0NN) from comments, keeping the explanatory text; reword false positives in comments only - real docs on fakes implementing BCL/library interfaces (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime) - reword the last 5 src/ false positives (CWE-1236, failures-1, year-0001, M365) Only non-comment edits: four one-line nested classes split across lines so members can carry docs. Build 0 errors / 0 warnings.
102 lines
4.6 KiB
C#
102 lines
4.6 KiB
C#
using System.Data;
|
|
using System.Data.Common;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using Microsoft.Data.SqlClient;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Repositories;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests.Repositories;
|
|
|
|
public class AuditPurgeBudgetTests
|
|
{
|
|
/// <summary>Verifies that a backlogged channel purge stops at the row budget, including a partial final batch.</summary>
|
|
/// <returns>A task that represents the asynchronous operation.</returns>
|
|
[Fact]
|
|
public async Task BackloggedChannel_StopsAtBudgetIncludingPartialFinalBatch()
|
|
{
|
|
using var connection = new BackloggedConnection();
|
|
await using var context = new ScadaBridgeDbContext(new DbContextOptionsBuilder<ScadaBridgeDbContext>()
|
|
.UseSqlServer(connection).Options);
|
|
var deleted = await new AuditLogRepository(context).PurgeChannelOlderThanAsync(
|
|
"ScriptTrace", DateTime.UtcNow, batchSize: 3, maxRows: 5);
|
|
Assert.Equal(5, deleted);
|
|
Assert.Equal(new[] { 3, 2 }, connection.RequestedBatches);
|
|
Assert.Equal(ConnectionState.Closed, connection.State);
|
|
}
|
|
|
|
private sealed class BackloggedConnection : DbConnection
|
|
{
|
|
/// <summary>Gets the batch sizes requested by the purge, in order.</summary>
|
|
public List<int> RequestedBatches { get; } = new();
|
|
private ConnectionState _state;
|
|
/// <inheritdoc />
|
|
[AllowNull] public override string ConnectionString { get; set; } = "Server=localhost;Database=test";
|
|
/// <inheritdoc />
|
|
public override string Database => "test";
|
|
/// <inheritdoc />
|
|
public override string DataSource => "localhost";
|
|
/// <inheritdoc />
|
|
public override string ServerVersion => "16.0";
|
|
/// <inheritdoc />
|
|
public override ConnectionState State => _state;
|
|
/// <inheritdoc />
|
|
public override void Open() => _state = ConnectionState.Open;
|
|
/// <inheritdoc />
|
|
public override void Close() => _state = ConnectionState.Closed;
|
|
/// <inheritdoc />
|
|
public override void ChangeDatabase(string databaseName) => throw new NotSupportedException();
|
|
/// <inheritdoc />
|
|
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) => throw new NotSupportedException();
|
|
/// <inheritdoc />
|
|
protected override DbCommand CreateDbCommand() => new BackloggedCommand(this);
|
|
}
|
|
|
|
private sealed class BackloggedCommand(BackloggedConnection owner) : DbCommand
|
|
{
|
|
private readonly SqlCommand _parameters = new();
|
|
/// <inheritdoc />
|
|
[AllowNull] public override string CommandText { get; set; } = "";
|
|
/// <inheritdoc />
|
|
public override int CommandTimeout { get; set; }
|
|
/// <inheritdoc />
|
|
public override CommandType CommandType { get; set; }
|
|
/// <inheritdoc />
|
|
public override bool DesignTimeVisible { get; set; }
|
|
/// <inheritdoc />
|
|
public override UpdateRowSource UpdatedRowSource { get; set; }
|
|
/// <inheritdoc />
|
|
protected override DbConnection? DbConnection { get; set; }
|
|
/// <inheritdoc />
|
|
protected override DbTransaction? DbTransaction { get; set; }
|
|
/// <inheritdoc />
|
|
protected override DbParameterCollection DbParameterCollection => _parameters.Parameters;
|
|
/// <inheritdoc />
|
|
protected override DbParameter CreateDbParameter() => new SqlParameter();
|
|
/// <inheritdoc />
|
|
public override int ExecuteNonQuery()
|
|
{
|
|
// A server with more eligible rows than any tick is allowed to delete.
|
|
// Fail rapidly if the repository loops indefinitely, without a timer-based test.
|
|
if (owner.RequestedBatches.Count >= 10)
|
|
throw new InvalidOperationException("Purge did not yield despite exhausting its row budget.");
|
|
var requested = (int)_parameters.Parameters["@batch"].Value;
|
|
owner.RequestedBatches.Add(requested);
|
|
return requested;
|
|
}
|
|
/// <inheritdoc />
|
|
public override object? ExecuteScalar() => throw new NotSupportedException();
|
|
/// <inheritdoc />
|
|
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) => throw new NotSupportedException();
|
|
/// <inheritdoc />
|
|
public override void Cancel() { }
|
|
/// <inheritdoc />
|
|
public override void Prepare() { }
|
|
/// <inheritdoc />
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing) _parameters.Dispose();
|
|
base.Dispose(disposing);
|
|
}
|
|
}
|
|
}
|