using System; using System.IO; using System.Threading; using Aging.Infrastructure.Core; using Aging.ResourceContracts; using Xunit; namespace Aging.MvpTests { public class FacadeLeaseLifecycleTests : IDisposable { private readonly string _dbPath; private readonly DbLeaseStore _store; public FacadeLeaseLifecycleTests() { _dbPath = Path.Combine(Path.GetTempPath(), "arb_" + Guid.NewGuid().ToString("N") + ".db"); _store = new DbLeaseStore(_dbPath, "local-main"); } public void Dispose() { try { File.Delete(_dbPath); File.Delete(_dbPath + "-wal"); File.Delete(_dbPath + "-shm"); } catch { } } private LeaseGrant Grab(string key, int ttl) => _store.Acquire(new AcquireRequest { OwnerId = "f", OwnerType = OwnerTypeV3.Production, ResourceKeys = new[] { key }, TtlSeconds = ttl }).Grants[0]; [Fact] public void Heartbeat_Renew_Extends_Expiry() { var g = Grab("can/x", 2); long before = g.ExpiresUtcMs; Thread.Sleep(1200); Assert.True(_store.Renew(g, 2)); Assert.True(_store.ProbeConflict("can/x").ExpiresUtcMs > before); } [Fact] public void Expired_Sweep_StaleRenew_False_And_RecoveryBlocked() { var g = Grab("can/y", 1); Thread.Sleep(1500); Assert.Equal(1, _store.SweepExpired()); Assert.False(_store.Renew(g, 5)); Assert.Null(_store.ProbeConflict("can/y")); Assert.Equal(LeasePhase.RecoveryRequired, _store.PhaseOf("can/y")); } } }