Browse Source

Created UseCases, tests, commands and registered in Program.cs

James 2 months ago
parent
commit
fd11176cb4

+ 27 - 0
RackPeek.Domain/Resources/Hardware/Server/Nic/AddNicUseCase.cs

@@ -0,0 +1,27 @@
+namespace RackPeek.Domain.Resources.Hardware.Server.Nic;
+
+public class AddNicUseCase(IHardwareRepository repository)
+{
+    public async Task ExecuteAsync(
+        string serverName,
+        string type,
+        int speed,
+        int ports)
+    {
+        var hardware = await repository.GetByNameAsync(serverName);
+
+        if (hardware is not Models.Server server)
+            return;
+
+        server.Nics ??= [];
+
+        server.Nics.Add(new Models.Nic
+        {
+            Type = type,
+            Speed = speed,
+            Ports = ports
+        });
+
+        await repository.UpdateAsync(server);
+    }
+}

+ 21 - 0
RackPeek.Domain/Resources/Hardware/Server/Nic/RemoveNicUseCase.cs

@@ -0,0 +1,21 @@
+namespace RackPeek.Domain.Resources.Hardware.Server.Nic;
+
+public class RemoveNicUseCase(IHardwareRepository repository)
+{
+    public async Task ExecuteAsync(string serverName, int index)
+    {
+        var hardware = await repository.GetByNameAsync(serverName);
+
+        if (hardware is not Models.Server server)
+            return;
+
+        server.Nics ??= [];
+
+        if (index < 0 || index >= server.Nics.Count)
+            throw new ArgumentOutOfRangeException(nameof(index), "NIC index out of range.");
+
+        server.Nics.RemoveAt(index);
+
+        await repository.UpdateAsync(server);
+    }
+}

+ 29 - 0
RackPeek.Domain/Resources/Hardware/Server/Nic/UpdateNicUseCase.cs

@@ -0,0 +1,29 @@
+namespace RackPeek.Domain.Resources.Hardware.Server.Nic;
+
+public class UpdateNicUseCase(IHardwareRepository repository)
+{
+    public async Task ExecuteAsync(
+        string serverName,
+        int index,
+        string type,
+        int speed,
+        int ports)
+    {
+        var hardware = await repository.GetByNameAsync(serverName);
+
+        if (hardware is not Models.Server server)
+            return;
+
+        server.Nics ??= [];
+
+        if (index < 0 || index >= server.Nics.Count)
+            throw new ArgumentOutOfRangeException(nameof(index), "NIC index out of range.");
+
+        var nic = server.Nics[index];
+        nic.Type = type;
+        nic.Speed = speed;
+        nic.Ports = ports;
+
+        await repository.UpdateAsync(server);
+    }
+}

+ 40 - 0
RackPeek/Commands/Server/Nics/ServerNicAddCommand.cs

@@ -0,0 +1,40 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Server.Nic;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Server.Nics;
+
+public class ServerNicAddSettings : ServerNameSettings
+{
+    [CommandOption("--type <TYPE>")]
+    public string Type { get; set; }
+
+    [CommandOption("--speed <SPEED>")]
+    public int Speed { get; set; }
+
+    [CommandOption("--ports <PORTS>")]
+    public int Ports { get; set; }
+}
+
+public class ServerNicAddCommand(IServiceProvider serviceProvider)
+    : AsyncCommand<ServerNicAddSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        ServerNicAddSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = serviceProvider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<AddNicUseCase>();
+
+        await useCase.ExecuteAsync(
+            settings.Name,
+            settings.Type,
+            settings.Speed,
+            settings.Ports);
+
+        AnsiConsole.MarkupLine($"[green]NIC added to '{settings.Name}'.[/]");
+        return 0;
+    }
+}

+ 32 - 0
RackPeek/Commands/Server/Nics/ServerNicRemoveCommand.cs

@@ -0,0 +1,32 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Server.Nic;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Server.Nics;
+
+public class ServerNicRemoveSettings : ServerNameSettings
+{
+    [CommandOption("--index <INDEX>")]
+    public int Index { get; set; }
+}
+
+public class ServerNicRemoveCommand(IServiceProvider serviceProvider)
+    : AsyncCommand<ServerNicRemoveSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        ServerNicRemoveSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = serviceProvider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<RemoveNicUseCase>();
+
+        await useCase.ExecuteAsync(
+            settings.Name,
+            settings.Index);
+
+        AnsiConsole.MarkupLine($"[green]NIC {settings.Index} removed from '{settings.Name}'.[/]");
+        return 0;
+    }
+}

+ 44 - 0
RackPeek/Commands/Server/Nics/ServerNicUpdateCommand.cs

@@ -0,0 +1,44 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Server.Nic;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Server.Nics;
+
+public class ServerNicUpdateSettings : ServerNameSettings
+{
+    [CommandOption("--index <INDEX>")]
+    public int Index { get; set; }
+
+    [CommandOption("--type <TYPE>")]
+    public string Type { get; set; }
+
+    [CommandOption("--speed <SPEED>")]
+    public int Speed { get; set; }
+
+    [CommandOption("--ports <PORTS>")]
+    public int Ports { get; set; }
+}
+
+public class ServerNicUpdateCommand(IServiceProvider serviceProvider)
+    : AsyncCommand<ServerNicUpdateSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        ServerNicUpdateSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = serviceProvider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<UpdateNicUseCase>();
+
+        await useCase.ExecuteAsync(
+            settings.Name,
+            settings.Index,
+            settings.Type,
+            settings.Speed,
+            settings.Ports);
+
+        AnsiConsole.MarkupLine($"[green]NIC {settings.Index} updated on '{settings.Name}'.[/]");
+        return 0;
+    }
+}

+ 28 - 0
RackPeek/Program.cs

@@ -7,9 +7,11 @@ using Microsoft.Extensions.Logging;
 using RackPeek.Commands;
 using RackPeek.Commands.Server;
 using RackPeek.Commands.Server.Cpus;
+using RackPeek.Commands.Server.Nics;
 using RackPeek.Domain.Resources.Hardware.Reports;
 using RackPeek.Domain.Resources.Hardware.Server;
 using RackPeek.Domain.Resources.Hardware.Server.Cpu;
+using RackPeek.Domain.Resources.Hardware.Server.Nic;
 using RackPeek.Yaml;
 
 namespace RackPeek;
@@ -91,6 +93,17 @@ public static class Program
         services.AddScoped<ServerCpuSetCommand>();
         services.AddScoped<ServerCpuRemoveCommand>();
         
+        // NIC use cases
+        services.AddScoped<AddNicUseCase>();
+        services.AddScoped<UpdateNicUseCase>();
+        services.AddScoped<RemoveNicUseCase>();
+        
+        // NIC commands
+        services.AddScoped<ServerNicAddCommand>();
+        services.AddScoped<ServerNicUpdateCommand>();
+        services.AddScoped<ServerNicRemoveCommand>();
+
+        
         // Spectre bootstrap
         var registrar = new TypeRegistrar(services);
         var app = new CommandApp(registrar);
@@ -137,6 +150,21 @@ public static class Program
                     cpu.AddCommand<ServerCpuRemoveCommand>("del")
                         .WithDescription("Remove a CPU from a server");
                 });
+                
+                server.AddBranch("nic", nic =>
+                {
+                    nic.SetDescription("Manage server NICs");
+
+                    nic.AddCommand<ServerNicAddCommand>("add")
+                        .WithDescription("Add a NIC to a server");
+
+                    nic.AddCommand<ServerNicUpdateCommand>("set")
+                        .WithDescription("Update a NIC on a server");
+
+                    nic.AddCommand<ServerNicRemoveCommand>("del")
+                        .WithDescription("Remove a NIC from a server");
+                });
+
             });
 
             // ----------------------------

+ 70 - 0
Tests/Hardware/AddNicUseCaseTests.cs

@@ -0,0 +1,70 @@
+using NSubstitute;
+using RackPeek.Domain.Resources.Hardware;
+using RackPeek.Domain.Resources.Hardware.Models;
+using RackPeek.Domain.Resources.Hardware.Server.Nic;
+using Xunit;
+
+namespace Tests.Hardware;
+
+public class AddNicUseCaseTests
+{
+    [Fact]
+    public async Task ExecuteAsync_Adds_nic_when_server_exists()
+    {
+        var repo = Substitute.For<IHardwareRepository>();
+        var server = new Server
+        {
+            Name = "node01",
+            Nics = new List<Nic>()
+        };
+
+        repo.GetByNameAsync("node01").Returns(server);
+
+        var sut = new AddNicUseCase(repo);
+
+        await sut.ExecuteAsync("node01", "10GBase-T", 10000, 2);
+
+        Assert.Single(server.Nics);
+        Assert.Equal("10GBase-T", server.Nics[0].Type);
+        Assert.Equal(10000, server.Nics[0].Speed!.Value);
+        Assert.Equal(2, server.Nics[0].Ports!.Value);
+
+        await repo.Received(1).UpdateAsync(Arg.Is<Server>(s =>
+            s.Nics.Count == 1 &&
+            s.Nics[0].Type == "10GBase-T"
+        ));
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Initializes_list_when_null()
+    {
+        var repo = Substitute.For<IHardwareRepository>();
+        var server = new Server { Name = "node01", Nics = null };
+
+        repo.GetByNameAsync("node01").Returns(server);
+
+        var sut = new AddNicUseCase(repo);
+
+        await sut.ExecuteAsync("node01", "SFP+", 10000, 1);
+
+        Assert.NotNull(server.Nics);
+        Assert.Single(server.Nics);
+
+        await repo.Received(1).UpdateAsync(Arg.Any<Server>());
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Does_nothing_when_server_not_found()
+    {
+        var repo = Substitute.For<IHardwareRepository>();
+
+        repo.GetByNameAsync("node01")
+            .Returns((RackPeek.Domain.Resources.Hardware.Models.Hardware?)null);
+
+        var sut = new AddNicUseCase(repo);
+
+        await sut.ExecuteAsync("node01", "SFP+", 10000, 1);
+
+        await repo.DidNotReceive().UpdateAsync(Arg.Any<Server>());
+    }
+}

+ 71 - 0
Tests/Hardware/RemoveNicUseCaseTests.cs

@@ -0,0 +1,71 @@
+using NSubstitute;
+using RackPeek.Domain.Resources.Hardware;
+using RackPeek.Domain.Resources.Hardware.Models;
+using RackPeek.Domain.Resources.Hardware.Server.Nic;
+using Xunit;
+
+namespace Tests.Hardware;
+
+public class RemoveNicUseCaseTests
+{
+    [Fact]
+    public async Task ExecuteAsync_Removes_nic_when_index_valid()
+    {
+        var repo = Substitute.For<IHardwareRepository>();
+        var server = new Server
+        {
+            Name = "node01",
+            Nics = new List<Nic>
+            {
+                new Nic { Type = "10GBase-T", Speed = 10000, Ports = 2 },
+                new Nic { Type = "SFP+", Speed = 10000, Ports = 1 }
+            }
+        };
+
+        repo.GetByNameAsync("node01").Returns(server);
+
+        var sut = new RemoveNicUseCase(repo);
+
+        await sut.ExecuteAsync("node01", 0);
+
+        Assert.Single(server.Nics);
+        Assert.Equal("SFP+", server.Nics[0].Type);
+
+        await repo.Received(1).UpdateAsync(Arg.Any<Server>());
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Throws_when_index_out_of_range()
+    {
+        var repo = Substitute.For<IHardwareRepository>();
+        var server = new Server
+        {
+            Name = "node01",
+            Nics = new List<Nic> { new Nic() }
+        };
+
+        repo.GetByNameAsync("node01").Returns(server);
+
+        var sut = new RemoveNicUseCase(repo);
+
+        await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() =>
+            sut.ExecuteAsync("node01", 1));
+
+        await repo.DidNotReceive().UpdateAsync(Arg.Any<Server>());
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Does_nothing_when_server_not_found()
+    {
+        var repo = Substitute.For<IHardwareRepository>();
+
+        repo.GetByNameAsync("node01")
+            .Returns((RackPeek.Domain.Resources.Hardware.Models.Hardware?)null);
+
+        var sut = new RemoveNicUseCase(repo);
+
+        await sut.ExecuteAsync("node01", 0);
+
+        await repo.DidNotReceive().UpdateAsync(Arg.Any<Server>());
+    }
+}

+ 71 - 0
Tests/Hardware/UpdateNicUseCaseTests.cs

@@ -0,0 +1,71 @@
+using NSubstitute;
+using RackPeek.Domain.Resources.Hardware;
+using RackPeek.Domain.Resources.Hardware.Models;
+using RackPeek.Domain.Resources.Hardware.Server.Nic;
+using Xunit;
+
+namespace Tests.Hardware;
+
+public class UpdateNicUseCaseTests
+{
+    [Fact]
+    public async Task ExecuteAsync_Updates_nic_when_index_valid()
+    {
+        var repo = Substitute.For<IHardwareRepository>();
+        var server = new Server
+        {
+            Name = "node01",
+            Nics = new List<Nic>
+            {
+                new Nic { Type = "10GBase-T", Speed = 10000, Ports = 2 }
+            }
+        };
+
+        repo.GetByNameAsync("node01").Returns(server);
+
+        var sut = new UpdateNicUseCase(repo);
+
+        await sut.ExecuteAsync("node01", 0, "SFP+", 10000, 1);
+
+        Assert.Equal("SFP+", server.Nics[0].Type);
+        Assert.Equal(10000, server.Nics[0].Speed!.Value);
+        Assert.Equal(1, server.Nics[0].Ports!.Value);
+
+        await repo.Received(1).UpdateAsync(Arg.Any<Server>());
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Throws_when_index_out_of_range()
+    {
+        var repo = Substitute.For<IHardwareRepository>();
+        var server = new Server
+        {
+            Name = "node01",
+            Nics = new List<Nic> { new Nic() }
+        };
+
+        repo.GetByNameAsync("node01").Returns(server);
+
+        var sut = new UpdateNicUseCase(repo);
+
+        await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() =>
+            sut.ExecuteAsync("node01", 1, "SFP+", 10000, 1));
+
+        await repo.DidNotReceive().UpdateAsync(Arg.Any<Server>());
+    }
+
+    [Fact]
+    public async Task ExecuteAsync_Does_nothing_when_server_not_found()
+    {
+        var repo = Substitute.For<IHardwareRepository>();
+
+        repo.GetByNameAsync("node01")
+            .Returns((RackPeek.Domain.Resources.Hardware.Models.Hardware?)null);
+
+        var sut = new UpdateNicUseCase(repo);
+
+        await sut.ExecuteAsync("node01", 0, "SFP+", 10000, 1);
+
+        await repo.DidNotReceive().UpdateAsync(Arg.Any<Server>());
+    }
+}