From 8e0cc71b2e1faae5c0c83dc55e4aa90730854b17 Mon Sep 17 00:00:00 2001 From: StellaOps Bot Date: Sun, 4 Jan 2026 12:38:35 +0200 Subject: [PATCH] DET-004: Refactor Policy BudgetLedger for determinism - Inject TimeProvider and IGuidProvider in BudgetLedger constructor - Replace DateTimeOffset.UtcNow with _timeProvider.GetUtcNow() - Replace Guid.NewGuid() with _guidProvider.NewGuid() - Add Determinism.Abstractions reference to Policy csproj Sprint: SPRINT_20260104_001_BE_determinism_timeprovider_injection Task: DET-004 (in progress - Policy module) --- .../StellaOps.Policy/Gates/BudgetLedger.cs | 25 +++++++++++++------ .../StellaOps.Policy/StellaOps.Policy.csproj | 1 + 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Policy/__Libraries/StellaOps.Policy/Gates/BudgetLedger.cs b/src/Policy/__Libraries/StellaOps.Policy/Gates/BudgetLedger.cs index 9f2d7e2a1..0d938546e 100644 --- a/src/Policy/__Libraries/StellaOps.Policy/Gates/BudgetLedger.cs +++ b/src/Policy/__Libraries/StellaOps.Policy/Gates/BudgetLedger.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using StellaOps.Determinism; namespace StellaOps.Policy.Gates; @@ -10,10 +11,18 @@ public sealed class BudgetLedger : IBudgetLedger { private readonly IBudgetStore _store; private readonly ILogger _logger; + private readonly TimeProvider _timeProvider; + private readonly IGuidProvider _guidProvider; - public BudgetLedger(IBudgetStore store, ILogger? logger = null) + public BudgetLedger( + IBudgetStore store, + TimeProvider? timeProvider = null, + IGuidProvider? guidProvider = null, + ILogger? logger = null) { _store = store ?? throw new ArgumentNullException(nameof(store)); + _timeProvider = timeProvider ?? TimeProvider.System; + _guidProvider = guidProvider ?? SystemGuidProvider.Instance; _logger = logger ?? NullLogger.Instance; } @@ -64,12 +73,12 @@ public sealed class BudgetLedger : IBudgetLedger // Record the consumption var entry = new BudgetEntry { - EntryId = Guid.NewGuid().ToString(), + EntryId = _guidProvider.NewGuid().ToString(), ServiceId = serviceId, Window = budget.Window, ReleaseId = releaseId, RiskPoints = riskPoints, - ConsumedAt = DateTimeOffset.UtcNow + ConsumedAt = _timeProvider.GetUtcNow() }; await _store.AddEntryAsync(entry, ct).ConfigureAwait(false); @@ -78,7 +87,7 @@ public sealed class BudgetLedger : IBudgetLedger var updatedBudget = budget with { Consumed = budget.Consumed + riskPoints, - UpdatedAt = DateTimeOffset.UtcNow + UpdatedAt = _timeProvider.GetUtcNow() }; await _store.UpdateAsync(updatedBudget, ct).ConfigureAwait(false); @@ -122,7 +131,7 @@ public sealed class BudgetLedger : IBudgetLedger var updatedBudget = budget with { Allocated = newAllocation, - UpdatedAt = DateTimeOffset.UtcNow + UpdatedAt = _timeProvider.GetUtcNow() }; await _store.UpdateAsync(updatedBudget, ct).ConfigureAwait(false); @@ -148,15 +157,15 @@ public sealed class BudgetLedger : IBudgetLedger Window = window, Allocated = DefaultBudgetAllocations.GetMonthlyAllocation(tier), Consumed = 0, - UpdatedAt = DateTimeOffset.UtcNow + UpdatedAt = _timeProvider.GetUtcNow() }; await _store.CreateAsync(budget, ct).ConfigureAwait(false); return budget; } - private static string GetCurrentWindow() => - DateTimeOffset.UtcNow.ToString("yyyy-MM"); + private string GetCurrentWindow() => + _timeProvider.GetUtcNow().ToString("yyyy-MM"); private Task GetServiceTierAsync(string serviceId, CancellationToken ct) { diff --git a/src/Policy/__Libraries/StellaOps.Policy/StellaOps.Policy.csproj b/src/Policy/__Libraries/StellaOps.Policy/StellaOps.Policy.csproj index 10f1a7b28..04d8ab1a8 100644 --- a/src/Policy/__Libraries/StellaOps.Policy/StellaOps.Policy.csproj +++ b/src/Policy/__Libraries/StellaOps.Policy/StellaOps.Policy.csproj @@ -28,5 +28,6 @@ +