feat: align legacy operator and playout behavior
This commit is contained in:
@@ -38,6 +38,81 @@ public sealed class LegacyBridgeProtocolTests
|
||||
Assert.Equal(1_400, pointer.TimestampMilliseconds);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(49, true)]
|
||||
[InlineData(50, false)]
|
||||
public void ParsePointer_UsesTheFullFiftyRowRuntimeMenuBoundary(
|
||||
int physicalIndex,
|
||||
bool expected)
|
||||
{
|
||||
var json =
|
||||
"{\"type\":\"cut-pointer-down\",\"payload\":{" +
|
||||
$"\"physicalIndex\":{physicalIndex},\"control\":false," +
|
||||
"\"shift\":false,\"x\":0,\"y\":0,\"timestampMilliseconds\":0}}";
|
||||
|
||||
Assert.Equal(expected, LegacyBridgeProtocol.TryParseIntent(
|
||||
json,
|
||||
out var parsed,
|
||||
out _));
|
||||
if (expected)
|
||||
{
|
||||
Assert.Equal(
|
||||
physicalIndex,
|
||||
Assert.IsType<LegacyCutPointerDownIntent>(parsed).PhysicalIndex);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("arrow-up", LegacyCutKeyboardKey.ArrowUp)]
|
||||
[InlineData("arrow-down", LegacyCutKeyboardKey.ArrowDown)]
|
||||
[InlineData("home", LegacyCutKeyboardKey.Home)]
|
||||
[InlineData("end", LegacyCutKeyboardKey.End)]
|
||||
[InlineData("space", LegacyCutKeyboardKey.Space)]
|
||||
public void ParseCutKeyDown_AcceptsOnlyClosedKeysAndModifiers(
|
||||
string key,
|
||||
LegacyCutKeyboardKey expected)
|
||||
{
|
||||
var json =
|
||||
"{\"type\":\"cut-key-down\",\"payload\":{" +
|
||||
$"\"key\":\"{key}\",\"control\":true,\"shift\":false}}}}";
|
||||
|
||||
Assert.True(LegacyBridgeProtocol.TryParseIntent(json, out var parsed, out _));
|
||||
var intent = Assert.IsType<LegacyCutKeyDownIntent>(parsed);
|
||||
Assert.Equal(expected, intent.Key);
|
||||
Assert.True(intent.Control);
|
||||
Assert.False(intent.Shift);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("page-down", "\"control\":false,\"shift\":false")]
|
||||
[InlineData("ArrowUp", "\"control\":false,\"shift\":false")]
|
||||
[InlineData("home", "\"control\":0,\"shift\":false")]
|
||||
[InlineData("home", "\"control\":false,\"shift\":false,\"physicalIndex\":0")]
|
||||
public void ParseCutKeyDown_RejectsUnknownOrAdditionalAuthority(
|
||||
string key,
|
||||
string fields)
|
||||
{
|
||||
var json =
|
||||
"{\"type\":\"cut-key-down\",\"payload\":{" +
|
||||
$"\"key\":\"{key}\",{fields}}}}}";
|
||||
|
||||
Assert.False(LegacyBridgeProtocol.TryParseIntent(json, out _, out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseClearCutSelection_AcceptsOnlyAnEmptyPayload()
|
||||
{
|
||||
Assert.True(LegacyBridgeProtocol.TryParseIntent(
|
||||
"{\"type\":\"clear-cut-selection\",\"payload\":{}}",
|
||||
out var parsed,
|
||||
out _));
|
||||
Assert.IsType<LegacyClearCutSelectionIntent>(parsed);
|
||||
Assert.False(LegacyBridgeProtocol.TryParseIntent(
|
||||
"{\"type\":\"clear-cut-selection\",\"payload\":{\"physicalIndex\":0}}",
|
||||
out _,
|
||||
out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsePlaylistSelection_PreservesModifiersAndRejectsUnsafeAuthority()
|
||||
{
|
||||
@@ -61,6 +136,27 @@ public sealed class LegacyBridgeProtocolTests
|
||||
out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsePlaylistActivation_AcceptsOnlyOneOpaqueRowId()
|
||||
{
|
||||
Assert.True(LegacyBridgeProtocol.TryParseIntent(
|
||||
"{\"type\":\"activate-playlist-row\",\"payload\":{\"rowId\":\"legacy-stock-00000003\"}}",
|
||||
out var parsed,
|
||||
out _));
|
||||
Assert.Equal(
|
||||
"legacy-stock-00000003",
|
||||
Assert.IsType<LegacyActivatePlaylistRowIntent>(parsed).RowId);
|
||||
|
||||
Assert.False(LegacyBridgeProtocol.TryParseIntent(
|
||||
"{\"type\":\"activate-playlist-row\",\"payload\":{\"rowId\":\"../row\"}}",
|
||||
out _,
|
||||
out _));
|
||||
Assert.False(LegacyBridgeProtocol.TryParseIntent(
|
||||
"{\"type\":\"activate-playlist-row\",\"payload\":{\"rowId\":\"legacy-stock-00000003\",\"enabled\":false}}",
|
||||
out _,
|
||||
out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsePlaylistReorder_UsesOnlyOpaqueRowsAndClosedDropPosition()
|
||||
{
|
||||
@@ -427,7 +523,7 @@ public sealed class LegacyBridgeProtocolTests
|
||||
"삼성",
|
||||
[new LegacyStockResultRow(0, "삼성전자")],
|
||||
0,
|
||||
[new LegacyCutViewRow(1, 2, "1열판기본_현재가", false, true)],
|
||||
[new LegacyCutViewRow(1, 2, "1열판기본_현재가", false, true, true)],
|
||||
[playlistRow],
|
||||
null,
|
||||
string.Empty,
|
||||
@@ -450,10 +546,45 @@ public sealed class LegacyBridgeProtocolTests
|
||||
Assert.Equal(
|
||||
"1열판기본_현재가",
|
||||
payload.GetProperty("cutRows")[0].GetProperty("rawLabel").GetString());
|
||||
Assert.True(payload.GetProperty("cutRows")[0].GetProperty("isFocused").GetBoolean());
|
||||
Assert.False(payload.GetProperty("playlist")[0].GetProperty("isSelected").GetBoolean());
|
||||
Assert.False(payload.GetProperty("playlist")[0].GetProperty("isActive").GetBoolean());
|
||||
Assert.True(payload.GetProperty("isBusy").GetBoolean());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SerializedState_PreservesInjectedRuntimeCutMenuOrderAndSeparators()
|
||||
{
|
||||
var controller = new LegacyOperatorController(
|
||||
new EmptyStockLookup(),
|
||||
cutRows:
|
||||
[
|
||||
new LegacyCutCatalogRow(
|
||||
0,
|
||||
1,
|
||||
LegacyStockCutCatalog.Rows[1].RawLabel,
|
||||
IsSeparator: false),
|
||||
new LegacyCutCatalogRow(1, null, string.Empty, IsSeparator: true),
|
||||
new LegacyCutCatalogRow(
|
||||
2,
|
||||
2,
|
||||
LegacyStockCutCatalog.Rows[0].RawLabel,
|
||||
IsSeparator: false)
|
||||
]);
|
||||
|
||||
var json = LegacyBridgeProtocol.SerializeState(controller.Current);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var rows = document.RootElement.GetProperty("payload").GetProperty("cutRows");
|
||||
Assert.Equal(3, rows.GetArrayLength());
|
||||
Assert.Equal("1열판기본_현재가", rows[0].GetProperty("rawLabel").GetString());
|
||||
Assert.Equal(0, rows[0].GetProperty("physicalIndex").GetInt32());
|
||||
Assert.True(rows[1].GetProperty("isSeparator").GetBoolean());
|
||||
Assert.Equal(string.Empty, rows[1].GetProperty("rawLabel").GetString());
|
||||
Assert.Equal("1열판기본_예상체결가", rows[2].GetProperty("rawLabel").GetString());
|
||||
Assert.Equal(2, rows[2].GetProperty("displayOrdinal").GetInt32());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SerializedNamedPlaylist_ProjectsOnlyOpaqueListAndSafetyState()
|
||||
{
|
||||
@@ -540,4 +671,12 @@ public sealed class LegacyBridgeProtocolTests
|
||||
string programCode,
|
||||
CancellationToken cancellationToken = default) => Task.CompletedTask;
|
||||
}
|
||||
|
||||
private sealed class EmptyStockLookup : ILegacyStockLookup
|
||||
{
|
||||
public Task<LegacyStockSearchData> SearchAsync(
|
||||
string query,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult(new LegacyStockSearchData(query, [], []));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,9 +65,25 @@ public sealed class LegacyPlayoutBridgeTests
|
||||
Assert.IsType<LegacyChooseBackgroundIntent>(choose);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(6)]
|
||||
[InlineData(19)]
|
||||
public void FadeDuration_AcceptsOriginalComboIndexRange(int duration)
|
||||
{
|
||||
Assert.True(LegacyBridgeProtocol.TryParseIntent(
|
||||
"{\"type\":\"set-fade-duration\",\"payload\":{\"duration\":" +
|
||||
duration + "}}",
|
||||
out var intent,
|
||||
out var error), error);
|
||||
Assert.Equal(duration,
|
||||
Assert.IsType<LegacySetFadeDurationIntent>(intent).Duration);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("-1")]
|
||||
[InlineData("61")]
|
||||
[InlineData("20")]
|
||||
[InlineData("60")]
|
||||
[InlineData("1.5")]
|
||||
[InlineData("\"6\"")]
|
||||
[InlineData("null")]
|
||||
@@ -124,7 +140,10 @@ public sealed class LegacyPlayoutBridgeTests
|
||||
BackgroundEnabled: true,
|
||||
BackgroundFileName: "기본.vrv",
|
||||
CanChangeBackground: false,
|
||||
Message: "준비 완료");
|
||||
Message: "준비 완료")
|
||||
{
|
||||
IsTakeOutCompletionPending = true
|
||||
};
|
||||
|
||||
using var document = JsonDocument.Parse(
|
||||
LegacyBridgeProtocol.SerializeState(snapshot, playout: playout));
|
||||
@@ -137,7 +156,8 @@ public sealed class LegacyPlayoutBridgeTests
|
||||
Assert.Equal("pageNext", projected.GetProperty("nextKind").GetString());
|
||||
Assert.Equal(2, projected.GetProperty("pageCount").GetInt32());
|
||||
Assert.Equal("기본.vrv", projected.GetProperty("backgroundFileName").GetString());
|
||||
Assert.False(projected.TryGetProperty("currentEntryId", out _));
|
||||
Assert.Equal("entry-1", projected.GetProperty("currentEntryId").GetString());
|
||||
Assert.True(projected.GetProperty("isTakeOutCompletionPending").GetBoolean());
|
||||
Assert.False(projected.TryGetProperty("builderKey", out _));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user