109 lines
4.1 KiB
C#
109 lines
4.1 KiB
C#
#nullable enable
|
|
|
|
namespace MBN_STOCK_WEBVIEW.DbWriteSmoke;
|
|
|
|
internal static class DevelopmentDatabaseWriteSmokeApp
|
|
{
|
|
internal const int SuccessExitCode = 0;
|
|
internal const int FailureExitCode = 1;
|
|
internal const int NotAcknowledgedExitCode = 2;
|
|
internal const int OutcomeUnknownExitCode = 3;
|
|
|
|
internal static async Task<int> RunAsync(
|
|
IReadOnlyList<string> arguments,
|
|
Func<string?, IReadOnlyList<DevelopmentDatabaseSmokeScenario>> createPlan,
|
|
TextWriter output,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(arguments);
|
|
ArgumentNullException.ThrowIfNull(createPlan);
|
|
ArgumentNullException.ThrowIfNull(output);
|
|
|
|
if (!SmokeCommandLine.TryParse(arguments, out var options))
|
|
{
|
|
await output.WriteLineAsync(SmokeCommandLine.Usage).ConfigureAwait(false);
|
|
return NotAcknowledgedExitCode;
|
|
}
|
|
|
|
if (options.ShowHelp)
|
|
{
|
|
await output.WriteLineAsync(SmokeCommandLine.Usage).ConfigureAwait(false);
|
|
await output.WriteLineAsync(
|
|
"No write occurs unless the exact development-database acknowledgement is present.")
|
|
.ConfigureAwait(false);
|
|
return SuccessExitCode;
|
|
}
|
|
|
|
if (!options.IsWriteAcknowledged)
|
|
{
|
|
await output.WriteLineAsync(
|
|
"BLOCKED: development database writes were not acknowledged; no configuration was loaded and no database call was made.")
|
|
.ConfigureAwait(false);
|
|
await output.WriteLineAsync(SmokeCommandLine.Usage).ConfigureAwait(false);
|
|
return NotAcknowledgedExitCode;
|
|
}
|
|
|
|
IReadOnlyList<DevelopmentDatabaseSmokeScenario> plan;
|
|
try
|
|
{
|
|
plan = createPlan(options.ConfigurationPath);
|
|
}
|
|
catch
|
|
{
|
|
await output.WriteLineAsync(
|
|
"FAIL: configuration or smoke-plan creation failed (details redacted).")
|
|
.ConfigureAwait(false);
|
|
return FailureExitCode;
|
|
}
|
|
|
|
await output.WriteLineAsync(
|
|
"START: isolated development DB write/readback/cleanup smoke; credentials, SQL, identifiers, and values are redacted.")
|
|
.ConfigureAwait(false);
|
|
|
|
foreach (var scenario in plan)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
await output.WriteLineAsync("RUN: " + scenario.Name).ConfigureAwait(false);
|
|
|
|
DevelopmentDatabaseSmokeResult result;
|
|
try
|
|
{
|
|
result = await scenario.RunAsync(cancellationToken).ConfigureAwait(false);
|
|
}
|
|
catch
|
|
{
|
|
await output.WriteLineAsync(
|
|
"FAIL: unclassified smoke failure; no retry was attempted (details redacted).")
|
|
.ConfigureAwait(false);
|
|
return FailureExitCode;
|
|
}
|
|
|
|
if (result.Passed)
|
|
{
|
|
await output.WriteLineAsync(
|
|
"PASS: create/save, fresh readback, cleanup, and absence verification completed.")
|
|
.ConfigureAwait(false);
|
|
continue;
|
|
}
|
|
|
|
await output.WriteLineAsync(
|
|
$"FAIL: stage={result.FailureStage}; failureClass={result.FailureClass}; cleanupAttempted={result.CleanupAttempted}; cleanupVerified={result.CleanupVerified}; details redacted.")
|
|
.ConfigureAwait(false);
|
|
if (result.OutcomeUnknown)
|
|
{
|
|
await output.WriteLineAsync(
|
|
"QUARANTINED: mutation outcome is unknown; no retry, cleanup mutation, or later scenario was issued. A uniquely prefixed record may require manual inspection.")
|
|
.ConfigureAwait(false);
|
|
return OutcomeUnknownExitCode;
|
|
}
|
|
|
|
return FailureExitCode;
|
|
}
|
|
|
|
await output.WriteLineAsync(
|
|
"PASS: every development DB write scenario was verified absent after cleanup.")
|
|
.ConfigureAwait(false);
|
|
return SuccessExitCode;
|
|
}
|
|
}
|