테스트 DB 계약과 실행 안전성 정렬
ci / backend (push) Failing after 0s
ci / static (push) Failing after 6s
ci / backend (pull_request) Failing after 1s
ci / static (pull_request) Failing after 7s
Build & Test with Secrets / build (pull_request) Failing after 1s
ci / frontend (push) Failing after 48s
Build & Test with Secrets / security-scan (pull_request) Successful in 5s
Build & Test with Secrets / frontend (pull_request) Failing after 1m23s
ci / frontend (pull_request) Failing after 1m32s
Build & Test with Secrets / notification (pull_request) Failing after 2s

This commit is contained in:
2026-08-02 17:37:12 +09:00
parent cc7d963755
commit 74ddd95a05
16 changed files with 258 additions and 66 deletions
@@ -0,0 +1,11 @@
namespace KArtSell.BuildingBlocks.Time;
public static class MarketTime
{
public static DateTime SeoulDateTime(DateTimeOffset utcNow)
{
var zone = TimeZoneInfo.FindSystemTimeZoneById(
OperatingSystem.IsWindows() ? "Korea Standard Time" : "Asia/Seoul");
return TimeZoneInfo.ConvertTime(utcNow, zone).DateTime;
}
}
@@ -1,4 +1,5 @@
using KArtSell.BuildingBlocks.Data;
using KArtSell.BuildingBlocks.Time;
using Microsoft.Extensions.Logging;
using System.Net.Http;
@@ -15,17 +16,20 @@ public sealed class RecommendationReportGenerator
private readonly IDbConnectionFactory _connectionFactory;
private readonly HttpClient _httpClient;
private readonly ILogger<RecommendationReportGenerator> _logger;
private readonly IClock _clock;
private readonly string _telegramBotToken;
private readonly string _telegramChatId;
public RecommendationReportGenerator(
IDbConnectionFactory connectionFactory,
HttpClient httpClient,
ILogger<RecommendationReportGenerator> logger)
ILogger<RecommendationReportGenerator> logger,
IClock clock)
{
_connectionFactory = connectionFactory;
_httpClient = httpClient;
_logger = logger;
_clock = clock;
_telegramBotToken = Environment.GetEnvironmentVariable("TELEGRAM_BOT") ?? string.Empty;
_telegramChatId = Environment.GetEnvironmentVariable("CHAT_ID") ?? string.Empty;
}
@@ -61,7 +65,7 @@ public sealed class RecommendationReportGenerator
return new RecommendationReport
{
ReportType = "Weekly",
ReportDate = DateTime.UtcNow,
ReportDate = _clock.UtcNow.DateTime,
PeriodStart = startDate,
PeriodEnd = endDate,
Recommendations = recommendations
@@ -80,7 +84,7 @@ public sealed class RecommendationReportGenerator
return new RecommendationReport
{
ReportType = "Monthly",
ReportDate = DateTime.UtcNow,
ReportDate = _clock.UtcNow.DateTime,
PeriodStart = startDate,
PeriodEnd = endDate,
Recommendations = recommendations
@@ -23,20 +23,22 @@ public sealed class Handler(IDbConnectionFactory connectionFactory, IClock clock
throw new InvalidOperationException($"Approval status is {existing.Value.Status}, not Pending");
// Update approval status (trigger will set approved_at)
var now = clock.UtcNow.DateTime;
var now = MarketTime.SeoulDateTime(clock.UtcNow);
await connection.ExecuteAsync("""
UPDATE model_operations.approval_queue
SET
status = 'Approved',
approved_by = @ApprovedBy,
approval_reason = @ApprovalReason
approval_reason = @ApprovalReason,
approved_at = @ApprovedAt
WHERE run_id = @RunId
""",
new
{
request.RunId,
ApprovedBy = approverUserId,
request.ApprovalReason
request.ApprovalReason,
ApprovedAt = now
});
return new Response(
@@ -23,18 +23,20 @@ public sealed class Handler(IDbConnectionFactory connectionFactory, IClock clock
throw new InvalidOperationException($"Approval status is {existing.Value.Status}, not Pending");
// Update approval status (trigger will set rejected_at)
var now = clock.UtcNow.DateTime;
var now = MarketTime.SeoulDateTime(clock.UtcNow);
await connection.ExecuteAsync("""
UPDATE model_operations.approval_queue
SET
status = 'Rejected',
rejection_reason = @RejectionReason
rejection_reason = @RejectionReason,
rejected_at = @RejectedAt
WHERE run_id = @RunId
""",
new
{
request.RunId,
request.RejectionReason
request.RejectionReason,
RejectedAt = now
});
return new Response(
@@ -1,6 +1,8 @@
using KArtSell.BuildingBlocks.Time;
namespace KArtSell.Modules.ModelOperations.Observability;
public sealed class StubObservabilityService : IObservabilityService
public sealed class StubObservabilityService(IClock clock) : IObservabilityService
{
public async Task<ObservabilityMetricsDto> GetMetricsAsync(CancellationToken cancellationToken)
{
@@ -23,7 +25,7 @@ public sealed class StubObservabilityService : IObservabilityService
DuplicateDetection = new DuplicateDetectionMetrics
{
ConstraintViolationCount = 0,
LastDetected = DateTime.UtcNow
LastDetected = clock.UtcNow.DateTime
},
Reconciliation = new ReconciliationMetrics
{