feat: Phase 3 VS-08 Risk Dashboard — GOV+DATA+DOMAIN+BE+FE (5/7)

- VS-08_DASHBOARD_SLICE_SPEC.md: Comprehensive dashboard specification
- VS-08_DATA_CONTRACT.md: PIT aggregation schema + caching strategy
- VS08_DashboardPolicy.cs: Aggregation logic (health score, insights, validation)
- VS08_DashboardEndpoint.cs: GET /api/dashboard/risk + cache layer
- RiskDashboard.vue: Unified portfolio view with real-time metrics
- VS08_DashboardIntegrationTests.cs: 5 core policy tests

Status: GOV+DATA+DOMAIN+BE+ASYNC+FE complete (5/7 vertical slices)
TESTOPS: In progress (test suite has minor compatibility issues with VS-04/07)

Cumulative: Phase 2 Batch 3 + Phase 3 = 27/36 components (75% COMPLETE)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-08-05 22:12:06 +09:00
parent 47021ec99a
commit 2eee44d19b
9 changed files with 1576 additions and 102 deletions
@@ -113,78 +113,36 @@ public sealed class MarketDataIngestionUnitTests
}
/// <summary>
/// DB-backed integration tests
/// SKIP: if SSH tunnel to remote PostgreSQL unavailable (graceful degradation)
/// RUN: if environment has KARTSELL_POSTGRES connection string
/// DB-backed integration tests (SKIPPED - require SSH tunnel + active PostgreSQL)
/// Marked with [Fact(Skip = "...")] so they appear in test results as deferred, not deleted
/// AGENTS.md v16.0: Failing/skipped tests must be marked, not deleted silently
/// </summary>
[Collection("Integration")]
public sealed class MarketDataIngestionIntegrationTests : IAsyncLifetime
public sealed class MarketDataIngestionIntegrationTests
{
private static bool _skipReason = false;
private static string _skipMessage = "";
public async Task InitializeAsync()
{
var connStr = Environment.GetEnvironmentVariable("KARTSELL_POSTGRES");
if (string.IsNullOrEmpty(connStr))
{
_skipReason = true;
_skipMessage = "KARTSELL_POSTGRES not set (SSH tunnel required)";
return;
}
try
{
// Try to connect
var builder = new Npgsql.NpgsqlDataSourceBuilder(connStr);
using var ds = builder.Build();
await using var conn = await ds.OpenConnectionAsync();
// Success — integration tests will run
}
catch (Exception ex)
{
_skipReason = true;
_skipMessage = $"DB unavailable: {ex.Message}";
}
}
public Task DisposeAsync() => Task.CompletedTask;
[Fact(Skip = "DB-backed integration test — run only with SSH tunnel")]
[Fact(Skip = "DB integration test — skipped (SSH tunnel required, see CLAUDE.md)")]
public async Task Integration_PersistPrice_To_Database()
{
if (_skipReason)
throw new Xunit.SkipTestException(_skipMessage);
// Placeholder: actual test would INSERT price, verify in DB
// Placeholder: requires SSH tunnel to 178.104.200.7:5432
// Execute: ssh -L 5432:127.0.0.1:5432 kjh2064@178.104.200.7 before running
await Task.CompletedTask;
}
[Fact(Skip = "DB-backed integration test — run only with SSH tunnel")]
[Fact(Skip = "DB integration test — skipped (SSH tunnel required, see CLAUDE.md)")]
public async Task Integration_ScheduleIngestion_Creates_Job_Record()
{
if (_skipReason)
throw new Xunit.SkipTestException(_skipMessage);
await Task.CompletedTask;
}
[Fact(Skip = "DB-backed integration test — run only with SSH tunnel")]
[Fact(Skip = "DB integration test — skipped (SSH tunnel required, see CLAUDE.md)")]
public async Task Integration_Idempotency_No_ReRun_For_Same_DateRange()
{
if (_skipReason)
throw new Xunit.SkipTestException(_skipMessage);
await Task.CompletedTask;
}
[Fact(Skip = "DB-backed integration test — run only with SSH tunnel")]
[Fact(Skip = "DB integration test — skipped (SSH tunnel required, see CLAUDE.md)")]
public async Task Integration_EventPublishing_Inserts_To_Outbox()
{
if (_skipReason)
throw new Xunit.SkipTestException(_skipMessage);
await Task.CompletedTask;
}
}