fix(test): fix assembly loading, remove mock server, resolve test warnings

- Create shared _TestHelper.ps1 for reliable module loading in both
  local dev and CI (fixes System.Runtime 9.0.0.0 FileNotFoundException
  on PS 7.x by trying Import-Module by name first, then local paths)
- Use (Get-Module).ModuleBase for manifest path resolution
- Remove PSProxmoxVE.MockServer project and MockIntegration tests
- Remove MockIntegration from ExcludeTag filters

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Clint Branham
2026-03-18 18:11:33 -05:00
parent b73c20fdbf
commit 0dc7f5f833
38 changed files with 80 additions and 1222 deletions
+2 -2
View File
@@ -96,7 +96,7 @@ jobs:
$config = New-PesterConfiguration
$config.Run.Path = "tests/PSProxmoxVE.Tests"
$config.Run.Exit = $true
$config.Filter.ExcludeTag = @("Integration", "MockIntegration")
$config.Filter.ExcludeTag = @("Integration")
$config.Output.Verbosity = "Detailed"
$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = "NUnitXml"
@@ -111,7 +111,7 @@ jobs:
$config = New-PesterConfiguration
$config.Run.Path = "tests/PSProxmoxVE.Tests"
$config.Run.Exit = $true
$config.Filter.ExcludeTag = @("Integration", "MockIntegration")
$config.Filter.ExcludeTag = @("Integration")
$config.Output.Verbosity = "Detailed"
$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = "NUnitXml"
-15
View File
@@ -13,8 +13,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSProxmoxVE", "src\PSProxmo
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSProxmoxVE.Core.Tests", "tests\PSProxmoxVE.Core.Tests\PSProxmoxVE.Core.Tests.csproj", "{E5F6A7B8-C9D0-1234-EF12-345678901234}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSProxmoxVE.MockServer", "tests\PSProxmoxVE.MockServer\PSProxmoxVE.MockServer.csproj", "{B7490D48-1665-41F4-B248-70051EC2F061}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -61,18 +59,6 @@ Global
{E5F6A7B8-C9D0-1234-EF12-345678901234}.Release|x64.Build.0 = Release|Any CPU
{E5F6A7B8-C9D0-1234-EF12-345678901234}.Release|x86.ActiveCfg = Release|Any CPU
{E5F6A7B8-C9D0-1234-EF12-345678901234}.Release|x86.Build.0 = Release|Any CPU
{B7490D48-1665-41F4-B248-70051EC2F061}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7490D48-1665-41F4-B248-70051EC2F061}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7490D48-1665-41F4-B248-70051EC2F061}.Debug|x64.ActiveCfg = Debug|Any CPU
{B7490D48-1665-41F4-B248-70051EC2F061}.Debug|x64.Build.0 = Debug|Any CPU
{B7490D48-1665-41F4-B248-70051EC2F061}.Debug|x86.ActiveCfg = Debug|Any CPU
{B7490D48-1665-41F4-B248-70051EC2F061}.Debug|x86.Build.0 = Debug|Any CPU
{B7490D48-1665-41F4-B248-70051EC2F061}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7490D48-1665-41F4-B248-70051EC2F061}.Release|Any CPU.Build.0 = Release|Any CPU
{B7490D48-1665-41F4-B248-70051EC2F061}.Release|x64.ActiveCfg = Release|Any CPU
{B7490D48-1665-41F4-B248-70051EC2F061}.Release|x64.Build.0 = Release|Any CPU
{B7490D48-1665-41F4-B248-70051EC2F061}.Release|x86.ActiveCfg = Release|Any CPU
{B7490D48-1665-41F4-B248-70051EC2F061}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -81,6 +67,5 @@ Global
{C3D4E5F6-A7B8-9012-CDEF-123456789012} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}
{D4E5F6A7-B8C9-0123-DEF1-234567890123} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}
{E5F6A7B8-C9D0-1234-EF12-345678901234} = {B2C3D4E5-F6A7-8901-BCDE-F12345678901}
{B7490D48-1665-41F4-B248-70051EC2F061} = {B2C3D4E5-F6A7-8901-BCDE-F12345678901}
EndGlobalSection
EndGlobal
@@ -1,209 +0,0 @@
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using PSProxmoxVE.MockServer.Routes;
namespace PSProxmoxVE.MockServer;
public record RequestRecord(string Method, string Path, string? Body, DateTime Timestamp);
public class MockPveServer : IDisposable
{
private readonly WebApplication _app;
private bool _disposed;
public string BaseUrl { get; }
public int Port { get; }
public List<RequestRecord> Requests { get; } = new();
private static readonly Dictionary<string, string> _fixtures = new();
private static readonly object _fixtureLock = new();
// Track task status calls per UPID for simulating running -> completed transitions
internal Dictionary<string, int> TaskStatusCallCounts { get; } = new();
private MockPveServer(WebApplication app, int port)
{
_app = app;
Port = port;
BaseUrl = $"https://localhost:{port}";
}
public static MockPveServer Start(int port = 0)
{
LoadFixtures();
var cert = GenerateSelfSignedCert();
var builder = WebApplication.CreateBuilder();
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenLocalhost(port, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
listenOptions.UseHttps(cert);
});
});
// Suppress default logging noise in tests
builder.Logging.ClearProviders();
var app = builder.Build();
var server = new MockPveServer(app, port);
// Request tracking middleware
app.Use(async (context, next) =>
{
string? body = null;
if (context.Request.ContentLength > 0 || context.Request.Headers.ContainsKey("Transfer-Encoding"))
{
context.Request.EnableBuffering();
using var reader = new StreamReader(context.Request.Body, leaveOpen: true);
body = await reader.ReadToEndAsync();
context.Request.Body.Position = 0;
}
server.Requests.Add(new RequestRecord(
context.Request.Method,
context.Request.Path + context.Request.QueryString,
body,
DateTime.UtcNow));
await next();
});
// Auth middleware
app.Use(async (context, next) =>
{
var path = context.Request.Path.Value ?? "";
var method = context.Request.Method;
// Skip auth for ticket creation and version endpoints
bool skipAuth = (path == "/api2/json/access/ticket" && method == "POST")
|| (path == "/api2/json/access/ticket" && method == "DELETE")
|| (path == "/api2/json/version" && method == "GET");
if (!skipAuth)
{
var hasCookie = context.Request.Headers["Cookie"].ToString().Contains("PVEAuthCookie=");
var hasToken = context.Request.Headers["Authorization"].ToString().StartsWith("PVEAPIToken=");
if (!hasCookie && !hasToken)
{
context.Response.StatusCode = 401;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync("{\"data\":null,\"errors\":{\"authentication\":\"invalid credentials\"}}");
return;
}
}
await next();
});
// Register all routes
app.MapAuthRoutes();
app.MapVersionRoutes();
app.MapNodeRoutes();
app.MapVmRoutes();
app.MapContainerRoutes();
app.MapStorageRoutes();
app.MapNetworkRoutes();
app.MapUserRoutes();
app.MapClusterRoutes();
app.MapTaskRoutes(server);
app.MapSnapshotRoutes();
app.StartAsync().GetAwaiter().GetResult();
// Resolve the actual port (important when port=0 for random assignment)
var addresses = app.Urls;
var actualPort = port;
var serverInstance = app.Services.GetRequiredService<IServer>();
if (serverInstance != null)
{
var addressFeature = serverInstance.Features.Get<IServerAddressesFeature>();
if (addressFeature != null)
{
var addr = addressFeature.Addresses.FirstOrDefault();
if (addr != null)
{
var uri = new Uri(addr);
actualPort = uri.Port;
}
}
}
return new MockPveServer(app, actualPort);
}
public void Reset()
{
Requests.Clear();
TaskStatusCallCounts.Clear();
}
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
_app.StopAsync().GetAwaiter().GetResult();
_app.DisposeAsync().AsTask().GetAwaiter().GetResult();
}
}
public static string GetFixture(string name)
{
if (_fixtures.TryGetValue(name, out var content))
return content;
return "{\"data\":null}";
}
public static string GenerateUpid(string node, string type, int vmid = 0)
=> $"UPID:{node}:{Guid.NewGuid():N}:{DateTimeOffset.UtcNow.ToUnixTimeSeconds():X}:{type}:{vmid}:root@pam:";
private static X509Certificate2 GenerateSelfSignedCert()
{
var rsa = RSA.Create(2048);
var req = new CertificateRequest("CN=pve-mock", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1));
return cert;
}
private static void LoadFixtures()
{
lock (_fixtureLock)
{
if (_fixtures.Count > 0) return;
// Resolve fixtures path relative to the assembly location
var basePath = AppContext.BaseDirectory;
var fixturesPath = Path.GetFullPath(Path.Combine(basePath, "..", "..", "..", "..", "PSProxmoxVE.Core.Tests", "Fixtures"));
// Fallback: try relative to the project source directory
if (!Directory.Exists(fixturesPath))
{
fixturesPath = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..", "PSProxmoxVE.Core.Tests", "Fixtures"));
}
if (!Directory.Exists(fixturesPath))
{
// Last resort: look from the working directory
fixturesPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..", "PSProxmoxVE.Core.Tests", "Fixtures"));
}
if (Directory.Exists(fixturesPath))
{
foreach (var file in Directory.GetFiles(fixturesPath, "*.json"))
{
var name = Path.GetFileNameWithoutExtension(file);
_fixtures[name] = File.ReadAllText(file);
}
}
}
}
}
@@ -1,9 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
-22
View File
@@ -1,22 +0,0 @@
using PSProxmoxVE.MockServer;
// When run standalone, start on a fixed port
using var server = MockPveServer.Start(port: 8006);
Console.WriteLine($"Mock Proxmox VE API server running at {server.BaseUrl}");
Console.WriteLine("Press Ctrl+C to stop.");
var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true;
cts.Cancel();
};
try
{
await Task.Delay(Timeout.Infinite, cts.Token);
}
catch (OperationCanceledException)
{
// Graceful shutdown
}
@@ -1,18 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PSProxmoxVE.MockServer.Routes;
public static class AuthRoutes
{
public static WebApplication MapAuthRoutes(this WebApplication app)
{
app.MapPost("/api2/json/access/ticket", () =>
Results.Content(MockPveServer.GetFixture("pve9_ticket_response"), "application/json"));
app.MapDelete("/api2/json/access/ticket", () =>
Results.Content("{\"data\":null}", "application/json"));
return app;
}
}
@@ -1,15 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PSProxmoxVE.MockServer.Routes;
public static class ClusterRoutes
{
public static WebApplication MapClusterRoutes(this WebApplication app)
{
app.MapGet("/api2/json/cluster/status", () =>
Results.Content(MockPveServer.GetFixture("pve9_cluster_status"), "application/json"));
return app;
}
}
@@ -1,36 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PSProxmoxVE.MockServer.Routes;
public static class ContainerRoutes
{
public static WebApplication MapContainerRoutes(this WebApplication app)
{
app.MapGet("/api2/json/nodes/{node}/lxc", (string node) =>
Results.Content(MockPveServer.GetFixture("pve9_containers"), "application/json"));
app.MapGet("/api2/json/nodes/{node}/lxc/{vmid}/config", (string node, int vmid) =>
Results.Content("{\"data\":{\"hostname\":\"test\",\"memory\":512,\"cores\":1}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/lxc", (string node) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "vzcreate")}\"}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/lxc/{vmid}/status/start", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "vzstart", vmid)}\"}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/lxc/{vmid}/status/stop", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "vzstop", vmid)}\"}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/lxc/{vmid}/status/shutdown", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "vzshutdown", vmid)}\"}}", "application/json"));
app.MapPut("/api2/json/nodes/{node}/lxc/{vmid}/config", (string node, int vmid) =>
Results.Content("{\"data\":null}", "application/json"));
app.MapDelete("/api2/json/nodes/{node}/lxc/{vmid}", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "vzdestroy", vmid)}\"}}", "application/json"));
return app;
}
}
@@ -1,35 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PSProxmoxVE.MockServer.Routes;
public static class NetworkRoutes
{
public static WebApplication MapNetworkRoutes(this WebApplication app)
{
app.MapGet("/api2/json/nodes/{node}/network", (string node) =>
Results.Content(MockPveServer.GetFixture("pve9_networks"), "application/json"));
app.MapPost("/api2/json/nodes/{node}/network", (string node) =>
Results.Content("{\"data\":null}", "application/json"));
app.MapPut("/api2/json/nodes/{node}/network/{iface}", (string node, string iface) =>
Results.Content("{\"data\":null}", "application/json"));
app.MapDelete("/api2/json/nodes/{node}/network/{iface}", (string node, string iface) =>
Results.Content("{\"data\":null}", "application/json"));
// Apply network config
app.MapPut("/api2/json/nodes/{node}/network", (string node) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "netapply")}\"}}", "application/json"));
// SDN routes
app.MapGet("/api2/json/cluster/sdn/zones", () =>
Results.Content(MockPveServer.GetFixture("pve9_sdn_zones"), "application/json"));
app.MapGet("/api2/json/cluster/sdn/vnets", () =>
Results.Content(MockPveServer.GetFixture("pve9_sdn_vnets"), "application/json"));
return app;
}
}
@@ -1,18 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PSProxmoxVE.MockServer.Routes;
public static class NodeRoutes
{
public static WebApplication MapNodeRoutes(this WebApplication app)
{
app.MapGet("/api2/json/nodes", () =>
Results.Content(MockPveServer.GetFixture("pve9_nodes"), "application/json"));
app.MapGet("/api2/json/nodes/{node}/status", (string node) =>
Results.Content(MockPveServer.GetFixture("pve9_node_status"), "application/json"));
return app;
}
}
@@ -1,24 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PSProxmoxVE.MockServer.Routes;
public static class SnapshotRoutes
{
public static WebApplication MapSnapshotRoutes(this WebApplication app)
{
app.MapGet("/api2/json/nodes/{node}/qemu/{vmid}/snapshot", (string node, int vmid) =>
Results.Content(MockPveServer.GetFixture("pve9_snapshots"), "application/json"));
app.MapPost("/api2/json/nodes/{node}/qemu/{vmid}/snapshot", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmsnapshot", vmid)}\"}}", "application/json"));
app.MapDelete("/api2/json/nodes/{node}/qemu/{vmid}/snapshot/{snapname}", (string node, int vmid, string snapname) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmdelsnapshot", vmid)}\"}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/qemu/{vmid}/snapshot/{snapname}/rollback", (string node, int vmid, string snapname) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmrollback", vmid)}\"}}", "application/json"));
return app;
}
}
@@ -1,28 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PSProxmoxVE.MockServer.Routes;
public static class StorageRoutes
{
public static WebApplication MapStorageRoutes(this WebApplication app)
{
app.MapGet("/api2/json/storage", () =>
Results.Content(MockPveServer.GetFixture("pve9_storage"), "application/json"));
app.MapGet("/api2/json/nodes/{node}/storage/{storage}/content", (string node, string storage) =>
Results.Content(MockPveServer.GetFixture("pve9_storage_content"), "application/json"));
app.MapPost("/api2/json/nodes/{node}/storage/{storage}/upload", (string node, string storage) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "upload")}\"}}", "application/json"))
.DisableAntiforgery();
app.MapPost("/api2/json/storage", () =>
Results.Content("{\"data\":null}", "application/json"));
app.MapDelete("/api2/json/storage/{storage}", (string storage) =>
Results.Content("{\"data\":null}", "application/json"));
return app;
}
}
@@ -1,33 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PSProxmoxVE.MockServer.Routes;
public static class TaskRoutes
{
public static WebApplication MapTaskRoutes(this WebApplication app, MockPveServer server)
{
app.MapGet("/api2/json/nodes/{node}/tasks/{upid}/status", (string node, string upid) =>
{
// Track call count per UPID: first call returns running, subsequent return completed
int count;
lock (server.TaskStatusCallCounts)
{
if (!server.TaskStatusCallCounts.TryGetValue(upid, out count))
count = 0;
count++;
server.TaskStatusCallCounts[upid] = count;
}
if (count <= 1)
return Results.Content(MockPveServer.GetFixture("pve9_task_running"), "application/json");
else
return Results.Content(MockPveServer.GetFixture("pve9_tasks"), "application/json");
});
app.MapGet("/api2/json/nodes/{node}/tasks/{upid}/log", (string node, string upid) =>
Results.Content("{\"data\":[{\"n\":1,\"t\":\"starting task\"}]}", "application/json"));
return app;
}
}
@@ -1,58 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PSProxmoxVE.MockServer.Routes;
public static class UserRoutes
{
public static WebApplication MapUserRoutes(this WebApplication app)
{
app.MapGet("/api2/json/access/users", () =>
Results.Content(MockPveServer.GetFixture("pve9_users"), "application/json"));
app.MapGet("/api2/json/access/users/{userid}", (string userid) =>
{
// Return first user from fixture
var fixture = MockPveServer.GetFixture("pve9_users");
try
{
var doc = System.Text.Json.JsonDocument.Parse(fixture);
var dataArray = doc.RootElement.GetProperty("data");
var first = dataArray[0];
return Results.Content($"{{\"data\":{first.GetRawText()}}}", "application/json");
}
catch
{
return Results.Content("{\"data\":null}", "application/json");
}
});
app.MapPost("/api2/json/access/users", () =>
Results.Content("{\"data\":null}", "application/json"));
app.MapPut("/api2/json/access/users/{userid}", (string userid) =>
Results.Content("{\"data\":null}", "application/json"));
app.MapDelete("/api2/json/access/users/{userid}", (string userid) =>
Results.Content("{\"data\":null}", "application/json"));
// Roles
app.MapGet("/api2/json/access/roles", () =>
Results.Content(MockPveServer.GetFixture("pve9_roles"), "application/json"));
app.MapPost("/api2/json/access/roles", () =>
Results.Content("{\"data\":null}", "application/json"));
app.MapDelete("/api2/json/access/roles/{roleid}", (string roleid) =>
Results.Content("{\"data\":null}", "application/json"));
// ACL
app.MapGet("/api2/json/access/acl", () =>
Results.Content("{\"data\":[]}", "application/json"));
app.MapPut("/api2/json/access/acl", () =>
Results.Content("{\"data\":null}", "application/json"));
return app;
}
}
@@ -1,15 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PSProxmoxVE.MockServer.Routes;
public static class VersionRoutes
{
public static WebApplication MapVersionRoutes(this WebApplication app)
{
app.MapGet("/api2/json/version", () =>
Results.Content(MockPveServer.GetFixture("pve9_version"), "application/json"));
return app;
}
}
@@ -1,77 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PSProxmoxVE.MockServer.Routes;
public static class VmRoutes
{
public static WebApplication MapVmRoutes(this WebApplication app)
{
app.MapGet("/api2/json/nodes/{node}/qemu", (string node) =>
Results.Content(MockPveServer.GetFixture("pve9_vms"), "application/json"));
app.MapGet("/api2/json/nodes/{node}/qemu/{vmid}/config", (string node, int vmid) =>
Results.Content(MockPveServer.GetFixture("pve9_vm_config"), "application/json"));
app.MapGet("/api2/json/nodes/{node}/qemu/{vmid}/status/current", (string node, int vmid) =>
{
// Return first VM from fixture as a single-item response
var fixture = MockPveServer.GetFixture("pve9_vms");
try
{
var doc = System.Text.Json.JsonDocument.Parse(fixture);
var dataArray = doc.RootElement.GetProperty("data");
foreach (var item in dataArray.EnumerateArray())
{
if (item.TryGetProperty("vmid", out var id) && id.GetInt32() == vmid)
return Results.Content($"{{\"data\":{item.GetRawText()}}}", "application/json");
}
// If no match, return first item
var first = dataArray[0];
return Results.Content($"{{\"data\":{first.GetRawText()}}}", "application/json");
}
catch
{
return Results.Content("{\"data\":null}", "application/json");
}
});
app.MapPost("/api2/json/nodes/{node}/qemu", (string node) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmcreate")}\"}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/qemu/{vmid}/status/start", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmstart", vmid)}\"}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/qemu/{vmid}/status/stop", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmstop", vmid)}\"}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/qemu/{vmid}/status/shutdown", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmshutdown", vmid)}\"}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/qemu/{vmid}/status/reset", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmreset", vmid)}\"}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/qemu/{vmid}/status/suspend", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmsuspend", vmid)}\"}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/qemu/{vmid}/status/resume", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmresume", vmid)}\"}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/qemu/{vmid}/clone", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmclone", vmid)}\"}}", "application/json"));
app.MapPost("/api2/json/nodes/{node}/qemu/{vmid}/migrate", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmigrate", vmid)}\"}}", "application/json"));
app.MapPut("/api2/json/nodes/{node}/qemu/{vmid}/config", (string node, int vmid) =>
Results.Content("{\"data\":null}", "application/json"));
app.MapPut("/api2/json/nodes/{node}/qemu/{vmid}/resize", (string node, int vmid) =>
Results.Content("{\"data\":null}", "application/json"));
app.MapDelete("/api2/json/nodes/{node}/qemu/{vmid}", (string node, int vmid) =>
Results.Content($"{{\"data\":\"{MockPveServer.GenerateUpid(node, "qmdestroy", vmid)}\"}}", "application/json"));
return app;
}
}
@@ -14,21 +14,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
$script:Availability = @{}
foreach ($name in @('Get-PveCloudInitConfig', 'Set-PveCloudInitConfig',
@@ -48,7 +34,7 @@ BeforeAll {
# ---------------------------------------------------------------------------
Describe 'Cloud-Init cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$manifestPath = Join-Path (Get-Module PSProxmoxVE).ModuleBase 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
@@ -10,21 +10,7 @@
BeforeAll {
# Resolve the built DLL relative to the repository root.
# Adjust the path if your build output directory differs.
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
}
Describe 'Connect-PveServer' {
@@ -6,21 +6,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
}
Describe 'Disconnect-PveServer' {
@@ -6,21 +6,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
}
Describe 'Test-PveConnection' {
@@ -10,21 +10,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
# Pre-calculate availability for each cmdlet.
$script:Availability = @{}
@@ -10,21 +10,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
$script:CmdExists = $null -ne (Get-Command 'Get-PveContainer' -ErrorAction SilentlyContinue)
}
@@ -34,7 +20,7 @@ Describe 'Get-PveContainer' {
Context 'Command existence' {
It 'Get-PveContainer should be listed in the module manifest CmdletsToExport' {
# The .psd1 declares the cmdlet; implementation may be pending.
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$manifestPath = Join-Path (Get-Module PSProxmoxVE).ModuleBase 'PSProxmoxVE.psd1'
if (Test-Path $manifestPath) {
$manifest = Import-PowerShellDataFile $manifestPath
$manifest.CmdletsToExport | Should -Contain 'Get-PveContainer'
@@ -27,21 +27,7 @@ BeforeAll {
# -----------------------------------------------------------------------
# Resolve and import module
# -----------------------------------------------------------------------
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$moduleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $moduleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $moduleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
# -----------------------------------------------------------------------
# Check required environment variables
@@ -1,311 +0,0 @@
#Requires -Modules Pester
<#
.SYNOPSIS
Integration tests that run PSProxmoxVE cmdlets against the mock PVE API server.
These validate end-to-end behavior: HTTP requests, auth, deserialization, pipeline.
.DESCRIPTION
The mock server is started before all tests and stopped after. Each test group
exercises real cmdlet execution against simulated PVE API responses. No real
Proxmox host is needed.
#>
BeforeAll {
# Find the built mock server
$mockServerProject = Join-Path $PSScriptRoot '../../PSProxmoxVE.MockServer/PSProxmoxVE.MockServer.csproj'
if (-not (Test-Path $mockServerProject)) {
throw "Mock server project not found at $mockServerProject"
}
# Find the built module DLL
$dllSearchPaths = @(
(Join-Path $PSScriptRoot '../../PSProxmoxVE.MockServer/bin/Release/net9.0/PSProxmoxVE.MockServer.dll'),
(Join-Path $PSScriptRoot '../../PSProxmoxVE.MockServer/bin/Debug/net9.0/PSProxmoxVE.MockServer.dll')
)
$mockServerDll = $dllSearchPaths | Where-Object { Test-Path $_ } | Select-Object -First 1
if (-not $mockServerDll) {
# Build it
$buildOutput = dotnet build $mockServerProject --configuration Release 2>&1 | Out-String
if ($LASTEXITCODE -ne 0) {
throw "Failed to build mock server (exit code $LASTEXITCODE): $buildOutput"
}
$mockServerDll = $dllSearchPaths | Where-Object { Test-Path $_ } | Select-Object -First 1
if (-not $mockServerDll) {
throw "Mock server build succeeded but DLL not found at expected paths: $($dllSearchPaths -join ', ')"
}
}
# Start mock server as a background process on a random port
$script:MockPort = Get-Random -Minimum 18000 -Maximum 19000
$script:MockHost = "localhost"
$script:MockUrl = "https://${script:MockHost}:${script:MockPort}"
$mockServerDir = Split-Path $mockServerDll -Parent
$script:MockProcess = Start-Process -FilePath 'dotnet' `
-ArgumentList @($mockServerDll, '--urls', "https://0.0.0.0:$($script:MockPort)") `
-WorkingDirectory $mockServerDir `
-PassThru -NoNewWindow -RedirectStandardOutput (Join-Path $TestDrive 'mock-stdout.log') `
-RedirectStandardError (Join-Path $TestDrive 'mock-stderr.log')
# Wait for server to become responsive
$maxWait = 30
$waited = 0
$ready = $false
while ($waited -lt $maxWait -and -not $ready) {
Start-Sleep -Seconds 1
$waited++
try {
# Use .NET directly to skip cert validation
$handler = [System.Net.Http.HttpClientHandler]::new()
$handler.ServerCertificateCustomValidationCallback = { $true }
$client = [System.Net.Http.HttpClient]::new($handler)
$result = $client.GetStringAsync("$($script:MockUrl)/api2/json/version").GetAwaiter().GetResult()
if ($result -match '"version"') {
$ready = $true
}
$client.Dispose()
$handler.Dispose()
} catch {
# Not ready yet
}
}
if (-not $ready) {
$stderr = if (Test-Path (Join-Path $TestDrive 'mock-stderr.log')) { Get-Content (Join-Path $TestDrive 'mock-stderr.log') -Raw } else { 'N/A' }
throw "Mock server failed to start within ${maxWait}s. Stderr: $stderr"
}
# Import the module
Import-Module PSProxmoxVE -Force -ErrorAction Stop
}
AfterAll {
# Stop mock server
if ($script:MockProcess -and -not $script:MockProcess.HasExited) {
$script:MockProcess.Kill()
$script:MockProcess.WaitForExit(5000)
}
# Disconnect any active session
try { Disconnect-PveServer -ErrorAction SilentlyContinue } catch { }
}
Describe 'Mock PVE Server - Authentication' -Tag 'MockIntegration' {
It 'Should connect with API token' {
$session = Connect-PveServer -Server $script:MockHost -Port $script:MockPort `
-ApiToken 'root@pam!test=00000000-0000-0000-0000-000000000000' `
-SkipCertificateCheck -PassThru
$session | Should -Not -BeNullOrEmpty
$session.Hostname | Should -Be $script:MockHost
$session.Port | Should -Be $script:MockPort
$session.AuthMode | Should -Be 'ApiToken'
$session.ServerVersion | Should -Not -BeNullOrEmpty
}
It 'Should connect with credentials' {
$secPassword = ConvertTo-SecureString 'testpass' -AsPlainText -Force
$cred = [PSCredential]::new('root@pam', $secPassword)
$session = Connect-PveServer -Server $script:MockHost -Port $script:MockPort `
-Credential $cred -SkipCertificateCheck -PassThru
$session | Should -Not -BeNullOrEmpty
$session.AuthMode | Should -Be 'Ticket'
$session.Ticket | Should -Not -BeNullOrEmpty
$session.CsrfToken | Should -Not -BeNullOrEmpty
$session.IsExpired | Should -Be $false
}
It 'Should report connection status' {
# Connect first
Connect-PveServer -Server $script:MockHost -Port $script:MockPort `
-ApiToken 'root@pam!test=00000000-0000-0000-0000-000000000000' `
-SkipCertificateCheck
$result = Test-PveConnection
$result | Should -Be $true
}
It 'Should disconnect cleanly' {
Connect-PveServer -Server $script:MockHost -Port $script:MockPort `
-ApiToken 'root@pam!test=00000000-0000-0000-0000-000000000000' `
-SkipCertificateCheck
{ Disconnect-PveServer } | Should -Not -Throw
$result = Test-PveConnection
$result | Should -Be $false
}
}
Describe 'Mock PVE Server - Node Operations' -Tag 'MockIntegration' {
BeforeAll {
Connect-PveServer -Server $script:MockHost -Port $script:MockPort `
-ApiToken 'root@pam!test=00000000-0000-0000-0000-000000000000' `
-SkipCertificateCheck
}
AfterAll {
try { Disconnect-PveServer -ErrorAction SilentlyContinue } catch { }
}
It 'Should list nodes' {
$nodes = Get-PveNode
$nodes | Should -Not -BeNullOrEmpty
$nodes[0].Node | Should -Not -BeNullOrEmpty
$nodes[0].Status | Should -Not -BeNullOrEmpty
}
It 'Should filter nodes by name' {
$nodes = Get-PveNode -Name 'pve1'
$nodes | Should -Not -BeNullOrEmpty
$nodes | ForEach-Object { $_.Node | Should -Be 'pve1' }
}
}
Describe 'Mock PVE Server - VM Operations' -Tag 'MockIntegration' {
BeforeAll {
Connect-PveServer -Server $script:MockHost -Port $script:MockPort `
-ApiToken 'root@pam!test=00000000-0000-0000-0000-000000000000' `
-SkipCertificateCheck
}
AfterAll {
try { Disconnect-PveServer -ErrorAction SilentlyContinue } catch { }
}
It 'Should list VMs' {
$vms = Get-PveVm -Node 'pve1'
$vms | Should -Not -BeNullOrEmpty
$vms.Count | Should -BeGreaterOrEqual 1
}
It 'Should filter VMs by status' {
$running = Get-PveVm -Node 'pve1' -Status 'running'
$running | Should -Not -BeNullOrEmpty
$running | ForEach-Object { $_.Status | Should -Be 'running' }
}
It 'Should get VM config' {
$config = Get-PveVmConfig -Node 'pve1' -VmId 100
$config | Should -Not -BeNullOrEmpty
$config.Cores | Should -BeGreaterThan 0
}
It 'Should get VM config via pipeline' {
$vms = Get-PveVm -Node 'pve1' -VmId 100
$config = $vms | Get-PveVmConfig
$config | Should -Not -BeNullOrEmpty
}
}
Describe 'Mock PVE Server - Storage Operations' -Tag 'MockIntegration' {
BeforeAll {
Connect-PveServer -Server $script:MockHost -Port $script:MockPort `
-ApiToken 'root@pam!test=00000000-0000-0000-0000-000000000000' `
-SkipCertificateCheck
}
AfterAll {
try { Disconnect-PveServer -ErrorAction SilentlyContinue } catch { }
}
It 'Should list storage' {
$storage = Get-PveStorage
$storage | Should -Not -BeNullOrEmpty
$storage[0].Storage | Should -Not -BeNullOrEmpty
}
It 'Should list storage content' {
$content = Get-PveStorageContent -Node 'pve1' -Storage 'local'
$content | Should -Not -BeNullOrEmpty
}
}
Describe 'Mock PVE Server - Network Operations' -Tag 'MockIntegration' {
BeforeAll {
Connect-PveServer -Server $script:MockHost -Port $script:MockPort `
-ApiToken 'root@pam!test=00000000-0000-0000-0000-000000000000' `
-SkipCertificateCheck
}
AfterAll {
try { Disconnect-PveServer -ErrorAction SilentlyContinue } catch { }
}
It 'Should list networks' {
$networks = Get-PveNetwork -Node 'pve1'
$networks | Should -Not -BeNullOrEmpty
$networks[0].Iface | Should -Not -BeNullOrEmpty
}
}
Describe 'Mock PVE Server - User Operations' -Tag 'MockIntegration' {
BeforeAll {
Connect-PveServer -Server $script:MockHost -Port $script:MockPort `
-ApiToken 'root@pam!test=00000000-0000-0000-0000-000000000000' `
-SkipCertificateCheck
}
AfterAll {
try { Disconnect-PveServer -ErrorAction SilentlyContinue } catch { }
}
It 'Should list users' {
$users = Get-PveUser
$users | Should -Not -BeNullOrEmpty
$users[0].UserId | Should -Not -BeNullOrEmpty
}
It 'Should list roles' {
$roles = Get-PveRole
$roles | Should -Not -BeNullOrEmpty
$roles[0].RoleId | Should -Not -BeNullOrEmpty
}
}
Describe 'Mock PVE Server - Cluster Operations' -Tag 'MockIntegration' {
BeforeAll {
Connect-PveServer -Server $script:MockHost -Port $script:MockPort `
-ApiToken 'root@pam!test=00000000-0000-0000-0000-000000000000' `
-SkipCertificateCheck
}
AfterAll {
try { Disconnect-PveServer -ErrorAction SilentlyContinue } catch { }
}
It 'Should list snapshots' {
$snapshots = Get-PveSnapshot -Node 'pve1' -VmId 100
$snapshots | Should -Not -BeNullOrEmpty
}
}
Describe 'Mock PVE Server - Container Operations' -Tag 'MockIntegration' {
BeforeAll {
Connect-PveServer -Server $script:MockHost -Port $script:MockPort `
-ApiToken 'root@pam!test=00000000-0000-0000-0000-000000000000' `
-SkipCertificateCheck
}
AfterAll {
try { Disconnect-PveServer -ErrorAction SilentlyContinue } catch { }
}
It 'Should list containers' {
$containers = Get-PveContainer -Node 'pve1'
$containers | Should -Not -BeNullOrEmpty
$containers[0].VmId | Should -BeGreaterThan 0
}
}
@@ -10,21 +10,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
$script:Availability = @{}
foreach ($name in @('Get-PveNetwork', 'New-PveNetwork', 'Set-PveNetwork',
@@ -44,7 +30,7 @@ BeforeAll {
# ---------------------------------------------------------------------------
Describe 'Network cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$manifestPath = Join-Path (Get-Module PSProxmoxVE).ModuleBase 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
@@ -14,21 +14,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
$script:SdnCmdlets = @(
'Get-PveSdnZone', 'New-PveSdnZone', 'Remove-PveSdnZone',
@@ -52,7 +38,7 @@ BeforeAll {
# ---------------------------------------------------------------------------
Describe 'SDN cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$manifestPath = Join-Path (Get-Module PSProxmoxVE).ModuleBase 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
@@ -12,21 +12,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
$script:Availability = @{}
foreach ($name in @('Get-PveSnapshot', 'New-PveSnapshot',
@@ -46,7 +32,7 @@ BeforeAll {
# ---------------------------------------------------------------------------
Describe 'Snapshot cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$manifestPath = Join-Path (Get-Module PSProxmoxVE).ModuleBase 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
@@ -9,21 +9,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
$script:Availability = @{}
foreach ($name in @('Get-PveStorage', 'Get-PveStorageContent',
@@ -43,14 +29,14 @@ BeforeAll {
# ---------------------------------------------------------------------------
Describe 'Storage cmdlets — manifest declarations' {
It 'Get-PveStorage should be declared in CmdletsToExport' {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$manifestPath = Join-Path (Get-Module PSProxmoxVE).ModuleBase 'PSProxmoxVE.psd1'
if (-not (Test-Path $manifestPath)) { Set-ItResult -Skipped -Because 'Manifest not found'; return }
$manifest = Import-PowerShellDataFile $manifestPath
$manifest.CmdletsToExport | Should -Contain 'Get-PveStorage'
}
It 'Get-PveStorageContent should be declared in CmdletsToExport' {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$manifestPath = Join-Path (Get-Module PSProxmoxVE).ModuleBase 'PSProxmoxVE.psd1'
if (-not (Test-Path $manifestPath)) { Set-ItResult -Skipped -Because 'Manifest not found'; return }
$manifest = Import-PowerShellDataFile $manifestPath
$manifest.CmdletsToExport | Should -Contain 'Get-PveStorageContent'
@@ -7,21 +7,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
$script:CmdExists = $null -ne (Get-Command 'Send-PveIso' -ErrorAction SilentlyContinue)
}
@@ -30,7 +16,7 @@ Describe 'Send-PveIso' {
Context 'Manifest declaration' {
It 'Should be declared in CmdletsToExport' {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$manifestPath = Join-Path (Get-Module PSProxmoxVE).ModuleBase 'PSProxmoxVE.psd1'
if (-not (Test-Path $manifestPath)) { Set-ItResult -Skipped -Because 'Manifest not found'; return }
$manifest = Import-PowerShellDataFile $manifestPath
$manifest.CmdletsToExport | Should -Contain 'Send-PveIso'
@@ -13,21 +13,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
$script:Availability = @{}
foreach ($name in @('Get-PveTemplate', 'New-PveTemplate',
@@ -47,7 +33,7 @@ BeforeAll {
# ---------------------------------------------------------------------------
Describe 'Template cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$manifestPath = Join-Path (Get-Module PSProxmoxVE).ModuleBase 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
@@ -9,21 +9,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
$allNames = @('Get-PveApiToken', 'New-PveApiToken', 'Remove-PveApiToken')
@@ -44,7 +30,7 @@ BeforeAll {
# ---------------------------------------------------------------------------
Describe 'API Token cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$manifestPath = Join-Path (Get-Module PSProxmoxVE).ModuleBase 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
@@ -11,21 +11,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
$allNames = @(
'Get-PveUser', 'New-PveUser', 'Remove-PveUser', 'Set-PveUser',
@@ -50,7 +36,7 @@ BeforeAll {
# ---------------------------------------------------------------------------
Describe 'User / Role / Permission cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$manifestPath = Join-Path (Get-Module PSProxmoxVE).ModuleBase 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
@@ -6,21 +6,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
}
Describe 'Get-PveVm' {
@@ -6,21 +6,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
}
Describe 'New-PveVm' {
@@ -6,21 +6,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
}
Describe 'Remove-PveVm' {
@@ -17,21 +17,7 @@
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net9.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
. $PSScriptRoot/../_TestHelper.ps1
# Helper: assert the standard lifecycle parameter set for Node/VmId/Wait.
function Assert-StandardLifecycleParams {
+47
View File
@@ -0,0 +1,47 @@
<#
.SYNOPSIS
Shared module-loading helper for Pester tests.
Dot-source this file inside BeforeAll to import PSProxmoxVE reliably
in both local-dev and CI environments.
#>
# If the module is already loaded, nothing to do.
if (Get-Module -Name PSProxmoxVE) { return }
# 1. Try importing by module name (works in CI where the module is
# installed to a PSModulePath location via dotnet publish + copy).
try {
Import-Module PSProxmoxVE -Force -ErrorAction Stop
return
}
catch {
# Module not on PSModulePath — fall through to local-path discovery.
}
# 2. Discover the built DLL from the local source tree.
# Prefer the framework that matches the running PowerShell edition.
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot '../..')
$moduleRoot = Join-Path $repoRoot 'src/PSProxmoxVE'
if ($PSVersionTable.PSEdition -eq 'Core') {
$frameworks = @('net9.0', 'net48')
}
else {
$frameworks = @('net48', 'net9.0')
}
$searchPaths = foreach ($fw in $frameworks) {
# Publish output (has all dependencies co-located)
Join-Path $repoRoot "publish/$fw/PSProxmoxVE.dll"
# Build output
Join-Path $moduleRoot "bin/Debug/$fw/PSProxmoxVE.dll"
Join-Path $moduleRoot "bin/Release/$fw/PSProxmoxVE.dll"
}
$moduleDll = $searchPaths | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $moduleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $moduleDll -Force -ErrorAction Stop
+1 -1
View File
@@ -270,7 +270,7 @@ function Invoke-PesterUnitTests {
$config = New-PesterConfiguration
$config.Run.Path = $pesterTestDir
$config.Filter.ExcludeTag = @('Integration', 'MockIntegration')
$config.Filter.ExcludeTag = @('Integration')
$config.Output.Verbosity = 'Detailed'
$config.Run.PassThru = $true