Files
MBN_STOCK_WEBVIEW/tools/MBN_STOCK_WEBVIEW.DbWriteSmoke/OwnedManualSmokeResidueRecovery.cs

173 lines
6.9 KiB
C#

#nullable enable
using System.Text.RegularExpressions;
using MBN_STOCK_WEBVIEW.Infrastructure;
using MMoneyCoderSharp.Data;
namespace MBN_STOCK_WEBVIEW.DbWriteSmoke;
internal static partial class OwnedManualSmokeResidueRecovery
{
internal const string RecoveryOption = "--reconcile-known-owned-manual-residue";
private static readonly IReadOnlyDictionary<ManualFinancialScreenKind, char> ScenarioCodes =
new Dictionary<ManualFinancialScreenKind, char>
{
[ManualFinancialScreenKind.RevenueComposition] = 'P',
[ManualFinancialScreenKind.GrowthMetrics] = 'G',
[ManualFinancialScreenKind.Sales] = 'S',
[ManualFinancialScreenKind.OperatingProfit] = 'O'
};
internal static bool IsRequested(IReadOnlyList<string> arguments) =>
arguments.Contains(RecoveryOption, StringComparer.Ordinal);
internal static async Task<int> RunAsync(
IReadOnlyList<string> arguments,
TextWriter output,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(arguments);
ArgumentNullException.ThrowIfNull(output);
if (!TryParse(arguments, out var configurationPath))
{
await output.WriteLineAsync(
"Usage: MBN_STOCK_WEBVIEW.DbWriteSmoke " + RecoveryOption + " " +
SmokeCommandLine.WriteAcknowledgement +
" [" + SmokeCommandLine.ConfigurationOption + " <database.local.json>]")
.ConfigureAwait(false);
return DevelopmentDatabaseWriteSmokeApp.NotAcknowledgedExitCode;
}
try
{
var options = new DatabaseOptionsLoader().Load(configurationPath);
options.Resilience.MaximumRetryCount = 0;
options.Resilience.InitialRetryDelayMilliseconds = 0;
var runtime = DatabaseRuntime.Create(options);
var service = new LegacyManualFinancialScreenService(
runtime.Executor,
new OracleManualFinancialMutationExecutor(
runtime.ConnectionFactory,
options.Resilience));
var prefix = SmokeRunIdentity.Prefix +
DateTimeOffset.UtcNow.ToString("yyMMdd", System.Globalization.CultureInfo.InvariantCulture);
var total = 0;
await output.WriteLineAsync(
"START: reconcile only the closed, current-UTC-date manual smoke namespace; details redacted.")
.ConfigureAwait(false);
foreach (var (screen, scenarioCode) in ScenarioCodes)
{
var result = await service.SearchAsync(
screen,
prefix,
LegacyManualFinancialScreenService.MaximumResults,
cancellationToken)
.ConfigureAwait(false);
var owned = result.Items.Where(item =>
IsOwned(item.Record.Identity.StockName, prefix, scenarioCode))
.ToArray();
if (result.IsTruncated || owned.Length > 1)
{
await output.WriteLineAsync(
"BLOCKED: the owned residue set was not singular and complete; writesAttempted=false.")
.ConfigureAwait(false);
return DevelopmentDatabaseWriteSmokeApp.FailureExitCode;
}
if (owned.Length == 0)
{
continue;
}
await service.DeleteAsync(owned[0], cancellationToken).ConfigureAwait(false);
total++;
try
{
_ = await service.GetAsync(
owned[0].Record.Identity,
cancellationToken)
.ConfigureAwait(false);
}
catch (ManualFinancialNotFoundException)
{
continue;
}
await output.WriteLineAsync(
"FAIL: an owned residue remained after one delete; no retry was issued.")
.ConfigureAwait(false);
return DevelopmentDatabaseWriteSmokeApp.FailureExitCode;
}
await output.WriteLineAsync(
$"PASS: reconciled={total}; every owned residue is absent; no retry was issued.")
.ConfigureAwait(false);
return DevelopmentDatabaseWriteSmokeApp.SuccessExitCode;
}
catch (ManualFinancialMutationException exception)
{
await output.WriteLineAsync(
"FAIL: failureClass=" + RedactedDatabaseFailureClassifier.Classify(exception) +
"; outcomeUnknown=" + exception.OutcomeUnknown.ToString().ToLowerInvariant() +
"; no retry was issued.")
.ConfigureAwait(false);
return exception.OutcomeUnknown
? DevelopmentDatabaseWriteSmokeApp.OutcomeUnknownExitCode
: DevelopmentDatabaseWriteSmokeApp.FailureExitCode;
}
catch (Exception exception)
{
await output.WriteLineAsync(
"FAIL: failureClass=" + RedactedDatabaseFailureClassifier.Classify(exception) +
"; no retry was issued; details redacted.")
.ConfigureAwait(false);
return DevelopmentDatabaseWriteSmokeApp.FailureExitCode;
}
}
private static bool IsOwned(string value, string prefix, char scenarioCode) =>
value.Length == 20 &&
value.StartsWith(prefix, StringComparison.Ordinal) &&
value[^1] == scenarioCode &&
OwnedNamePattern().IsMatch(value);
private static bool TryParse(
IReadOnlyList<string> arguments,
out string? configurationPath)
{
configurationPath = null;
var recovery = false;
var acknowledged = false;
for (var index = 0; index < arguments.Count; index++)
{
switch (arguments[index])
{
case RecoveryOption when !recovery:
recovery = true;
break;
case SmokeCommandLine.WriteAcknowledgement when !acknowledged:
acknowledged = true;
break;
case SmokeCommandLine.ConfigurationOption when configurationPath is null &&
index + 1 < arguments.Count:
configurationPath = arguments[++index];
if (string.IsNullOrWhiteSpace(configurationPath))
{
return false;
}
break;
default:
return false;
}
}
return recovery && acknowledged;
}
[GeneratedRegex("^MBW[0-9]{6}[0-9A-F]{10}[PGSO]$", RegexOptions.CultureInvariant)]
private static partial Regex OwnedNamePattern();
}