293 lines
11 KiB
C#
293 lines
11 KiB
C#
using MMoneyCoderSharp.Data;
|
|
using MBN_STOCK_WEBVIEW.Core.Playout;
|
|
using MBN_STOCK_WEBVIEW.Core.Playout.Scenes;
|
|
|
|
namespace MBN_STOCK_WEBVIEW.LegacyApplication.Tests;
|
|
|
|
public sealed class LegacyOperatorPlayoutRequestTests
|
|
{
|
|
[Fact]
|
|
public void EmptyPlaylistCannotCreatePlayoutRequest()
|
|
{
|
|
var controller = new LegacyOperatorController(new StaticLookup());
|
|
|
|
Assert.Throws<InvalidOperationException>(controller.CreatePlayoutRequest);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RequestPreservesNativePlaylistIdentityEnabledStateAndSelection()
|
|
{
|
|
var controller = new LegacyOperatorController(new StaticLookup());
|
|
await controller.SearchStocksAsync("삼성", LegacySearchTrigger.Button);
|
|
controller.SelectStock(0);
|
|
AddCut(controller, physicalIndex: 0, timestamp: 1_000);
|
|
AddCut(controller, physicalIndex: 1, timestamp: 2_000);
|
|
var rows = controller.Current.Playlist;
|
|
controller.SetPlaylistRowEnabled(rows[0].RowId, enabled: false);
|
|
controller.SelectPlaylistRow(rows[1].RowId, control: false, shift: false);
|
|
|
|
var request = controller.CreatePlayoutRequest();
|
|
|
|
Assert.Equal(1, request.SelectedIndexZeroBased);
|
|
Assert.Equal(2, request.Playlist.Count);
|
|
Assert.Equal(rows.Select(row => row.RowId),
|
|
request.Playlist.Select(entry => entry.EntryId));
|
|
Assert.False(request.Playlist[0].IsEnabled);
|
|
Assert.True(request.Playlist[1].IsEnabled);
|
|
Assert.All(request.Playlist, entry =>
|
|
Assert.Equal("005930", entry.Selection?.DataCode));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false, false, "")]
|
|
[InlineData(true, false, "MA5")]
|
|
[InlineData(false, true, "MA20")]
|
|
[InlineData(true, true, "MA5,MA20")]
|
|
public async Task CandleAddedBeforeToggleSamplesCurrentMovingAveragesAtF8Request(
|
|
bool ma5,
|
|
bool ma20,
|
|
string expectedGraphicType)
|
|
{
|
|
var controller = new LegacyOperatorController(new StaticLookup());
|
|
await controller.SearchStocksAsync("stock", LegacySearchTrigger.Button);
|
|
controller.SelectStock(0);
|
|
var candle = LegacyStockCutCatalog.Rows.First(row =>
|
|
!row.IsSeparator &&
|
|
LegacyStockCutCatalog.ResolveCutAlias(row.RawLabel, isNxt: false) == "8051");
|
|
AddCut(controller, candle.PhysicalIndex, 1_000);
|
|
var rawBeforeToggle = Assert.Single(controller.Current.Playlist);
|
|
var rawGraphicType = rawBeforeToggle.ToPlayoutEntry().Selection!.GraphicType;
|
|
|
|
controller.SetMovingAverages(ma5, ma20);
|
|
var entry = Assert.Single(controller.CreatePlayoutRequest().Playlist);
|
|
|
|
Assert.Equal(expectedGraphicType, entry.Selection!.GraphicType);
|
|
Assert.Equal(rawGraphicType,
|
|
Assert.Single(controller.Current.Playlist).ToPlayoutEntry().Selection!.GraphicType);
|
|
Assert.Equal(
|
|
new LegacySceneMovingAverageSelection(ma5, ma20),
|
|
entry.MovingAverageSelectionSource!.Current);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task MovingAverageOverlayDoesNotAlterNonCandleRequest()
|
|
{
|
|
var controller = new LegacyOperatorController(new StaticLookup());
|
|
await controller.SearchStocksAsync("stock", LegacySearchTrigger.Button);
|
|
controller.SelectStock(0);
|
|
var current = LegacyStockCutCatalog.Rows.First(row =>
|
|
!row.IsSeparator &&
|
|
LegacyStockCutCatalog.ResolveCutAlias(row.RawLabel, isNxt: false) == "5001");
|
|
AddCut(controller, current.PhysicalIndex, 1_000);
|
|
var raw = Assert.Single(controller.Current.Playlist).ToPlayoutEntry();
|
|
|
|
controller.SetMovingAverages(ma5: false, ma20: false);
|
|
var requested = Assert.Single(controller.CreatePlayoutRequest().Playlist);
|
|
|
|
Assert.Equal(raw.Selection, requested.Selection);
|
|
Assert.Null(requested.MovingAverageSelectionSource);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(0)]
|
|
[InlineData(6)]
|
|
[InlineData(19)]
|
|
public async Task RequestAppliesCurrentGlobalDissolveIndexToEveryExistingRow(
|
|
int globalFadeDuration)
|
|
{
|
|
var controller = await CreateControllerWithCutsAsync(3);
|
|
Assert.All(controller.Current.Playlist, row => Assert.Equal(6, row.FadeDuration));
|
|
|
|
var request = controller.CreatePlayoutRequest(globalFadeDuration);
|
|
|
|
Assert.All(request.Playlist, entry =>
|
|
Assert.Equal(globalFadeDuration, entry.FadeDuration));
|
|
Assert.All(controller.Current.Playlist, row => Assert.Equal(6, row.FadeDuration));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(-1)]
|
|
[InlineData(20)]
|
|
[InlineData(60)]
|
|
public async Task RequestRejectsGlobalDissolveIndexOutsideOriginalComboRange(
|
|
int globalFadeDuration)
|
|
{
|
|
var controller = await CreateControllerWithCutsAsync(1);
|
|
|
|
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
|
controller.CreatePlayoutRequest(globalFadeDuration));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("5단 표그래프", ScenePageSize.Five)]
|
|
[InlineData("6종목 현재가", ScenePageSize.Six)]
|
|
[InlineData("12종목 현재가", ScenePageSize.Twelve)]
|
|
public void ExactLegacyGraphicTypeFreezesTheOperatorPageSuffix(
|
|
string graphicType,
|
|
ScenePageSize expectedPageSize)
|
|
{
|
|
var entry = OperatorRow(graphicType, "2/3").ToPlayoutEntry();
|
|
|
|
Assert.Equal(expectedPageSize, entry.PageNavigation?.PageSize);
|
|
Assert.Equal(3, entry.PageNavigation?.FrozenPageCount);
|
|
Assert.True(entry.PageNavigation?.IsEnabled);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("5단 표그래프 ")]
|
|
[InlineData("5단표그래프")]
|
|
[InlineData("five-row")]
|
|
public void NonCanonicalGraphicTypeExplicitlyDisablesPageNext(string graphicType)
|
|
{
|
|
var entry = OperatorRow(graphicType, "1/9").ToPlayoutEntry();
|
|
|
|
Assert.NotNull(entry.PageNavigation);
|
|
Assert.False(entry.PageNavigation.IsEnabled);
|
|
Assert.Null(entry.PageNavigation.PageSize);
|
|
Assert.Null(entry.PageNavigation.FrozenPageCount);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("1/0")]
|
|
[InlineData("1/21")]
|
|
[InlineData("2/1")]
|
|
[InlineData("1 /2")]
|
|
public void CanonicalPagedGraphicRejectsInvalidOperatorPageText(string pageText)
|
|
{
|
|
var row = OperatorRow("5단 표그래프", pageText);
|
|
|
|
Assert.Throws<InvalidOperationException>(row.ToPlayoutEntry);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task NextProgressUpdatesProgramPageWithoutReplacingOperatorSelection()
|
|
{
|
|
var controller = await CreateControllerWithCutsAsync(3);
|
|
var rows = controller.Current.Playlist;
|
|
controller.SelectPlaylistRow(rows[2].RowId, control: false, shift: false);
|
|
|
|
var state = controller.ReflectSuccessfulPlayout(
|
|
rows[0].RowId,
|
|
pageIndexZeroBased: 1,
|
|
pageCount: 3,
|
|
synchronizeOperatorSelection: false);
|
|
|
|
Assert.Equal("2/3", state.Playlist[0].PageText);
|
|
Assert.True(state.Playlist[2].IsSelected);
|
|
Assert.False(state.Playlist[0].IsSelected);
|
|
Assert.Equal(2, controller.CreatePlayoutRequest().SelectedIndexZeroBased);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task TakeSkipMovesOnlyTheF8CandidateAndPreservesDeleteSelection()
|
|
{
|
|
var controller = await CreateControllerWithCutsAsync(2);
|
|
var rows = controller.Current.Playlist;
|
|
controller.SelectPlaylistRow(rows[0].RowId, control: false, shift: false);
|
|
controller.ReflectSuccessfulPlayout(
|
|
rows[0].RowId,
|
|
pageIndexZeroBased: 2,
|
|
pageCount: 3,
|
|
synchronizeOperatorSelection: false);
|
|
|
|
var state = controller.ReflectSuccessfulPlayout(
|
|
rows[1].RowId,
|
|
pageIndexZeroBased: 0,
|
|
pageCount: 2,
|
|
synchronizeOperatorSelection: true);
|
|
|
|
Assert.Equal("1/2", state.Playlist[1].PageText);
|
|
Assert.True(state.Playlist[0].IsSelected);
|
|
Assert.False(state.Playlist[1].IsSelected);
|
|
Assert.True(state.Playlist[1].IsActive);
|
|
Assert.Equal(1, controller.CreatePlayoutRequest().SelectedIndexZeroBased);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SuccessfulF8DoesNotReplaceTheRowsAccumulatedForDelete()
|
|
{
|
|
var controller = await CreateControllerWithCutsAsync(3);
|
|
var rows = controller.Current.Playlist;
|
|
controller.SelectPlaylistRow(rows[0].RowId, control: false, shift: false);
|
|
controller.SelectPlaylistRow(rows[2].RowId, control: true, shift: false);
|
|
|
|
var reflected = controller.ReflectSuccessfulPlayout(
|
|
rows[2].RowId,
|
|
pageIndexZeroBased: 0,
|
|
pageCount: 1,
|
|
synchronizeOperatorSelection: true);
|
|
|
|
Assert.Equal(
|
|
[rows[0].RowId, rows[2].RowId],
|
|
reflected.Playlist.Where(row => row.IsSelected).Select(row => row.RowId));
|
|
Assert.True(reflected.Playlist[2].IsActive);
|
|
|
|
var deleted = controller.DeleteSelectedPlaylistRows();
|
|
Assert.Equal(rows[1].RowId, Assert.Single(deleted.Playlist).RowId);
|
|
}
|
|
|
|
private static async Task<LegacyOperatorController> CreateControllerWithCutsAsync(int count)
|
|
{
|
|
var controller = new LegacyOperatorController(new StaticLookup());
|
|
await controller.SearchStocksAsync("?쇱꽦", LegacySearchTrigger.Button);
|
|
controller.SelectStock(0);
|
|
for (var index = 0; index < count; index++)
|
|
{
|
|
AddCut(controller, index, 1_000 + (index * 1_000));
|
|
}
|
|
|
|
return controller;
|
|
}
|
|
|
|
private static LegacyOperatorPlaylistRow OperatorRow(
|
|
string graphicType,
|
|
string pageText) =>
|
|
new(
|
|
"row-1",
|
|
true,
|
|
"테마",
|
|
"반도체",
|
|
graphicType,
|
|
"현재가",
|
|
pageText,
|
|
"00123",
|
|
"paged-cut",
|
|
6,
|
|
SceneAlias: "5074",
|
|
PlayoutSelection: new(
|
|
"테마",
|
|
"반도체",
|
|
graphicType,
|
|
"현재가",
|
|
"00123"));
|
|
|
|
private static void AddCut(
|
|
LegacyOperatorController controller,
|
|
int physicalIndex,
|
|
long timestamp)
|
|
{
|
|
controller.CutPointerDown(
|
|
physicalIndex,
|
|
control: false,
|
|
shift: false,
|
|
x: 1,
|
|
y: physicalIndex * 23,
|
|
timestampMilliseconds: timestamp);
|
|
controller.DropSelectedCutsOnPlaylist();
|
|
}
|
|
|
|
private sealed class StaticLookup : ILegacyStockLookup
|
|
{
|
|
public Task<LegacyStockSearchData> SearchAsync(
|
|
string query,
|
|
CancellationToken cancellationToken = default) =>
|
|
Task.FromResult(new LegacyStockSearchData(
|
|
query,
|
|
["삼성전자"],
|
|
[new LegacyStockIdentity(
|
|
LegacyDomesticStockMarket.Kospi,
|
|
"삼성전자",
|
|
"005930")]));
|
|
}
|
|
}
|