fix(deployment-manager): resolve DeploymentManager-003..011 — atomic status commit, orphan-delete handling, semaphore reclamation, structured diff, options binding, lifecycle test coverage
This commit is contained in:
@@ -92,4 +92,52 @@ public class OperationLockManagerTests
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
// ── DeploymentManager-005: semaphore must not leak ──
|
||||
|
||||
[Fact]
|
||||
public async Task AcquireAsync_ReleasedLock_RemovesSemaphoreEntry()
|
||||
{
|
||||
// A semaphore that is created, used, and fully released must not be
|
||||
// retained — otherwise every distinct instance name leaks a
|
||||
// SemaphoreSlim (a kernel handle) for the life of the process.
|
||||
using (await _lockManager.AcquireAsync("transient-inst", TimeSpan.FromSeconds(5)))
|
||||
{
|
||||
Assert.Equal(1, _lockManager.TrackedLockCount);
|
||||
}
|
||||
|
||||
// After release, the entry is reclaimed.
|
||||
Assert.Equal(0, _lockManager.TrackedLockCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AcquireAsync_ManyDistinctInstances_DoesNotAccumulateSemaphores()
|
||||
{
|
||||
// Simulates the long-running central process: many instances are
|
||||
// deployed/disabled over time. Their semaphores must be reclaimed.
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
using var handle = await _lockManager.AcquireAsync($"churn-{i}", TimeSpan.FromSeconds(5));
|
||||
}
|
||||
|
||||
Assert.Equal(0, _lockManager.TrackedLockCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AcquireAsync_ContendedLock_KeepsSemaphoreUntilLastReleaseThenReclaims()
|
||||
{
|
||||
// While a second caller is waiting, the semaphore must survive the
|
||||
// first release; only when the last holder releases is it reclaimed.
|
||||
var first = await _lockManager.AcquireAsync("contended", TimeSpan.FromSeconds(5));
|
||||
|
||||
var secondAcquire = _lockManager.AcquireAsync("contended", TimeSpan.FromSeconds(5));
|
||||
|
||||
first.Dispose(); // hands the lock to the waiter; entry must NOT be removed
|
||||
var second = await secondAcquire;
|
||||
|
||||
Assert.Equal(1, _lockManager.TrackedLockCount);
|
||||
second.Dispose();
|
||||
|
||||
Assert.Equal(0, _lockManager.TrackedLockCount);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user