using Moq; using QuantEngine.Application.Services; using QuantEngine.Core.Domain; using QuantEngine.Core.Interfaces; namespace QuantEngine.Core.Tests; public class HistoryIngestionServiceTests { [Fact] public async Task AppendDecisionAsync_NormalizesTypedPayload() { var store = new Mock(MockBehavior.Strict); store.Setup(s => s.AppendAsync("decision_result_history", It.Is>(payload => payload["instrument_id"] != null && payload["instrument_id"]!.ToString() == "005930" && payload["action"] != null && payload["action"]!.ToString() == "BUY" && payload["gate"] != null && payload["gate"]!.ToString() == "PASS" && payload["source_version"] != null && payload["source_version"]!.ToString() == "v1"))).ReturnsAsync(1); var service = new HistoryIngestionService(store.Object); var result = await service.AppendDecisionAsync( new FinalDecisionResult { FinalAction = " BUY ", ActionPriority = 1, PriorityScore = 12.3, DecisionSource = " v1 " }, new SellDecisionResult { Action = "SELL", Validation = " PASS " }, null, " 005930 ", " ", null); Assert.Equal(1, result); store.VerifyAll(); } [Fact] public async Task AppendFactorOutputAsync_RejectsBlankCoreFields() { var service = new HistoryIngestionService(new Mock().Object); await Assert.ThrowsAsync(() => service.AppendFactorOutputAsync( " ", "1.0", 1.0, "PASS")); } [Fact] public async Task AppendDecisionAsync_RejectsNullRawPayload() { var service = new HistoryIngestionService(new Mock().Object); await Assert.ThrowsAsync(() => service.AppendDecisionAsync((IDictionary)null!)); } }