feat: consolidate legacy parity migration baseline

This commit is contained in:
2026-07-20 22:06:12 +09:00
parent 5eb4054120
commit 4a54977341
66 changed files with 5391 additions and 460 deletions

View File

@@ -56,6 +56,52 @@ public sealed class LegacyNamedPlaylistOperatorIntegrationTests
loaded.NamedPlaylist?.DocumentFreshness);
}
[Theory]
[InlineData("fixed-047")]
[InlineData("fixed-044")]
[InlineData("fixed-076")]
[InlineData("fixed-063")]
[InlineData("fixed-085")]
[InlineData("fixed-105")]
[InlineData("fixed-234")]
[InlineData("fixed-236")]
[InlineData("fixed-275")]
[InlineData("fixed-233")]
[InlineData("fixed-240")]
[InlineData("fixed-235")]
[InlineData("fixed-244")]
[InlineData("fixed-302")]
[InlineData("fixed-243")]
[InlineData("fixed-136")]
public async Task Save_and_load_round_trip_restores_fixed_playout_selection(
string actionId)
{
var persistence = new OperatorNamedPlaylistPersistence([]);
var controller = CreateController(persistence);
var action = LegacyFixedActionCatalog.Default.GetAction(actionId);
controller.SelectTab(action.Market switch
{
LegacyFixedMarket.Overseas => LegacyOperatorTabId.Overseas,
LegacyFixedMarket.Exchange => LegacyOperatorTabId.Exchange,
LegacyFixedMarket.Index => LegacyOperatorTabId.Index,
_ => throw new ArgumentOutOfRangeException()
});
controller.ActivateFixedAction(actionId);
var before = Assert.Single(controller.CreatePlayoutRequest().Playlist);
var listed = await controller.RefreshNamedPlaylistsAsync();
var definitionId = Assert.Single(
listed.NamedPlaylist!.Definitions).DefinitionId;
controller.SelectNamedPlaylist(definitionId);
await controller.SaveCurrentNamedPlaylistAsync();
var loaded = await controller.LoadNamedPlaylistByIdAsync(definitionId);
var after = Assert.Single(controller.CreatePlayoutRequest().Playlist);
Assert.True(loaded.CommandResult?.Succeeded);
Assert.Equal(before.CutCode, after.CutCode);
Assert.Equal(before.Selection, after.Selection);
}
[Fact]
public async Task LoadSelected_ReplacesPlaylistWithTypedOriginalSevenFields()
{
@@ -123,11 +169,294 @@ public sealed class LegacyNamedPlaylistOperatorIntegrationTests
Assert.Equal(before.RowId, Assert.Single(after.Playlist).RowId);
Assert.NotNull(after.Dialog);
Assert.Contains("1행", after.Dialog.Message, StringComparison.Ordinal);
Assert.Equal(LegacyOperatorStatusKind.Error, after.StatusKind);
Assert.Equal("load-named-playlist-by-id", after.CommandResult?.Command);
Assert.False(after.CommandResult?.Succeeded);
}
[Theory]
[InlineData("INDIVIDUAL", "INDEX", "MANUAL_NET_SELL", "MANUAL_NET_SELL")]
[InlineData("FOREIGN", "INDEX", "MANUAL_NET_SELL", "MANUAL_NET_SELL")]
[InlineData("INSTITUTION", "INDEX", "MANUAL_NET_SELL", "MANUAL_NET_SELL")]
[InlineData("개인 순매도 상위(수동)", "지수", "순매도 상위", "순매도 상위")]
[InlineData("지수", "개인 순매도 상위(수동)", "순매도 상위", "순매도 상위")]
public async Task Load_by_opaque_id_restores_trusted_manual_net_sell_alias(
string groupCode,
string subject,
string graphicType,
string subtype)
{
var persistence = new OperatorNamedPlaylistPersistence(
[
new NamedPlaylistStoredItem(
0,
true,
new LegacySceneSelection(
groupCode,
subject,
graphicType,
subtype,
string.Empty),
new NamedPlaylistPageState(1, 1))
]);
var controller = CreateController(persistence);
var listed = await controller.RefreshNamedPlaylistsAsync();
var definitionId = Assert.Single(
listed.NamedPlaylist!.Definitions).DefinitionId;
var loaded = await controller.LoadNamedPlaylistByIdAsync(definitionId);
Assert.True(loaded.CommandResult?.Succeeded);
Assert.Null(loaded.Dialog);
var row = Assert.Single(loaded.Playlist);
Assert.Equal("5025", row.SceneAlias);
var expectedAudience = groupCode == "FOREIGN" || subject.StartsWith("외국인", StringComparison.Ordinal)
? "FOREIGN"
: groupCode == "INSTITUTION" || subject.StartsWith("기관", StringComparison.Ordinal)
? "INSTITUTION"
: "INDIVIDUAL";
Assert.Equal(expectedAudience, row.PlayoutSelection?.GroupCode);
Assert.Equal("INDEX", row.PlayoutSelection?.Subject);
Assert.Equal("MANUAL_NET_SELL", row.PlayoutSelection?.GraphicType);
}
[Fact]
public async Task Load_by_opaque_id_restores_versioned_vi_alias()
{
var persistence = new OperatorNamedPlaylistPersistence(
[
new NamedPlaylistStoredItem(
0,
true,
new LegacySceneSelection(
"PAGED_VI",
TrustedViManualReference.SubjectSentinel,
"INPUT_ORDER",
"CURRENT",
new string('a', 64)),
new NamedPlaylistPageState(1, 1))
]);
var controller = CreateController(persistence);
var listed = await controller.RefreshNamedPlaylistsAsync();
var definitionId = Assert.Single(
listed.NamedPlaylist!.Definitions).DefinitionId;
var loaded = await controller.LoadNamedPlaylistByIdAsync(definitionId);
Assert.True(loaded.CommandResult?.Succeeded);
Assert.Null(loaded.Dialog);
Assert.Equal("5074", Assert.Single(loaded.Playlist).SceneAlias);
}
[Fact]
public async Task Load_by_opaque_id_restores_saved_visible_vi_fields_to_versioned_identity()
{
var version = new string('b', 64);
var persistence = new OperatorNamedPlaylistPersistence(
[
new NamedPlaylistStoredItem(
0,
true,
new LegacySceneSelection(
"지수",
"삼성전자,SK하이닉스",
"5단 표그래프",
"VI 발동(수동)",
version),
new NamedPlaylistPageState(1, 1))
]);
var controller = CreateController(persistence);
var listed = await controller.RefreshNamedPlaylistsAsync();
var definitionId = Assert.Single(
listed.NamedPlaylist!.Definitions).DefinitionId;
var loaded = await controller.LoadNamedPlaylistByIdAsync(definitionId);
var row = Assert.Single(loaded.Playlist);
Assert.True(loaded.CommandResult?.Succeeded);
Assert.Equal("5074", row.SceneAlias);
Assert.Equal("PAGED_VI", row.PlayoutSelection?.GroupCode);
Assert.Equal(TrustedViManualReference.SubjectSentinel,
row.PlayoutSelection?.Subject);
Assert.Equal(version, row.PlayoutSelection?.DataCode);
}
[Theory]
[InlineData("액면가", S5011DetailBranch.FaceValue)]
[InlineData("PBR", S5011DetailBranch.Valuation)]
[InlineData("거래량", S5011DetailBranch.Volume)]
public async Task Load_by_opaque_id_restores_saved_stock_detail_branch(
string visibleSubtype,
S5011DetailBranch expectedBranch)
{
var persistence = new OperatorNamedPlaylistPersistence(
[
new NamedPlaylistStoredItem(
0,
true,
new LegacySceneSelection(
"코스피",
"삼성전자",
"1열판상세",
visibleSubtype,
"005930"),
new NamedPlaylistPageState(1, 1))
]);
var controller = CreateController(persistence);
var listed = await controller.RefreshNamedPlaylistsAsync();
var loaded = await controller.LoadNamedPlaylistByIdAsync(
Assert.Single(listed.NamedPlaylist!.Definitions).DefinitionId);
var row = Assert.Single(loaded.Playlist);
Assert.True(loaded.CommandResult?.Succeeded);
Assert.Equal("5011", row.SceneAlias);
Assert.Equal(expectedBranch.ToString(), row.PlayoutSelection?.Subtype);
Assert.Equal("005930", row.PlayoutSelection?.DataCode);
}
[Fact]
public async Task Load_by_opaque_id_restores_saved_industry_square_selection()
{
var persistence = new OperatorNamedPlaylistPersistence(
[
new NamedPlaylistStoredItem(
0,
true,
new LegacySceneSelection(
"업종_코스피",
"코스피 네모그래프",
"네모그래프",
string.Empty,
string.Empty),
new NamedPlaylistPageState(1, 1))
]);
var controller = CreateController(persistence);
var listed = await controller.RefreshNamedPlaylistsAsync();
var loaded = await controller.LoadNamedPlaylistByIdAsync(
Assert.Single(listed.NamedPlaylist!.Definitions).DefinitionId);
var row = Assert.Single(loaded.Playlist);
Assert.True(loaded.CommandResult?.Succeeded);
Assert.Equal("8001", row.SceneAlias);
Assert.Equal(
new LegacySceneSelection(
"INDUSTRY_KOSPI",
"KOSPI",
"네모그래프",
string.Empty,
string.Empty),
row.PlayoutSelection);
}
[Fact]
public async Task Load_by_opaque_id_restores_saved_comparison_selections()
{
var stockIdentity = "KOSPI:016360|KOSPI:068290";
var persistence = new OperatorNamedPlaylistPersistence(
[
StoredComparison(0, "s5026", "종목별 비교분석_캔들 그래프", stockIdentity),
StoredComparison(1, "s5087", "종목별 비교분석_라인 그래프", stockIdentity),
StoredComparison(2, "s5029", "5일", stockIdentity),
new NamedPlaylistStoredItem(
3,
true,
new LegacySceneSelection(
"비교",
"코스닥 지수 ↔ 코스피 지수",
"s8018",
"2열판",
string.Empty),
new NamedPlaylistPageState(1, 1)),
new NamedPlaylistStoredItem(
4,
true,
new LegacySceneSelection(
"비교",
"선물 지수 ↔ 코스피 지수",
"s5032",
"2열판",
string.Empty),
new NamedPlaylistPageState(1, 1))
]);
var controller = CreateController(persistence);
var listed = await controller.RefreshNamedPlaylistsAsync();
var loaded = await controller.LoadNamedPlaylistByIdAsync(
Assert.Single(listed.NamedPlaylist!.Definitions).DefinitionId);
Assert.True(loaded.CommandResult?.Succeeded);
Assert.Equal(
["5026", "5087", "5029", "8032", "5032"],
loaded.Playlist.Select(row => row.SceneAlias));
Assert.Equal(
["COMPARISON_CANDLE", "COMPARISON_LINE", "RETURN_COMPARISON"],
loaded.Playlist.Take(3).Select(row => row.PlayoutSelection?.GraphicType));
Assert.All(loaded.Playlist.Take(3), row =>
{
Assert.Equal("STOCK", row.PlayoutSelection?.GroupCode);
Assert.Equal("삼성증권,삼성출판사", row.PlayoutSelection?.Subject);
Assert.Equal(stockIdentity, row.PlayoutSelection?.DataCode);
});
Assert.Equal("Kosdaq,Kospi", loaded.Playlist[3].PlayoutSelection?.Subject);
Assert.Equal("Futures,Kospi", loaded.Playlist[4].PlayoutSelection?.Subject);
}
[Theory]
[InlineData("KOSDAQ", "APS", "주요매출 구성", "REVENUE_COMPOSITION")]
[InlineData("KOSPI", "AJ네트웍스", "성장성 지표", "GROWTH_METRICS")]
[InlineData("KOSDAQ", "3S", "매출액", "SALES")]
[InlineData("KOSDAQ", "3S", "영업이익", "OPERATING_PROFIT")]
public async Task Load_by_opaque_id_restores_saved_manual_financial_selection(
string market,
string stockName,
string visibleGraphicType,
string canonicalGraphicType)
{
var persistence = new OperatorNamedPlaylistPersistence(
[
new NamedPlaylistStoredItem(
0,
true,
new LegacySceneSelection(
market,
stockName,
visibleGraphicType,
canonicalGraphicType,
"000001"),
new NamedPlaylistPageState(1, 1))
]);
var controller = CreateController(persistence);
var listed = await controller.RefreshNamedPlaylistsAsync();
var loaded = await controller.LoadNamedPlaylistByIdAsync(
Assert.Single(listed.NamedPlaylist!.Definitions).DefinitionId);
var row = Assert.Single(loaded.Playlist);
Assert.True(loaded.CommandResult?.Succeeded);
Assert.Equal(canonicalGraphicType, row.PlayoutSelection?.GraphicType);
Assert.Equal(string.Empty, row.PlayoutSelection?.Subtype);
Assert.Equal(string.Empty, row.PlayoutSelection?.DataCode);
}
private static NamedPlaylistStoredItem StoredComparison(
int itemIndex,
string builderKey,
string detail,
string dataCode) =>
new(
itemIndex,
true,
new LegacySceneSelection(
"비교",
"삼성증권 ↔ 삼성출판사",
builderKey,
detail,
dataCode),
new NamedPlaylistPageState(1, 1));
[Fact]
public async Task SaveCurrent_WritesVisibleSevenFieldsOnceAndReadsBackFresh()
{