using MBN_STOCK_WEBVIEW.Core.Playout.Scenes; using MMoneyCoderSharp.Data; namespace MBN_STOCK_WEBVIEW.LegacyApplication.Tests; public sealed class LegacyOperatorComparisonIntegrationTests { [Fact] public async Task ComparisonTabBuildsPairAndAddsAuthoritativeSceneRow() { var data = new StaticComparisonDataService(); var store = new MemoryComparisonPairStore(); var controller = new LegacyOperatorController( new EmptyStockLookup(), comparisonWorkflow: new LegacyComparisonWorkflowController(data, store)); var selected = await controller.SelectTabAsync(LegacyOperatorTabId.Comparison); Assert.NotNull(selected.Comparison); Assert.Empty(selected.Comparison.SavedPairs); var searched = await controller.SearchComparisonDomesticAsync("반도체"); var results = searched.Comparison!.DomesticSearch.Results; controller.ChooseComparisonDomesticResult(results[0].SelectionId); controller.ChooseComparisonDomesticResult(results[1].SelectionId); var saved = await controller.AddComparisonPairAsync(); Assert.Single(saved.Comparison!.SavedPairs); Assert.Single(store.Writes); var added = controller.ActivateComparisonAction("comparison-candle"); var row = Assert.Single(added.Playlist); Assert.Equal("5026", row.ToPlayoutEntry().CutCode); Assert.Equal( "KOSPI:000660|KOSPI:005930", Assert.IsType(row.ToPlayoutEntry().Selection).DataCode); Assert.Equal("비교", row.MarketText); } [Fact] public async Task ComparisonParentDoubleClickAddsOnlyDirectChildrenInOriginalOrder() { var controller = new LegacyOperatorController( new EmptyStockLookup(), comparisonWorkflow: new LegacyComparisonWorkflowController( new StaticComparisonDataService(), new MemoryComparisonPairStore())); await controller.SelectTabAsync(LegacyOperatorTabId.Comparison); var searched = await controller.SearchComparisonDomesticAsync("반도체"); controller.ChooseComparisonDomesticResult( searched.Comparison!.DomesticSearch.Results[0].SelectionId); controller.ChooseComparisonDomesticResult( searched.Comparison.DomesticSearch.Results[1].SelectionId); await controller.AddComparisonPairAsync(); var added = controller.ActivateComparisonSection(1); Assert.Collection( added.Playlist, row => Assert.Equal("5026", row.SceneAlias), row => Assert.Equal("5087", row.SceneAlias)); } private sealed class EmptyStockLookup : ILegacyStockLookup { public Task SearchAsync( string query, CancellationToken cancellationToken = default) => Task.FromResult( new LegacyStockSearchData(query, [], [])); } private sealed class StaticComparisonDataService : ILegacyComparisonDataService { private static readonly IReadOnlyList Domestic = [ LegacyComparisonTarget.CreateKrxStock( LegacyComparisonDomesticMarket.Kospi, "삼성전자", "005930"), LegacyComparisonTarget.CreateKrxStock( LegacyComparisonDomesticMarket.Kospi, "SK하이닉스", "000660") ]; public Task> SearchDomesticAsync( string query, int maximumResults, CancellationToken cancellationToken = default) => Task.FromResult(Domestic); public Task> SearchWorldAsync( string query, int maximumResults, CancellationToken cancellationToken = default) => Task.FromResult< IReadOnlyList>([]); public Task ResolveStoredTargetAsync( LegacyComparisonStoredTargetReference target, CancellationToken cancellationToken = default) => Task.FromResult< LegacyComparisonTarget?>(null); } private sealed class MemoryComparisonPairStore : ILegacyComparisonPairStore { public List> Writes { get; } = []; public Task ReadCp949Async(CancellationToken cancellationToken = default) => Task.FromResult(null); public Task WriteCp949Async( IReadOnlyList rows, CancellationToken cancellationToken = default) { Writes.Add(rows.ToArray()); return Task.CompletedTask; } } } public sealed class LegacyComparisonProductionAdapterTests { [Fact] public async Task DataAdapterMapsDomesticNxtAndWorldIdentities() { var lookup = new StaticStockLookup(); var world = new StaticWorldLookup(); var service = new LegacyComparisonDataService(lookup, world); var domestic = await service.SearchDomesticAsync("삼성", 10); Assert.Equal(LegacyComparisonTargetKind.KrxStock, domestic[0].Kind); Assert.Equal(LegacyComparisonTargetKind.NxtStock, domestic[1].Kind); Assert.Equal("삼성전자(NXT)", domestic[1].DisplayName); var overseas = Assert.Single(await service.SearchWorldAsync("APPLE", 10)); Assert.Equal("world:US:AAPL", overseas.TargetId); var resolved = await service.ResolveStoredTargetAsync( new LegacyComparisonStoredTargetReference("APPLE", string.Empty)); Assert.Equal(overseas, resolved); } [Fact] public async Task FileStoreRoundTripsStrictCp949WithAtomicTarget() { var directory = Path.Combine( Path.GetTempPath(), $"mbn-comparison-{Guid.NewGuid():N}"); var path = Path.Combine(directory, "종목비교.dat"); try { var store = new LegacyComparisonPairFileStore(path); await store.WriteCp949Async( [ new LegacyComparisonPairWriteRow( 1, "삼성전자", "코스피", "SK하이닉스", "코스피") ]); var bytes = Assert.IsType(await store.ReadCp949Async()); var parsed = Assert.Single(LegacyComparisonFileParser.Parse(bytes)); Assert.Equal("삼성전자", parsed.First.Name); Assert.Empty(Directory.EnumerateFiles(directory, "*.tmp")); } finally { if (Directory.Exists(directory)) { Directory.Delete(directory, recursive: true); } } } private sealed class StaticStockLookup : ILegacyStockLookup { public Task SearchAsync( string query, CancellationToken cancellationToken = default) => Task.FromResult( new LegacyStockSearchData( query, ["삼성전자", "삼성전자(NXT)"], [ new LegacyStockIdentity( LegacyDomesticStockMarket.Kospi, "삼성전자", "005930"), new LegacyStockIdentity( LegacyDomesticStockMarket.NxtKospi, "삼성전자(NXT)", "005930") ])); } private sealed class StaticWorldLookup : IWorldStockSearchService { public Task SearchAsync( string query, int maximumResults = LegacyWorldStockSearchService.DefaultMaximumResults, CancellationToken cancellationToken = default) => Task.FromResult( new WorldStockSearchResult( query.Trim(), DateTimeOffset.UtcNow, [new WorldStockSearchItem("APPLE", "AAPL", "US")], false)); } }