From 832aa6e4df4f47d1d419b64be65f29a6af977afd Mon Sep 17 00:00:00 2001 From: "goodolclint-claude[bot]" <323206664+goodolclint-claude[bot]@users.noreply.github.com> Date: Thu, 3 Sep 2026 18:04:43 +0000 Subject: [PATCH] refactor: delete ten zero-caller public service methods (#220) (#225) Delete CloudInitService.GetCloudInitConfig, ClusterConfigService.GetTotem/ GetQdevice/GetApiVersion, ContainerService.GetContainer, HaService.GetManagerStatus, NodeService.GetVersion, TaskService.GetTaskLog, UserService.GetUser, and VmService.ShutdownVm. #207 found these had no caller outside tests/; re-confirmed by grep against main at 3ea0e11 before this change. Also deletes the xUnit tests that existed only to exercise each method, CloudInitService's now-unused CloudInitFields array, and HaService's now-unused JsonHelper using directive. Co-authored-by: goodolclint-claude[bot] <323206664+goodolclint-claude[bot]@users.noreply.github.com> --- .../Services/CloudInitService.cs | 36 ------------ .../Services/ClusterConfigService.cs | 45 --------------- .../Services/ContainerService.cs | 16 ------ src/PSProxmoxVE.Core/Services/HaService.cs | 16 ------ src/PSProxmoxVE.Core/Services/NodeService.cs | 18 ------ src/PSProxmoxVE.Core/Services/TaskService.cs | 19 ------- src/PSProxmoxVE.Core/Services/UserService.cs | 21 ------- src/PSProxmoxVE.Core/Services/VmService.cs | 22 -------- .../Services/CloudInitServiceTests.cs | 55 ------------------- .../Services/ClusterConfigServiceTests.cs | 37 ------------- .../Services/HaServiceTests.cs | 19 ------- .../Services/TaskServiceTests.cs | 29 ---------- .../Services/UserServiceTests.cs | 22 -------- 13 files changed, 355 deletions(-) diff --git a/src/PSProxmoxVE.Core/Services/CloudInitService.cs b/src/PSProxmoxVE.Core/Services/CloudInitService.cs index 3446c52..23c5c53 100644 --- a/src/PSProxmoxVE.Core/Services/CloudInitService.cs +++ b/src/PSProxmoxVE.Core/Services/CloudInitService.cs @@ -14,14 +14,6 @@ namespace PSProxmoxVE.Core.Services /// public class CloudInitService : PveServiceBase { - // Cloud-Init field names as used in the PVE API - private static readonly string[] CloudInitFields = - { - "ciuser", "cipassword", "sshkeys", - "ipconfig0", "ipconfig1", "ipconfig2", "ipconfig3", - "nameserver", "searchdomain", "cicustom" - }; - private readonly VmService _vmService; /// @@ -57,34 +49,6 @@ namespace PSProxmoxVE.Core.Services return _vmService.GetVmConfig(session, node, vmid); } - /// - /// Retrieves the Cloud-Init specific configuration fields for a VM. - /// Internally fetches the full VM config and extracts the CI fields. - /// - public PveCloudInitConfig GetCloudInitConfig(PveSession session, string node, int vmid) - { - if (session == null) throw new ArgumentNullException(nameof(session)); - if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node)); - - return Invoke(session, client => - { - var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/config") - .GetAwaiter().GetResult(); - var data = JObject.Parse(response)["data"]; - if (data == null) return new PveCloudInitConfig(); - - // Extract only the Cloud-Init fields into a reduced JObject for deserialization - var ciObj = new JObject(); - foreach (var field in CloudInitFields) - { - if (data[field] != null) - ciObj[field] = data[field]; - } - - return ciObj.ToObject() ?? new PveCloudInitConfig(); - }); - } - /// /// Updates Cloud-Init configuration fields on a VM. Only fields present in /// are changed; unspecified fields are left as-is. diff --git a/src/PSProxmoxVE.Core/Services/ClusterConfigService.cs b/src/PSProxmoxVE.Core/Services/ClusterConfigService.cs index 1409f18..bb9c9fc 100644 --- a/src/PSProxmoxVE.Core/Services/ClusterConfigService.cs +++ b/src/PSProxmoxVE.Core/Services/ClusterConfigService.cs @@ -229,51 +229,6 @@ namespace PSProxmoxVE.Core.Services }); } - /// - /// Returns the Corosync totem configuration (GET /cluster/config/totem). - /// - public Dictionary GetTotem(PveSession session) - { - if (session == null) throw new ArgumentNullException(nameof(session)); - - return Invoke(session, client => - { - var response = client.GetAsync("cluster/config/totem").GetAwaiter().GetResult(); - var data = JObject.Parse(response)["data"]; - return JsonHelper.ToDictionary(data as JObject); - }); - } - - /// - /// Returns the external quorum device (qdevice) status (GET /cluster/config/qdevice). - /// - public Dictionary GetQdevice(PveSession session) - { - if (session == null) throw new ArgumentNullException(nameof(session)); - - return Invoke(session, client => - { - var response = client.GetAsync("cluster/config/qdevice").GetAwaiter().GetResult(); - var data = JObject.Parse(response)["data"]; - return JsonHelper.ToDictionary(data as JObject); - }); - } - - /// - /// Returns the cluster API version (GET /cluster/config/apiversion). - /// - public int GetApiVersion(PveSession session) - { - if (session == null) throw new ArgumentNullException(nameof(session)); - - return Invoke(session, client => - { - var response = client.GetAsync("cluster/config/apiversion").GetAwaiter().GetResult(); - var data = JObject.Parse(response)["data"]; - return data?.ToObject() ?? 0; - }); - } - /// /// Returns the cluster-wide options (GET /cluster/options). /// diff --git a/src/PSProxmoxVE.Core/Services/ContainerService.cs b/src/PSProxmoxVE.Core/Services/ContainerService.cs index 557654a..7fcbf2a 100644 --- a/src/PSProxmoxVE.Core/Services/ContainerService.cs +++ b/src/PSProxmoxVE.Core/Services/ContainerService.cs @@ -96,22 +96,6 @@ namespace PSProxmoxVE.Core.Services }); } - /// - /// Returns a single container by its ID on the specified node. - /// - public PveContainer GetContainer(PveSession session, string node, int vmid) - { - if (session == null) throw new ArgumentNullException(nameof(session)); - if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node)); - - var containers = GetContainersOnNode(session, node); - var ct = containers.FirstOrDefault(c => c.VmId == vmid); - if (ct == null) - throw new InvalidOperationException($"Container {vmid} not found on node '{node}'."); - ct.Node ??= node; - return ct; - } - /// /// Returns the full configuration of a container. /// diff --git a/src/PSProxmoxVE.Core/Services/HaService.cs b/src/PSProxmoxVE.Core/Services/HaService.cs index 83606d0..6eb8b5c 100644 --- a/src/PSProxmoxVE.Core/Services/HaService.cs +++ b/src/PSProxmoxVE.Core/Services/HaService.cs @@ -4,7 +4,6 @@ using Newtonsoft.Json.Linq; using PSProxmoxVE.Core.Authentication; using PSProxmoxVE.Core.Client; using PSProxmoxVE.Core.Models.HA; -using PSProxmoxVE.Core.Utilities; namespace PSProxmoxVE.Core.Services { @@ -285,21 +284,6 @@ namespace PSProxmoxVE.Core.Services }); } - /// - /// Returns the full HA manager status as a raw JSON object. - /// - public Dictionary GetManagerStatus(PveSession session) - { - if (session == null) throw new ArgumentNullException(nameof(session)); - - return Invoke(session, client => - { - var response = client.GetAsync("cluster/ha/status/manager_status").GetAwaiter().GetResult(); - var data = JObject.Parse(response)["data"]; - return JsonHelper.ToDictionary(data as JObject); - }); - } - // ------------------------------------------------------------------------- // Rules (PVE 9.0+) // ------------------------------------------------------------------------- diff --git a/src/PSProxmoxVE.Core/Services/NodeService.cs b/src/PSProxmoxVE.Core/Services/NodeService.cs index 957a079..d6d3d0a 100644 --- a/src/PSProxmoxVE.Core/Services/NodeService.cs +++ b/src/PSProxmoxVE.Core/Services/NodeService.cs @@ -175,23 +175,5 @@ namespace PSProxmoxVE.Core.Services return PveTaskResponse.Parse(response, node); }); } - - /// - /// Returns the Proxmox VE version running on the server. - /// - public PveVersion GetVersion(PveSession session) - { - if (session == null) throw new ArgumentNullException(nameof(session)); - - return Invoke(session, client => - { - var response = client.GetAsync("version").GetAwaiter().GetResult(); - var data = JObject.Parse(response)["data"]; - var versionStr = data?["version"]?.ToString(); - if (string.IsNullOrEmpty(versionStr)) - throw new InvalidOperationException("Failed to retrieve PVE version from API response."); - return PveVersion.Parse(versionStr!); - }); - } } } diff --git a/src/PSProxmoxVE.Core/Services/TaskService.cs b/src/PSProxmoxVE.Core/Services/TaskService.cs index e9a49d8..ec816dd 100644 --- a/src/PSProxmoxVE.Core/Services/TaskService.cs +++ b/src/PSProxmoxVE.Core/Services/TaskService.cs @@ -83,25 +83,6 @@ namespace PSProxmoxVE.Core.Services return task; } - /// - /// Returns all log lines for a task identified by its UPID. - /// - public PveTaskLog[] GetTaskLog(PveSession session, string node, string upid) - { - if (session == null) throw new ArgumentNullException(nameof(session)); - if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node)); - if (string.IsNullOrWhiteSpace(upid)) throw new ArgumentNullException(nameof(upid)); - - return Invoke(session, client => - { - var encodedUpid = Uri.EscapeDataString(upid); - var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/tasks/{encodedUpid}/log") - .GetAwaiter().GetResult(); - var data = JObject.Parse(response)["data"]; - return data?.ToObject() ?? Array.Empty(); - }); - } - /// /// Polls the task status until it completes, throws on timeout or failure. One HTTP /// client is held open for the whole wait. diff --git a/src/PSProxmoxVE.Core/Services/UserService.cs b/src/PSProxmoxVE.Core/Services/UserService.cs index f223491..8ce712d 100644 --- a/src/PSProxmoxVE.Core/Services/UserService.cs +++ b/src/PSProxmoxVE.Core/Services/UserService.cs @@ -44,27 +44,6 @@ namespace PSProxmoxVE.Core.Services }); } - /// Returns a single user by their user ID (e.g. "admin@pam"). - /// The authenticated PVE session. - /// The user ID in "username@realm" format. - public PveUser GetUser(PveSession session, string userId) - { - if (session == null) throw new ArgumentNullException(nameof(session)); - if (string.IsNullOrWhiteSpace(userId)) throw new ArgumentNullException(nameof(userId)); - - return Invoke(session, client => - { - var encodedId = Uri.EscapeDataString(userId); - var response = client.GetAsync($"access/users/{encodedId}").GetAwaiter().GetResult(); - var data = JObject.Parse(response)["data"]; - var user = data?.ToObject() ?? new PveUser(); - // The single-user endpoint may not echo back the userid - if (string.IsNullOrEmpty(user.UserId)) - user.UserId = userId; - return user; - }); - } - /// /// Creates a new user account. /// diff --git a/src/PSProxmoxVE.Core/Services/VmService.cs b/src/PSProxmoxVE.Core/Services/VmService.cs index 047b81c..593555c 100644 --- a/src/PSProxmoxVE.Core/Services/VmService.cs +++ b/src/PSProxmoxVE.Core/Services/VmService.cs @@ -309,28 +309,6 @@ namespace PSProxmoxVE.Core.Services public PveTask StopVm(PveSession session, string node, int vmid) => PostStatus(session, node, vmid, "stop"); - /// Gracefully shuts down a VM. Returns the task UPID. - /// The authenticated PVE session. - /// The cluster node name. - /// The VM ID. - /// Optional shutdown timeout in seconds. - public PveTask ShutdownVm(PveSession session, string node, int vmid, int? timeoutSeconds = null) - { - if (session == null) throw new ArgumentNullException(nameof(session)); - if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node)); - - var formData = new Dictionary(); - if (timeoutSeconds.HasValue) - formData["timeout"] = timeoutSeconds.Value.ToString(); - - return Invoke(session, client => - { - var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/status/shutdown", formData) - .GetAwaiter().GetResult(); - return PveTaskResponse.Parse(response, node); - }); - } - /// /// Reboots a VM through PVE's native reboot endpoint. Returns the task UPID. /// diff --git a/tests/PSProxmoxVE.Core.Tests/Services/CloudInitServiceTests.cs b/tests/PSProxmoxVE.Core.Tests/Services/CloudInitServiceTests.cs index b578d21..bd55fea 100644 --- a/tests/PSProxmoxVE.Core.Tests/Services/CloudInitServiceTests.cs +++ b/tests/PSProxmoxVE.Core.Tests/Services/CloudInitServiceTests.cs @@ -17,61 +17,6 @@ namespace PSProxmoxVE.Core.Tests.Services "root@pam!testtoken=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"); } - [Fact] - public void GetCloudInitConfig_ReturnsConfig() - { - // Arrange - var json = @"{""data"": { - ""ciuser"": ""ubuntu"", - ""ipconfig0"": ""ip=dhcp"", - ""nameserver"": ""8.8.8.8"", - ""searchdomain"": ""example.com"", - ""sshkeys"": ""ssh-rsa%20AAAA...%20user%40host"", - ""boot"": ""order=scsi0;net0"", - ""cores"": 4, - ""memory"": 8192 - }}"; - var mockClient = new Mock(); - mockClient.Setup(c => c.GetAsync("nodes/pve1/qemu/100/config")).ReturnsAsync(json); - var service = new CloudInitService(mockClient.Object); - - // Act - var config = service.GetCloudInitConfig(CreateSession(), "pve1", 100); - - // Assert - Assert.NotNull(config); - Assert.Equal("ubuntu", config.CiUser); - Assert.Equal("ip=dhcp", config.IpConfig0); - Assert.Equal("8.8.8.8", config.Nameserver); - Assert.Equal("example.com", config.Searchdomain); - Assert.Equal("ssh-rsa%20AAAA...%20user%40host", config.SshKeys); - mockClient.Verify(c => c.GetAsync("nodes/pve1/qemu/100/config"), Times.Once); - } - - [Fact] - public void GetCloudInitConfig_ExcludesNonCiFields() - { - // Arrange — response includes non-CI fields that should be ignored - var json = @"{""data"": { - ""ciuser"": ""admin"", - ""cores"": 4, - ""memory"": 8192, - ""boot"": ""order=scsi0"" - }}"; - var mockClient = new Mock(); - mockClient.Setup(c => c.GetAsync("nodes/pve1/qemu/200/config")).ReturnsAsync(json); - var service = new CloudInitService(mockClient.Object); - - // Act - var config = service.GetCloudInitConfig(CreateSession(), "pve1", 200); - - // Assert - Assert.Equal("admin", config.CiUser); - // Non-CI fields should not appear in the result - Assert.Null(config.IpConfig0); - Assert.Null(config.Nameserver); - } - [Fact] public void SetCloudInitConfig_CallsPutAsyncWithCorrectResource() { diff --git a/tests/PSProxmoxVE.Core.Tests/Services/ClusterConfigServiceTests.cs b/tests/PSProxmoxVE.Core.Tests/Services/ClusterConfigServiceTests.cs index 03970a2..228a6b2 100644 --- a/tests/PSProxmoxVE.Core.Tests/Services/ClusterConfigServiceTests.cs +++ b/tests/PSProxmoxVE.Core.Tests/Services/ClusterConfigServiceTests.cs @@ -225,43 +225,6 @@ namespace PSProxmoxVE.Core.Tests.Services Times.Once); } - [Fact] - public void GetTotem_ReturnsJObject() - { - // Arrange - var json = @"{""data"": {""version"": ""2"", ""secauth"": ""on"", ""cluster_name"": ""pve-cluster""}}"; - var mockClient = new Mock(); - mockClient.Setup(c => c.GetAsync("cluster/config/totem")).ReturnsAsync(json); - var service = new ClusterConfigService(mockClient.Object); - - // Act - var totem = service.GetTotem(CreateSession()); - - // Assert - Assert.NotNull(totem); - Assert.Equal("2", totem["version"]?.ToString()); - Assert.Equal("on", totem["secauth"]?.ToString()); - Assert.Equal("pve-cluster", totem["cluster_name"]?.ToString()); - mockClient.Verify(c => c.GetAsync("cluster/config/totem"), Times.Once); - } - - [Fact] - public void GetApiVersion_ReturnsInt() - { - // Arrange - var json = @"{""data"": 10}"; - var mockClient = new Mock(); - mockClient.Setup(c => c.GetAsync("cluster/config/apiversion")).ReturnsAsync(json); - var service = new ClusterConfigService(mockClient.Object); - - // Act - var version = service.GetApiVersion(CreateSession()); - - // Assert - Assert.Equal(10, version); - mockClient.Verify(c => c.GetAsync("cluster/config/apiversion"), Times.Once); - } - [Fact] public void GetClusterOptions_ReturnsPveClusterOptions() { diff --git a/tests/PSProxmoxVE.Core.Tests/Services/HaServiceTests.cs b/tests/PSProxmoxVE.Core.Tests/Services/HaServiceTests.cs index c9ef2de..e74fefc 100644 --- a/tests/PSProxmoxVE.Core.Tests/Services/HaServiceTests.cs +++ b/tests/PSProxmoxVE.Core.Tests/Services/HaServiceTests.cs @@ -337,25 +337,6 @@ namespace PSProxmoxVE.Core.Tests.Services mockClient.Verify(c => c.GetAsync("cluster/ha/status/current"), Times.Once); } - [Fact] - public void GetManagerStatus_ReturnsJObject() - { - // Arrange - var json = @"{""data"": {""manager_status"": {""master_node"": ""pve1""}, ""quorum"": {""node"": ""pve1""}}}"; - var mockClient = new Mock(); - mockClient.Setup(c => c.GetAsync("cluster/ha/status/manager_status")).ReturnsAsync(json); - var service = new HaService(mockClient.Object); - - // Act - var status = service.GetManagerStatus(CreateSession()); - - // Assert - Assert.NotNull(status); - Assert.NotNull(status["manager_status"]); - Assert.NotNull(status["quorum"]); - mockClient.Verify(c => c.GetAsync("cluster/ha/status/manager_status"), Times.Once); - } - // ----------------------------------------------------------------- // Rules // ----------------------------------------------------------------- diff --git a/tests/PSProxmoxVE.Core.Tests/Services/TaskServiceTests.cs b/tests/PSProxmoxVE.Core.Tests/Services/TaskServiceTests.cs index aa09841..42425ae 100644 --- a/tests/PSProxmoxVE.Core.Tests/Services/TaskServiceTests.cs +++ b/tests/PSProxmoxVE.Core.Tests/Services/TaskServiceTests.cs @@ -58,35 +58,6 @@ namespace PSProxmoxVE.Core.Tests.Services Assert.True(task.IsSuccessful); } - [Fact] - public void GetTaskLog_HappyPath_ReturnsLogEntries() - { - // Arrange - var json = @"{ - ""data"": [ - { ""n"": 1, ""t"": ""starting task qmstart"" }, - { ""n"": 2, ""t"": ""VM 100 started"" }, - { ""n"": 3, ""t"": ""TASK OK"" } - ] - }"; - - var mockClient = new Mock(); - mockClient.Setup(c => c.GetAsync(It.IsAny())) - .ReturnsAsync(json); - - var service = new TaskService(mockClient.Object); - var session = CreateSession(); - - // Act - var logs = service.GetTaskLog(session, TestNode, TestUpid); - - // Assert - Assert.Equal(3, logs.Length); - Assert.Equal(1, logs[0].LineNumber); - Assert.Equal("starting task qmstart", logs[0].Text); - Assert.Equal("TASK OK", logs[2].Text); - } - [Fact] public void GetTasks_HappyPath_ReturnsCorrectCount() { diff --git a/tests/PSProxmoxVE.Core.Tests/Services/UserServiceTests.cs b/tests/PSProxmoxVE.Core.Tests/Services/UserServiceTests.cs index da4a8b8..8fd0933 100644 --- a/tests/PSProxmoxVE.Core.Tests/Services/UserServiceTests.cs +++ b/tests/PSProxmoxVE.Core.Tests/Services/UserServiceTests.cs @@ -53,28 +53,6 @@ namespace PSProxmoxVE.Core.Tests.Services _mockClient.Verify(c => c.GetAsync("access/users"), Times.Once); } - [Fact] - public void GetUser_ReturnsSingleUser() - { - // Arrange - _mockClient.Setup(c => c.GetAsync("access/users/root%40pam")) - .ReturnsAsync(@"{""data"":{""email"":""root@example.com"",""firstname"":""Root"",""lastname"":""Admin"",""enable"":1}}"); - - // Act - var result = _service.GetUser(_session, "root@pam"); - - // Assert - Assert.Equal("root@pam", result.UserId); - Assert.Equal("root@example.com", result.Email); - Assert.Equal(1, result.Enabled); - } - - [Fact] - public void GetUser_NullUserId_ThrowsArgumentNullException() - { - Assert.Throws(() => _service.GetUser(_session, null!)); - } - [Fact] public void CreateUser_PostsFormData() {