Files
MBN_STOCK_WEBVIEW/tests/MBN_STOCK_WEBVIEW.LegacyApplication.Tests/LegacyParityStockSearchServiceTests.cs

158 lines
6.3 KiB
C#

using System.Data;
using MBN_STOCK_WEBVIEW.LegacyApplication;
using MMoneyCoderSharp.Data;
namespace MBN_STOCK_WEBVIEW.LegacyApplication.Tests;
public sealed class LegacyParityStockSearchServiceTests
{
[Fact]
public async Task Search_PreservesProviderAndRowOrderWithoutLimitOrSort()
{
var executor = new RecordingExecutor(new Dictionary<string, DataTable>
{
["LEGACY_PARITY_KOSPI_MASTER"] = Table(("중복", "000001")),
["LEGACY_PARITY_KOSDAQ_MASTER"] = Table(("중복", "100001")),
["LEGACY_PARITY_NXT_KOSPI_MASTER"] = Table(("중복(NXT)", "200001")),
["LEGACY_PARITY_NXT_KOSDAQ_MASTER"] = Table(("중복", "300001")),
["LEGACY_PARITY_KOSPI"] = Table(("가-2", "000002"), ("가-1", "000001")),
["LEGACY_PARITY_KOSDAQ"] = Table(("나", "100001")),
["LEGACY_PARITY_NXT_KOSPI"] = Table(("다(NXT)", "200001")),
["LEGACY_PARITY_NXT_KOSDAQ"] = Table(("라(NXT)", "300001"))
});
var service = new LegacyParityStockSearchService(executor);
var result = await service.SearchAsync(" a%_ ");
Assert.Equal(["가-2", "가-1", "나", "다(NXT)", "라(NXT)"], result.DisplayNames);
Assert.Equal("300001", result.ResolveLastByDisplayName("중복")?.StockCode);
Assert.Equal(LegacyDomesticStockMarket.NxtKosdaq,
result.ResolveLastByDisplayName("중복")?.Market);
Assert.Equal(8, executor.Calls.Count);
foreach (var call in executor.Calls.Skip(4))
{
Assert.DoesNotContain("ORDER BY", call.Spec.Sql, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("LIMIT", call.Spec.Sql, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("ROWNUM", call.Spec.Sql, StringComparison.OrdinalIgnoreCase);
Assert.Single(call.Spec.Parameters);
Assert.Equal(" A%_ ", call.Spec.Parameters[0].Value);
}
}
[Fact]
public async Task EmptySearch_IsAllowedAndMasterSnapshotIsCached()
{
var tables = new Dictionary<string, DataTable>();
foreach (var name in new[]
{
"LEGACY_PARITY_KOSPI", "LEGACY_PARITY_KOSDAQ",
"LEGACY_PARITY_NXT_KOSPI", "LEGACY_PARITY_NXT_KOSDAQ"
})
{
tables[name] = Table();
tables[$"{name}_MASTER"] = Table();
}
var executor = new RecordingExecutor(tables);
var service = new LegacyParityStockSearchService(executor);
await service.SearchAsync(string.Empty);
await service.SearchAsync(string.Empty);
Assert.Equal(12, executor.Calls.Count);
Assert.Equal(4, executor.Calls.Count(call => call.TableName.EndsWith("_MASTER", StringComparison.Ordinal)));
Assert.All(executor.Calls.Where(call => !call.TableName.EndsWith("_MASTER", StringComparison.Ordinal)),
call => Assert.Equal(string.Empty, call.Spec.Parameters[0].Value));
}
[Fact]
public async Task InitialConsonantSearch_FiltersOnlyLegacySearchCandidatesInProviderOrder()
{
var executor = new RecordingExecutor(new Dictionary<string, DataTable>
{
["LEGACY_PARITY_KOSPI_MASTER"] = Table(
("삼성전자", "005930")),
["LEGACY_PARITY_KOSDAQ_MASTER"] = Table(
("삼성중공업", "010140")),
["LEGACY_PARITY_NXT_KOSPI_MASTER"] = Table(
("삼성전자(NXT)", "005930"),
("삼성화재(NXT)", "000810")),
["LEGACY_PARITY_NXT_KOSDAQ_MASTER"] = Table(
("카카오게임즈(NXT)", "293490")),
["LEGACY_PARITY_KOSPI"] = Table(
("삼성전자", "005930"),
("서울반도체", "046890")),
["LEGACY_PARITY_KOSDAQ"] = Table(
("삼성중공업", "010140")),
["LEGACY_PARITY_NXT_KOSPI"] = Table(
("삼성전자(NXT)", "005930")),
["LEGACY_PARITY_NXT_KOSDAQ"] = Table()
});
var service = new LegacyParityStockSearchService(executor);
var result = await service.SearchAsync("ㅅㅅ");
Assert.Equal(
["삼성전자", "삼성중공업", "삼성전자(NXT)"],
result.DisplayNames);
Assert.DoesNotContain("삼성화재(NXT)", result.DisplayNames);
Assert.NotNull(result.ResolveLastByDisplayName("삼성화재(NXT)"));
Assert.Equal(8, executor.Calls.Count);
Assert.All(
executor.Calls.Where(call => !call.TableName.EndsWith("_MASTER", StringComparison.Ordinal)),
call => Assert.Equal(string.Empty, call.Spec.Parameters[0].Value));
Assert.Contains(
executor.Calls,
call => call.TableName == "LEGACY_PARITY_NXT_KOSPI" &&
call.Spec.Sql.Contains("N_ONLINE", StringComparison.Ordinal));
}
private static DataTable Table(params (string Name, string Code)[] rows)
{
var table = new DataTable();
table.Columns.Add("STOCK_NAME", typeof(string));
table.Columns.Add("STOCK_CODE", typeof(string));
foreach (var row in rows)
{
table.Rows.Add(row.Name, row.Code);
}
return table;
}
private sealed class RecordingExecutor : IDataQueryExecutor
{
private readonly IReadOnlyDictionary<string, DataTable> _tables;
public RecordingExecutor(IReadOnlyDictionary<string, DataTable> tables)
{
_tables = tables;
}
public List<Call> Calls { get; } = [];
public DataTable Execute(DataSourceKind source, string tableName, string query) =>
throw new NotSupportedException();
public Task<DataTable> ExecuteAsync(
DataSourceKind source,
string tableName,
string query,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public Task<DataTable> ExecuteAsync(
DataSourceKind source,
string tableName,
DataQuerySpec query,
CancellationToken cancellationToken = default)
{
Calls.Add(new Call(source, tableName, query));
return Task.FromResult(_tables[tableName].Copy());
}
}
private sealed record Call(DataSourceKind Source, string TableName, DataQuerySpec Spec);
}