308 lines
12 KiB
C#
308 lines
12 KiB
C#
using MBN_STOCK_WEBVIEW.Core.Playout.Scenes;
|
|
using MMoneyCoderSharp.Data;
|
|
|
|
namespace MBN_STOCK_WEBVIEW.LegacyApplication.Tests;
|
|
|
|
public sealed class LegacyOperatorComparisonIntegrationTests
|
|
{
|
|
[Fact]
|
|
public async Task ComparisonTabBuildsPairAndAddsAuthoritativeSceneRow()
|
|
{
|
|
var data = new StaticComparisonDataService();
|
|
var store = new MemoryComparisonPairStore();
|
|
var controller = new LegacyOperatorController(
|
|
new EmptyStockLookup(),
|
|
comparisonWorkflow: new LegacyComparisonWorkflowController(data, store));
|
|
|
|
var selected = await controller.SelectTabAsync(LegacyOperatorTabId.Comparison);
|
|
Assert.NotNull(selected.Comparison);
|
|
Assert.Empty(selected.Comparison.SavedPairs);
|
|
|
|
var searched = await controller.SearchComparisonDomesticAsync("반도체");
|
|
var results = searched.Comparison!.DomesticSearch.Results;
|
|
controller.ChooseComparisonDomesticResult(results[0].SelectionId);
|
|
controller.ChooseComparisonDomesticResult(results[1].SelectionId);
|
|
var saved = await controller.AddComparisonPairAsync();
|
|
Assert.Single(saved.Comparison!.SavedPairs);
|
|
Assert.Single(store.Writes);
|
|
|
|
var added = controller.ActivateComparisonAction("comparison-candle");
|
|
var row = Assert.Single(added.Playlist);
|
|
Assert.Equal("5026", row.ToPlayoutEntry().CutCode);
|
|
Assert.Equal(
|
|
"KOSPI:000660|KOSPI:005930",
|
|
Assert.IsType<LegacySceneSelection>(row.ToPlayoutEntry().Selection).DataCode);
|
|
Assert.Equal("비교", row.MarketText);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ComparisonParentDoubleClickAddsOnlyDirectChildrenInOriginalOrder()
|
|
{
|
|
var controller = new LegacyOperatorController(
|
|
new EmptyStockLookup(),
|
|
comparisonWorkflow: new LegacyComparisonWorkflowController(
|
|
new StaticComparisonDataService(),
|
|
new MemoryComparisonPairStore()));
|
|
await controller.SelectTabAsync(LegacyOperatorTabId.Comparison);
|
|
var searched = await controller.SearchComparisonDomesticAsync("반도체");
|
|
controller.ChooseComparisonDomesticResult(
|
|
searched.Comparison!.DomesticSearch.Results[0].SelectionId);
|
|
controller.ChooseComparisonDomesticResult(
|
|
searched.Comparison.DomesticSearch.Results[1].SelectionId);
|
|
await controller.AddComparisonPairAsync();
|
|
|
|
var added = controller.ActivateComparisonSection(1);
|
|
|
|
Assert.Collection(
|
|
added.Playlist,
|
|
row => Assert.Equal("5026", row.SceneAlias),
|
|
row => Assert.Equal("5087", row.SceneAlias));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAllComparisonPairs_NoPreservesRowsAndPerformsNoWrite()
|
|
{
|
|
var (controller, store) = await CreateControllerWithSavedPairAsync();
|
|
var writesBeforeRequest = store.Writes.Count;
|
|
|
|
var requested = await controller.DeleteComparisonPairAsync(all: true);
|
|
|
|
Assert.Equal("모두 삭제하겠습니까?", requested.Dialog?.Message);
|
|
Assert.Equal("MmoneyCoder", requested.Dialog?.Caption);
|
|
Assert.True(requested.Dialog?.IsConfirmation);
|
|
Assert.Single(requested.Comparison!.SavedPairs);
|
|
Assert.Equal(writesBeforeRequest, store.Writes.Count);
|
|
|
|
var dismissed = controller.DismissDialog();
|
|
Assert.True(dismissed.Dialog?.IsConfirmation);
|
|
Assert.Equal(writesBeforeRequest, store.Writes.Count);
|
|
|
|
var canceled = controller.CancelDialog();
|
|
|
|
Assert.Null(canceled.Dialog);
|
|
Assert.Single(canceled.Comparison!.SavedPairs);
|
|
Assert.Equal(writesBeforeRequest, store.Writes.Count);
|
|
|
|
await controller.ConfirmDialogAsync();
|
|
Assert.Equal(writesBeforeRequest, store.Writes.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAllComparisonPairs_YesConsumesConfirmationBeforeSingleWrite()
|
|
{
|
|
var (controller, store) = await CreateControllerWithSavedPairAsync();
|
|
var writesBeforeRequest = store.Writes.Count;
|
|
await controller.DeleteComparisonPairAsync(all: true);
|
|
|
|
var confirmed = await controller.ConfirmDialogAsync();
|
|
|
|
Assert.Null(confirmed.Dialog);
|
|
Assert.Empty(confirmed.Comparison!.SavedPairs);
|
|
Assert.Equal(writesBeforeRequest + 1, store.Writes.Count);
|
|
Assert.Empty(store.Writes[^1]);
|
|
|
|
await controller.ConfirmDialogAsync();
|
|
Assert.Equal(writesBeforeRequest + 1, store.Writes.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAllComparisonPairs_ChangedListInvalidatesPendingConfirmation()
|
|
{
|
|
var (controller, store) = await CreateControllerWithSavedPairAsync();
|
|
var writesBeforeRequest = store.Writes.Count;
|
|
var requested = await controller.DeleteComparisonPairAsync(all: true);
|
|
var rowId = Assert.Single(requested.Comparison!.SavedPairs).RowId;
|
|
controller.SelectComparisonPair(rowId);
|
|
await controller.DeleteComparisonPairAsync(all: true);
|
|
|
|
var rejected = await controller.ConfirmDialogAsync();
|
|
|
|
Assert.Single(rejected.Comparison!.SavedPairs);
|
|
Assert.Equal(writesBeforeRequest, store.Writes.Count);
|
|
Assert.Equal(LegacyOperatorStatusKind.Error, rejected.StatusKind);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAllComparisonPairs_EmptyListStillPromptsAndYesDoesNotWrite()
|
|
{
|
|
var store = new MemoryComparisonPairStore();
|
|
var controller = new LegacyOperatorController(
|
|
new EmptyStockLookup(),
|
|
comparisonWorkflow: new LegacyComparisonWorkflowController(
|
|
new StaticComparisonDataService(),
|
|
store));
|
|
await controller.SelectTabAsync(LegacyOperatorTabId.Comparison);
|
|
|
|
var requested = await controller.DeleteComparisonPairAsync(all: true);
|
|
|
|
Assert.Equal("모두 삭제하겠습니까?", requested.Dialog?.Message);
|
|
Assert.True(requested.Dialog?.IsConfirmation);
|
|
var confirmed = await controller.ConfirmDialogAsync();
|
|
Assert.Empty(confirmed.Comparison!.SavedPairs);
|
|
Assert.Empty(store.Writes);
|
|
}
|
|
|
|
private static async Task<(LegacyOperatorController Controller,
|
|
MemoryComparisonPairStore Store)> CreateControllerWithSavedPairAsync()
|
|
{
|
|
var store = new MemoryComparisonPairStore();
|
|
var controller = new LegacyOperatorController(
|
|
new EmptyStockLookup(),
|
|
comparisonWorkflow: new LegacyComparisonWorkflowController(
|
|
new StaticComparisonDataService(),
|
|
store));
|
|
await controller.SelectTabAsync(LegacyOperatorTabId.Comparison);
|
|
var searched = await controller.SearchComparisonDomesticAsync("반도체");
|
|
controller.ChooseComparisonDomesticResult(
|
|
searched.Comparison!.DomesticSearch.Results[0].SelectionId);
|
|
controller.ChooseComparisonDomesticResult(
|
|
searched.Comparison.DomesticSearch.Results[1].SelectionId);
|
|
await controller.AddComparisonPairAsync();
|
|
return (controller, store);
|
|
}
|
|
|
|
private sealed class EmptyStockLookup : ILegacyStockLookup
|
|
{
|
|
public Task<LegacyStockSearchData> SearchAsync(
|
|
string query,
|
|
CancellationToken cancellationToken = default) => Task.FromResult(
|
|
new LegacyStockSearchData(query, [], []));
|
|
}
|
|
|
|
private sealed class StaticComparisonDataService : ILegacyComparisonDataService
|
|
{
|
|
private static readonly IReadOnlyList<LegacyComparisonTarget> Domestic =
|
|
[
|
|
LegacyComparisonTarget.CreateKrxStock(
|
|
LegacyComparisonDomesticMarket.Kospi,
|
|
"삼성전자",
|
|
"005930"),
|
|
LegacyComparisonTarget.CreateKrxStock(
|
|
LegacyComparisonDomesticMarket.Kospi,
|
|
"SK하이닉스",
|
|
"000660")
|
|
];
|
|
|
|
public Task<IReadOnlyList<LegacyComparisonTarget>> SearchDomesticAsync(
|
|
string query,
|
|
int maximumResults,
|
|
CancellationToken cancellationToken = default) => Task.FromResult(Domestic);
|
|
|
|
public Task<IReadOnlyList<LegacyComparisonTarget>> SearchWorldAsync(
|
|
string query,
|
|
int maximumResults,
|
|
CancellationToken cancellationToken = default) => Task.FromResult<
|
|
IReadOnlyList<LegacyComparisonTarget>>([]);
|
|
|
|
public Task<LegacyComparisonTarget?> ResolveStoredTargetAsync(
|
|
LegacyComparisonStoredTargetReference target,
|
|
CancellationToken cancellationToken = default) => Task.FromResult<
|
|
LegacyComparisonTarget?>(null);
|
|
}
|
|
|
|
private sealed class MemoryComparisonPairStore : ILegacyComparisonPairStore
|
|
{
|
|
public List<IReadOnlyList<LegacyComparisonPairWriteRow>> Writes { get; } = [];
|
|
|
|
public Task<byte[]?> ReadCp949Async(CancellationToken cancellationToken = default) =>
|
|
Task.FromResult<byte[]?>(null);
|
|
|
|
public Task WriteCp949Async(
|
|
IReadOnlyList<LegacyComparisonPairWriteRow> rows,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
Writes.Add(rows.ToArray());
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|
|
}
|
|
|
|
public sealed class LegacyComparisonProductionAdapterTests
|
|
{
|
|
[Fact]
|
|
public async Task DataAdapterMapsDomesticNxtAndWorldIdentities()
|
|
{
|
|
var lookup = new StaticStockLookup();
|
|
var world = new StaticWorldLookup();
|
|
var service = new LegacyComparisonDataService(lookup, world);
|
|
|
|
var domestic = await service.SearchDomesticAsync("삼성", 10);
|
|
Assert.Equal(LegacyComparisonTargetKind.KrxStock, domestic[0].Kind);
|
|
Assert.Equal(LegacyComparisonTargetKind.NxtStock, domestic[1].Kind);
|
|
Assert.Equal("삼성전자(NXT)", domestic[1].DisplayName);
|
|
|
|
var overseas = Assert.Single(await service.SearchWorldAsync("APPLE", 10));
|
|
Assert.Equal("world:US:AAPL", overseas.TargetId);
|
|
var resolved = await service.ResolveStoredTargetAsync(
|
|
new LegacyComparisonStoredTargetReference("APPLE", string.Empty));
|
|
Assert.Equal(overseas, resolved);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task FileStoreRoundTripsStrictCp949WithAtomicTarget()
|
|
{
|
|
var directory = Path.Combine(
|
|
Path.GetTempPath(),
|
|
$"mbn-comparison-{Guid.NewGuid():N}");
|
|
var path = Path.Combine(directory, "종목비교.dat");
|
|
try
|
|
{
|
|
var store = new LegacyComparisonPairFileStore(path);
|
|
await store.WriteCp949Async(
|
|
[
|
|
new LegacyComparisonPairWriteRow(
|
|
1,
|
|
"삼성전자",
|
|
"코스피",
|
|
"SK하이닉스",
|
|
"코스피")
|
|
]);
|
|
|
|
var bytes = Assert.IsType<byte[]>(await store.ReadCp949Async());
|
|
var parsed = Assert.Single(LegacyComparisonFileParser.Parse(bytes));
|
|
Assert.Equal("삼성전자", parsed.First.Name);
|
|
Assert.Empty(Directory.EnumerateFiles(directory, "*.tmp"));
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(directory))
|
|
{
|
|
Directory.Delete(directory, recursive: true);
|
|
}
|
|
}
|
|
}
|
|
|
|
private sealed class StaticStockLookup : ILegacyStockLookup
|
|
{
|
|
public Task<LegacyStockSearchData> SearchAsync(
|
|
string query,
|
|
CancellationToken cancellationToken = default) => Task.FromResult(
|
|
new LegacyStockSearchData(
|
|
query,
|
|
["삼성전자", "삼성전자(NXT)"],
|
|
[
|
|
new LegacyStockIdentity(
|
|
LegacyDomesticStockMarket.Kospi,
|
|
"삼성전자",
|
|
"005930"),
|
|
new LegacyStockIdentity(
|
|
LegacyDomesticStockMarket.NxtKospi,
|
|
"삼성전자(NXT)",
|
|
"005930")
|
|
]));
|
|
}
|
|
|
|
private sealed class StaticWorldLookup : IWorldStockSearchService
|
|
{
|
|
public Task<WorldStockSearchResult> SearchAsync(
|
|
string query,
|
|
int maximumResults = LegacyWorldStockSearchService.DefaultMaximumResults,
|
|
CancellationToken cancellationToken = default) => Task.FromResult(
|
|
new WorldStockSearchResult(
|
|
query.Trim(),
|
|
DateTimeOffset.UtcNow,
|
|
[new WorldStockSearchItem("APPLE", "AAPL", "US")],
|
|
false));
|
|
}
|
|
}
|