Remove Jenkinsfile and add unit tests for various models including Address, Domain, Nameserver, Section, Session, Subnetwork, Tag, Vlan, and Vrf. Introduce mock classes for HTTP requests and cmdlet testing.
This commit is contained in:
21
Jenkinsfile
vendored
21
Jenkinsfile
vendored
@@ -1,21 +0,0 @@
|
|||||||
pipeline {
|
|
||||||
agent {
|
|
||||||
label '.net7.0'
|
|
||||||
}
|
|
||||||
stages {
|
|
||||||
stage('Build classlib') {
|
|
||||||
steps {
|
|
||||||
sh '''cd classlib
|
|
||||||
dotnet build --no-incremental --force --configuration Release
|
|
||||||
dotnet publish -c Release'''
|
|
||||||
|
|
||||||
contentReplace(configs: [fileContentReplaceConfig(configs: [fileContentReplaceItemConfig(replace: 'ModuleVersion = \'2.0.$BUILD_NUMBER\'', search: 'ModuleVersion = \'2.0\'')], fileEncoding: 'UTF-8', filePath: 'ps.ipam.psd1')])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
post {
|
|
||||||
success {
|
|
||||||
archiveArtifacts artifacts: 'classlib/bin/Release/netstandard2.1/publish/*.dll, *.psd1, *.psm1, LICENSE, **/*.ps1xml, **/*.ps1', followSymlinks: false, onlyIfSuccessful: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
139
classlib.tests/Mocks/CmdletTestHelper.cs
Normal file
139
classlib.tests/Mocks/CmdletTestHelper.cs
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
namespace PS.IPAM.Tests.Mocks;
|
||||||
|
|
||||||
|
using System.Management.Automation;
|
||||||
|
using System.Management.Automation.Host;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper class for testing PowerShell cmdlets.
|
||||||
|
/// </summary>
|
||||||
|
public class CmdletTestHelper<T> : IDisposable where T : PSCmdlet, new()
|
||||||
|
{
|
||||||
|
private readonly T _cmdlet;
|
||||||
|
private readonly List<object> _output = new();
|
||||||
|
private readonly List<ErrorRecord> _errors = new();
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
public CmdletTestHelper()
|
||||||
|
{
|
||||||
|
_cmdlet = new T();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the cmdlet instance for setting parameters.
|
||||||
|
/// </summary>
|
||||||
|
public T Cmdlet => _cmdlet;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the output objects written by the cmdlet.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<object> Output => _output.AsReadOnly();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the errors written by the cmdlet.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<ErrorRecord> Errors => _errors.AsReadOnly();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether any errors were written.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasErrors => _errors.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes the cmdlet and captures output.
|
||||||
|
/// </summary>
|
||||||
|
public void Invoke()
|
||||||
|
{
|
||||||
|
// Use reflection to call the protected ProcessRecord method
|
||||||
|
var processMethod = typeof(T).GetMethod("ProcessRecord",
|
||||||
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
|
||||||
|
if (processMethod == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("ProcessRecord method not found on cmdlet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up a mock command runtime to capture output
|
||||||
|
var runtime = new MockCommandRuntime(_output, _errors);
|
||||||
|
var runtimeProperty = typeof(Cmdlet).GetProperty("CommandRuntime",
|
||||||
|
BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
|
||||||
|
runtimeProperty?.SetValue(_cmdlet, runtime);
|
||||||
|
|
||||||
|
// Invoke the method
|
||||||
|
try
|
||||||
|
{
|
||||||
|
processMethod.Invoke(_cmdlet, null);
|
||||||
|
}
|
||||||
|
catch (TargetInvocationException ex) when (ex.InnerException != null)
|
||||||
|
{
|
||||||
|
// Unwrap the exception
|
||||||
|
throw ex.InnerException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mock implementation of ICommandRuntime for capturing cmdlet output.
|
||||||
|
/// This is a minimal implementation that only handles the methods we need for testing.
|
||||||
|
/// </summary>
|
||||||
|
internal class MockCommandRuntime : ICommandRuntime
|
||||||
|
{
|
||||||
|
private readonly List<object> _output;
|
||||||
|
private readonly List<ErrorRecord> _errors;
|
||||||
|
|
||||||
|
public MockCommandRuntime(List<object> output, List<ErrorRecord> errors)
|
||||||
|
{
|
||||||
|
_output = output;
|
||||||
|
_errors = errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PSHost Host => null!;
|
||||||
|
public PSTransactionContext CurrentPSTransaction => null!;
|
||||||
|
|
||||||
|
public bool ShouldContinue(string query, string caption) => true;
|
||||||
|
public bool ShouldContinue(string query, string caption, ref bool yesToAll, ref bool noToAll) => true;
|
||||||
|
public bool ShouldProcess(string target) => true;
|
||||||
|
public bool ShouldProcess(string target, string action) => true;
|
||||||
|
public bool ShouldProcess(string verboseDescription, string verboseWarning, string caption) => true;
|
||||||
|
public bool ShouldProcess(string verboseDescription, string verboseWarning, string caption, out ShouldProcessReason shouldProcessReason)
|
||||||
|
{
|
||||||
|
shouldProcessReason = ShouldProcessReason.None;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TransactionAvailable() => false;
|
||||||
|
|
||||||
|
public void ThrowTerminatingError(ErrorRecord errorRecord) => throw errorRecord.Exception;
|
||||||
|
|
||||||
|
public void WriteCommandDetail(string text) { }
|
||||||
|
public void WriteDebug(string text) { }
|
||||||
|
public void WriteError(ErrorRecord errorRecord) => _errors.Add(errorRecord);
|
||||||
|
public void WriteObject(object sendToPipeline) => _output.Add(sendToPipeline);
|
||||||
|
public void WriteObject(object sendToPipeline, bool enumerateCollection)
|
||||||
|
{
|
||||||
|
if (enumerateCollection && sendToPipeline is System.Collections.IEnumerable enumerable)
|
||||||
|
{
|
||||||
|
foreach (var item in enumerable)
|
||||||
|
{
|
||||||
|
_output.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_output.Add(sendToPipeline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void WriteProgress(ProgressRecord progressRecord) { }
|
||||||
|
public void WriteProgress(long sourceId, ProgressRecord progressRecord) { }
|
||||||
|
public void WriteVerbose(string text) { }
|
||||||
|
public void WriteWarning(string text) { }
|
||||||
|
}
|
||||||
172
classlib.tests/Mocks/MockHttpMessageHandler.cs
Normal file
172
classlib.tests/Mocks/MockHttpMessageHandler.cs
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
namespace PS.IPAM.Tests.Mocks;
|
||||||
|
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A mock HTTP message handler for testing HTTP requests without making actual network calls.
|
||||||
|
/// This handler does not dispose itself when the HttpClient is disposed, allowing reuse in tests.
|
||||||
|
/// </summary>
|
||||||
|
public class MockHttpMessageHandler : HttpMessageHandler
|
||||||
|
{
|
||||||
|
private readonly Queue<MockResponse> _responses = new();
|
||||||
|
private readonly List<HttpRequestMessage> _requests = new();
|
||||||
|
private bool _disposed = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all requests that were sent through this handler.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<HttpRequestMessage> Requests => _requests.AsReadOnly();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the last request that was sent through this handler.
|
||||||
|
/// </summary>
|
||||||
|
public HttpRequestMessage? LastRequest => _requests.LastOrDefault();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues a response to be returned for the next request.
|
||||||
|
/// </summary>
|
||||||
|
public MockHttpMessageHandler WithResponse(HttpStatusCode statusCode, string content, string contentType = "application/json")
|
||||||
|
{
|
||||||
|
_responses.Enqueue(new MockResponse(statusCode, content, contentType));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues a successful JSON response.
|
||||||
|
/// </summary>
|
||||||
|
public MockHttpMessageHandler WithJsonResponse(string jsonContent)
|
||||||
|
{
|
||||||
|
return WithResponse(HttpStatusCode.OK, jsonContent, "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues a successful response with phpIPAM-style wrapper.
|
||||||
|
/// </summary>
|
||||||
|
public MockHttpMessageHandler WithSuccessResponse(string dataJson)
|
||||||
|
{
|
||||||
|
var response = $"{{\"code\":200,\"success\":true,\"data\":{dataJson}}}";
|
||||||
|
return WithJsonResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues a 404 Not Found response.
|
||||||
|
/// </summary>
|
||||||
|
public MockHttpMessageHandler WithNotFoundResponse()
|
||||||
|
{
|
||||||
|
return WithResponse(HttpStatusCode.NotFound, "{\"code\":404,\"success\":false,\"message\":\"Not found\"}", "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues an error response.
|
||||||
|
/// </summary>
|
||||||
|
public MockHttpMessageHandler WithErrorResponse(HttpStatusCode statusCode, string message)
|
||||||
|
{
|
||||||
|
var response = $"{{\"code\":{(int)statusCode},\"success\":false,\"message\":\"{message}\"}}";
|
||||||
|
return WithResponse(statusCode, response, "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues an exception to be thrown on the next request.
|
||||||
|
/// </summary>
|
||||||
|
public MockHttpMessageHandler WithException(Exception exception)
|
||||||
|
{
|
||||||
|
_responses.Enqueue(new MockResponse(exception));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(MockHttpMessageHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
_requests.Add(request);
|
||||||
|
|
||||||
|
if (_responses.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("No mock response configured. Call WithResponse() before making requests.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var mockResponse = _responses.Dequeue();
|
||||||
|
|
||||||
|
if (mockResponse.Exception != null)
|
||||||
|
{
|
||||||
|
throw mockResponse.Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = new HttpResponseMessage(mockResponse.StatusCode)
|
||||||
|
{
|
||||||
|
Content = new StringContent(mockResponse.Content, System.Text.Encoding.UTF8, mockResponse.ContentType),
|
||||||
|
RequestMessage = request
|
||||||
|
};
|
||||||
|
|
||||||
|
return Task.FromResult(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
// Don't actually dispose - allow reuse in tests
|
||||||
|
// The test itself is responsible for cleanup
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actually disposes the handler. Call this in test cleanup.
|
||||||
|
/// </summary>
|
||||||
|
public void ForceDispose()
|
||||||
|
{
|
||||||
|
_disposed = true;
|
||||||
|
base.Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that a request was made to the expected URL.
|
||||||
|
/// </summary>
|
||||||
|
public bool WasRequestMadeTo(string urlContains)
|
||||||
|
{
|
||||||
|
return _requests.Any(r => r.RequestUri?.ToString().Contains(urlContains) == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that a request with the expected method was made.
|
||||||
|
/// </summary>
|
||||||
|
public bool WasRequestMadeWithMethod(HttpMethod method)
|
||||||
|
{
|
||||||
|
return _requests.Any(r => r.Method == method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value of a header from the last request.
|
||||||
|
/// </summary>
|
||||||
|
public string? GetLastRequestHeader(string headerName)
|
||||||
|
{
|
||||||
|
if (LastRequest?.Headers.TryGetValues(headerName, out var values) == true)
|
||||||
|
{
|
||||||
|
return values.FirstOrDefault();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockResponse
|
||||||
|
{
|
||||||
|
public HttpStatusCode StatusCode { get; }
|
||||||
|
public string Content { get; }
|
||||||
|
public string ContentType { get; }
|
||||||
|
public Exception? Exception { get; }
|
||||||
|
|
||||||
|
public MockResponse(HttpStatusCode statusCode, string content, string contentType)
|
||||||
|
{
|
||||||
|
StatusCode = statusCode;
|
||||||
|
Content = content;
|
||||||
|
ContentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MockResponse(Exception exception)
|
||||||
|
{
|
||||||
|
Exception = exception;
|
||||||
|
StatusCode = HttpStatusCode.InternalServerError;
|
||||||
|
Content = string.Empty;
|
||||||
|
ContentType = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
116
classlib.tests/Models/AddressTests.cs
Normal file
116
classlib.tests/Models/AddressTests.cs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
namespace PS.IPAM.Tests.Models;
|
||||||
|
|
||||||
|
using FluentAssertions;
|
||||||
|
using PS.IPAM;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class AddressTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_SetsAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var id = 1;
|
||||||
|
var subnetId = 10;
|
||||||
|
var ip = "192.168.1.100";
|
||||||
|
var isGateway = true;
|
||||||
|
var description = "Test server";
|
||||||
|
var hostname = "server01.example.com";
|
||||||
|
var mac = "00:11:22:33:44:55";
|
||||||
|
var owner = "admin";
|
||||||
|
var tagId = 2;
|
||||||
|
var deviceId = 5;
|
||||||
|
var location = "DC1";
|
||||||
|
var port = "eth0";
|
||||||
|
var note = "Production server";
|
||||||
|
var lastSeen = new DateTime(2026, 1, 15, 10, 30, 0);
|
||||||
|
var excludePing = false;
|
||||||
|
var ptrIgnore = true;
|
||||||
|
var ptr = 1;
|
||||||
|
var firewallObject = "FW_SERVER01";
|
||||||
|
var editDate = new DateTime(2026, 1, 10, 8, 0, 0);
|
||||||
|
var customerId = 100;
|
||||||
|
var extendedData = new Dictionary<string, object> { { "custom_field1", "value1" } };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var address = new Address(
|
||||||
|
id, subnetId, ip, isGateway, description, hostname, mac, owner,
|
||||||
|
tagId, deviceId, location, port, note, lastSeen, excludePing,
|
||||||
|
ptrIgnore, ptr, firewallObject, editDate, customerId, extendedData
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
address.Id.Should().Be(id);
|
||||||
|
address.SubnetId.Should().Be(subnetId);
|
||||||
|
address.Ip.Should().Be(ip);
|
||||||
|
address.IsGateway.Should().Be(isGateway);
|
||||||
|
address.Description.Should().Be(description);
|
||||||
|
address.Hostname.Should().Be(hostname);
|
||||||
|
address.MAC.Should().Be(mac);
|
||||||
|
address.Owner.Should().Be(owner);
|
||||||
|
address.TagId.Should().Be(tagId);
|
||||||
|
address.DeviceId.Should().Be(deviceId);
|
||||||
|
address.Location.Should().Be(location);
|
||||||
|
address.Port.Should().Be(port);
|
||||||
|
address.Note.Should().Be(note);
|
||||||
|
address.LastSeen.Should().Be(lastSeen);
|
||||||
|
address.ExcludePing.Should().Be(excludePing);
|
||||||
|
address.PTRignore.Should().Be(ptrIgnore);
|
||||||
|
address.PTR.Should().Be(ptr);
|
||||||
|
address.FirewallAddressObject.Should().Be(firewallObject);
|
||||||
|
address.EditDate.Should().Be(editDate);
|
||||||
|
address.CustomerId.Should().Be(customerId);
|
||||||
|
address.ExtendedData.Should().BeEquivalentTo(extendedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithNullOptionalFields_SetsNullValues()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var address = new Address(
|
||||||
|
1, 10, "10.0.0.1", false, "", "", "", "",
|
||||||
|
0, 0, "", "", "", null, false,
|
||||||
|
false, 0, "", null, 0, null
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
address.LastSeen.Should().BeNull();
|
||||||
|
address.EditDate.Should().BeNull();
|
||||||
|
address.ExtendedData.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToString_ReturnsIpAddress()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var address = new Address(
|
||||||
|
1, 10, "192.168.1.50", false, "Test", "host.local", "", "",
|
||||||
|
0, 0, "", "", "", null, false,
|
||||||
|
false, 0, "", null, 0, null
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = address.ToString();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be("192.168.1.50");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("10.0.0.1")]
|
||||||
|
[InlineData("172.16.0.100")]
|
||||||
|
[InlineData("192.168.255.255")]
|
||||||
|
[InlineData("2001:db8::1")]
|
||||||
|
public void ToString_ReturnsCorrectIp_ForVariousAddresses(string expectedIp)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var address = new Address(
|
||||||
|
1, 1, expectedIp, false, "", "", "", "",
|
||||||
|
0, 0, "", "", "", null, false,
|
||||||
|
false, 0, "", null, 0, null
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
address.ToString().Should().Be(expectedIp);
|
||||||
|
}
|
||||||
|
}
|
||||||
43
classlib.tests/Models/DomainTests.cs
Normal file
43
classlib.tests/Models/DomainTests.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
namespace PS.IPAM.Tests.Models;
|
||||||
|
|
||||||
|
using FluentAssertions;
|
||||||
|
using PS.IPAM;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class DomainTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_SetsAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var id = 1;
|
||||||
|
var name = "Default";
|
||||||
|
var description = "Default L2 domain";
|
||||||
|
var sections = "1;2;3";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var domain = new Domain(id, name, description, sections);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
domain.Id.Should().Be(id);
|
||||||
|
domain.Name.Should().Be(name);
|
||||||
|
domain.Description.Should().Be(description);
|
||||||
|
domain.Sections.Should().Be(sections);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Default")]
|
||||||
|
[InlineData("Datacenter1")]
|
||||||
|
[InlineData("Branch_Office")]
|
||||||
|
public void ToString_ReturnsDomainName(string domainName)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var domain = new Domain(1, domainName, "", "");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = domain.ToString();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(domainName);
|
||||||
|
}
|
||||||
|
}
|
||||||
82
classlib.tests/Models/NameserverTests.cs
Normal file
82
classlib.tests/Models/NameserverTests.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
namespace PS.IPAM.Tests.Models;
|
||||||
|
|
||||||
|
using FluentAssertions;
|
||||||
|
using PS.IPAM;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class NameserverTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_SetsAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var id = 1;
|
||||||
|
var name = "Google DNS";
|
||||||
|
var nameServers = "8.8.8.8;8.8.4.4";
|
||||||
|
var description = "Google Public DNS";
|
||||||
|
var permissions = "{\"3\":\"2\"}";
|
||||||
|
var editDate = new DateTime(2026, 1, 10);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var nameserver = new Nameserver(id, name, nameServers, description, permissions, editDate);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
nameserver.Id.Should().Be(id);
|
||||||
|
nameserver.Name.Should().Be(name);
|
||||||
|
nameserver.Description.Should().Be(description);
|
||||||
|
nameserver.Permissions.Should().Be(permissions);
|
||||||
|
nameserver.EditDate.Should().Be(editDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_ParsesNameservers_BySemicolon()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var nameServersString = "8.8.8.8;8.8.4.4;1.1.1.1";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var nameserver = new Nameserver(1, "Test", nameServersString, "", "", null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
nameserver.NameServers.Should().HaveCount(3);
|
||||||
|
nameserver.NameServers.Should().ContainInOrder("8.8.8.8", "8.8.4.4", "1.1.1.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithSingleNameserver_ReturnsArrayWithOneElement()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var nameServersString = "8.8.8.8";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var nameserver = new Nameserver(1, "Test", nameServersString, "", "", null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
nameserver.NameServers.Should().HaveCount(1);
|
||||||
|
nameserver.NameServers[0].Should().Be("8.8.8.8");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithEmptyNameservers_ReturnsArrayWithEmptyString()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var nameServersString = "";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var nameserver = new Nameserver(1, "Test", nameServersString, "", "", null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
nameserver.NameServers.Should().HaveCount(1);
|
||||||
|
nameserver.NameServers[0].Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithNullEditDate_SetsNull()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var nameserver = new Nameserver(1, "Test", "8.8.8.8", "", "", null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
nameserver.EditDate.Should().BeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
77
classlib.tests/Models/SectionTests.cs
Normal file
77
classlib.tests/Models/SectionTests.cs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
namespace PS.IPAM.Tests.Models;
|
||||||
|
|
||||||
|
using FluentAssertions;
|
||||||
|
using PS.IPAM;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class SectionTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_SetsAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var id = 1;
|
||||||
|
var name = "Production";
|
||||||
|
var description = "Production networks section";
|
||||||
|
var masterSectionId = 0;
|
||||||
|
var permissions = "{\"3\":\"2\"}";
|
||||||
|
var strictMode = true;
|
||||||
|
var subnetOrdering = "subnet,asc";
|
||||||
|
var order = 1;
|
||||||
|
var editDate = new DateTime(2026, 1, 5);
|
||||||
|
var showSubnet = true;
|
||||||
|
var showVlan = true;
|
||||||
|
var showVRF = false;
|
||||||
|
var showSupernetOnly = false;
|
||||||
|
var dnsId = 1;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var section = new Section(
|
||||||
|
id, name, description, masterSectionId, permissions, strictMode,
|
||||||
|
subnetOrdering, order, editDate, showSubnet, showVlan, showVRF,
|
||||||
|
showSupernetOnly, dnsId
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
section.Id.Should().Be(id);
|
||||||
|
section.Name.Should().Be(name);
|
||||||
|
section.Description.Should().Be(description);
|
||||||
|
section.MasterSectionId.Should().Be(masterSectionId);
|
||||||
|
section.Permissions.Should().Be(permissions);
|
||||||
|
section.StrictMode.Should().Be(strictMode);
|
||||||
|
section.SubnetOrdering.Should().Be(subnetOrdering);
|
||||||
|
section.Order.Should().Be(order);
|
||||||
|
section.EditDate.Should().Be(editDate);
|
||||||
|
section.ShowSubnet.Should().Be(showSubnet);
|
||||||
|
section.ShowVlan.Should().Be(showVlan);
|
||||||
|
section.ShowVRF.Should().Be(showVRF);
|
||||||
|
section.ShowSupernetOnly.Should().Be(showSupernetOnly);
|
||||||
|
section.DNSId.Should().Be(dnsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Production")]
|
||||||
|
[InlineData("Development")]
|
||||||
|
[InlineData("DMZ")]
|
||||||
|
public void ToString_ReturnsSectionName(string sectionName)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var section = new Section(1, sectionName, "", 0, "", false, "", 0, null, false, false, false, false, 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = section.ToString();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(sectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithNullEditDate_SetsNull()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var section = new Section(1, "Test", "", 0, "", false, "", 0, null, false, false, false, false, 0);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
section.EditDate.Should().BeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
92
classlib.tests/Models/SessionTests.cs
Normal file
92
classlib.tests/Models/SessionTests.cs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
namespace PS.IPAM.Tests.Models;
|
||||||
|
|
||||||
|
using FluentAssertions;
|
||||||
|
using PS.IPAM;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class SessionTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithCredentialsAuth_SetsAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var authType = AuthType.credentials;
|
||||||
|
var token = "test-token-123";
|
||||||
|
var appId = "myApp";
|
||||||
|
var url = "https://ipam.example.com";
|
||||||
|
var expires = new DateTime(2026, 12, 31, 23, 59, 59);
|
||||||
|
var credentials = new object(); // Mock credentials
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var session = new Session(authType, token, appId, url, expires, credentials);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.AuthType.Should().Be(AuthType.credentials);
|
||||||
|
session.Token.Should().Be(token);
|
||||||
|
session.AppID.Should().Be(appId);
|
||||||
|
session.URL.Should().Be(url);
|
||||||
|
session.Expires.Should().Be(expires);
|
||||||
|
session.Credentials.Should().BeSameAs(credentials);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithTokenAuth_SetsAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var authType = AuthType.token;
|
||||||
|
var token = "static-api-token";
|
||||||
|
var appId = "apiApp";
|
||||||
|
var url = "https://ipam.test.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var session = new Session(authType, token, appId, url, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.AuthType.Should().Be(AuthType.token);
|
||||||
|
session.Token.Should().Be(token);
|
||||||
|
session.AppID.Should().Be(appId);
|
||||||
|
session.URL.Should().Be(url);
|
||||||
|
session.Expires.Should().BeNull();
|
||||||
|
session.Credentials.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Token_CanBeModified()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new Session(AuthType.credentials, "old-token", "app", "https://test.com", null, null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
session.Token = "new-token";
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.Token.Should().Be("new-token");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Expires_CanBeModified()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new Session(AuthType.credentials, "token", "app", "https://test.com", null, null);
|
||||||
|
var newExpiry = new DateTime(2027, 1, 1);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
session.Expires = newExpiry;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.Expires.Should().Be(newExpiry);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToString_ReturnsDefaultObjectString()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new Session(AuthType.token, "token", "app", "https://test.com", null, null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = session.ToString();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Contain("PS.IPAM.Session");
|
||||||
|
}
|
||||||
|
}
|
||||||
142
classlib.tests/Models/SubnetworkTests.cs
Normal file
142
classlib.tests/Models/SubnetworkTests.cs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
namespace PS.IPAM.Tests.Models;
|
||||||
|
|
||||||
|
using FluentAssertions;
|
||||||
|
using PS.IPAM;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class SubnetworkTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_SetsAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var id = 1;
|
||||||
|
var subnet = "192.168.1.0";
|
||||||
|
var mask = 24;
|
||||||
|
var sectionId = 5;
|
||||||
|
var description = "Production network";
|
||||||
|
var linkedSubnet = "linked-123";
|
||||||
|
var firewallObject = "FW_PROD";
|
||||||
|
var vrfId = 2;
|
||||||
|
var masterSubnetId = 0;
|
||||||
|
var allowRequests = true;
|
||||||
|
var vlanId = 100;
|
||||||
|
var showName = true;
|
||||||
|
var deviceId = 10;
|
||||||
|
var permissions = "rw";
|
||||||
|
var pingSubnet = true;
|
||||||
|
var discoverSubnet = false;
|
||||||
|
var resolveDNS = true;
|
||||||
|
var dnsRecursive = false;
|
||||||
|
var dnsRecords = true;
|
||||||
|
var nameserverId = 3;
|
||||||
|
var scanAgent = false;
|
||||||
|
var isFolder = false;
|
||||||
|
var isFull = false;
|
||||||
|
var isPool = true;
|
||||||
|
var tagId = 1;
|
||||||
|
var threshold = 80;
|
||||||
|
var locationId = 4;
|
||||||
|
var editDate = new DateTime(2026, 1, 10);
|
||||||
|
var lastScan = new DateTime(2026, 1, 9);
|
||||||
|
var lastDiscovery = new DateTime(2026, 1, 8);
|
||||||
|
var calculation = new { maxhosts = 254 };
|
||||||
|
var customFields = new Dictionary<string, object> { { "custom_env", "prod" } };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var subnetwork = new Subnetwork(
|
||||||
|
id, subnet, mask, sectionId, description, linkedSubnet, firewallObject,
|
||||||
|
vrfId, masterSubnetId, allowRequests, vlanId, showName, deviceId,
|
||||||
|
permissions, pingSubnet, discoverSubnet, resolveDNS, dnsRecursive,
|
||||||
|
dnsRecords, nameserverId, scanAgent, isFolder, isFull, isPool,
|
||||||
|
tagId, threshold, locationId, editDate, lastScan, lastDiscovery,
|
||||||
|
calculation, customFields
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
subnetwork.Id.Should().Be(id);
|
||||||
|
subnetwork.Subnet.Should().Be(subnet);
|
||||||
|
subnetwork.Mask.Should().Be(mask);
|
||||||
|
subnetwork.SectionId.Should().Be(sectionId);
|
||||||
|
subnetwork.Description.Should().Be(description);
|
||||||
|
subnetwork.LinkedSubnet.Should().Be(linkedSubnet);
|
||||||
|
subnetwork.FirewallAddressObject.Should().Be(firewallObject);
|
||||||
|
subnetwork.VrfId.Should().Be(vrfId);
|
||||||
|
subnetwork.MasterSubnetId.Should().Be(masterSubnetId);
|
||||||
|
subnetwork.AllowRequests.Should().Be(allowRequests);
|
||||||
|
subnetwork.VlanId.Should().Be(vlanId);
|
||||||
|
subnetwork.ShowName.Should().Be(showName);
|
||||||
|
subnetwork.DeviceId.Should().Be(deviceId);
|
||||||
|
subnetwork.Permissions.Should().Be(permissions);
|
||||||
|
subnetwork.PingSubnet.Should().Be(pingSubnet);
|
||||||
|
subnetwork.DiscoverSubnet.Should().Be(discoverSubnet);
|
||||||
|
subnetwork.ResolveDNS.Should().Be(resolveDNS);
|
||||||
|
subnetwork.DNSRecursive.Should().Be(dnsRecursive);
|
||||||
|
subnetwork.DNSRecords.Should().Be(dnsRecords);
|
||||||
|
subnetwork.NameserverId.Should().Be(nameserverId);
|
||||||
|
subnetwork.ScanAgent.Should().Be(scanAgent);
|
||||||
|
subnetwork.IsFolder.Should().Be(isFolder);
|
||||||
|
subnetwork.IsFull.Should().Be(isFull);
|
||||||
|
subnetwork.IsPool.Should().Be(isPool);
|
||||||
|
subnetwork.TagId.Should().Be(tagId);
|
||||||
|
subnetwork.Threshold.Should().Be(threshold);
|
||||||
|
subnetwork.LocationId.Should().Be(locationId);
|
||||||
|
subnetwork.EditDate.Should().Be(editDate);
|
||||||
|
subnetwork.LastScan.Should().Be(lastScan);
|
||||||
|
subnetwork.LastDiscovery.Should().Be(lastDiscovery);
|
||||||
|
subnetwork.Calculation.Should().BeEquivalentTo(calculation);
|
||||||
|
subnetwork.ExtendedData.Should().BeEquivalentTo(customFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("192.168.1.0", 24, "192.168.1.0/24")]
|
||||||
|
[InlineData("10.0.0.0", 8, "10.0.0.0/8")]
|
||||||
|
[InlineData("172.16.0.0", 16, "172.16.0.0/16")]
|
||||||
|
[InlineData("192.168.100.0", 30, "192.168.100.0/30")]
|
||||||
|
public void GetCIDR_ReturnsCidrNotation(string subnet, int mask, string expectedCidr)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var subnetwork = CreateSubnetwork(subnet, mask);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = subnetwork.GetCIDR();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedCidr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToString_ReturnsCidrNotation()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var subnetwork = CreateSubnetwork("10.10.0.0", 16);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = subnetwork.ToString();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be("10.10.0.0/16");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToString_EqualsGetCIDR()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var subnetwork = CreateSubnetwork("172.20.0.0", 12);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
subnetwork.ToString().Should().Be(subnetwork.GetCIDR());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Subnetwork CreateSubnetwork(string subnet, int mask)
|
||||||
|
{
|
||||||
|
return new Subnetwork(
|
||||||
|
1, subnet, mask, 1, "", "", "",
|
||||||
|
0, 0, false, 0, false, 0,
|
||||||
|
"", false, false, false, false,
|
||||||
|
false, 0, false, false, false, false,
|
||||||
|
0, 0, 0, null, null, null,
|
||||||
|
new object(), null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
classlib.tests/Models/TagTests.cs
Normal file
77
classlib.tests/Models/TagTests.cs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
namespace PS.IPAM.Tests.Models;
|
||||||
|
|
||||||
|
using FluentAssertions;
|
||||||
|
using PS.IPAM;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class TagTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_SetsAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var id = 1;
|
||||||
|
var type = "Used";
|
||||||
|
var showTag = true;
|
||||||
|
var bgColor = "#5cb85c";
|
||||||
|
var fgColor = "#ffffff";
|
||||||
|
var compress = "Yes";
|
||||||
|
var locked = "No";
|
||||||
|
var updateTag = true;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tag = new Tag(id, type, showTag, bgColor, fgColor, compress, locked, updateTag);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
tag.Id.Should().Be(id);
|
||||||
|
tag.Type.Should().Be(type);
|
||||||
|
tag.ShowTag.Should().Be(showTag);
|
||||||
|
tag.BGColor.Should().Be(bgColor);
|
||||||
|
tag.FGColor.Should().Be(fgColor);
|
||||||
|
tag.Compress.Should().BeTrue();
|
||||||
|
tag.Locked.Should().BeFalse();
|
||||||
|
tag.UpdateTag.Should().Be(updateTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Yes", true)]
|
||||||
|
[InlineData("No", false)]
|
||||||
|
[InlineData("", false)]
|
||||||
|
[InlineData("yes", false)] // Case sensitive
|
||||||
|
[InlineData("true", false)] // Only "Yes" is true
|
||||||
|
public void StringToBool_ConvertsCorrectly(string input, bool expected)
|
||||||
|
{
|
||||||
|
// The StringToBool is private, so we test through the constructor
|
||||||
|
// Using Compress field which uses StringToBool
|
||||||
|
var tag = new Tag(1, "Test", false, "", "", input, "No", false);
|
||||||
|
|
||||||
|
tag.Compress.Should().Be(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Used")]
|
||||||
|
[InlineData("Available")]
|
||||||
|
[InlineData("Reserved")]
|
||||||
|
[InlineData("DHCP")]
|
||||||
|
public void ToString_ReturnsTagType(string tagType)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var tag = new Tag(1, tagType, false, "", "", "No", "No", false);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = tag.ToString();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(tagType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Locked_WithYes_IsTrue()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var tag = new Tag(1, "Test", false, "", "", "No", "Yes", false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
tag.Locked.Should().BeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
76
classlib.tests/Models/VlanTests.cs
Normal file
76
classlib.tests/Models/VlanTests.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
namespace PS.IPAM.Tests.Models;
|
||||||
|
|
||||||
|
using FluentAssertions;
|
||||||
|
using PS.IPAM;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class VlanTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_SetsAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var vlanId = 100;
|
||||||
|
var domainId = 1;
|
||||||
|
var name = "Production VLAN";
|
||||||
|
var number = 100;
|
||||||
|
var description = "Production network VLAN";
|
||||||
|
var editDate = new DateTime(2026, 1, 15);
|
||||||
|
var customerId = 50;
|
||||||
|
var customFields = new Dictionary<string, object> { { "custom_location", "DC1" } };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var vlan = new Vlan(vlanId, domainId, name, number, description, editDate, customerId, customFields);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
vlan.Id.Should().Be(vlanId);
|
||||||
|
vlan.VlanId.Should().Be(vlanId);
|
||||||
|
vlan.DomainId.Should().Be(domainId);
|
||||||
|
vlan.Name.Should().Be(name);
|
||||||
|
vlan.Number.Should().Be(number);
|
||||||
|
vlan.Description.Should().Be(description);
|
||||||
|
vlan.EditDate.Should().Be(editDate);
|
||||||
|
vlan.CustomerId.Should().Be(customerId);
|
||||||
|
vlan.ExtendedData.Should().BeEquivalentTo(customFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Id_And_VlanId_AreSameValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var vlanId = 200;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var vlan = new Vlan(vlanId, 1, "Test", 200, "", null, 0, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
vlan.Id.Should().Be(vlan.VlanId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(1)]
|
||||||
|
[InlineData(100)]
|
||||||
|
[InlineData(4094)]
|
||||||
|
public void ToString_ReturnsVlanNumber(int vlanNumber)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var vlan = new Vlan(1, 1, "Test", vlanNumber, "", null, 0, null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = vlan.ToString();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(vlanNumber.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithNullOptionalFields_SetsNullValues()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var vlan = new Vlan(1, 1, "Test", 10, "", null, 0, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
vlan.EditDate.Should().BeNull();
|
||||||
|
vlan.ExtendedData.Should().BeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
60
classlib.tests/Models/VrfTests.cs
Normal file
60
classlib.tests/Models/VrfTests.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
namespace PS.IPAM.Tests.Models;
|
||||||
|
|
||||||
|
using FluentAssertions;
|
||||||
|
using PS.IPAM;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class VrfTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_SetsAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var id = 1;
|
||||||
|
var name = "VRF_PROD";
|
||||||
|
var rd = "65000:100";
|
||||||
|
var description = "Production VRF";
|
||||||
|
var sections = "1;2;3";
|
||||||
|
var editDate = new DateTime(2026, 1, 10);
|
||||||
|
var customFields = new Dictionary<string, object> { { "custom_tenant", "CustomerA" } };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var vrf = new Vrf(id, name, rd, description, sections, editDate, customFields);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
vrf.Id.Should().Be(id);
|
||||||
|
vrf.Name.Should().Be(name);
|
||||||
|
vrf.RouteDistinguisher.Should().Be(rd);
|
||||||
|
vrf.Description.Should().Be(description);
|
||||||
|
vrf.Sections.Should().Be(sections);
|
||||||
|
vrf.EditDate.Should().Be(editDate);
|
||||||
|
vrf.ExtendedData.Should().BeEquivalentTo(customFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("VRF_DEFAULT")]
|
||||||
|
[InlineData("Production_VRF")]
|
||||||
|
[InlineData("CUSTOMER_A")]
|
||||||
|
public void ToString_ReturnsVrfName(string vrfName)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var vrf = new Vrf(1, vrfName, "65000:1", "", "", null, null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = vrf.ToString();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(vrfName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithNullOptionalFields_SetsNullValues()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var vrf = new Vrf(1, "Test", "65000:1", "", "", null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
vrf.EditDate.Should().BeNull();
|
||||||
|
vrf.ExtendedData.Should().BeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
33
classlib.tests/classlib.tests.csproj
Normal file
33
classlib.tests/classlib.tests.csproj
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
|
<RootNamespace>PS.IPAM.Tests</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.6.4" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.5">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Moq" Version="4.20.70" />
|
||||||
|
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||||
|
<PackageReference Include="PowerShellStandard.Library" Version="5.1.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\classlib\classlib.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user