236 lines
10 KiB
C#
236 lines
10 KiB
C#
using System.Text.Json;
|
|
using MBN_STOCK_WEBVIEW.LegacyApplication;
|
|
using MMoneyCoderSharp.Data;
|
|
|
|
namespace MBN_STOCK_WEBVIEW.LegacyBridge.Tests;
|
|
|
|
public sealed class LegacyManualFinancialBridgeTests
|
|
{
|
|
[Theory]
|
|
[InlineData("revenue-composition", ManualFinancialScreenKind.RevenueComposition)]
|
|
[InlineData("growth-metrics", ManualFinancialScreenKind.GrowthMetrics)]
|
|
[InlineData("sales", ManualFinancialScreenKind.Sales)]
|
|
[InlineData("operating-profit", ManualFinancialScreenKind.OperatingProfit)]
|
|
public void Open_uses_closed_GraphE_screen_identity(
|
|
string screen,
|
|
ManualFinancialScreenKind expected)
|
|
{
|
|
var json = JsonSerializer.Serialize(new
|
|
{
|
|
type = "open-manual-financial",
|
|
payload = new { screen }
|
|
});
|
|
|
|
Assert.True(LegacyBridgeProtocol.TryParseIntent(json, out var parsed, out _));
|
|
Assert.Equal(expected, Assert.IsType<LegacyOpenManualFinancialIntent>(parsed).Screen);
|
|
Assert.False(LegacyBridgeProtocol.TryParseIntent(
|
|
"{\"type\":\"open-manual-financial\",\"payload\":{\"screen\":\"INPUT_SELL\"}}",
|
|
out _,
|
|
out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void Save_parses_all_typed_record_shapes_without_accepting_DB_authority()
|
|
{
|
|
AssertSaveType<ManualRevenueCompositionRecord>(
|
|
"""
|
|
{"screen":"revenue-composition","stockName":"Alpha","baseDate":"2026.07","slices":[{"label":"A","percentageText":"25"},null,null,null,null]}
|
|
""");
|
|
AssertSaveType<ManualGrowthMetricsRecord>(
|
|
"""
|
|
{"screen":"growth-metrics","stockName":"Alpha","salesGrowth":[1,2,null,4],"operatingProfitGrowth":[5,6,7,8],"netAssetGrowth":[9,10,11,12],"netIncomeGrowth":[13,14,15,16],"periods":["1Q","2Q","3Q","4Q"]}
|
|
""");
|
|
AssertSaveType<ManualSalesRecord>(
|
|
"""
|
|
{"screen":"sales","stockName":"Alpha","quarters":[{"quarter":"1Q","value":1},{"quarter":"2Q","value":2},{"quarter":"3Q","value":3},{"quarter":"4Q","value":4},{"quarter":"5Q","value":5},{"quarter":"6Q","value":6}]}
|
|
""");
|
|
AssertSaveType<ManualOperatingProfitRecord>(
|
|
"""
|
|
{"screen":"operating-profit","stockName":"Alpha","quarters":[{"quarter":"1Q","value":1},{"quarter":"2Q","value":2},{"quarter":"3Q","value":3},{"quarter":"4Q","value":4},{"quarter":"5Q","value":5},{"quarter":"6Q","value":6}]}
|
|
""");
|
|
|
|
Assert.False(LegacyBridgeProtocol.TryParseIntent(
|
|
"""
|
|
{"type":"save-manual-financial","payload":{"record":{"screen":"sales","stockName":"Alpha","rowVersion":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","quarters":[]}}}
|
|
""",
|
|
out _,
|
|
out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void CRUD_and_action_intents_accept_only_opaque_ids_or_empty_payloads()
|
|
{
|
|
Assert.True(LegacyBridgeProtocol.TryParseIntent(
|
|
"{\"type\":\"load-manual-financial\",\"payload\":{\"resultId\":\"mf-row-0123456789abcdef\"}}",
|
|
out var loaded,
|
|
out _));
|
|
Assert.IsType<LegacyLoadManualFinancialIntent>(loaded);
|
|
|
|
foreach (var type in new[]
|
|
{
|
|
"begin-manual-financial-create",
|
|
"delete-manual-financial",
|
|
"delete-all-manual-financial",
|
|
"close-manual-financial"
|
|
})
|
|
{
|
|
Assert.True(LegacyBridgeProtocol.TryParseIntent(
|
|
$"{{\"type\":\"{type}\",\"payload\":{{}}}}",
|
|
out _,
|
|
out _));
|
|
Assert.False(LegacyBridgeProtocol.TryParseIntent(
|
|
$"{{\"type\":\"{type}\",\"payload\":{{\"table\":\"INPUT_SELL\"}}}}",
|
|
out _,
|
|
out _));
|
|
}
|
|
|
|
Assert.True(LegacyBridgeProtocol.TryParseIntent(
|
|
"{\"type\":\"activate-manual-financial-action\",\"payload\":{\"actionId\":\"sales\"}}",
|
|
out var action,
|
|
out _));
|
|
Assert.Equal(
|
|
"sales",
|
|
Assert.IsType<LegacyActivateManualFinancialActionIntent>(action).ActionId);
|
|
Assert.False(LegacyBridgeProtocol.TryParseIntent(
|
|
"{\"type\":\"activate-manual-financial-action\",\"payload\":{\"actionId\":\"s5080\"}}",
|
|
out _,
|
|
out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void GraphE_find_and_name_sort_require_strict_generation_bound_payloads()
|
|
{
|
|
Assert.True(LegacyBridgeProtocol.TryParseIntent(
|
|
"{\"type\":\"find-manual-financial\",\"payload\":{\"query\":\"Alpha\",\"direction\":\"down\",\"generation\":7}}",
|
|
out var find,
|
|
out _));
|
|
var parsedFind = Assert.IsType<LegacyFindManualFinancialIntent>(find);
|
|
Assert.Equal(LegacyManualFinancialFindDirection.Down, parsedFind.Direction);
|
|
Assert.Equal(7, parsedFind.Generation);
|
|
|
|
Assert.True(LegacyBridgeProtocol.TryParseIntent(
|
|
"{\"type\":\"toggle-manual-financial-name-sort\",\"payload\":{\"generation\":7}}",
|
|
out var sort,
|
|
out _));
|
|
Assert.Equal(7,
|
|
Assert.IsType<LegacyToggleManualFinancialNameSortIntent>(sort).Generation);
|
|
|
|
Assert.False(LegacyBridgeProtocol.TryParseIntent(
|
|
"{\"type\":\"find-manual-financial\",\"payload\":{\"query\":\"Alpha\",\"direction\":\"sideways\",\"generation\":7}}",
|
|
out _,
|
|
out _));
|
|
Assert.False(LegacyBridgeProtocol.TryParseIntent(
|
|
"{\"type\":\"toggle-manual-financial-name-sort\",\"payload\":{\"generation\":7,\"table\":\"INPUT_SELL\"}}",
|
|
out _,
|
|
out _));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Projection_exposes_editor_values_but_not_rowVersion_or_DB_scene_authority()
|
|
{
|
|
var manualService = new StaticManualService();
|
|
var workflow = new LegacyManualFinancialWorkflow(
|
|
manualService,
|
|
new StaticStockService());
|
|
var controller = new LegacyOperatorController(
|
|
new EmptyStockLookup(),
|
|
manualFinancialWorkflow: workflow);
|
|
var opened = await controller.OpenManualFinancialAsync(ManualFinancialScreenKind.Sales);
|
|
var loaded = await controller.LoadManualFinancialAsync(
|
|
Assert.Single(opened.ManualFinancial!.Rows).ResultId);
|
|
|
|
using var document = JsonDocument.Parse(LegacyBridgeProtocol.SerializeState(loaded));
|
|
var manual = document.RootElement.GetProperty("payload").GetProperty("manualFinancial");
|
|
|
|
Assert.Equal("sales", manual.GetProperty("screen").GetString());
|
|
Assert.True(manual.GetProperty("generation").GetInt64() > 0);
|
|
Assert.Equal("ascending", manual.GetProperty("nameSortDirection").GetString());
|
|
Assert.Equal("Alpha", manual.GetProperty("selectedRecord").GetProperty("stockName").GetString());
|
|
Assert.Equal("sales", manual.GetProperty("action").GetProperty("actionId").GetString());
|
|
Assert.False(manual.TryGetProperty("rowVersion", out _));
|
|
Assert.False(manual.TryGetProperty("tableName", out _));
|
|
Assert.False(manual.TryGetProperty("builderKey", out _));
|
|
Assert.False(manual.GetProperty("stockCandidates")[0].TryGetProperty("code", out _));
|
|
}
|
|
|
|
private static void AssertSaveType<TRecord>(string recordJson)
|
|
where TRecord : ManualFinancialRecord
|
|
{
|
|
var json = $"{{\"type\":\"save-manual-financial\",\"payload\":{{\"record\":{recordJson}}}}}";
|
|
Assert.True(LegacyBridgeProtocol.TryParseIntent(json, out var parsed, out _));
|
|
Assert.IsType<TRecord>(Assert.IsType<LegacySaveManualFinancialIntent>(parsed).Record);
|
|
}
|
|
|
|
private sealed class EmptyStockLookup : ILegacyStockLookup
|
|
{
|
|
public Task<LegacyStockSearchData> SearchAsync(
|
|
string query,
|
|
CancellationToken cancellationToken = default) =>
|
|
Task.FromResult(new LegacyStockSearchData(query, [], []));
|
|
}
|
|
|
|
private sealed class StaticStockService : IStockSearchService
|
|
{
|
|
public Task<StockSearchResult> SearchAsync(
|
|
string query,
|
|
int maximumResults = LegacyStockSearchService.DefaultMaximumResults,
|
|
CancellationToken cancellationToken = default) =>
|
|
Task.FromResult(new StockSearchResult(
|
|
query,
|
|
DateTimeOffset.UtcNow,
|
|
[new StockSearchItem(
|
|
StockMarket.Kospi,
|
|
DataSourceKind.Oracle,
|
|
"Alpha",
|
|
"000001")],
|
|
false));
|
|
}
|
|
|
|
private sealed class StaticManualService : IManualFinancialScreenService
|
|
{
|
|
private static readonly ManualFinancialSnapshot Row = new(
|
|
new ManualSalesRecord(
|
|
new ManualFinancialIdentity(ManualFinancialScreenKind.Sales, "Alpha"),
|
|
Enumerable.Range(1, 6)
|
|
.Select(index => new ManualQuarterValue($"{index}Q", index))
|
|
.ToArray()),
|
|
new string('A', 64));
|
|
|
|
public bool CanMutate => false;
|
|
|
|
public Task<ManualFinancialSearchResult> SearchAsync(
|
|
ManualFinancialScreenKind screen,
|
|
string query = "",
|
|
int maximumResults = LegacyManualFinancialScreenService.DefaultMaximumResults,
|
|
CancellationToken cancellationToken = default) =>
|
|
Task.FromResult(new ManualFinancialSearchResult(
|
|
screen,
|
|
query,
|
|
DateTimeOffset.UtcNow,
|
|
screen == ManualFinancialScreenKind.Sales ? [Row] : [],
|
|
false));
|
|
|
|
public Task<ManualFinancialSnapshot> GetAsync(
|
|
ManualFinancialIdentity identity,
|
|
CancellationToken cancellationToken = default) =>
|
|
Task.FromResult(Row);
|
|
|
|
public Task<ManualFinancialMutationReceipt> CreateAsync(
|
|
ManualFinancialRecord record,
|
|
CancellationToken cancellationToken = default) => throw new NotSupportedException();
|
|
|
|
public Task<ManualFinancialMutationReceipt> UpdateAsync(
|
|
ManualFinancialSnapshot expected,
|
|
ManualFinancialRecord replacement,
|
|
CancellationToken cancellationToken = default) => throw new NotSupportedException();
|
|
|
|
public Task<ManualFinancialMutationReceipt> DeleteAsync(
|
|
ManualFinancialSnapshot expected,
|
|
CancellationToken cancellationToken = default) => throw new NotSupportedException();
|
|
|
|
public Task<ManualFinancialMutationReceipt> DeleteAllAsync(
|
|
ManualFinancialScreenKind screen,
|
|
CancellationToken cancellationToken = default) => throw new NotSupportedException();
|
|
}
|
|
}
|