using System.Text.Json; using MBN_STOCK_WEBVIEW.LegacyApplication; using MBN_STOCK_WEBVIEW.LegacyBridge; using MMoneyCoderSharp.Data; namespace MBN_STOCK_WEBVIEW.LegacyBridge.Tests; public sealed class LegacyOverseasBridgeTests { [Theory] [InlineData( "{\"type\":\"select-overseas-fixed-index\",\"payload\":{\"targetId\":\"nasdaq\"}}", typeof(LegacySelectOverseasFixedIndexIntent))] [InlineData( "{\"type\":\"search-overseas-industries\",\"payload\":{\"query\":\"반도체\"}}", typeof(LegacySearchOverseasIndustriesIntent))] [InlineData( "{\"type\":\"search-overseas-stocks\",\"payload\":{\"query\":\"Apple\"}}", typeof(LegacySearchOverseasStocksIntent))] [InlineData( "{\"type\":\"select-overseas-industry\",\"payload\":{\"selectionId\":\"overseas-industry-00000001-0001\"}}", typeof(LegacySelectOverseasIndustryIntent))] [InlineData( "{\"type\":\"select-overseas-stock\",\"payload\":{\"selectionId\":\"overseas-stock-00000001-0001\"}}", typeof(LegacySelectOverseasStockIntent))] [InlineData( "{\"type\":\"activate-overseas-action\",\"payload\":{\"actionId\":\"stock-candle-20d\"}}", typeof(LegacyActivateOverseasActionIntent))] public void ParsesStrictOverseasIntents(string json, Type expectedType) { Assert.True(LegacyBridgeProtocol.TryParseIntent(json, out var intent, out var error)); Assert.Null(error); Assert.IsType(expectedType, intent); } [Theory] [InlineData("{\"type\":\"select-overseas-fixed-index\",\"payload\":{\"targetId\":\"not-registered\"}}")] [InlineData("{\"type\":\"activate-overseas-action\",\"payload\":{\"actionId\":\"8061\"}}")] [InlineData("{\"type\":\"select-overseas-stock\",\"payload\":{\"selectionId\":\"../../stock\"}}")] [InlineData("{\"type\":\"search-overseas-industries\",\"payload\":{\"query\":\"bad\\u0001query\"}}")] [InlineData("{\"type\":\"search-overseas-stocks\",\"payload\":{\"query\":\"Apple\",\"symbol\":\"NAS@AAPL\"}}")] [InlineData("{\"type\":\"activate-overseas-action\",\"payload\":{\"actionId\":12}}")] public void RejectsUnknownMalformedAndAuthorityBearingOverseasIntents(string json) { Assert.False(LegacyBridgeProtocol.TryParseIntent(json, out var intent, out var error)); Assert.Null(intent); Assert.NotNull(error); } [Fact] public async Task ProjectionExposesDisplayStateAndOpaqueIdsButNoSceneAuthority() { var workflow = new LegacyOverseasSelectionWorkflow( new StaticIndustryService(), new StaticStockService()); workflow.SelectFixedIndex("nasdaq"); var industries = await workflow.SearchIndustriesAsync("반도체"); workflow.SelectIndustry(industries.Industry.Results[0].SelectionId); var stocks = await workflow.SearchStocksAsync("Apple"); workflow.SelectStock(stocks.Stock.Results[0].SelectionId); var snapshot = new LegacyOperatorSnapshot( 7, string.Empty, [], null, [], [], null, string.Empty, LegacyOperatorStatusKind.Neutral, Overseas: workflow.Current); var json = LegacyBridgeProtocol.SerializeState(snapshot); using var document = JsonDocument.Parse(json); var overseas = document.RootElement.GetProperty("payload").GetProperty("overseas"); Assert.Equal(10, overseas.GetProperty("fixedIndexTargets").GetArrayLength()); Assert.Equal("nasdaq", overseas.GetProperty("selectedFixedIndexId").GetString()); Assert.Equal("나스닥", overseas.GetProperty("selectedFixedIndexName").GetString()); Assert.Equal( "overseas-industry-00000001-0001", overseas.GetProperty("industry").GetProperty("selectedId").GetString()); Assert.Equal( "overseas-stock-00000001-0001", overseas.GetProperty("stock").GetProperty("selectedId").GetString()); Assert.Equal(12, overseas.GetProperty("actions").GetArrayLength()); Assert.All( overseas.GetProperty("actions").EnumerateArray(), action => Assert.True(action.GetProperty("isAvailable").GetBoolean())); Assert.DoesNotContain("builderKey", json, StringComparison.OrdinalIgnoreCase); Assert.DoesNotContain("sceneAlias", json, StringComparison.OrdinalIgnoreCase); Assert.DoesNotContain("dataCode", json, StringComparison.OrdinalIgnoreCase); Assert.DoesNotContain("NAS@AAPL", json, StringComparison.Ordinal); Assert.DoesNotContain("NAS@SOX", json, StringComparison.Ordinal); } private sealed class StaticIndustryService : IOverseasIndustryIndexSearchService { public Task SearchAsync( string query, int maximumResults = LegacyOverseasIndustryIndexSearchService.DefaultMaximumResults, CancellationToken cancellationToken = default) => Task.FromResult(new OverseasIndustryIndexSearchResult( query, DateTimeOffset.Parse("2026-07-15T10:00:00+09:00"), [new OverseasIndustryIndexItem("반도체", "US Semiconductor", "NAS@SOX")], IsTruncated: false)); } private sealed class StaticStockService : IWorldStockSearchService { public Task SearchAsync( string query, int maximumResults = LegacyWorldStockSearchService.DefaultMaximumResults, CancellationToken cancellationToken = default) => Task.FromResult(new WorldStockSearchResult( query, DateTimeOffset.Parse("2026-07-15T10:00:00+09:00"), [new WorldStockSearchItem("Apple", "NAS@AAPL", "US")], IsTruncated: false)); } }