252 lines
11 KiB
C#
252 lines
11 KiB
C#
using MMoneyCoderSharp.Data;
|
|
|
|
namespace MBN_STOCK_WEBVIEW.LegacyApplication.Tests;
|
|
|
|
public sealed class LegacyTradingHaltWorkflowTests
|
|
{
|
|
private static readonly TradingHaltSelection KospiAlpha =
|
|
new(TradingHaltMarket.Kospi, "005930", "가온전자");
|
|
|
|
private static readonly TradingHaltSelection KospiBeta =
|
|
new(TradingHaltMarket.Kospi, "000660", "한빛반도체");
|
|
|
|
private static readonly TradingHaltSelection KosdaqAlpha =
|
|
new(TradingHaltMarket.Kosdaq, "035720", "다온테크");
|
|
|
|
[Fact]
|
|
public async Task Search_AutomaticallyActivatesFirstFarPointRowAndActions()
|
|
{
|
|
var service = new DelegateTradingHaltService
|
|
{
|
|
Search = (query, _, _) => Task.FromResult(
|
|
SearchResult(query, KospiAlpha, KospiBeta, KosdaqAlpha))
|
|
};
|
|
var controller = new LegacyTradingHaltWorkflowController(service);
|
|
|
|
var searched = await controller.SearchAsync(string.Empty);
|
|
var current = controller.MaterializeSelectedAction(
|
|
LegacyTradingHaltWorkflowController.CurrentActionId);
|
|
|
|
Assert.Equal(searched.Results[0].SelectionId, searched.Selected?.SelectionId);
|
|
Assert.True(searched.Results[0].IsSelected);
|
|
Assert.All(searched.Actions, action => Assert.True(action.IsAvailable));
|
|
Assert.Equal("005930", current.StockCode);
|
|
Assert.Equal("가온전자", current.DisplayName);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SearchSelect_MaterializesClosedCurrentAndCandleDraftsFromActionIdOnly()
|
|
{
|
|
var service = new DelegateTradingHaltService
|
|
{
|
|
Search = (query, _, _) => Task.FromResult(
|
|
SearchResult(query, KospiAlpha, KospiBeta, KosdaqAlpha))
|
|
};
|
|
var controller = new LegacyTradingHaltWorkflowController(service);
|
|
|
|
var search = await controller.SearchAsync(" 거래 ");
|
|
var selected = controller.SelectResult(search.Results[2].SelectionId);
|
|
controller.SetMovingAverages(ma5: true, ma20: true);
|
|
var current = controller.MaterializeSelectedAction(
|
|
LegacyTradingHaltWorkflowController.CurrentActionId);
|
|
var candle = controller.MaterializeSelectedAction(
|
|
LegacyTradingHaltWorkflowController.CandleActionId);
|
|
|
|
Assert.Equal("거래", service.SearchQuery);
|
|
Assert.Equal(500, service.SearchMaximum);
|
|
Assert.Equal("다온테크", selected.Selected?.DisplayName);
|
|
Assert.Collection(
|
|
selected.Actions,
|
|
action =>
|
|
{
|
|
Assert.Equal("current", action.ActionId);
|
|
Assert.True(action.IsAvailable);
|
|
},
|
|
action =>
|
|
{
|
|
Assert.Equal("candle-120d", action.ActionId);
|
|
Assert.True(action.IsAvailable);
|
|
});
|
|
|
|
Assert.Equal("s5001", current.BuilderKey);
|
|
Assert.Equal("5001", current.CutCode);
|
|
Assert.Equal(new[] { "5001" }, current.Aliases);
|
|
Assert.Equal("거래정지-현재가", current.Detail);
|
|
Assert.Equal("stock", current.Category);
|
|
Assert.Equal("kosdaq", current.Market);
|
|
Assert.Equal("KOSDAQ", current.Selection.GroupCode);
|
|
Assert.Equal("다온테크", current.Selection.Subject);
|
|
Assert.Equal("1열판기본", current.Selection.GraphicType);
|
|
Assert.Equal("HALTED", current.Selection.Subtype);
|
|
Assert.Equal("035720", current.Selection.DataCode);
|
|
Assert.False(current.Ma5);
|
|
Assert.False(current.Ma20);
|
|
Assert.Null(current.CandlePeriodDays);
|
|
|
|
Assert.Equal("s8010", candle.BuilderKey);
|
|
Assert.Equal("8051", candle.CutCode);
|
|
Assert.Equal(new[] { "8051" }, candle.Aliases);
|
|
Assert.Equal("캔들그래프-거래정지 · 120일", candle.Detail);
|
|
Assert.Equal("chart", candle.Category);
|
|
Assert.Equal("kosdaq", candle.Market);
|
|
Assert.Equal("KOSDAQ_STOCK", candle.Selection.GroupCode);
|
|
Assert.Equal("다온테크", candle.Selection.Subject);
|
|
Assert.Equal("MA5,MA20", candle.Selection.GraphicType);
|
|
Assert.Equal("TRADING_HALT", candle.Selection.Subtype);
|
|
Assert.Equal("035720", candle.Selection.DataCode);
|
|
Assert.True(candle.Ma5);
|
|
Assert.True(candle.Ma20);
|
|
Assert.Equal(120, candle.CandlePeriodDays);
|
|
Assert.Equal("1/1", candle.PageText);
|
|
Assert.True(candle.IsEnabled);
|
|
Assert.Equal(6, candle.FadeDuration);
|
|
Assert.Equal("legacy-trading-halt-workflow", candle.Source);
|
|
Assert.Equal(candle.DraftId, candle.ToLegacyPlaylistEntry().EntryId);
|
|
|
|
var method = typeof(LegacyTradingHaltWorkflowController).GetMethod(
|
|
nameof(LegacyTradingHaltWorkflowController.MaterializeSelectedAction));
|
|
Assert.NotNull(method);
|
|
Assert.Collection(
|
|
method.GetParameters(),
|
|
parameter => Assert.Equal("actionId", parameter.Name));
|
|
Assert.Throws<ArgumentOutOfRangeException>(
|
|
() => controller.MaterializeSelectedAction("8051-caller-override"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task NameSort_FirstClickIsDescendingThenAscending_AndKeepsSelection()
|
|
{
|
|
var service = new DelegateTradingHaltService
|
|
{
|
|
Search = (query, _, _) => Task.FromResult(
|
|
SearchResult(query, KospiAlpha, KospiBeta, KosdaqAlpha))
|
|
};
|
|
var controller = new LegacyTradingHaltWorkflowController(service);
|
|
var source = await controller.SearchAsync(string.Empty);
|
|
controller.SelectResult(source.Results[0].SelectionId);
|
|
|
|
var descending = controller.ToggleNameSort();
|
|
var ascending = controller.ToggleNameSort();
|
|
|
|
Assert.Equal(
|
|
new[] { "가온전자", "한빛반도체", "다온테크" },
|
|
source.Results.Select(row => row.DisplayName));
|
|
Assert.Equal(LegacyTradingHaltNameSort.Descending, descending.NameSort);
|
|
Assert.Equal(
|
|
new[] { "한빛반도체", "다온테크", "가온전자" },
|
|
descending.Results.Select(row => row.DisplayName));
|
|
Assert.Equal(LegacyTradingHaltNameSort.Ascending, ascending.NameSort);
|
|
Assert.Equal(
|
|
new[] { "가온전자", "다온테크", "한빛반도체" },
|
|
ascending.Results.Select(row => row.DisplayName));
|
|
Assert.Equal("005930", ascending.Selected?.StockCode);
|
|
Assert.True(Assert.Single(ascending.Results, row => row.IsSelected).IsSelected);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LateSearchResponse_CannotReplaceNewerTradingHaltResult()
|
|
{
|
|
var first = new TaskCompletionSource<TradingHaltSelectionResult>(
|
|
TaskCreationOptions.RunContinuationsAsynchronously);
|
|
var second = new TaskCompletionSource<TradingHaltSelectionResult>(
|
|
TaskCreationOptions.RunContinuationsAsynchronously);
|
|
var service = new DelegateTradingHaltService
|
|
{
|
|
Search = (query, _, _) => query == "첫째" ? first.Task : second.Task
|
|
};
|
|
var controller = new LegacyTradingHaltWorkflowController(service);
|
|
|
|
var firstRequest = controller.SearchAsync("첫째");
|
|
var secondRequest = controller.SearchAsync("둘째");
|
|
second.SetResult(SearchResult("둘째", KosdaqAlpha));
|
|
var newest = await secondRequest;
|
|
first.SetResult(SearchResult("첫째", KospiAlpha));
|
|
var staleCompletion = await firstRequest;
|
|
|
|
Assert.Equal("둘째", newest.Query);
|
|
Assert.Equal("다온테크", Assert.Single(newest.Results).DisplayName);
|
|
Assert.Equal("둘째", staleCompletion.Query);
|
|
Assert.Equal("다온테크", Assert.Single(staleCompletion.Results).DisplayName);
|
|
Assert.Equal("둘째", controller.Current.Query);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvalidIdentityOrSourceOrder_IsRejectedAndLeavesErrorSnapshot()
|
|
{
|
|
var duplicateName = new TradingHaltSelection(
|
|
TradingHaltMarket.Kospi,
|
|
"005931",
|
|
KospiAlpha.DisplayName);
|
|
var service = new DelegateTradingHaltService
|
|
{
|
|
Search = (query, _, _) => Task.FromResult(
|
|
SearchResult(query, KospiAlpha, duplicateName))
|
|
};
|
|
var controller = new LegacyTradingHaltWorkflowController(service);
|
|
|
|
await Assert.ThrowsAsync<TradingHaltSelectionDataException>(
|
|
() => controller.SearchAsync(string.Empty));
|
|
|
|
Assert.Equal(LegacyTradingHaltRequestStatus.Error, controller.Current.Status);
|
|
Assert.Empty(controller.Current.Results);
|
|
Assert.Equal(LegacyOperatorStatusKind.Error, controller.Current.StatusKind);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task TradingHaltSnapshots_AreImmutableAcrossLaterQueries()
|
|
{
|
|
var service = new DelegateTradingHaltService
|
|
{
|
|
Search = (query, _, _) => Task.FromResult(
|
|
query == "A"
|
|
? SearchResult(query, KospiAlpha)
|
|
: SearchResult(query, KosdaqAlpha))
|
|
};
|
|
var controller = new LegacyTradingHaltWorkflowController(service);
|
|
var before = await controller.SearchAsync("A");
|
|
|
|
var after = await controller.SearchAsync("B");
|
|
|
|
Assert.Equal("가온전자", Assert.Single(before.Results).DisplayName);
|
|
Assert.Equal("다온테크", Assert.Single(after.Results).DisplayName);
|
|
var rows = Assert.IsAssignableFrom<ICollection<LegacyTradingHaltResultView>>(
|
|
before.Results);
|
|
Assert.Throws<NotSupportedException>(() => rows.Clear());
|
|
var actions = Assert.IsAssignableFrom<ICollection<LegacyTradingHaltActionView>>(
|
|
before.Actions);
|
|
Assert.Throws<NotSupportedException>(() => actions.Add(before.Actions[0]));
|
|
}
|
|
|
|
private static TradingHaltSelectionResult SearchResult(
|
|
string query,
|
|
params TradingHaltSelection[] selections) =>
|
|
new(
|
|
query,
|
|
DateTimeOffset.Parse("2026-07-15T00:00:00+09:00"),
|
|
selections,
|
|
false);
|
|
|
|
private sealed class DelegateTradingHaltService : ITradingHaltSelectionService
|
|
{
|
|
public Func<string, int, CancellationToken, Task<TradingHaltSelectionResult>> Search
|
|
{
|
|
get;
|
|
init;
|
|
} = (query, _, _) => Task.FromResult(SearchResult(query));
|
|
|
|
public string? SearchQuery { get; private set; }
|
|
|
|
public int SearchMaximum { get; private set; }
|
|
|
|
public Task<TradingHaltSelectionResult> SearchAsync(
|
|
string query = "",
|
|
int maximumResults = LegacyTradingHaltSelectionService.DefaultMaximumResults,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
SearchQuery = query;
|
|
SearchMaximum = maximumResults;
|
|
return Search(query, maximumResults, cancellationToken);
|
|
}
|
|
}
|
|
}
|