refactor: route the Nodes cmdlets through NodeService (#126) (#197)

Get-PveNode and Get-PveNodeStatus built their own PveHttpClient and
parsed the response inline. Both now call the existing NodeService
methods (GetNodes, GetNodeStatus), which already carried matching
requests. NodeService.GetNodeStatus gained the node-name stamp the
cmdlet used to apply after deserializing, and both GetNodes and
GetNodeStatus regained the missing-data guard the cmdlets had, which
the pre-existing service implementation lacked.

Co-authored-by: goodolclint-claude[bot] <323206664+goodolclint-claude[bot]@users.noreply.github.com>
This commit is contained in:
goodolclint-claude[bot]
2026-09-03 00:04:48 +00:00
committed by GitHub
parent 17bb2987b2
commit 9af67f78e6
4 changed files with 84 additions and 60 deletions
@@ -44,6 +44,18 @@ namespace PSProxmoxVE.Core.Tests.Services
mockClient.Verify(c => c.GetAsync("nodes"), Times.Once);
}
[Fact]
public void GetNodes_MissingDataField_Throws()
{
// Arrange
var mockClient = new Mock<IPveHttpClient>();
mockClient.Setup(c => c.GetAsync("nodes")).ReturnsAsync("{}");
var service = new NodeService(mockClient.Object);
// Act & Assert
Assert.Throws<InvalidOperationException>(() => service.GetNodes(CreateSession()));
}
[Fact]
public void GetNodeStatus_ReturnsPveNodeStatus()
{
@@ -70,6 +82,61 @@ namespace PSProxmoxVE.Core.Tests.Services
mockClient.Verify(c => c.GetAsync("nodes/pve1/status"), Times.Once);
}
[Fact]
public void GetNodeStatus_StampsNodeWhenResponseOmitsIt()
{
// Arrange
var json = @"{""data"": {""status"": ""online"", ""maxcpu"": 16}}";
var mockClient = new Mock<IPveHttpClient>();
mockClient.Setup(c => c.GetAsync("nodes/pve1/status")).ReturnsAsync(json);
var service = new NodeService(mockClient.Object);
// Act
var status = service.GetNodeStatus(CreateSession(), "pve1");
// Assert
Assert.Equal("pve1", status.Node);
mockClient.Verify(c => c.GetAsync("nodes/pve1/status"), Times.Once);
}
[Fact]
public void GetNodeStatus_EscapesNodeInPath()
{
// Arrange
var json = @"{""data"": {""node"": ""pve node"", ""status"": ""online""}}";
string? capturedPath = null;
var calls = 0;
var mockClient = new Mock<IPveHttpClient>();
mockClient.Setup(c => c.GetAsync(It.IsAny<string>()))
.Callback<string>(path =>
{
capturedPath = path;
calls++;
})
.ReturnsAsync(json);
var service = new NodeService(mockClient.Object);
// Act
var status = service.GetNodeStatus(CreateSession(), "pve node");
// Assert
Assert.Equal("nodes/pve%20node/status", capturedPath);
Assert.Equal(1, calls);
Assert.Equal("pve node", status.Node);
}
[Fact]
public void GetNodeStatus_MissingDataField_Throws()
{
// Arrange
var mockClient = new Mock<IPveHttpClient>();
mockClient.Setup(c => c.GetAsync("nodes/pve1/status")).ReturnsAsync("{}");
var service = new NodeService(mockClient.Object);
// Act & Assert
Assert.Throws<InvalidOperationException>(() => service.GetNodeStatus(CreateSession(), "pve1"));
}
[Fact]
public void GetNodeConfig_ReturnsDictionary()
{