504 lines
15 KiB
C#
504 lines
15 KiB
C#
namespace PS.IPAM.Tests.Helpers;
|
|
|
|
using System.Net;
|
|
using FluentAssertions;
|
|
using PS.IPAM;
|
|
using PS.IPAM.Helpers;
|
|
using PS.IPAM.Tests.Mocks;
|
|
using Xunit;
|
|
|
|
[Collection("Sequential")]
|
|
public class RequestHelperTests : IDisposable
|
|
{
|
|
private MockHttpMessageHandler? _mockHandler;
|
|
|
|
public RequestHelperTests()
|
|
{
|
|
// Clean state before each test
|
|
SessionManager.CloseSession();
|
|
RequestHelper.TestHttpHandler = null;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
// Clean up after each test
|
|
SessionManager.CloseSession();
|
|
RequestHelper.TestHttpHandler = null;
|
|
_mockHandler?.ForceDispose();
|
|
}
|
|
|
|
private void SetupSession(AuthType authType = AuthType.token)
|
|
{
|
|
if (authType == AuthType.token)
|
|
{
|
|
SessionManager.CreateSessionWithToken("https://ipam.example.com", "testapp", "test-token");
|
|
}
|
|
else
|
|
{
|
|
// For credentials auth, we need to set up a session manually with future expiry
|
|
var session = new Session(
|
|
AuthType.credentials,
|
|
"cred-token",
|
|
"testapp",
|
|
"https://ipam.example.com",
|
|
DateTime.Now.AddHours(1),
|
|
null
|
|
);
|
|
SessionManager.CurrentSession = session;
|
|
}
|
|
}
|
|
|
|
private MockHttpMessageHandler SetupMockHandler()
|
|
{
|
|
_mockHandler = new MockHttpMessageHandler();
|
|
RequestHelper.TestHttpHandler = _mockHandler;
|
|
return _mockHandler;
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithNoSession_ThrowsException()
|
|
{
|
|
// Arrange - no session set up
|
|
|
|
// Act
|
|
var action = async () => await RequestHelper.InvokeRequest("GET", controllers.addresses);
|
|
|
|
// Assert
|
|
await action.Should().ThrowAsync<Exception>().WithMessage("No session available!");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithValidSession_BuildsCorrectUri()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithSuccessResponse("{\"id\":1,\"ip\":\"192.168.1.1\"}");
|
|
|
|
// Act
|
|
await RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address, null, null, new[] { "1" });
|
|
|
|
// Assert
|
|
handler.LastRequest.Should().NotBeNull();
|
|
handler.LastRequest!.RequestUri!.ToString().Should().Be("https://ipam.example.com/api/testapp/addresses/1/");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithSubController_BuildsCorrectUri()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithSuccessResponse("[]");
|
|
|
|
// Act
|
|
await RequestHelper.InvokeRequest("GET", controllers.subnets, null, subcontrollers.tags, null, new[] { "1" });
|
|
|
|
// Assert
|
|
handler.LastRequest!.RequestUri!.ToString().Should().Contain("/subnets/tags/1/");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithTokenAuth_AddsPhpipamTokenHeader()
|
|
{
|
|
// Arrange
|
|
SetupSession(AuthType.token);
|
|
var handler = SetupMockHandler();
|
|
handler.WithSuccessResponse("{}");
|
|
|
|
// Act
|
|
await RequestHelper.InvokeRequest("GET", controllers.addresses);
|
|
|
|
// Assert
|
|
handler.GetLastRequestHeader("phpipam-token").Should().Be("test-token");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithCredentialsAuth_AddsTokenHeader()
|
|
{
|
|
// Arrange
|
|
SetupSession(AuthType.credentials);
|
|
var handler = SetupMockHandler();
|
|
handler.WithSuccessResponse("{}");
|
|
|
|
// Act
|
|
await RequestHelper.InvokeRequest("GET", controllers.addresses);
|
|
|
|
// Assert
|
|
handler.GetLastRequestHeader("token").Should().Be("cred-token");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_GetMethod_UsesHttpGet()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithSuccessResponse("{}");
|
|
|
|
// Act
|
|
await RequestHelper.InvokeRequest("GET", controllers.addresses);
|
|
|
|
// Assert
|
|
handler.LastRequest!.Method.Should().Be(HttpMethod.Get);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_PostMethod_UsesHttpPost()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithSuccessResponse("{\"id\":1}");
|
|
|
|
// Act
|
|
await RequestHelper.InvokeRequest("POST", controllers.addresses, null, null, new { ip = "10.0.0.1" });
|
|
|
|
// Assert
|
|
handler.LastRequest!.Method.Should().Be(HttpMethod.Post);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_PatchMethod_UsesHttpPatch()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithSuccessResponse("{\"id\":1}");
|
|
|
|
// Act
|
|
await RequestHelper.InvokeRequest("PATCH", controllers.addresses, null, null, new { description = "updated" }, new[] { "1" });
|
|
|
|
// Assert
|
|
handler.LastRequest!.Method.Should().Be(new HttpMethod("PATCH"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_DeleteMethod_UsesHttpDelete()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithSuccessResponse("{}");
|
|
|
|
// Act
|
|
await RequestHelper.InvokeRequest("DELETE", controllers.addresses, null, null, null, new[] { "1" });
|
|
|
|
// Assert
|
|
handler.LastRequest!.Method.Should().Be(HttpMethod.Delete);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_With404Response_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithNotFoundResponse();
|
|
|
|
// Act
|
|
var result = await RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address, null, null, new[] { "999" });
|
|
|
|
// Assert
|
|
result.Should().BeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithErrorResponse_ThrowsHttpRequestException()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithErrorResponse(HttpStatusCode.InternalServerError, "Server error");
|
|
|
|
// Act
|
|
var action = async () => await RequestHelper.InvokeRequest("GET", controllers.addresses);
|
|
|
|
// Assert
|
|
await action.Should().ThrowAsync<HttpRequestException>();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithAddressType_ReturnsAddressObject()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
var addressJson = @"{
|
|
""id"": 1,
|
|
""subnetId"": 10,
|
|
""ip"": ""192.168.1.100"",
|
|
""is_gateway"": false,
|
|
""description"": ""Test server"",
|
|
""hostname"": ""server01"",
|
|
""mac"": ""00:11:22:33:44:55"",
|
|
""owner"": ""admin"",
|
|
""tag"": 2,
|
|
""deviceId"": 0,
|
|
""location"": """",
|
|
""port"": """",
|
|
""note"": """",
|
|
""lastSeen"": null,
|
|
""excludePing"": false,
|
|
""PTRignore"": false,
|
|
""PTR"": 0,
|
|
""firewallAddressObject"": """",
|
|
""editDate"": null,
|
|
""customer_id"": 0
|
|
}";
|
|
handler.WithSuccessResponse(addressJson);
|
|
|
|
// Act
|
|
var result = await RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address, null, null, new[] { "1" });
|
|
|
|
// Assert
|
|
result.Should().BeOfType<Address>();
|
|
var address = (Address)result!;
|
|
address.Id.Should().Be(1);
|
|
address.Ip.Should().Be("192.168.1.100");
|
|
address.Hostname.Should().Be("server01");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithAddressArray_ReturnsListOfAddresses()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
var addressArrayJson = @"[
|
|
{""id"": 1, ""subnetId"": 10, ""ip"": ""192.168.1.1"", ""hostname"": ""host1""},
|
|
{""id"": 2, ""subnetId"": 10, ""ip"": ""192.168.1.2"", ""hostname"": ""host2""}
|
|
]";
|
|
handler.WithSuccessResponse(addressArrayJson);
|
|
|
|
// Act
|
|
var result = await RequestHelper.InvokeRequest("GET", controllers.subnets, types.Address, null, null, new[] { "10", "addresses" });
|
|
|
|
// Assert
|
|
result.Should().BeAssignableTo<IEnumerable<object>>();
|
|
var addresses = ((IEnumerable<object>)result!).ToList();
|
|
addresses.Should().HaveCount(2);
|
|
addresses[0].Should().BeOfType<Address>();
|
|
((Address)addresses[0]).Ip.Should().Be("192.168.1.1");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithVlanType_ReturnsVlanObject()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
var vlanJson = @"{
|
|
""vlanId"": 100,
|
|
""domainId"": 1,
|
|
""name"": ""Production"",
|
|
""number"": 100,
|
|
""description"": ""Production VLAN"",
|
|
""editDate"": null,
|
|
""customer_id"": 0
|
|
}";
|
|
handler.WithSuccessResponse(vlanJson);
|
|
|
|
// Act
|
|
var result = await RequestHelper.InvokeRequest("GET", controllers.vlan, types.Vlan, null, null, new[] { "100" });
|
|
|
|
// Assert
|
|
result.Should().BeOfType<Vlan>();
|
|
var vlan = (Vlan)result!;
|
|
vlan.Name.Should().Be("Production");
|
|
vlan.Number.Should().Be(100);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithSubnetworkType_ReturnsSubnetworkObject()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
var subnetJson = @"{
|
|
""id"": 1,
|
|
""subnet"": ""192.168.1.0"",
|
|
""mask"": 24,
|
|
""sectionId"": 1,
|
|
""description"": ""Test subnet"",
|
|
""linked_subnet"": """",
|
|
""firewallAddressObject"": """",
|
|
""vrfId"": 0,
|
|
""masterSubnetId"": 0,
|
|
""allowRequests"": false,
|
|
""vlanId"": 100,
|
|
""showName"": false,
|
|
""deviceId"": 0,
|
|
""permissions"": """",
|
|
""pingSubnet"": false,
|
|
""discoverSubnet"": false,
|
|
""resolveDNS"": false,
|
|
""DNSrecursive"": false,
|
|
""DNSrecords"": false,
|
|
""nameserverId"": 0,
|
|
""scanAgent"": false,
|
|
""isFolder"": false,
|
|
""isFull"": false,
|
|
""isPool"": false,
|
|
""state"": 1,
|
|
""threshold"": 0,
|
|
""location"": 0,
|
|
""editDate"": null,
|
|
""lastScan"": null,
|
|
""lastDiscovery"": null,
|
|
""calculation"": {}
|
|
}";
|
|
handler.WithSuccessResponse(subnetJson);
|
|
|
|
// Act
|
|
var result = await RequestHelper.InvokeRequest("GET", controllers.subnets, types.Subnetwork, null, null, new[] { "1" });
|
|
|
|
// Assert
|
|
result.Should().BeOfType<Subnetwork>();
|
|
var subnet = (Subnetwork)result!;
|
|
subnet.Subnet.Should().Be("192.168.1.0");
|
|
subnet.Mask.Should().Be(24);
|
|
subnet.GetCIDR().Should().Be("192.168.1.0/24");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithSectionType_ReturnsSectionObject()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
var sectionJson = @"{
|
|
""id"": 1,
|
|
""name"": ""Production"",
|
|
""description"": ""Production section"",
|
|
""masterSection"": 0,
|
|
""permissions"": """",
|
|
""strictMode"": false,
|
|
""subnetOrdering"": ""default"",
|
|
""order"": 1,
|
|
""editDate"": null,
|
|
""showSubnet"": true,
|
|
""showVlan"": true,
|
|
""showVRF"": false,
|
|
""showSupernetOnly"": false,
|
|
""DNS"": 0
|
|
}";
|
|
handler.WithSuccessResponse(sectionJson);
|
|
|
|
// Act
|
|
var result = await RequestHelper.InvokeRequest("GET", controllers.sections, types.Section, null, null, new[] { "1" });
|
|
|
|
// Assert
|
|
result.Should().BeOfType<Section>();
|
|
var section = (Section)result!;
|
|
section.Name.Should().Be("Production");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithCustomFields_ParsesExtendedData()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
var addressJson = @"{
|
|
""id"": 1,
|
|
""subnetId"": 10,
|
|
""ip"": ""192.168.1.100"",
|
|
""hostname"": ""server01"",
|
|
""custom_environment"": ""production"",
|
|
""custom_owner"": ""team-a""
|
|
}";
|
|
handler.WithSuccessResponse(addressJson);
|
|
|
|
// Act
|
|
var result = await RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address, null, null, new[] { "1" });
|
|
|
|
// Assert
|
|
var address = (Address)result!;
|
|
address.ExtendedData.Should().NotBeNull();
|
|
var extendedData = (Dictionary<string, object>)address.ExtendedData!;
|
|
extendedData.Should().ContainKey("custom_environment");
|
|
extendedData.Should().ContainKey("custom_owner");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithNoType_ReturnsDynamicData()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithSuccessResponse(@"{""some"": ""data""}");
|
|
|
|
// Act
|
|
var result = await RequestHelper.InvokeRequest("GET", controllers.tools);
|
|
|
|
// Assert
|
|
result.Should().NotBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_PostWithParameters_SerializesJsonBody()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithSuccessResponse(@"{""id"": 1}");
|
|
|
|
var parameters = new { ip = "10.0.0.1", subnetId = 5, description = "New address" };
|
|
|
|
// Act
|
|
await RequestHelper.InvokeRequest("POST", controllers.addresses, null, null, parameters);
|
|
|
|
// Assert
|
|
handler.LastRequest!.Content.Should().NotBeNull();
|
|
var content = await handler.LastRequest.Content!.ReadAsStringAsync();
|
|
content.Should().Contain("10.0.0.1");
|
|
content.Should().Contain("subnetId");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_SetsAcceptJsonHeader()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithSuccessResponse("{}");
|
|
|
|
// Act
|
|
await RequestHelper.InvokeRequest("GET", controllers.addresses);
|
|
|
|
// Assert
|
|
handler.LastRequest!.Headers.Accept.Should().Contain(h => h.MediaType == "application/json");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithEmptyResponse_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithResponse(HttpStatusCode.OK, "", "application/json");
|
|
|
|
// Act
|
|
var result = await RequestHelper.InvokeRequest("GET", controllers.addresses);
|
|
|
|
// Assert
|
|
result.Should().BeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeRequest_WithNullDataInResponse_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
SetupSession();
|
|
var handler = SetupMockHandler();
|
|
handler.WithJsonResponse(@"{""code"": 200, ""success"": true, ""data"": null}");
|
|
|
|
// Act
|
|
var result = await RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address);
|
|
|
|
// Assert
|
|
result.Should().BeNull();
|
|
}
|
|
}
|