Initial commit

This commit is contained in:
2026-07-03 16:37:12 +07:00
commit 63b8c1ea8b
1931 changed files with 640587 additions and 0 deletions

View File

@@ -0,0 +1,176 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using RobotNet10.FleetManager.Services;
using RobotNet10.StorageManager;
using System.Text;
namespace RobotNet10.RobotManager.Test.Services;
/// <summary>
/// Unit tests for RobotModelImageStorageService
/// </summary>
[TestFixture]
public class RobotModelImageStorageServiceTests
{
private string _testDirectory = null!;
private RobotModelImageStorageService _service = null!;
private IOptionsMonitor<StorageConfig> _optionsMonitor = null!;
[SetUp]
public void Setup()
{
// Create a temporary test directory
_testDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(_testDirectory);
// Create StorageConfig for local storage
var storageConfig = new StorageConfig
{
UsingLocal = true,
LocalFolder = _testDirectory,
RetryCount = 3
};
// Mock IOptionsMonitor<StorageConfig>
var mockOptionsMonitor = new Moq.Mock<IOptionsMonitor<StorageConfig>>();
mockOptionsMonitor.Setup(m => m.Get("RobotModelImages")).Returns(storageConfig);
_optionsMonitor = mockOptionsMonitor.Object;
var logger = new LoggerFactory().CreateLogger<RobotModelImageStorageService>();
_service = new RobotModelImageStorageService(_optionsMonitor, logger);
}
[TearDown]
public void TearDown()
{
// Dispose service to clean up StorageManager
_service?.Dispose();
// Clean up test directory
if (Directory.Exists(_testDirectory))
{
Directory.Delete(_testDirectory, true);
}
}
[Test]
public async Task SaveImageAsync_ValidImage_SavesToFileSystem()
{
// Arrange
var robotModelId = Guid.NewGuid();
var imageBytes = Encoding.UTF8.GetBytes("Fake PNG image data");
using var imageStream = new MemoryStream(imageBytes);
// Act
await _service.SaveImageAsync(robotModelId, imageStream);
// Assert
// StorageManager stores files in path "robotModelImages" with objectName = robotModelId
var expectedPath = Path.Combine(_testDirectory, "robotModelImages", $"{robotModelId}.png");
Assert.That(File.Exists(expectedPath), Is.True);
}
[Test]
public async Task GetImageAsync_ExistingImage_ReturnsStream()
{
// Arrange
var robotModelId = Guid.NewGuid();
var imageBytes = Encoding.UTF8.GetBytes("Fake PNG image data");
using var saveStream = new MemoryStream(imageBytes);
await _service.SaveImageAsync(robotModelId, saveStream);
// Act
using var result = await _service.GetImageAsync(robotModelId);
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result, Is.InstanceOf<Stream>());
Assert.That(result!.Length, Is.GreaterThan(0));
}
[Test]
public async Task GetImageAsync_NonExistentImage_ReturnsNull()
{
// Arrange
var nonExistentId = Guid.NewGuid();
// Act
var result = await _service.GetImageAsync(nonExistentId);
// Assert
Assert.That(result, Is.Null);
}
[Test]
public async Task DeleteImageAsync_ExistingImage_DeletesFile()
{
// Arrange
var robotModelId = Guid.NewGuid();
var imageBytes = Encoding.UTF8.GetBytes("Fake PNG image data");
using var saveStream = new MemoryStream(imageBytes);
await _service.SaveImageAsync(robotModelId, saveStream);
// Act
await _service.DeleteImageAsync(robotModelId);
// Assert
// StorageManager stores files in path "robotModelImages" with objectName = robotModelId
var expectedPath = Path.Combine(_testDirectory, "robotModelImages", $"{robotModelId}.png");
Assert.That(File.Exists(expectedPath), Is.False);
}
[Test]
public async Task DeleteImageAsync_NonExistentImage_DoesNotThrow()
{
// Arrange
var nonExistentId = Guid.NewGuid();
// Act & Assert - Should not throw
Assert.DoesNotThrowAsync(async () => await _service.DeleteImageAsync(nonExistentId));
}
[Test]
public async Task ImageExistsAsync_ExistingImage_ReturnsTrue()
{
// Arrange
var robotModelId = Guid.NewGuid();
var imageBytes = Encoding.UTF8.GetBytes("Fake PNG image data");
using var saveStream = new MemoryStream(imageBytes);
await _service.SaveImageAsync(robotModelId, saveStream);
// Act
var result = await _service.ImageExistsAsync(robotModelId);
// Assert
Assert.That(result, Is.True);
}
[Test]
public async Task ImageExistsAsync_NonExistentImage_ReturnsFalse()
{
// Arrange
var nonExistentId = Guid.NewGuid();
// Act
var result = await _service.ImageExistsAsync(nonExistentId);
// Assert
Assert.That(result, Is.False);
}
[Test]
public async Task GetImageDimensionsAsync_InvalidImageStream_ThrowsException()
{
// Arrange
// Note: This test uses invalid image data to verify error handling
var imageBytes = Encoding.UTF8.GetBytes("Invalid image data");
using var imageStream = new MemoryStream(imageBytes);
// Act & Assert
// Since we're using invalid image data, this should throw InvalidOperationException
// (which wraps UnknownImageFormatException from ImageSharp)
var ex = Assert.ThrowsAsync<InvalidOperationException>(async () =>
await _service.GetImageDimensionsAsync(imageStream));
Assert.That(ex!.Message, Does.Contain("Invalid image format or corrupted file"));
}
}

View File

@@ -0,0 +1,328 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using RobotNet10.FleetManager.Data;
using RobotNet10.FleetManager.Services;
using RobotNet10.FleetManager.Shared.DTOs.RobotModel;
using RobotNet10.FleetManager.Shared.Enums;
using RobotNet10.RobotManager.Test.Helpers;
namespace RobotNet10.RobotManager.Test.Services;
/// <summary>
/// Unit tests for RobotModelService
/// </summary>
[TestFixture]
public class RobotModelServiceTests
{
private ApplicationDbContext _context = null!;
private RobotModelService _service = null!;
[SetUp]
public void Setup()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
_context = new ApplicationDbContext(options);
var logger = new LoggerFactory().CreateLogger<RobotModelService>();
var _logger = new FleetManager.Services.Logger<RobotModelService>(logger);
_service = new RobotModelService(_context, _logger);
}
[TearDown]
public void TearDown()
{
_context.Dispose();
}
[Test]
public async Task CreateAsync_ValidData_ReturnsCreatedRobotModel()
{
// Arrange
var request = new CreateRobotModelRequest
{
ModelName = "TestModel",
Length = 1.2,
Width = 0.8,
ImageWidth = 800,
ImageHeight = 600,
NavigationPointX = 0.0,
NavigationPointY = 0.0,
NavigationType = NavigationType.Differential
};
// Act
var result = await _service.CreateAsync(request);
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.ModelName, Is.EqualTo(request.ModelName));
Assert.That(result.Length, Is.EqualTo(request.Length));
Assert.That(result.Width, Is.EqualTo(request.Width));
Assert.That(result.NavigationType, Is.EqualTo(request.NavigationType));
Assert.That(result.Id, Is.Not.EqualTo(Guid.Empty));
}
[Test]
public async Task CreateAsync_DuplicateModelName_ThrowsInvalidOperationException()
{
// Arrange
var existingModel = TestHelpers.CreateTestRobotModel(modelName: "ExistingModel");
_context.RobotModels.Add(existingModel);
await _context.SaveChangesAsync();
var request = new CreateRobotModelRequest
{
ModelName = "ExistingModel",
Length = 1.0,
Width = 0.5,
ImageWidth = 800,
ImageHeight = 600,
NavigationPointX = 0.0,
NavigationPointY = 0.0,
NavigationType = NavigationType.Differential
};
// Act & Assert
var ex = Assert.ThrowsAsync<InvalidOperationException>(async () => await _service.CreateAsync(request));
Assert.That(ex!.Message, Does.Contain("already exists"));
}
[Test]
public async Task GetAllAsync_ReturnsAllRobotModels()
{
// Arrange
TestHelpers.SeedTestData(_context);
// Act
var result = await _service.GetAllAsync();
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.EqualTo(2));
}
[Test]
public async Task GetByIdAsync_ExistingId_ReturnsRobotModel()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
await _context.SaveChangesAsync();
// Act
var result = await _service.GetByIdAsync(model.Id);
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result!.Id, Is.EqualTo(model.Id));
Assert.That(result.ModelName, Is.EqualTo(model.ModelName));
}
[Test]
public async Task GetByIdAsync_NonExistentId_ReturnsNull()
{
// Arrange
var nonExistentId = Guid.NewGuid();
// Act
var result = await _service.GetByIdAsync(nonExistentId);
// Assert
Assert.That(result, Is.Null);
}
[Test]
public async Task UpdateAsync_ValidData_UpdatesRobotModel()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
await _context.SaveChangesAsync();
var request = new UpdateRobotModelRequest
{
ModelName = "UpdatedModel",
Length = 2.0,
Width = 1.0,
ImageWidth = 1000,
ImageHeight = 800,
NavigationPointX = 0.1,
NavigationPointY = 0.2,
NavigationType = NavigationType.OmniDrive
};
// Act
var result = await _service.UpdateAsync(model.Id, request);
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.ModelName, Is.EqualTo(request.ModelName));
Assert.That(result.Length, Is.EqualTo(request.Length));
Assert.That(result.NavigationType, Is.EqualTo(request.NavigationType));
}
[Test]
public async Task UpdateAsync_NonExistentId_ThrowsKeyNotFoundException()
{
// Arrange
var nonExistentId = Guid.NewGuid();
var request = new UpdateRobotModelRequest
{
ModelName = "Test",
Length = 1.0,
Width = 0.5,
ImageWidth = 800,
ImageHeight = 600,
NavigationPointX = 0.0,
NavigationPointY = 0.0,
NavigationType = NavigationType.Differential
};
// Act & Assert
Assert.ThrowsAsync<KeyNotFoundException>(async () => await _service.UpdateAsync(nonExistentId, request));
}
[Test]
public async Task DeleteAsync_ExistingId_ReturnsTrue()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
await _context.SaveChangesAsync();
// Act
var result = await _service.DeleteAsync(model.Id);
// Assert
Assert.That(result, Is.True);
var deleted = await _context.RobotModels.FindAsync(model.Id);
Assert.That(deleted, Is.Null);
}
[Test]
public async Task DeleteAsync_NonExistentId_ReturnsFalse()
{
// Arrange
var nonExistentId = Guid.NewGuid();
// Act
var result = await _service.DeleteAsync(nonExistentId);
// Assert
Assert.That(result, Is.False);
}
[Test]
public async Task DeleteAsync_ModelWithRobots_ThrowsInvalidOperationException()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
var robot = TestHelpers.CreateTestRobot(modelId: model.Id);
_context.Robots.Add(robot);
await _context.SaveChangesAsync();
// Act & Assert
var ex = Assert.ThrowsAsync<InvalidOperationException>(async () => await _service.DeleteAsync(model.Id));
Assert.That(ex, Is.Not.Null);
Assert.That(ex!.Message, Does.Contain("Cannot delete").Or.Contain("cannot be deleted").Or.Contain("being used"));
}
[Test]
public async Task SearchAsync_WithQuery_ReturnsMatchingModels()
{
// Arrange
TestHelpers.SeedTestData(_context);
// Act
var result = await _service.SearchAsync("AMR-T800");
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.EqualTo(1));
Assert.That(result[0].ModelName, Is.EqualTo("AMR-T800"));
}
[Test]
public async Task SearchAsync_EmptyQuery_ReturnsAllModels()
{
// Arrange
TestHelpers.SeedTestData(_context);
// Act
var result = await _service.SearchAsync("");
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.EqualTo(2));
}
[Test]
public async Task ExistsAsync_ExistingModelName_ReturnsTrue()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel(modelName: "ExistingModel");
_context.RobotModels.Add(model);
await _context.SaveChangesAsync();
// Act
var result = await _service.ExistsAsync("ExistingModel");
// Assert
Assert.That(result, Is.True);
}
[Test]
public async Task ExistsAsync_NonExistentModelName_ReturnsFalse()
{
// Arrange
// No models in database
// Act
var result = await _service.ExistsAsync("NonExistentModel");
// Assert
Assert.That(result, Is.False);
}
[Test]
public async Task GetUsageInfoAsync_ModelWithRobots_ReturnsUsageInfo()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
var robot1 = TestHelpers.CreateTestRobot(modelId: model.Id);
var robot2 = TestHelpers.CreateTestRobot(robotId: "ROBOT-002", modelId: model.Id);
_context.Robots.AddRange(robot1, robot2);
await _context.SaveChangesAsync();
// Act
var result = await _service.GetUsageInfoAsync(model.Id);
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.RobotCount, Is.EqualTo(2));
Assert.That(result.CanDelete, Is.False);
}
[Test]
public async Task GetUsageInfoAsync_ModelWithoutRobots_ReturnsZeroCount()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
await _context.SaveChangesAsync();
// Act
var result = await _service.GetUsageInfoAsync(model.Id);
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.RobotCount, Is.EqualTo(0));
Assert.That(result.CanDelete, Is.True);
}
}

View File

@@ -0,0 +1,350 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using RobotNet10.FleetManager.Data;
using RobotNet10.FleetManager.Services;
using RobotNet10.FleetManager.Shared.DTOs.Robot;
using RobotNet10.RobotManager.Test.Helpers;
namespace RobotNet10.RobotManager.Test.Services;
/// <summary>
/// Unit tests for RobotService
/// </summary>
[TestFixture]
public class RobotServiceTests
{
private ApplicationDbContext _context = null!;
private RobotService _service = null!;
[SetUp]
public void Setup()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
_context = new ApplicationDbContext(options);
var logger = new LoggerFactory().CreateLogger<RobotService>();
var _logger = new FleetManager.Services.Logger<RobotService>(logger);
_service = new RobotService(_context, _logger);
}
[TearDown]
public void TearDown()
{
_context.Dispose();
}
[Test]
public async Task CreateAsync_ValidData_ReturnsCreatedRobot()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
await _context.SaveChangesAsync();
var request = new CreateRobotRequest
{
RobotId = "ROBOT-001",
Name = "Test Robot",
ModelId = model.Id,
MapId = null
};
// Act
var result = await _service.CreateAsync(request);
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.RobotId, Is.EqualTo(request.RobotId));
Assert.That(result.Name, Is.EqualTo(request.Name));
Assert.That(result.ModelId, Is.EqualTo(request.ModelId));
Assert.That(result.Id, Is.Not.EqualTo(Guid.Empty));
}
[Test]
public async Task CreateAsync_DuplicateRobotId_ThrowsInvalidOperationException()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
var existingRobot = TestHelpers.CreateTestRobot(robotId: "ROBOT-001", modelId: model.Id);
_context.Robots.Add(existingRobot);
await _context.SaveChangesAsync();
var request = new CreateRobotRequest
{
RobotId = "ROBOT-001",
Name = "Another Robot",
ModelId = model.Id
};
// Act & Assert
var ex = Assert.ThrowsAsync<InvalidOperationException>(async () => await _service.CreateAsync(request));
Assert.That(ex!.Message, Does.Contain("already exists"));
}
[Test]
public async Task CreateAsync_NonExistentModelId_ThrowsKeyNotFoundException()
{
// Arrange
var request = new CreateRobotRequest
{
RobotId = "ROBOT-001",
Name = "Test Robot",
ModelId = Guid.NewGuid() // Non-existent model
};
// Act & Assert
Assert.ThrowsAsync<KeyNotFoundException>(async () => await _service.CreateAsync(request));
}
[Test]
public async Task GetAllAsync_ReturnsAllRobots()
{
// Arrange
TestHelpers.SeedTestData(_context);
// Act
var result = await _service.GetAllAsync();
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.EqualTo(3));
}
[Test]
public async Task GetAllAsync_WithModelIdFilter_ReturnsFilteredRobots()
{
// Arrange
TestHelpers.SeedTestData(_context);
var modelId = Guid.Parse("11111111-1111-1111-1111-111111111111");
// Act
var result = await _service.GetAllAsync(modelId, null);
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.EqualTo(2)); // 2 robots use model1
Assert.That(result.All(r => r.ModelId == modelId), Is.True);
}
[Test]
public async Task GetByIdAsync_ExistingId_ReturnsRobot()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
var robot = TestHelpers.CreateTestRobot(modelId: model.Id);
_context.Robots.Add(robot);
await _context.SaveChangesAsync();
// Act
var result = await _service.GetByIdAsync(robot.Id);
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result!.Id, Is.EqualTo(robot.Id));
Assert.That(result.RobotId, Is.EqualTo(robot.RobotId));
}
[Test]
public async Task GetByIdAsync_NonExistentId_ReturnsNull()
{
// Arrange
var nonExistentId = Guid.NewGuid();
// Act
var result = await _service.GetByIdAsync(nonExistentId);
// Assert
Assert.That(result, Is.Null);
}
[Test]
public async Task GetByRobotIdAsync_ExistingRobotId_ReturnsRobot()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
var robot = TestHelpers.CreateTestRobot(robotId: "ROBOT-001", modelId: model.Id);
_context.Robots.Add(robot);
await _context.SaveChangesAsync();
// Act
var result = await _service.GetByRobotIdAsync("ROBOT-001");
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result!.RobotId, Is.EqualTo("ROBOT-001"));
}
[Test]
public async Task GetByRobotIdAsync_NonExistentRobotId_ReturnsNull()
{
// Arrange
// No robots in database
// Act
var result = await _service.GetByRobotIdAsync("NON-EXISTENT");
// Assert
Assert.That(result, Is.Null);
}
[Test]
public async Task UpdateAsync_ValidData_UpdatesRobot()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
var robot = TestHelpers.CreateTestRobot(modelId: model.Id);
_context.Robots.Add(robot);
await _context.SaveChangesAsync();
var request = new UpdateRobotRequest
{
RobotId = "UPDATED-001",
Name = "Updated Robot",
ModelId = model.Id,
MapId = null
};
// Act
var result = await _service.UpdateAsync(robot.Id, request);
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.RobotId, Is.EqualTo(request.RobotId));
Assert.That(result.Name, Is.EqualTo(request.Name));
}
[Test]
public async Task UpdateAsync_NonExistentId_ThrowsKeyNotFoundException()
{
// Arrange
var nonExistentId = Guid.NewGuid();
var request = new UpdateRobotRequest
{
RobotId = "ROBOT-001",
Name = "Test",
ModelId = Guid.NewGuid()
};
// Act & Assert
Assert.ThrowsAsync<KeyNotFoundException>(async () => await _service.UpdateAsync(nonExistentId, request));
}
[Test]
public async Task DeleteAsync_ExistingId_ReturnsTrue()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
var robot = TestHelpers.CreateTestRobot(modelId: model.Id);
_context.Robots.Add(robot);
await _context.SaveChangesAsync();
// Act
var result = await _service.DeleteAsync(robot.Id);
// Assert
Assert.That(result, Is.True);
var deleted = await _context.Robots.FindAsync(robot.Id);
Assert.That(deleted, Is.Null);
}
[Test]
public async Task DeleteAsync_NonExistentId_ReturnsFalse()
{
// Arrange
var nonExistentId = Guid.NewGuid();
// Act
var result = await _service.DeleteAsync(nonExistentId);
// Assert
Assert.That(result, Is.False);
}
[Test]
public async Task GetByModelIdAsync_ReturnsRobotsForModel()
{
// Arrange
TestHelpers.SeedTestData(_context);
var modelId = Guid.Parse("11111111-1111-1111-1111-111111111111");
// Act
var result = await _service.GetByModelIdAsync(modelId);
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.EqualTo(2));
Assert.That(result.All(r => r.ModelId == modelId), Is.True);
}
[Test]
public async Task SearchAsync_WithQuery_ReturnsMatchingRobots()
{
// Arrange
TestHelpers.SeedTestData(_context);
// Act
var result = await _service.SearchAsync("ROBOT-001");
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.EqualTo(1));
Assert.That(result[0].RobotId, Is.EqualTo("ROBOT-001"));
}
[Test]
public async Task SearchAsync_EmptyQuery_ReturnsAllRobots()
{
// Arrange
TestHelpers.SeedTestData(_context);
// Act
var result = await _service.SearchAsync("");
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.EqualTo(3));
}
[Test]
public async Task ExistsAsync_ExistingRobotId_ReturnsTrue()
{
// Arrange
var model = TestHelpers.CreateTestRobotModel();
_context.RobotModels.Add(model);
var robot = TestHelpers.CreateTestRobot(robotId: "ROBOT-001", modelId: model.Id);
_context.Robots.Add(robot);
await _context.SaveChangesAsync();
// Act
var result = await _service.ExistsAsync("ROBOT-001");
// Assert
Assert.That(result, Is.True);
}
[Test]
public async Task ExistsAsync_NonExistentRobotId_ReturnsFalse()
{
// Arrange
// No robots in database
// Act
var result = await _service.ExistsAsync("NON-EXISTENT");
// Assert
Assert.That(result, Is.False);
}
}