using QuantEngine.Application.Services; using QuantEngine.Core.Interfaces; using QuantEngine.Core.Models; namespace QuantEngine.Core.Tests; public class ApplicationServiceTests { [Fact] public async Task WorkspaceService_ForwardsSettingAndHistoryOperations() { var repo = new FakeWorkspaceRepository(); var history = new FakeHistoryStore(); var service = new WorkspaceService(repo, history); var setting = new Setting { Ordinal = 1, Key = "risk_mode", ValueJson = "\"RISK_ON\"" }; Assert.True(await service.UpsertSettingAsync(setting)); Assert.Equal(setting, repo.LastSetting); var payload = new Dictionary { ["foo"] = "bar" }; Assert.Equal(1, await service.AppendHistoryAsync("decision_result_history", payload)); Assert.Equal("decision_result_history", history.LastDomain); Assert.Equal("bar", history.LastPayload?["foo"]); } [Fact] public async Task ApprovalService_ForwardsApprovalAndLockOperations() { var repo = new FakeWorkspaceRepository(); var service = new ApprovalService(repo); var approval = new WorkspaceApproval { Domain = "settings", TargetRef = "portfolio", Status = "APPROVED" }; Assert.True(await service.UpsertApprovalAsync(approval)); Assert.Equal(approval, repo.LastApproval); var lockRow = new WorkspaceLock { Domain = "settings", TargetRef = "portfolio", LockedBy = "qa", Reason = "review" }; Assert.True(await service.AcquireLockAsync(lockRow)); Assert.Equal(lockRow, repo.LastLock); Assert.True(await service.ReleaseLockAsync("settings", "portfolio")); Assert.Equal(("settings", "portfolio"), repo.LastReleasedLock); } [Fact] public async Task CollectionService_AppendsRunSnapshotAndErrorRecords() { var history = new FakeHistoryStore(); var service = new CollectionService(history); await service.AppendRunAsync(new CollectionRun { RunId = "run-1", CollectorName = "kis", StartedAt = "2026-06-26T09:00:00+09:00", Status = "PASS" }); Assert.Equal("collection_run_history", history.LastDomain); Assert.Equal("run-1", history.LastPayload?["run_id"]); await service.AppendSnapshotAsync(new CollectionSnapshot { RunId = "run-1", DatasetName = "decision_result_history", Ticker = "005930", SourcePriority = "KIS", SourceStatus = "PASS", PayloadJson = "{}", ProvenanceJson = "{}" }); Assert.Equal("collection_snapshot_history", history.LastDomain); Assert.Equal("005930", history.LastPayload?["ticker"]); await service.AppendSourceErrorAsync(new CollectionSourceError { RunId = "run-1", SourceName = "naver", ErrorKind = "TIMEOUT", ErrorMessage = "timeout" }); Assert.Equal("collection_source_error_history", history.LastDomain); Assert.Equal("TIMEOUT", history.LastPayload?["error_kind"]); } [Fact] public async Task FormulaService_ForwardsFormulaExecutionAndHistory() { var history = new FakeHistoryStore(); var service = new FormulaService(history); var timing = service.ComputeTimingDecision(new Dictionary { ["entryModeGate"] = "PASS", ["entryMode"] = "BREAKOUT", ["leaderGate"] = "PASS", ["acGate"] = "CLEAR", ["priceStatus"] = "PRICE_OK", ["atr20"] = 1.0, ["leaderTotal"] = 4, ["flowCredit"] = 0.7, ["avgTradeValue5D"] = 100, ["spreadPct"] = 0.5 }); Assert.NotEqual(string.Empty, timing.Action); await service.AppendFormulaRunAsync("timing", new Dictionary { ["action"] = timing.Action, ["entry_score"] = timing.EntryScore }); Assert.Equal("formula_timing_history", history.LastDomain); Assert.Equal(timing.Action, history.LastPayload?["action"]); } private sealed class FakeWorkspaceRepository : IWorkspaceRepository { public Setting? LastSetting { get; private set; } public WorkspaceApproval? LastApproval { get; private set; } public WorkspaceLock? LastLock { get; private set; } public (string Domain, string TargetRef)? LastReleasedLock { get; private set; } public Task> GetSettingsAsync() => Task.FromResult(Enumerable.Empty()); public Task GetSettingByKeyAsync(string key) => Task.FromResult(null); public Task UpsertSettingAsync(Setting setting) { LastSetting = setting; return Task.FromResult(true); } public Task DeleteSettingAsync(string key) => Task.FromResult(true); public Task> GetAccountSnapshotsAsync() => Task.FromResult(Enumerable.Empty()); public Task InsertAccountSnapshotsAsync(IEnumerable snapshots) => Task.FromResult(true); public Task ClearAccountSnapshotsAsync() => Task.FromResult(true); public Task> GetApprovalsAsync() => Task.FromResult(Enumerable.Empty()); public Task GetApprovalAsync(string domain, string targetRef) => Task.FromResult(null); public Task UpsertApprovalAsync(WorkspaceApproval approval) { LastApproval = approval; return Task.FromResult(true); } public Task> GetLocksAsync() => Task.FromResult(Enumerable.Empty()); public Task GetLockAsync(string domain, string targetRef) => Task.FromResult(null); public Task AcquireLockAsync(WorkspaceLock @lock) { LastLock = @lock; return Task.FromResult(true); } public Task ReleaseLockAsync(string domain, string targetRef) { LastReleasedLock = (domain, targetRef); return Task.FromResult(true); } } private sealed class FakeHistoryStore : IPostgresqlHistoryStore { public string? LastDomain { get; private set; } public IDictionary? LastPayload { get; private set; } public Task AppendAsync(string domain, IDictionary payload) { LastDomain = domain; LastPayload = new Dictionary(payload); return Task.FromResult(1); } public Task>> SnapshotAsync(string domain, int limit = 500) => Task.FromResult>>(Array.Empty>()); } }