feat: establish legacy parity migration baseline
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
using MBN_STOCK_WEBVIEW.LegacyApplication;
|
||||
|
||||
namespace MBN_STOCK_WEBVIEW.LegacyApplication.Tests;
|
||||
|
||||
public sealed class LegacyOperatorControllerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task EmptyButtonIsNoOpButEmptyEnterSearches()
|
||||
{
|
||||
var lookup = Lookup();
|
||||
var controller = new LegacyOperatorController(lookup);
|
||||
|
||||
await controller.SearchStocksAsync(string.Empty, LegacySearchTrigger.Button);
|
||||
Assert.Equal(0, lookup.CallCount);
|
||||
|
||||
await controller.SearchStocksAsync(string.Empty, LegacySearchTrigger.Enter);
|
||||
Assert.Equal(1, lookup.CallCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExactPointerDoubleClick_AppendsOneLegacyRow()
|
||||
{
|
||||
var controller = new LegacyOperatorController(Lookup());
|
||||
await controller.SearchStocksAsync("삼성", LegacySearchTrigger.Button);
|
||||
controller.SelectStock(0);
|
||||
|
||||
controller.CutPointerDown(1, false, false, 20, 45, 1_000);
|
||||
var state = controller.CutPointerDown(1, false, false, 20, 45, 1_400);
|
||||
|
||||
var row = Assert.Single(state.Playlist);
|
||||
Assert.Equal("코스피", row.MarketText);
|
||||
Assert.Equal("삼성전자", row.StockName);
|
||||
Assert.Equal("1열판기본", row.GraphicType);
|
||||
Assert.Equal("현재가", row.Subtype);
|
||||
Assert.Equal("005930", row.StockCode);
|
||||
Assert.Equal("5001", row.ToPlayoutEntry().CutCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExactPointerDoubleClick_AllowsZeroBasedWebTimestamp()
|
||||
{
|
||||
var controller = new LegacyOperatorController(Lookup());
|
||||
await controller.SearchStocksAsync("삼성", LegacySearchTrigger.Button);
|
||||
controller.SelectStock(0);
|
||||
|
||||
controller.CutPointerDown(1, false, false, 20, 45, 0);
|
||||
var state = controller.CutPointerDown(1, false, false, 20, 45, 499);
|
||||
|
||||
Assert.Single(state.Playlist);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DifferentPointerPosition_DoesNotCountAsLegacyDoubleClick()
|
||||
{
|
||||
var controller = new LegacyOperatorController(Lookup());
|
||||
await controller.SearchStocksAsync("삼성", LegacySearchTrigger.Button);
|
||||
controller.SelectStock(0);
|
||||
|
||||
controller.CutPointerDown(1, false, false, 20, 45, 1_000);
|
||||
var state = controller.CutPointerDown(1, false, false, 21, 45, 1_200);
|
||||
|
||||
Assert.Empty(state.Playlist);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task NativeSelectionAndLegacyDoubleClickRowsStaySeparateAtShiftEdges()
|
||||
{
|
||||
var controller = new LegacyOperatorController(Lookup());
|
||||
await controller.SearchStocksAsync("삼성", LegacySearchTrigger.Button);
|
||||
controller.SelectStock(0);
|
||||
|
||||
var firstShift = controller.CutPointerDown(2, false, true, 20, 69, 1_000);
|
||||
Assert.Equal(
|
||||
[0, 1, 2],
|
||||
firstShift.CutRows.Where(row => row.IsSelected).Select(row => row.PhysicalIndex));
|
||||
|
||||
var noDoubleClick = controller.CutPointerDown(2, false, false, 20, 69, 1_400);
|
||||
Assert.Empty(noDoubleClick.Playlist);
|
||||
|
||||
var sameRowShift = controller.CutPointerDown(2, false, true, 21, 69, 2_000);
|
||||
Assert.Equal(
|
||||
[2],
|
||||
sameRowShift.CutRows.Where(row => row.IsSelected).Select(row => row.PhysicalIndex));
|
||||
|
||||
var stillNoDoubleClick = controller.CutPointerDown(2, false, false, 21, 69, 2_400);
|
||||
Assert.Empty(stillNoDoubleClick.Playlist);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ControlAndShiftSelection_DropsInPhysicalOrderAndKeepsDuplicates()
|
||||
{
|
||||
var controller = new LegacyOperatorController(Lookup());
|
||||
await controller.SearchStocksAsync("삼성", LegacySearchTrigger.Button);
|
||||
controller.SelectStock(0);
|
||||
controller.CutPointerDown(0, false, false, 1, 1, 1_000);
|
||||
controller.CutPointerDown(2, true, false, 1, 47, 2_000);
|
||||
controller.CutPointerDown(1, true, true, 1, 24, 3_000);
|
||||
|
||||
var first = controller.DropSelectedCutsOnPlaylist();
|
||||
var second = controller.DropSelectedCutsOnPlaylist();
|
||||
|
||||
Assert.Equal(3, first.Playlist.Count);
|
||||
Assert.Equal(6, second.Playlist.Count);
|
||||
Assert.Equal(
|
||||
["예상체결가", "현재가", "시간외단일가"],
|
||||
first.Playlist.Select(row => row.Subtype));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SameRowShiftStillDropsTheNativeListViewSelection()
|
||||
{
|
||||
var controller = new LegacyOperatorController(Lookup());
|
||||
await controller.SearchStocksAsync("삼성", LegacySearchTrigger.Button);
|
||||
controller.SelectStock(0);
|
||||
controller.CutPointerDown(1, false, false, 1, 24, 1_000);
|
||||
controller.CutPointerDown(1, false, true, 2, 24, 2_000);
|
||||
|
||||
var state = controller.DropSelectedCutsOnPlaylist();
|
||||
|
||||
Assert.Equal("현재가", Assert.Single(state.Playlist).Subtype);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task NxtOnlyAllowsCurrentAndShowsOriginalWarning()
|
||||
{
|
||||
var controller = new LegacyOperatorController(Lookup());
|
||||
await controller.SearchStocksAsync("넥스트", LegacySearchTrigger.Button);
|
||||
controller.SelectStock(1);
|
||||
controller.CutPointerDown(0, false, false, 1, 1, 1_000);
|
||||
var warning = controller.DropSelectedCutsOnPlaylist();
|
||||
|
||||
Assert.Empty(warning.Playlist);
|
||||
Assert.Equal("NXT는 제공하지 않습니다.", warning.Dialog?.Message);
|
||||
|
||||
controller.DismissDialog();
|
||||
controller.CutPointerDown(1, false, false, 1, 24, 2_000);
|
||||
var accepted = controller.DropSelectedCutsOnPlaylist();
|
||||
Assert.Equal("N5001", Assert.Single(accepted.Playlist).ToPlayoutEntry().CutCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task NxtMultiSelection_PausesAtEachOriginalModalBeforeContinuing()
|
||||
{
|
||||
var controller = new LegacyOperatorController(Lookup());
|
||||
await controller.SearchStocksAsync("넥스트", LegacySearchTrigger.Button);
|
||||
controller.SelectStock(1);
|
||||
controller.CutPointerDown(0, false, false, 1, 1, 1_000);
|
||||
controller.CutPointerDown(1, true, false, 1, 24, 2_000);
|
||||
controller.CutPointerDown(2, true, false, 1, 47, 3_000);
|
||||
|
||||
var firstWarning = controller.DropSelectedCutsOnPlaylist();
|
||||
Assert.Empty(firstWarning.Playlist);
|
||||
Assert.Equal("NXT는 제공하지 않습니다.", firstWarning.Dialog?.Message);
|
||||
|
||||
var secondWarning = controller.DismissDialog();
|
||||
Assert.Equal("N5001", Assert.Single(secondWarning.Playlist).ToPlayoutEntry().CutCode);
|
||||
Assert.Equal("NXT는 제공하지 않습니다.", secondWarning.Dialog?.Message);
|
||||
|
||||
var finished = controller.DismissDialog();
|
||||
Assert.Single(finished.Playlist);
|
||||
Assert.Null(finished.Dialog);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task NxtSeparatorStillShowsOriginalWarning()
|
||||
{
|
||||
var controller = new LegacyOperatorController(Lookup());
|
||||
await controller.SearchStocksAsync("넥스트", LegacySearchTrigger.Button);
|
||||
controller.SelectStock(1);
|
||||
controller.CutPointerDown(3, false, false, 1, 70, 1_000);
|
||||
|
||||
var warning = controller.DropSelectedCutsOnPlaylist();
|
||||
|
||||
Assert.Empty(warning.Playlist);
|
||||
Assert.Equal("NXT는 제공하지 않습니다.", warning.Dialog?.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task NewSearchClearsStockSelectionButNotCutCatalogOrPlaylist()
|
||||
{
|
||||
var controller = new LegacyOperatorController(Lookup());
|
||||
await controller.SearchStocksAsync("삼성", LegacySearchTrigger.Button);
|
||||
controller.SelectStock(0);
|
||||
controller.CutPointerDown(1, false, false, 1, 24, 1_000);
|
||||
controller.DropSelectedCutsOnPlaylist();
|
||||
|
||||
var state = await controller.SearchStocksAsync("다시", LegacySearchTrigger.Button);
|
||||
|
||||
Assert.Null(state.SelectedStockIndex);
|
||||
Assert.Equal(33, state.CutRows.Count);
|
||||
Assert.Single(state.Playlist);
|
||||
}
|
||||
|
||||
private static FakeLookup Lookup() => new(new LegacyStockSearchData(
|
||||
"query",
|
||||
["삼성전자", "넥스트(NXT)"],
|
||||
[
|
||||
new LegacyStockIdentity(LegacyDomesticStockMarket.Kospi, "삼성전자", "005930"),
|
||||
new LegacyStockIdentity(LegacyDomesticStockMarket.NxtKospi, "넥스트(NXT)", "A00001")
|
||||
]));
|
||||
|
||||
private sealed class FakeLookup : ILegacyStockLookup
|
||||
{
|
||||
private readonly LegacyStockSearchData _result;
|
||||
|
||||
public FakeLookup(LegacyStockSearchData result)
|
||||
{
|
||||
_result = result;
|
||||
}
|
||||
|
||||
public int CallCount { get; private set; }
|
||||
|
||||
public Task<LegacyStockSearchData> SearchAsync(
|
||||
string query,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
CallCount++;
|
||||
return Task.FromResult(_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user