Files
MBN_STOCK_WEBVIEW/tests/MBN_STOCK_WEBVIEW.LegacyApplication.Tests/LegacyOperatorPlayoutRequestTests.cs

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")]));
}
}