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", ApiController.Addresses); // Assert await action.Should().ThrowAsync().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", ApiController.Addresses, ModelType.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", ApiController.Subnets, null, ApiSubController.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", ApiController.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", ApiController.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", ApiController.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", ApiController.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", ApiController.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", ApiController.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", ApiController.Addresses, ModelType.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", ApiController.Addresses); // Assert await action.Should().ThrowAsync(); } [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", ApiController.Addresses, ModelType.Address, null, null, new[] { "1" }); // Assert result.Should().BeOfType
(); 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", ApiController.Subnets, ModelType.Address, null, null, new[] { "10", "addresses" }); // Assert result.Should().BeAssignableTo>(); var addresses = ((IEnumerable)result!).ToList(); addresses.Should().HaveCount(2); addresses[0].Should().BeOfType
(); ((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", ApiController.Vlan, ModelType.Vlan, null, null, new[] { "100" }); // Assert result.Should().BeOfType(); 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", ApiController.Subnets, ModelType.Subnetwork, null, null, new[] { "1" }); // Assert result.Should().BeOfType(); var subnet = (Subnetwork)result!; subnet.Subnet.Should().Be("192.168.1.0"); subnet.Mask.Should().Be(24); subnet.CIDR.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", ApiController.Sections, ModelType.Section, null, null, new[] { "1" }); // Assert result.Should().BeOfType
(); 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", ApiController.Addresses, ModelType.Address, null, null, new[] { "1" }); // Assert var address = (Address)result!; address.ExtendedData.Should().NotBeNull(); var extendedData = 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", ApiController.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", ApiController.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", ApiController.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", ApiController.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", ApiController.Addresses, ModelType.Address); // Assert result.Should().BeNull(); } }