using MMoneyCoderSharp.Data; namespace MBN_STOCK_WEBVIEW.LegacyApplication.Tests; public sealed class LegacyTabActivationParityTests { [Fact] public async Task ThemeEachActivation_LoadsOriginalEmptyQueryOncePerActivation() { // Original evidence: Control/UC4.cs UC4_Load starts timer1 and its first // timer1_Tick calls GetThemeByKeyword with the initially empty txts value. var service = new CountingThemeService(); var controller = new LegacyOperatorController( Lookup(), themeWorkflow: new LegacyThemeWorkflow(service)); var first = await controller.SelectTabAsync(LegacyOperatorTabId.Theme); Assert.Equal(string.Empty, service.Queries.Single()); Assert.Single(first.Theme!.SearchResults); await controller.SelectTabAsync(LegacyOperatorTabId.Theme); Assert.Single(service.Queries); await controller.SelectTabAsync(LegacyOperatorTabId.Index); var second = await controller.SelectTabAsync(LegacyOperatorTabId.Theme); Assert.Collection( service.Queries, query => Assert.Equal(string.Empty, query), query => Assert.Equal(string.Empty, query)); Assert.Single(second.Theme!.SearchResults); } [Fact] public async Task ExpertEachActivation_LoadsOriginalFullListOncePerActivation() { // Original evidence: Control/UC6.cs UC6_Load calls Data_Load, whose query // reads the complete EXPERT_LIST without a filter. var service = new CountingExpertService(); var controller = new LegacyOperatorController( Lookup(), expertWorkflow: new LegacyExpertWorkflowController(service)); var first = await controller.SelectTabAsync(LegacyOperatorTabId.Expert); Assert.Equal(string.Empty, service.Queries.Single()); Assert.Single(first.Expert!.Search.Results); await controller.SelectTabAsync(LegacyOperatorTabId.Expert); Assert.Single(service.Queries); await controller.SelectTabAsync(LegacyOperatorTabId.Index); var second = await controller.SelectTabAsync(LegacyOperatorTabId.Expert); Assert.Collection( service.Queries, query => Assert.Equal(string.Empty, query), query => Assert.Equal(string.Empty, query)); Assert.Single(second.Expert!.Search.Results); } [Fact] public async Task DragSwapThatActivatesTheme_UsesTheSameNativeInitialLoad() { var service = new CountingThemeService(); var controller = new LegacyOperatorController( Lookup(), themeWorkflow: new LegacyThemeWorkflow(service)); var state = await controller.SwapTabsAsync( LegacyOperatorTabId.Theme, LegacyOperatorTabId.Exchange); Assert.Equal(string.Empty, service.Queries.Single()); Assert.Single(state.Theme!.SearchResults); Assert.Equal( LegacyOperatorTabId.Theme, Assert.Single(state.Tabs!, tab => tab.IsActive).Id); } [Fact] public async Task SupersededThemeActivation_DoesNotCommitAndRetriesWhenNeeded() { var service = new DelayedThemeService(); var controller = new LegacyOperatorController( Lookup(), themeWorkflow: new LegacyThemeWorkflow(service)); var firstActivation = controller.SelectTabAsync(LegacyOperatorTabId.Theme); await service.FirstRequestStarted.Task.WaitAsync(TimeSpan.FromSeconds(5)); controller.SelectTab(LegacyOperatorTabId.Index); service.ReleaseFirstRequest.TrySetResult(); var superseded = await firstActivation; Assert.Equal( LegacyOperatorTabId.Index, Assert.Single(superseded.Tabs!, tab => tab.IsActive).Id); var retried = await controller.SelectTabAsync(LegacyOperatorTabId.Theme); Assert.Equal(2, service.CallCount); Assert.Single(retried.Theme!.SearchResults); } private static ILegacyStockLookup Lookup() => new StaticLookup(new LegacyStockSearchData(string.Empty, [], [])); private sealed class StaticLookup : ILegacyStockLookup { private readonly LegacyStockSearchData _data; public StaticLookup(LegacyStockSearchData data) { _data = data; } public Task SearchAsync( string text, CancellationToken cancellationToken = default) => Task.FromResult(_data); } private sealed class CountingThemeService : IThemeSelectionService { private static readonly ThemeSelectionIdentity Identity = new( ThemeMarket.Krx, ThemeSession.NotApplicable, "00000001", "Theme A"); public List Queries { get; } = []; public Task SearchAsync( string query, ThemeSession nxtSession, int maximumResults = LegacyThemeSelectionService.DefaultMaximumResults, CancellationToken cancellationToken = default) { Queries.Add(query); return Task.FromResult(CreateThemeResult(query, nxtSession)); } public Task PreviewAsync( ThemeSelectionIdentity identity, int maximumItems = LegacyThemeSelectionService.DefaultMaximumPreviewItems, CancellationToken cancellationToken = default) => Task.FromResult( new ThemePreviewResult(identity, DateTimeOffset.UtcNow, [], false)); public static ThemeSearchResult CreateThemeResult( string query, ThemeSession nxtSession) => new( query, nxtSession, DateTimeOffset.UtcNow, [new ThemeSelectorItem(Identity, DataSourceKind.Oracle)], false); } private sealed class DelayedThemeService : IThemeSelectionService { private int _callCount; public TaskCompletionSource FirstRequestStarted { get; } = new( TaskCreationOptions.RunContinuationsAsynchronously); public TaskCompletionSource ReleaseFirstRequest { get; } = new( TaskCreationOptions.RunContinuationsAsynchronously); public int CallCount => Volatile.Read(ref _callCount); public async Task SearchAsync( string query, ThemeSession nxtSession, int maximumResults = LegacyThemeSelectionService.DefaultMaximumResults, CancellationToken cancellationToken = default) { if (Interlocked.Increment(ref _callCount) == 1) { FirstRequestStarted.TrySetResult(); await ReleaseFirstRequest.Task.WaitAsync(cancellationToken); } return CountingThemeService.CreateThemeResult(query, nxtSession); } public Task PreviewAsync( ThemeSelectionIdentity identity, int maximumItems = LegacyThemeSelectionService.DefaultMaximumPreviewItems, CancellationToken cancellationToken = default) => Task.FromResult( new ThemePreviewResult(identity, DateTimeOffset.UtcNow, [], false)); } private sealed class CountingExpertService : IExpertSelectionService { private static readonly ExpertSelectionIdentity Identity = new("0001", "Expert A"); public List Queries { get; } = []; public Task SearchAsync( string query = "", int maximumResults = LegacyExpertSelectionService.DefaultMaximumResults, CancellationToken cancellationToken = default) { Queries.Add(query); return Task.FromResult(new ExpertSearchResult( query, DateTimeOffset.UtcNow, [new ExpertSelectorItem(Identity, DataSourceKind.Oracle)], false)); } public Task PreviewAsync( ExpertSelectionIdentity identity, int maximumItems = LegacyExpertSelectionService.DefaultMaximumPreviewItems, CancellationToken cancellationToken = default) => Task.FromResult( new ExpertPreviewResult(identity, DateTimeOffset.UtcNow, [], false)); } }