Browse Source

Access points tests and completed Program.cs

James 2 months ago
parent
commit
452d6536a3

+ 49 - 0
RackPeek/Program.cs

@@ -2,6 +2,7 @@
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using RackPeek.Commands;
+using RackPeek.Commands.AccessPoints;
 using RackPeek.Commands.Server;
 using RackPeek.Commands.Server.Cpus;
 using RackPeek.Commands.Server.Drives;
@@ -10,6 +11,7 @@ using RackPeek.Commands.Server.Nics;
 using RackPeek.Commands.Switches;
 using RackPeek.Commands.Systems;
 using RackPeek.Domain.Resources.Hardware;
+using RackPeek.Domain.Resources.Hardware.AccessPoints;
 using RackPeek.Domain.Resources.Hardware.Reports;
 using RackPeek.Domain.Resources.Hardware.Server;
 using RackPeek.Domain.Resources.Hardware.Server.Cpu;
@@ -183,6 +185,26 @@ public static class CliBootstrap
         services.AddScoped<SystemDeleteCommand>();
         services.AddScoped<SystemAddCommand>();
         services.AddScoped<SystemReportCommand>();
+        
+        // AccessPoint use cases
+        services.AddScoped<AddAccessPointUseCase>();
+        services.AddScoped<DeleteAccessPointUseCase>();
+        services.AddScoped<GetAccessPointUseCase>();
+        services.AddScoped<GetAccessPointsUseCase>();
+        services.AddScoped<UpdateAccessPointUseCase>();
+        services.AddScoped<DescribeAccessPointUseCase>();
+        
+        // AccessPoint commands
+        services.AddScoped<AccessPointAddCommand>();
+        services.AddScoped<AccessPointDeleteCommand>();
+        services.AddScoped<AccessPointDescribeCommand>();
+        services.AddScoped<AccessPointGetByNameCommand>();
+        services.AddScoped<AccessPointGetCommand>();
+        services.AddScoped<AccessPointSetCommand>();
+
+
+        
+        
 
         
         
@@ -326,6 +348,33 @@ public static class CliBootstrap
                         .WithDescription("Delete a system");
                 });
                 
+                config.AddBranch("accesspoints", ap =>
+                {
+                    ap.SetDescription("Manage access points");
+
+                    ap.AddCommand<AccessPointReportCommand>("summary")
+                        .WithDescription("Show access point hardware report");
+
+                    ap.AddCommand<AccessPointAddCommand>("add")
+                        .WithDescription("Add a new access point");
+
+                    ap.AddCommand<AccessPointGetCommand>("list")
+                        .WithDescription("List access points");
+
+                    ap.AddCommand<AccessPointGetByNameCommand>("get")
+                        .WithDescription("Get an access point by name");
+
+                    ap.AddCommand<AccessPointDescribeCommand>("describe")
+                        .WithDescription("Show detailed information about an access point");
+
+                    ap.AddCommand<AccessPointSetCommand>("set")
+                        .WithDescription("Update access point properties");
+
+                    ap.AddCommand<AccessPointDeleteCommand>("del")
+                        .WithDescription("Delete an access point");
+                });
+
+                
                 // ----------------------------
                 // Reports (read-only summaries)
                 // ----------------------------

+ 48 - 0
Tests/Hardware/AccessPoints/AddAccessPointUseCaseTests.cs

@@ -0,0 +1,48 @@
+using NSubstitute;
+using RackPeek.Domain.Resources.Hardware;
+using RackPeek.Domain.Resources.Hardware.Models;
+using RackPeek.Domain.Resources.Hardware.AccessPoints;
+
+
+
+namespace Tests.Hardware.AccessPoints;
+
+public class AddAccessPointUseCaseTests
+{
+    [Fact]
+    public async Task ExecuteAsync_Adds_new_ap_when_not_exists()
+    {
+        // Arrange
+        var repo = Substitute.For<IHardwareRepository>();
+        repo.GetByNameAsync("ap01").Returns((RackPeek.Domain.Resources.Hardware.Models.Hardware?)null);
+
+        var sut = new AddAccessPointUseCase(repo);
+
+        // Act
+        await sut.ExecuteAsync("ap01");
+
+        // Assert
+        await repo.Received(1).AddAsync(Arg.Is<AccessPoint>(ap =>
+            ap.Name == "ap01"
+        ));
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Throws_if_ap_already_exists()
+    {
+        // Arrange
+        var repo = Substitute.For<IHardwareRepository>();
+        repo.GetByNameAsync("ap01").Returns(new AccessPoint { Name = "ap01" });
+
+        var sut = new AddAccessPointUseCase(repo);
+
+        // Act
+        var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
+            await sut.ExecuteAsync("ap01")
+        );
+
+        // Assert
+        Assert.Equal("Access point 'ap01' already exists.", ex.Message);
+        await repo.DidNotReceive().AddAsync(Arg.Any<AccessPoint>());
+    }
+}

+ 45 - 0
Tests/Hardware/AccessPoints/DeleteAccessPointUseCaseTests.cs

@@ -0,0 +1,45 @@
+using NSubstitute;
+using RackPeek.Domain.Resources.Hardware;
+using RackPeek.Domain.Resources.Hardware.Models;
+using RackPeek.Domain.Resources.Hardware.AccessPoints;
+
+
+namespace Tests.Hardware.AccessPoints;
+
+public class DeleteAccessPointUseCaseTests
+{
+    [Fact]
+    public async Task ExecuteAsync_Deletes_ap_when_exists()
+    {
+        // Arrange
+        var repo = Substitute.For<IHardwareRepository>();
+        repo.GetByNameAsync("ap01").Returns(new AccessPoint { Name = "ap01" });
+
+        var sut = new DeleteAccessPointUseCase(repo);
+
+        // Act
+        await sut.ExecuteAsync("ap01");
+
+        // Assert
+        await repo.Received(1).DeleteAsync("ap01");
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Throws_if_ap_not_found()
+    {
+        // Arrange
+        var repo = Substitute.For<IHardwareRepository>();
+        repo.GetByNameAsync("ap01").Returns((RackPeek.Domain.Resources.Hardware.Models.Hardware?)null);
+
+        var sut = new DeleteAccessPointUseCase(repo);
+
+        // Act
+        var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
+            await sut.ExecuteAsync("ap01")
+        );
+
+        // Assert
+        Assert.Equal("Access point 'ap01' not found.", ex.Message);
+        await repo.DidNotReceive().DeleteAsync(Arg.Any<string>());
+    }
+}

+ 50 - 0
Tests/Hardware/AccessPoints/DescribeAccessPointUseCaseTests.cs

@@ -0,0 +1,50 @@
+using NSubstitute;
+using RackPeek.Domain.Resources.Hardware;
+using RackPeek.Domain.Resources.Hardware.Models;
+using RackPeek.Domain.Resources.Hardware.AccessPoints;
+
+
+namespace Tests.Hardware.AccessPoints;
+
+public class DescribeAccessPointUseCaseTests
+{
+    [Fact]
+    public async Task ExecuteAsync_Returns_null_when_ap_not_found()
+    {
+        // Arrange
+        var repo = Substitute.For<IHardwareRepository>();
+        repo.GetByNameAsync("ap01").Returns((RackPeek.Domain.Resources.Hardware.Models.Hardware?)null);
+
+        var sut = new DescribeAccessPointUseCase(repo);
+
+        // Act
+        var result = await sut.ExecuteAsync("ap01");
+
+        // Assert
+        Assert.Null(result);
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Returns_description_when_ap_exists()
+    {
+        // Arrange
+        var repo = Substitute.For<IHardwareRepository>();
+        repo.GetByNameAsync("ap01").Returns(new AccessPoint
+        {
+            Name = "ap01",
+            Model = "U6-Lite",
+            Speed = 1.2
+        });
+
+        var sut = new DescribeAccessPointUseCase(repo);
+
+        // Act
+        var result = await sut.ExecuteAsync("ap01");
+
+        // Assert
+        Assert.NotNull(result);
+        Assert.Equal("ap01", result.Name);
+        Assert.Equal("U6-Lite", result.Model);
+        Assert.Equal(1.2, result.Speed);
+    }
+}

+ 43 - 0
Tests/Hardware/AccessPoints/GetAccessPointUseCaseTests.cs

@@ -0,0 +1,43 @@
+using NSubstitute;
+using RackPeek.Domain.Resources.Hardware;
+using RackPeek.Domain.Resources.Hardware.Models;
+using RackPeek.Domain.Resources.Hardware.AccessPoints;
+
+namespace Tests.Hardware.AccessPoints;
+
+public class GetAccessPointUseCaseTests
+{
+    [Fact]
+    public async Task ExecuteAsync_Returns_ap_when_it_exists()
+    {
+        // Arrange
+        var repo = Substitute.For<IHardwareRepository>();
+        var ap = new AccessPoint { Name = "ap01" };
+        repo.GetByNameAsync("ap01").Returns(ap);
+
+        var sut = new GetAccessPointUseCase(repo);
+
+        // Act
+        var result = await sut.ExecuteAsync("ap01");
+
+        // Assert
+        Assert.NotNull(result);
+        Assert.Same(ap, result);
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Returns_null_when_hardware_is_not_ap()
+    {
+        // Arrange
+        var repo = Substitute.For<IHardwareRepository>();
+        repo.GetByNameAsync("node01").Returns(new Server { Name = "node01" });
+
+        var sut = new GetAccessPointUseCase(repo);
+
+        // Act
+        var result = await sut.ExecuteAsync("node01");
+
+        // Assert
+        Assert.Null(result);
+    }
+}

+ 51 - 0
Tests/Hardware/AccessPoints/GetAccessPointsUseCaseTest.cs

@@ -0,0 +1,51 @@
+using NSubstitute;
+using RackPeek.Domain.Resources.Hardware;
+using RackPeek.Domain.Resources.Hardware.Models;
+using RackPeek.Domain.Resources.Hardware.AccessPoints;
+
+
+namespace Tests.Hardware.AccessPoints;
+
+public class GetAccessPointsUseCaseTests
+{
+    [Fact]
+    public async Task ExecuteAsync_Returns_only_access_points()
+    {
+        // Arrange
+        var repo = Substitute.For<IHardwareRepository>();
+        repo.GetAllAsync().Returns([
+            new AccessPoint { Name = "ap01" },
+            new Server { Name = "node01" },
+            new AccessPoint { Name = "ap02" }
+        ]);
+
+        var sut = new GetAccessPointsUseCase(repo);
+
+        // Act
+        var result = await sut.ExecuteAsync();
+
+        // Assert
+        Assert.Equal(2, result.Count);
+        Assert.All(result, ap => Assert.IsType<AccessPoint>(ap));
+        Assert.Contains(result, ap => ap.Name == "ap01");
+        Assert.Contains(result, ap => ap.Name == "ap02");
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Returns_empty_list_when_no_access_points_exist()
+    {
+        // Arrange
+        var repo = Substitute.For<IHardwareRepository>();
+        repo.GetAllAsync().Returns([
+            new Server { Name = "node01" }
+        ]);
+
+        var sut = new GetAccessPointsUseCase(repo);
+
+        // Act
+        var result = await sut.ExecuteAsync();
+
+        // Assert
+        Assert.Empty(result);
+    }
+}

+ 87 - 0
Tests/Hardware/AccessPoints/UpdateAccessPointUseCaseTests.cs

@@ -0,0 +1,87 @@
+using NSubstitute;
+using RackPeek.Domain.Resources.Hardware;
+using RackPeek.Domain.Resources.Hardware.Models;
+using RackPeek.Domain.Resources.Hardware.AccessPoints;
+
+namespace Tests.Hardware.AccessPoints;
+
+public class UpdateAccessPointUseCaseTests
+{
+    [Fact]
+    public async Task ExecuteAsync_Throws_when_ap_not_found()
+    {
+        // Arrange
+        var repo = Substitute.For<IHardwareRepository>();
+        repo.GetByNameAsync("ap01").Returns((RackPeek.Domain.Resources.Hardware.Models.Hardware?)null);
+
+        var sut = new UpdateAccessPointUseCase(repo);
+
+        // Act
+        var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
+            await sut.ExecuteAsync("ap01")
+        );
+
+        // Assert
+        Assert.Equal("Access point 'ap01' not found.", ex.Message);
+        await repo.DidNotReceive().UpdateAsync(Arg.Any<AccessPoint>());
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Updates_only_provided_fields()
+    {
+        // Arrange
+        var existing = new AccessPoint
+        {
+            Name = "ap01",
+            Model = "OldModel",
+            Speed = 1.0
+        };
+
+        var repo = Substitute.For<IHardwareRepository>();
+        repo.GetByNameAsync("ap01").Returns(existing);
+
+        var sut = new UpdateAccessPointUseCase(repo);
+
+        // Act
+        await sut.ExecuteAsync(
+            "ap01",
+            model: "NewModel",
+            speed: 2.5
+        );
+
+        // Assert
+        await repo.Received(1).UpdateAsync(Arg.Is<AccessPoint>(ap =>
+            ap.Name == "ap01" &&
+            ap.Model == "NewModel" &&
+            ap.Speed == 2.5
+        ));
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Does_not_update_model_when_empty_or_whitespace()
+    {
+        // Arrange
+        var existing = new AccessPoint
+        {
+            Name = "ap01",
+            Model = "KeepMe",
+            Speed = 1.0
+        };
+
+        var repo = Substitute.For<IHardwareRepository>();
+        repo.GetByNameAsync("ap01").Returns(existing);
+
+        var sut = new UpdateAccessPointUseCase(repo);
+
+        // Act
+        await sut.ExecuteAsync(
+            "ap01",
+            model: "   "
+        );
+
+        // Assert
+        await repo.Received(1).UpdateAsync(Arg.Is<AccessPoint>(ap =>
+            ap.Model == "KeepMe"
+        ));
+    }
+}

+ 0 - 1
Tests/Tests.csproj

@@ -25,7 +25,6 @@
     </ItemGroup>
 
     <ItemGroup>
-      <Folder Include="Hardware\AccessPoint\" />
       <Folder Include="Hardware\Desktop\" />
       <Folder Include="Hardware\Ups\" />
     </ItemGroup>