feat: complete legacy parity and modernize operator UI

This commit is contained in:
2026-07-22 12:34:46 +09:00
parent fc4007d676
commit 939c252d23
149 changed files with 26515 additions and 1736 deletions

View File

@@ -68,6 +68,75 @@ public sealed class OracleManualFinancialMutationExecutorTests
Assert.True(plan.TransactionDisposed);
}
[Fact]
public async Task Raw_update_accepts_Core_opaque_ROWID_command_and_binds_legacy_controls()
{
var plan = new Plan
{
ExecuteAsync = (index, _) => Task.FromResult(index == 0 ? -1 : 1)
};
var factory = new Factory(plan, 17);
var service = new LegacyManualFinancialScreenService(
new StaticRawQueryExecutor(),
Executor(factory));
var listed = Assert.Single((await service.SearchAsync(
ManualFinancialScreenKind.Sales)).RawItems);
var values = listed.Record.StorageValues.ToArray();
values[0] = " raw_\u0000value ";
var receipt = await service.UpdateRawAsync(
listed,
new ManualFinancialRawRecord(listed.Record.Identity, values));
Assert.Equal(ManualFinancialMutationKind.Update, receipt.Kind);
Assert.Equal(2, plan.Commands.Count);
var command = plan.Commands[1];
Assert.Contains(
"ROWID = CHARTOROWID(:row_handle)",
command.CommandText,
StringComparison.Ordinal);
Assert.Equal(" raw_\u0000value ", command.ParametersSnapshot
.Single(parameter => parameter.ParameterName == "field_1").Value);
Assert.Equal("AAAAAAAAAAAAAAAAAA", command.ParametersSnapshot
.Single(parameter => parameter.ParameterName == "row_handle").Value);
}
[Fact]
public async Task Raw_update_row_version_mismatch_rolls_back_once_without_retry()
{
var plan = new Plan
{
ExecuteAsync = (index, _) => Task.FromResult(index == 0 ? -1 : 0)
};
var factory = new Factory(plan, 17);
var service = new LegacyManualFinancialScreenService(
new StaticRawQueryExecutor(),
Executor(factory));
var listed = Assert.Single((await service.SearchAsync(
ManualFinancialScreenKind.Sales)).RawItems);
var values = listed.Record.StorageValues.ToArray();
values[0] = "A\u001fB";
values[1] = "C\0";
var exception = await Assert.ThrowsAsync<ManualFinancialMutationRejectedException>(() =>
service.UpdateRawAsync(
listed,
new ManualFinancialRawRecord(listed.Record.Identity, values)));
Assert.Equal(
ManualFinancialMutationRejectionReason.NotFoundOrChanged,
exception.Reason);
Assert.False(exception.OutcomeUnknown);
Assert.Equal(1, factory.CreateCount);
Assert.Equal(1, plan.RollbackCount);
Assert.Equal(0, plan.CommitCount);
Assert.Equal(2, plan.Commands.Count);
Assert.Equal([1, 1], plan.Commands.Select(command => command.ExecuteCount));
Assert.Contains("CASE WHEN STOCK_NAME IS NULL THEN 'N'", plan.Commands[1].CommandText,
StringComparison.Ordinal);
Assert.DoesNotContain("CHR(31)", plan.Commands[1].CommandText, StringComparison.Ordinal);
}
[Fact]
public async Task DeniedAuthorizationCreatesNoConnectionOrTransaction()
{
@@ -205,6 +274,46 @@ public sealed class OracleManualFinancialMutationExecutorTests
throw new InvalidOperationException("A mutation test must not read.");
}
private sealed class StaticRawQueryExecutor : IDataQueryExecutor
{
public DataTable Execute(DataSourceKind source, string tableName, string query) =>
throw new NotSupportedException();
public Task<DataTable> ExecuteAsync(
DataSourceKind source,
string tableName,
string query,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public Task<DataTable> ExecuteAsync(
DataSourceKind source,
string tableName,
DataQuerySpec query,
CancellationToken cancellationToken = default)
{
var table = new DataTable();
foreach (var column in ManualFinancialCutContracts.Get(
ManualFinancialScreenKind.Sales).StorageColumns)
{
table.Columns.Add(column, typeof(string));
}
table.Columns.Add("ROW_VERSION", typeof(string));
table.Columns.Add("ROW_HANDLE", typeof(string));
table.Rows.Add(
"Alpha",
"_10",
"2Q_20",
"3Q_30",
"4Q_40",
"5Q_50",
"6Q_60",
Version,
"AAAAAAAAAAAAAAAAAA");
return Task.FromResult(table);
}
}
private sealed class Factory(Plan plan, int commandTimeoutSeconds = 15)
: IDatabaseConnectionFactory
{