280 lines
8.2 KiB
C#
280 lines
8.2 KiB
C#
using System.Collections;
|
|
using System.Data;
|
|
using System.Data.Common;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using MBN_STOCK_WEBVIEW.Infrastructure;
|
|
using MMoneyCoderSharp.Data;
|
|
|
|
namespace MBN_STOCK_WEBVIEW.Infrastructure.Tests;
|
|
|
|
internal sealed class ConnectionPlan
|
|
{
|
|
public Func<CancellationToken, Task> OpenAsync { get; init; } = _ => Task.CompletedTask;
|
|
|
|
public Func<CancellationToken, Task<DbDataReader>> ExecuteReaderAsync { get; init; } =
|
|
_ => Task.FromResult<DbDataReader>(new DataTable().CreateDataReader());
|
|
|
|
public bool Disposed { get; set; }
|
|
|
|
public int? ObservedCommandTimeoutSeconds { get; set; }
|
|
|
|
public static ConnectionPlan Success(DataTable data) => new()
|
|
{
|
|
ExecuteReaderAsync = _ => Task.FromResult<DbDataReader>(data.CreateDataReader())
|
|
};
|
|
}
|
|
|
|
internal sealed class FakeConnectionFactory : IDatabaseConnectionFactory
|
|
{
|
|
private readonly Queue<ConnectionPlan> _plans;
|
|
|
|
public FakeConnectionFactory(params ConnectionPlan[] plans)
|
|
{
|
|
_plans = new Queue<ConnectionPlan>(plans);
|
|
}
|
|
|
|
public int CreateCount { get; private set; }
|
|
|
|
public List<DbConnection> CreatedConnections { get; } = [];
|
|
|
|
public DbConnection Create(DataSourceKind source)
|
|
{
|
|
CreateCount++;
|
|
if (!_plans.TryDequeue(out var plan))
|
|
{
|
|
throw new InvalidOperationException("No fake connection plan remains.");
|
|
}
|
|
|
|
var connection = new FakeDbConnection(plan);
|
|
CreatedConnections.Add(connection);
|
|
return connection;
|
|
}
|
|
|
|
public int GetCommandTimeoutSeconds(DataSourceKind source) => 5;
|
|
}
|
|
|
|
internal sealed class StubErrorDetector(bool transient) : ITransientDatabaseErrorDetector
|
|
{
|
|
public bool IsTransient(DataSourceKind source, Exception exception) => transient;
|
|
|
|
public bool IsTimeout(DataSourceKind source, Exception exception) => exception is TimeoutException;
|
|
|
|
public int? GetProviderErrorCode(DataSourceKind source, Exception exception) => null;
|
|
}
|
|
|
|
internal sealed class TestDatabaseException(string message) : Exception(message);
|
|
|
|
internal sealed class FakeDbConnection(ConnectionPlan plan) : DbConnection
|
|
{
|
|
private ConnectionState _state = ConnectionState.Closed;
|
|
|
|
[AllowNull]
|
|
public override string ConnectionString { get; set; } = string.Empty;
|
|
|
|
public override string Database => "FakeDatabase";
|
|
|
|
public override string DataSource => "FakeDataSource";
|
|
|
|
public override string ServerVersion => "1.0";
|
|
|
|
public override ConnectionState State => _state;
|
|
|
|
public override void ChangeDatabase(string databaseName)
|
|
{
|
|
}
|
|
|
|
public override void Close() => _state = ConnectionState.Closed;
|
|
|
|
public override void Open() => OpenAsync(CancellationToken.None).GetAwaiter().GetResult();
|
|
|
|
public override async Task OpenAsync(CancellationToken cancellationToken)
|
|
{
|
|
await plan.OpenAsync(cancellationToken);
|
|
_state = ConnectionState.Open;
|
|
}
|
|
|
|
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) =>
|
|
throw new NotSupportedException();
|
|
|
|
protected override DbCommand CreateDbCommand() => new FakeDbCommand(this, plan);
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
plan.Disposed = true;
|
|
_state = ConnectionState.Closed;
|
|
}
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
}
|
|
|
|
internal sealed class FakeDbCommand(DbConnection connection, ConnectionPlan plan) : DbCommand
|
|
{
|
|
private readonly FakeDbParameterCollection _parameters = new();
|
|
|
|
[AllowNull]
|
|
public override string CommandText { get; set; } = string.Empty;
|
|
|
|
public override int CommandTimeout { get; set; }
|
|
|
|
public override CommandType CommandType { get; set; }
|
|
|
|
public override bool DesignTimeVisible { get; set; }
|
|
|
|
public override UpdateRowSource UpdatedRowSource { get; set; }
|
|
|
|
protected override DbConnection? DbConnection { get; set; } = connection;
|
|
|
|
protected override DbParameterCollection DbParameterCollection => _parameters;
|
|
|
|
protected override DbTransaction? DbTransaction { get; set; }
|
|
|
|
public override void Cancel()
|
|
{
|
|
}
|
|
|
|
public override int ExecuteNonQuery() => throw new NotSupportedException();
|
|
|
|
public override object? ExecuteScalar() => throw new NotSupportedException();
|
|
|
|
public override void Prepare()
|
|
{
|
|
}
|
|
|
|
protected override DbParameter CreateDbParameter() => new FakeDbParameter();
|
|
|
|
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) =>
|
|
ExecuteDbDataReaderAsync(behavior, CancellationToken.None).GetAwaiter().GetResult();
|
|
|
|
protected override Task<DbDataReader> ExecuteDbDataReaderAsync(
|
|
CommandBehavior behavior,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
plan.ObservedCommandTimeoutSeconds = CommandTimeout;
|
|
return plan.ExecuteReaderAsync(cancellationToken);
|
|
}
|
|
}
|
|
|
|
internal sealed class FakeDbParameter : DbParameter
|
|
{
|
|
public override DbType DbType { get; set; }
|
|
|
|
public override ParameterDirection Direction { get; set; }
|
|
|
|
public override bool IsNullable { get; set; }
|
|
|
|
[AllowNull]
|
|
public override string ParameterName { get; set; } = string.Empty;
|
|
|
|
public override int Size { get; set; }
|
|
|
|
[AllowNull]
|
|
public override string SourceColumn { get; set; } = string.Empty;
|
|
|
|
public override bool SourceColumnNullMapping { get; set; }
|
|
|
|
public override object? Value { get; set; }
|
|
|
|
public override void ResetDbType()
|
|
{
|
|
}
|
|
}
|
|
|
|
internal sealed class FakeDbParameterCollection : DbParameterCollection
|
|
{
|
|
private readonly List<DbParameter> _parameters = [];
|
|
|
|
public override int Count => _parameters.Count;
|
|
|
|
public override object SyncRoot => ((ICollection)_parameters).SyncRoot;
|
|
|
|
public override int Add(object value)
|
|
{
|
|
_parameters.Add(RequireParameter(value));
|
|
return _parameters.Count - 1;
|
|
}
|
|
|
|
public override void AddRange(Array values)
|
|
{
|
|
foreach (var value in values)
|
|
{
|
|
Add(value ?? throw new ArgumentNullException(nameof(values)));
|
|
}
|
|
}
|
|
|
|
public override void Clear() => _parameters.Clear();
|
|
|
|
public override bool Contains(object value) =>
|
|
value is DbParameter parameter && _parameters.Contains(parameter);
|
|
|
|
public override bool Contains(string value) => IndexOf(value) >= 0;
|
|
|
|
public override void CopyTo(Array array, int index) =>
|
|
((ICollection)_parameters).CopyTo(array, index);
|
|
|
|
public override IEnumerator GetEnumerator() => _parameters.GetEnumerator();
|
|
|
|
public override int IndexOf(object value) =>
|
|
value is DbParameter parameter ? _parameters.IndexOf(parameter) : -1;
|
|
|
|
public override int IndexOf(string parameterName) =>
|
|
_parameters.FindIndex(
|
|
parameter => string.Equals(
|
|
parameter.ParameterName,
|
|
parameterName,
|
|
StringComparison.OrdinalIgnoreCase));
|
|
|
|
public override void Insert(int index, object value) =>
|
|
_parameters.Insert(index, RequireParameter(value));
|
|
|
|
public override void Remove(object value)
|
|
{
|
|
if (value is DbParameter parameter)
|
|
{
|
|
_parameters.Remove(parameter);
|
|
}
|
|
}
|
|
|
|
public override void RemoveAt(int index) => _parameters.RemoveAt(index);
|
|
|
|
public override void RemoveAt(string parameterName)
|
|
{
|
|
var index = IndexOf(parameterName);
|
|
if (index >= 0)
|
|
{
|
|
RemoveAt(index);
|
|
}
|
|
}
|
|
|
|
protected override DbParameter GetParameter(int index) => _parameters[index];
|
|
|
|
protected override DbParameter GetParameter(string parameterName)
|
|
{
|
|
var index = IndexOf(parameterName);
|
|
return index >= 0
|
|
? _parameters[index]
|
|
: throw new IndexOutOfRangeException();
|
|
}
|
|
|
|
protected override void SetParameter(int index, DbParameter value) => _parameters[index] = value;
|
|
|
|
protected override void SetParameter(string parameterName, DbParameter value)
|
|
{
|
|
var index = IndexOf(parameterName);
|
|
if (index >= 0)
|
|
{
|
|
_parameters[index] = value;
|
|
}
|
|
else
|
|
{
|
|
_parameters.Add(value);
|
|
}
|
|
}
|
|
|
|
private static DbParameter RequireParameter(object value) =>
|
|
value as DbParameter
|
|
?? throw new ArgumentException("A DbParameter is required.", nameof(value));
|
|
}
|