639 lines
26 KiB
C#
639 lines
26 KiB
C#
using System.Data;
|
|
using MBN_STOCK_WEBVIEW.Core.Playout.Scenes;
|
|
using MMoneyCoderSharp.Data;
|
|
|
|
namespace MBN_STOCK_WEBVIEW.LegacyApplication.Tests;
|
|
|
|
public sealed class LegacyManualFinancialOperatorControllerTests
|
|
{
|
|
[Fact]
|
|
public async Task GraphE_open_warns_when_invalid_legacy_rows_were_isolated()
|
|
{
|
|
var service = new FakeManualService(Sales("Alpha", 1))
|
|
{
|
|
RejectedItemCount = 2
|
|
};
|
|
|
|
var opened = await Controller(service).OpenManualFinancialAsync(
|
|
ManualFinancialScreenKind.Sales);
|
|
|
|
Assert.Single(opened.ManualFinancial!.Rows);
|
|
Assert.Equal(2, opened.ManualFinancial.ListRejectedItemCount);
|
|
Assert.Equal(LegacyOperatorStatusKind.Warning, opened.StatusKind);
|
|
Assert.Contains("2행", opened.StatusMessage, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Strict_ready_GraphE_load_clears_previous_raw_row_warning()
|
|
{
|
|
var service = new LegacyManualFinancialScreenService(
|
|
new RawGraphEQueryExecutor());
|
|
var controller = Controller(service);
|
|
var opened = await controller.OpenManualFinancialAsync(
|
|
ManualFinancialScreenKind.Sales);
|
|
var rawRow = opened.ManualFinancial!.Rows.Single(row => !row.IsPlayoutReady);
|
|
var readyRow = opened.ManualFinancial.Rows.Single(row => row.IsPlayoutReady);
|
|
|
|
var rawLoaded = await controller.LoadManualFinancialAsync(rawRow.ResultId);
|
|
|
|
Assert.False(rawLoaded.ManualFinancial!.IsPlayoutReady);
|
|
Assert.Equal(LegacyOperatorStatusKind.Warning, rawLoaded.StatusKind);
|
|
Assert.Contains("원문", rawLoaded.StatusMessage, StringComparison.Ordinal);
|
|
|
|
var readyLoaded = await controller.LoadManualFinancialAsync(readyRow.ResultId);
|
|
|
|
Assert.True(readyLoaded.ManualFinancial!.IsPlayoutReady);
|
|
Assert.Equal(LegacyOperatorStatusKind.Information, readyLoaded.StatusKind);
|
|
Assert.Contains("Alpha", readyLoaded.StatusMessage, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("원문", readyLoaded.StatusMessage, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Unreadable_row_failure_clears_previous_detail_and_playout_authority()
|
|
{
|
|
var service = new FakeManualService(Sales("Alpha", 1))
|
|
{
|
|
RejectedItemCount = 1,
|
|
UnreadableStockNames = ["Broken"],
|
|
GetOverride = identity => identity.StockName == "Broken"
|
|
? throw new ManualFinancialDataException("Unreadable legacy row.")
|
|
: Sales("Alpha", 1)
|
|
};
|
|
var controller = Controller(service);
|
|
var opened = await controller.OpenManualFinancialAsync(
|
|
ManualFinancialScreenKind.Sales);
|
|
var valid = opened.ManualFinancial!.Rows.Single(row => row.IsDataReadable);
|
|
var unreadable = opened.ManualFinancial.Rows.Single(row => !row.IsDataReadable);
|
|
var loaded = await controller.LoadManualFinancialAsync(valid.ResultId);
|
|
Assert.True(loaded.ManualFinancial!.CanMaterializePlaylist);
|
|
|
|
var rejected = await controller.LoadManualFinancialAsync(unreadable.ResultId);
|
|
|
|
var failed = rejected.ManualFinancial!;
|
|
Assert.Equal(unreadable.ResultId, failed.SelectedRowId);
|
|
Assert.Null(failed.SelectedRecord);
|
|
Assert.False(failed.CanSave);
|
|
Assert.False(failed.CanDelete);
|
|
Assert.False(failed.CanMaterializePlaylist);
|
|
Assert.NotNull(rejected.Dialog);
|
|
|
|
var activated = await controller.ActivateManualFinancialResultAsync(
|
|
unreadable.ResultId);
|
|
Assert.Empty(activated.Playlist);
|
|
Assert.False(activated.CommandResult?.Succeeded);
|
|
Assert.Equal(unreadable.ResultId, activated.CommandResult?.TargetId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Stale_result_handle_clears_previous_detail_before_it_is_rejected()
|
|
{
|
|
var service = new FakeManualService(Sales("Alpha", 1));
|
|
var controller = Controller(service);
|
|
var opened = await controller.OpenManualFinancialAsync(
|
|
ManualFinancialScreenKind.Sales);
|
|
var valid = Assert.Single(opened.ManualFinancial!.Rows);
|
|
var loaded = await controller.LoadManualFinancialAsync(valid.ResultId);
|
|
Assert.True(loaded.ManualFinancial!.CanMaterializePlaylist);
|
|
|
|
var rejected = await controller.LoadManualFinancialAsync("stale-result-id");
|
|
|
|
Assert.Null(rejected.ManualFinancial!.SelectedRowId);
|
|
Assert.Null(rejected.ManualFinancial.SelectedRecord);
|
|
Assert.False(rejected.ManualFinancial.CanSave);
|
|
Assert.False(rejected.ManualFinancial.CanDelete);
|
|
Assert.False(rejected.ManualFinancial.CanMaterializePlaylist);
|
|
Assert.NotNull(rejected.Dialog);
|
|
Assert.Equal(1, service.GetCalls);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GraphE_result_double_click_fresh_loads_appends_once_and_closes_on_success()
|
|
{
|
|
var service = new FakeManualService(Sales("Alpha", 1));
|
|
var controller = Controller(service);
|
|
var opened = await controller.OpenManualFinancialAsync(
|
|
ManualFinancialScreenKind.Sales);
|
|
var resultId = Assert.Single(opened.ManualFinancial!.Rows).ResultId;
|
|
|
|
var activated = await controller.ActivateManualFinancialResultAsync(resultId);
|
|
|
|
Assert.Null(activated.ManualFinancial);
|
|
Assert.Equal(1, service.GetCalls);
|
|
Assert.Equal(0, service.MutationCalls);
|
|
Assert.Equal("5080", Assert.Single(activated.Playlist).ToPlayoutEntry().CutCode);
|
|
Assert.Equal("activate-manual-financial-result", activated.CommandResult?.Command);
|
|
Assert.Equal(resultId, activated.CommandResult?.TargetId);
|
|
Assert.True(activated.CommandResult?.Succeeded);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GraphE_result_double_click_rejects_identity_change_without_append_or_close()
|
|
{
|
|
var service = new FakeManualService(Sales("Alpha", 1))
|
|
{
|
|
GetOverride = _ => Sales("Beta", 2)
|
|
};
|
|
var controller = Controller(service);
|
|
var opened = await controller.OpenManualFinancialAsync(
|
|
ManualFinancialScreenKind.Sales);
|
|
var resultId = Assert.Single(opened.ManualFinancial!.Rows).ResultId;
|
|
|
|
var rejected = await controller.ActivateManualFinancialResultAsync(resultId);
|
|
|
|
Assert.NotNull(rejected.ManualFinancial);
|
|
Assert.Empty(rejected.Playlist);
|
|
Assert.Equal(1, service.GetCalls);
|
|
Assert.Equal(0, service.MutationCalls);
|
|
Assert.NotNull(rejected.Dialog);
|
|
Assert.False(rejected.CommandResult?.Succeeded);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Existing_GraphE_row_loads_verifies_and_adds_CSharp_owned_playlist_row()
|
|
{
|
|
var service = new FakeManualService(Sales("Alpha", 1));
|
|
var controller = Controller(service);
|
|
|
|
var opened = await controller.OpenManualFinancialAsync(
|
|
ManualFinancialScreenKind.Sales);
|
|
var rowId = Assert.Single(opened.ManualFinancial!.Rows).ResultId;
|
|
var loaded = await controller.LoadManualFinancialAsync(rowId);
|
|
|
|
Assert.True(loaded.ManualFinancial!.CanMaterializePlaylist);
|
|
Assert.Equal(0, service.MutationCalls);
|
|
var added = controller.ActivateManualFinancialAction("sales");
|
|
var playlist = Assert.Single(added.Playlist);
|
|
var entry = playlist.ToPlayoutEntry();
|
|
Assert.Equal("5080", entry.CutCode);
|
|
var selection = Assert.IsType<LegacySceneSelection>(entry.Selection);
|
|
Assert.Equal("Alpha", selection.Subject);
|
|
Assert.Equal("SALES", selection.GraphicType);
|
|
Assert.Equal("1/1", playlist.PageText);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GraphE_find_loads_fresh_detail_and_stale_sort_generation_opens_modal()
|
|
{
|
|
var service = new FakeManualService(Sales("Alpha", 1));
|
|
var controller = Controller(service);
|
|
var opened = await controller.OpenManualFinancialAsync(
|
|
ManualFinancialScreenKind.Sales);
|
|
var generation = opened.ManualFinancial!.Revision;
|
|
|
|
var found = await controller.FindManualFinancialAsync(
|
|
"Alpha",
|
|
LegacyManualFinancialFindDirection.Down,
|
|
generation);
|
|
|
|
Assert.Equal("Alpha", found.ManualFinancial!.SelectedRecord!.Identity.StockName);
|
|
Assert.Equal(1, service.GetCalls);
|
|
var sorted = controller.ToggleManualFinancialNameSort(
|
|
found.ManualFinancial.Revision);
|
|
var stale = controller.ToggleManualFinancialNameSort(
|
|
found.ManualFinancial.Revision);
|
|
Assert.NotNull(stale.Dialog);
|
|
Assert.Equal(
|
|
sorted.ManualFinancial!.NameSortDirection,
|
|
stale.ManualFinancial!.NameSortDirection);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Create_does_not_write_until_explicit_save_and_fresh_readback_completes()
|
|
{
|
|
var service = new FakeManualService();
|
|
var controller = Controller(service);
|
|
await controller.OpenManualFinancialAsync(ManualFinancialScreenKind.OperatingProfit);
|
|
controller.BeginManualFinancialCreate();
|
|
var searched = await controller.SearchManualFinancialStocksAsync("Alpha");
|
|
controller.SelectManualFinancialStock(
|
|
Assert.Single(searched.ManualFinancial!.StockCandidates).ResultId);
|
|
|
|
Assert.Equal(0, service.MutationCalls);
|
|
var saved = await controller.SaveManualFinancialAsync(Profit("Alpha", 10));
|
|
|
|
Assert.Equal(1, service.MutationCalls);
|
|
Assert.Equal(1, service.GetCalls);
|
|
Assert.Equal(
|
|
LegacyManualFinancialWorkflowStatus.WriteCommitted,
|
|
saved.ManualFinancial!.Status);
|
|
Assert.True(saved.ManualFinancial.CanMaterializePlaylist);
|
|
Assert.Equal("저장되었습니다", saved.Dialog?.Message);
|
|
Assert.Equal("MmoneyCoder", saved.Dialog?.Caption);
|
|
Assert.False(saved.Dialog?.IsConfirmation);
|
|
Assert.DoesNotContain("password", saved.StatusMessage, StringComparison.OrdinalIgnoreCase);
|
|
|
|
var dismissed = controller.DismissDialog();
|
|
Assert.Null(dismissed.Dialog);
|
|
Assert.NotNull(dismissed.ManualFinancial);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Empty_GraphE_stock_confirm_is_a_quiet_no_op()
|
|
{
|
|
var service = new FakeManualService();
|
|
var controller = Controller(service);
|
|
var opened = await controller.OpenManualFinancialAsync(
|
|
ManualFinancialScreenKind.OperatingProfit);
|
|
var creating = controller.BeginManualFinancialCreate();
|
|
|
|
var unchanged = await controller.SearchManualFinancialStocksAsync(" ");
|
|
|
|
Assert.Equal(creating.Revision, unchanged.Revision);
|
|
Assert.Equal(creating.ManualFinancial!.Revision, unchanged.ManualFinancial!.Revision);
|
|
Assert.Empty(unchanged.ManualFinancial.StockCandidates);
|
|
Assert.Null(unchanged.Dialog);
|
|
Assert.Equal(0, service.MutationCalls);
|
|
Assert.NotEqual(opened.Revision, unchanged.Revision);
|
|
|
|
var noSelection = controller.SelectManualFinancialStock(string.Empty);
|
|
Assert.Equal(unchanged.Revision, noSelection.Revision);
|
|
Assert.Null(noSelection.Dialog);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Known_GraphE_save_rejection_uses_specific_sanitized_dialog_and_keeps_editor()
|
|
{
|
|
const string safeMessage =
|
|
"The create precondition failed and the Oracle transaction was rolled back.";
|
|
var service = new FakeManualService
|
|
{
|
|
MutationException = new ManualFinancialMutationRejectedException(
|
|
ManualFinancialMutationRejectionReason.DuplicateIdentity,
|
|
safeMessage)
|
|
};
|
|
var controller = Controller(service);
|
|
await controller.OpenManualFinancialAsync(ManualFinancialScreenKind.OperatingProfit);
|
|
controller.BeginManualFinancialCreate();
|
|
var searched = await controller.SearchManualFinancialStocksAsync("Alpha");
|
|
controller.SelectManualFinancialStock(
|
|
Assert.Single(searched.ManualFinancial!.StockCandidates).ResultId);
|
|
|
|
var rejected = await controller.SaveManualFinancialAsync(Profit("Alpha", 10));
|
|
|
|
Assert.Equal(1, service.MutationCalls);
|
|
Assert.Equal(
|
|
LegacyManualFinancialWorkflowStatus.WriteRejected,
|
|
rejected.ManualFinancial!.Status);
|
|
Assert.Equal(safeMessage, rejected.StatusMessage);
|
|
Assert.Equal(safeMessage, rejected.Dialog?.Message);
|
|
Assert.Equal("MmoneyCoder", rejected.Dialog?.Caption);
|
|
Assert.DoesNotContain("DB연결", rejected.Dialog!.Message, StringComparison.Ordinal);
|
|
|
|
var dismissed = controller.DismissDialog();
|
|
Assert.Null(dismissed.Dialog);
|
|
Assert.NotNull(dismissed.ManualFinancial);
|
|
Assert.True(dismissed.ManualFinancial.CanSave);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Malformed_GraphE_editor_data_is_not_misreported_as_a_database_outage()
|
|
{
|
|
var service = new FakeManualService();
|
|
var controller = Controller(service);
|
|
await controller.OpenManualFinancialAsync(ManualFinancialScreenKind.OperatingProfit);
|
|
controller.BeginManualFinancialCreate();
|
|
var searched = await controller.SearchManualFinancialStocksAsync("Alpha");
|
|
controller.SelectManualFinancialStock(
|
|
Assert.Single(searched.ManualFinancial!.StockCandidates).ResultId);
|
|
|
|
var rejected = await controller.SaveManualFinancialAsync(
|
|
Assert.IsType<ManualSalesRecord>(Sales("Alpha", 1).Record));
|
|
|
|
Assert.NotNull(rejected.ManualFinancial);
|
|
Assert.NotNull(rejected.Dialog);
|
|
Assert.DoesNotContain("DB연결", rejected.Dialog!.Message, StringComparison.Ordinal);
|
|
Assert.Equal(0, service.MutationCalls);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Delete_and_delete_all_require_MmoneyCoder_confirmation_before_writing()
|
|
{
|
|
var service = new FakeManualService(Sales("Alpha", 1));
|
|
var controller = Controller(service);
|
|
var opened = await controller.OpenManualFinancialAsync(ManualFinancialScreenKind.Sales);
|
|
await controller.LoadManualFinancialAsync(
|
|
Assert.Single(opened.ManualFinancial!.Rows).ResultId);
|
|
|
|
Assert.Equal(0, service.MutationCalls);
|
|
var deleteRequested = await controller.DeleteManualFinancialAsync(all: false);
|
|
Assert.Equal("선택한 종목을 삭제하겠습니까?", deleteRequested.Dialog?.Message);
|
|
Assert.Equal("MmoneyCoder", deleteRequested.Dialog?.Caption);
|
|
Assert.True(deleteRequested.Dialog?.IsConfirmation);
|
|
Assert.Equal(0, service.MutationCalls);
|
|
|
|
var canceled = controller.CancelDialog();
|
|
Assert.Null(canceled.Dialog);
|
|
Assert.Equal(0, service.MutationCalls);
|
|
Assert.Single(canceled.ManualFinancial!.Rows);
|
|
|
|
await controller.DeleteManualFinancialAsync(all: false);
|
|
var deleted = await controller.ConfirmDialogAsync();
|
|
Assert.Equal(1, service.MutationCalls);
|
|
Assert.Empty(deleted.ManualFinancial!.Rows);
|
|
|
|
var allRequested = await controller.DeleteManualFinancialAsync(all: true);
|
|
Assert.Equal("모든 종목을 삭제하겠습니까?", allRequested.Dialog?.Message);
|
|
Assert.True(allRequested.Dialog?.IsConfirmation);
|
|
Assert.Equal(1, service.MutationCalls);
|
|
|
|
var all = await controller.ConfirmDialogAsync();
|
|
Assert.Equal(2, service.MutationCalls);
|
|
Assert.Empty(all.ManualFinancial!.Rows);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Delete_without_a_GraphE_row_selection_remains_a_silent_no_op()
|
|
{
|
|
var service = new FakeManualService(Sales("Alpha", 1));
|
|
var controller = Controller(service);
|
|
await controller.OpenManualFinancialAsync(ManualFinancialScreenKind.Sales);
|
|
|
|
var result = await controller.DeleteManualFinancialAsync(all: false);
|
|
|
|
Assert.Null(result.Dialog);
|
|
Assert.Equal(0, service.MutationCalls);
|
|
Assert.Single(result.ManualFinancial!.Rows);
|
|
}
|
|
|
|
private static LegacyOperatorController Controller(IManualFinancialScreenService service)
|
|
{
|
|
var workflow = new LegacyManualFinancialWorkflow(
|
|
service,
|
|
new FakeStockService(),
|
|
new LegacyManualFinancialWriteSafetyState());
|
|
return new LegacyOperatorController(
|
|
new EmptyStockLookup(),
|
|
manualFinancialWorkflow: workflow);
|
|
}
|
|
|
|
private static ManualFinancialSnapshot Sales(string stockName, int version) =>
|
|
new(
|
|
new ManualSalesRecord(
|
|
new ManualFinancialIdentity(ManualFinancialScreenKind.Sales, stockName),
|
|
Enumerable.Range(1, 6)
|
|
.Select(index => new ManualQuarterValue($"{index}Q", index))
|
|
.ToArray()),
|
|
Version(version));
|
|
|
|
private static ManualOperatingProfitRecord Profit(string stockName, int offset) =>
|
|
new(
|
|
new ManualFinancialIdentity(
|
|
ManualFinancialScreenKind.OperatingProfit,
|
|
stockName),
|
|
Enumerable.Range(1, 6)
|
|
.Select(index => new ManualQuarterValue($"{index}Q", offset + index))
|
|
.ToArray());
|
|
|
|
private static string Version(int value) => value.ToString("X64");
|
|
|
|
private sealed class EmptyStockLookup : ILegacyStockLookup
|
|
{
|
|
public Task<LegacyStockSearchData> SearchAsync(
|
|
string query,
|
|
CancellationToken cancellationToken = default) =>
|
|
Task.FromResult(new LegacyStockSearchData(query, [], []));
|
|
}
|
|
|
|
private sealed class FakeStockService : IStockSearchService
|
|
{
|
|
public Task<StockSearchResult> SearchAsync(
|
|
string query,
|
|
int maximumResults = LegacyStockSearchService.DefaultMaximumResults,
|
|
CancellationToken cancellationToken = default) =>
|
|
Task.FromResult(new StockSearchResult(
|
|
query,
|
|
DateTimeOffset.UtcNow,
|
|
[new StockSearchItem(
|
|
StockMarket.Kospi,
|
|
DataSourceKind.Oracle,
|
|
"Alpha",
|
|
"000001")],
|
|
false));
|
|
}
|
|
|
|
private sealed class FakeManualService : IManualFinancialScreenService
|
|
{
|
|
private readonly Dictionary<(ManualFinancialScreenKind, string), ManualFinancialSnapshot>
|
|
_rows = new();
|
|
private int _version = 10;
|
|
|
|
internal FakeManualService(params ManualFinancialSnapshot[] rows)
|
|
{
|
|
foreach (var row in rows)
|
|
{
|
|
_rows[(row.Record.Identity.Screen, row.Record.Identity.StockName)] = row;
|
|
}
|
|
}
|
|
|
|
public bool CanMutate => true;
|
|
|
|
internal int MutationCalls { get; private set; }
|
|
|
|
internal int GetCalls { get; private set; }
|
|
|
|
internal ManualFinancialMutationException? MutationException { get; init; }
|
|
|
|
internal int RejectedItemCount { get; init; }
|
|
|
|
internal IReadOnlyList<string> UnreadableStockNames { get; init; } =
|
|
Array.Empty<string>();
|
|
|
|
internal Func<ManualFinancialIdentity, ManualFinancialSnapshot>? GetOverride
|
|
{
|
|
get;
|
|
init;
|
|
}
|
|
|
|
public Task<ManualFinancialSearchResult> SearchAsync(
|
|
ManualFinancialScreenKind screen,
|
|
string query = "",
|
|
int maximumResults = LegacyManualFinancialScreenService.DefaultMaximumResults,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var rows = _rows.Values
|
|
.Where(row => row.Record.Identity.Screen == screen)
|
|
.Where(row => query.Length == 0 ||
|
|
row.Record.Identity.StockName.Contains(
|
|
query,
|
|
StringComparison.OrdinalIgnoreCase))
|
|
.Take(maximumResults)
|
|
.ToArray();
|
|
return Task.FromResult(new ManualFinancialSearchResult(
|
|
screen,
|
|
query,
|
|
DateTimeOffset.UtcNow,
|
|
rows,
|
|
false)
|
|
{
|
|
RejectedItemCount = RejectedItemCount,
|
|
UnreadableStockNames = UnreadableStockNames
|
|
});
|
|
}
|
|
|
|
public Task<ManualFinancialSnapshot> GetAsync(
|
|
ManualFinancialIdentity identity,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
GetCalls++;
|
|
return Task.FromResult(
|
|
GetOverride?.Invoke(identity) ??
|
|
_rows[(identity.Screen, identity.StockName)]);
|
|
}
|
|
|
|
public Task<ManualFinancialMutationReceipt> CreateAsync(
|
|
ManualFinancialRecord record,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
MutationCalls++;
|
|
if (MutationException is not null)
|
|
{
|
|
throw MutationException;
|
|
}
|
|
|
|
_rows[(record.Identity.Screen, record.Identity.StockName)] =
|
|
new ManualFinancialSnapshot(record, Version(++_version));
|
|
return Task.FromResult(Receipt(
|
|
ManualFinancialMutationKind.Create,
|
|
record.Identity.Screen,
|
|
record.Identity.StockName,
|
|
1));
|
|
}
|
|
|
|
public Task<ManualFinancialMutationReceipt> UpdateAsync(
|
|
ManualFinancialSnapshot expected,
|
|
ManualFinancialRecord replacement,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
MutationCalls++;
|
|
if (MutationException is not null)
|
|
{
|
|
throw MutationException;
|
|
}
|
|
|
|
_rows[(replacement.Identity.Screen, replacement.Identity.StockName)] =
|
|
new ManualFinancialSnapshot(replacement, Version(++_version));
|
|
return Task.FromResult(Receipt(
|
|
ManualFinancialMutationKind.Update,
|
|
replacement.Identity.Screen,
|
|
replacement.Identity.StockName,
|
|
1));
|
|
}
|
|
|
|
public Task<ManualFinancialMutationReceipt> DeleteAsync(
|
|
ManualFinancialSnapshot expected,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
MutationCalls++;
|
|
_rows.Remove((
|
|
expected.Record.Identity.Screen,
|
|
expected.Record.Identity.StockName));
|
|
return Task.FromResult(Receipt(
|
|
ManualFinancialMutationKind.DeleteOne,
|
|
expected.Record.Identity.Screen,
|
|
expected.Record.Identity.StockName,
|
|
1));
|
|
}
|
|
|
|
public Task<ManualFinancialMutationReceipt> DeleteAllAsync(
|
|
ManualFinancialScreenKind screen,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
MutationCalls++;
|
|
var keys = _rows.Keys.Where(key => key.Item1 == screen).ToArray();
|
|
foreach (var key in keys)
|
|
{
|
|
_rows.Remove(key);
|
|
}
|
|
|
|
return Task.FromResult(Receipt(
|
|
ManualFinancialMutationKind.DeleteAll,
|
|
screen,
|
|
string.Empty,
|
|
keys.Length));
|
|
}
|
|
|
|
private static ManualFinancialMutationReceipt Receipt(
|
|
ManualFinancialMutationKind kind,
|
|
ManualFinancialScreenKind screen,
|
|
string stockName,
|
|
int affectedRows) =>
|
|
new(
|
|
Guid.NewGuid(),
|
|
kind,
|
|
screen,
|
|
stockName,
|
|
DateTimeOffset.UtcNow,
|
|
affectedRows);
|
|
}
|
|
|
|
private sealed class RawGraphEQueryExecutor : IDataQueryExecutor
|
|
{
|
|
private readonly DataTable _rows = CreateRows();
|
|
|
|
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)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
if (!string.Equals(
|
|
tableName,
|
|
"MANUAL_FINANCIAL_SELL_RAW_GET",
|
|
StringComparison.Ordinal))
|
|
{
|
|
return Task.FromResult(_rows.Copy());
|
|
}
|
|
|
|
var stockName = Assert.IsType<string>(query.Parameters.Single(
|
|
parameter => string.Equals(
|
|
parameter.Name,
|
|
"stock_name",
|
|
StringComparison.OrdinalIgnoreCase)).Value);
|
|
var result = _rows.Clone();
|
|
var row = Assert.Single(_rows.AsEnumerable().Where(candidate =>
|
|
string.Equals(
|
|
candidate.Field<string>("STOCK_NAME"),
|
|
stockName,
|
|
StringComparison.Ordinal)));
|
|
result.ImportRow(row);
|
|
return Task.FromResult(result);
|
|
}
|
|
|
|
private static DataTable CreateRows()
|
|
{
|
|
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",
|
|
"1Q_10", "2Q_20", "3Q_30", "4Q_40", "5Q_50", "6Q_60",
|
|
Version(1),
|
|
"AAAAAAAAAAAAAAAA01");
|
|
table.Rows.Add(
|
|
"Broken",
|
|
"1Q_not-a-number", "2Q_20", "3Q_30", "4Q_40", "5Q_50", "6Q_60",
|
|
Version(2),
|
|
"AAAAAAAAAAAAAAAA02");
|
|
return table;
|
|
}
|
|
}
|
|
}
|