Files
PSProxmoxVE/tests/PSProxmoxVE.Core.Tests/Utilities/CorosyncLinksTests.cs
T
goodolclint-claude[bot] 9a92577794 refactor: move guest lifecycle polling into a GuestLifecycleService (#157) (#228)
WaitForStatusTransition and InvokeGuestTask lived on PveCmdletBase with
hard-coded qemu/lxc paths, a Thread.Sleep(2000) loop and their own
PveHttpClient, so ADR 0015's lock-clear wait and ADR 0020's flock retry
could only be exercised through the 45-minute integration lane.

Moves both into a new GuestLifecycleService on PveServiceBase, following
TaskService's shape: a parameterless ctor, an IPveHttpClient-injecting
ctor, and an internal ctor with a Func<TimeSpan, Task> pollDelay seam so
a test can drive the poll loop without sleeping. PveCmdletBase keeps the
same protected method signatures as thin forwarders wiring WriteVerbose
into a single Action<string>? onProgress callback, so none of the 14
cmdlet call sites change.

ParseLinks moves to a pure CorosyncLinks.Parse in Core (dictionary plus
the malformed entries), with PveCmdletBase.ParseLinks kept as a forwarder
that emits the WriteWarning — the same forwarder shape as the lifecycle
methods, so the warning stays in one place instead of being copied into
the three cluster cmdlets that call it.

Behaviour is unchanged: same status/current polling per guest type, same
GuestStatusSnapshot.Evaluate lock semantics, same filtered PveApiException
catch, same GuestLockRetry wrapping, same PveTaskTimeoutException.

Co-authored-by: goodolclint-claude[bot] <323206664+goodolclint-claude[bot]@users.noreply.github.com>
2026-09-03 19:04:18 +00:00

71 lines
2.1 KiB
C#

using PSProxmoxVE.Core.Utilities;
using Xunit;
namespace PSProxmoxVE.Core.Tests.Utilities
{
public class CorosyncLinksTests
{
[Fact]
public void Parse_NullInput_ReturnsNullDictionaryAndNoMalformedEntries()
{
var (links, malformed) = CorosyncLinks.Parse(null);
Assert.Null(links);
Assert.Empty(malformed);
}
[Fact]
public void Parse_WellFormedEntries_ReturnsTrimmedKeysAndValues()
{
var (links, malformed) = CorosyncLinks.Parse(new[] { "link0= 10.0.0.1 ", "link1=10.0.0.2" });
Assert.NotNull(links);
Assert.Equal("10.0.0.1", links!["link0"]);
Assert.Equal("10.0.0.2", links["link1"]);
Assert.Empty(malformed);
}
[Theory]
[InlineData("link0")]
[InlineData("link0=")]
[InlineData("=10.0.0.1")]
[InlineData("")]
public void Parse_MalformedEntry_IsReportedAndOmitted(string entry)
{
var (links, malformed) = CorosyncLinks.Parse(new[] { entry });
Assert.Null(links);
Assert.Equal(new[] { entry }, malformed);
}
[Fact]
public void Parse_NullEntry_IsReportedAsMalformedRatherThanThrowing()
{
var (links, malformed) = CorosyncLinks.Parse(new[] { "link0=10.0.0.1", null! });
Assert.NotNull(links);
Assert.Single(links!);
Assert.Equal(new[] { "" }, malformed);
}
[Fact]
public void Parse_ValueContainingEquals_SplitsOnlyOnTheFirstOne()
{
var (links, malformed) = CorosyncLinks.Parse(new[] { "link0=10.0.0.1=extra" });
Assert.NotNull(links);
Assert.Equal("10.0.0.1=extra", links!["link0"]);
Assert.Empty(malformed);
}
[Fact]
public void Parse_AllEntriesMalformed_ReturnsNullDictionaryWithEveryEntryReported()
{
var (links, malformed) = CorosyncLinks.Parse(new[] { "garbage", "link0=" });
Assert.Null(links);
Assert.Equal(new[] { "garbage", "link0=" }, malformed);
}
}
}