You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
445 lines
17 KiB
C#
445 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Xunit;
|
|
using Moq;
|
|
using Microsoft.AspNetCore.SignalR;
|
|
using Haoliang.Core.Services;
|
|
using Haoliang.Models.Models.Device;
|
|
using Haoliang.Models.Models.Production;
|
|
using Haoliang.Models.Models.System;
|
|
|
|
namespace Haoliang.Tests.Services
|
|
{
|
|
public class RealTimeServiceTests
|
|
{
|
|
private readonly Mock<IHubContext<RealTimeHub>> _mockHubContext;
|
|
private readonly Mock<IDeviceCollectionService> _mockDeviceCollectionService;
|
|
private readonly Mock<IProductionService> _mockProductionService;
|
|
private readonly Mock<IAlarmService> _mockAlarmService;
|
|
private readonly Mock<ICacheService> _mockCacheService;
|
|
private readonly RealTimeService _realTimeService;
|
|
|
|
public RealTimeServiceTests()
|
|
{
|
|
_mockHubContext = new Mock<IHubContext<RealTimeHub>>();
|
|
_mockDeviceCollectionService = new Mock<IDeviceCollectionService>();
|
|
_mockProductionService = new Mock<IProductionService>();
|
|
_mockAlarmService = new Mock<IAlarmService>();
|
|
_mockCacheService = new Mock<ICacheService>();
|
|
|
|
_realTimeService = new RealTimeService(
|
|
_mockHubContext.Object,
|
|
_mockDeviceCollectionService.Object,
|
|
_mockProductionService.Object,
|
|
_mockAlarmService.Object,
|
|
_mockCacheService.Object
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ConnectClientAsync_ValidConnection_ConnectsClient()
|
|
{
|
|
// Arrange
|
|
var connectionId = "test-connection-id";
|
|
var userId = "test-user-id";
|
|
var clientType = "web";
|
|
|
|
// Act
|
|
await _realTimeService.ConnectClientAsync(connectionId, userId, clientType);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Clients.Client(connectionId)
|
|
.SendAsync("ClientConnected",
|
|
It.Is<object>(o =>
|
|
dynamic obj = o &&
|
|
obj.GetType().GetProperty("ClientId").GetValue(obj) == connectionId &&
|
|
obj.GetType().GetProperty("UserId").GetValue(obj) == userId &&
|
|
obj.GetType().GetProperty("ClientType").GetValue(obj) == clientType),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DisconnectClientAsync_ValidConnection_DisconnectsClient()
|
|
{
|
|
// Arrange
|
|
var connectionId = "test-connection-id";
|
|
|
|
// Act
|
|
await _realTimeService.DisconnectClientAsync(connectionId);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Clients.AllExcept(connectionId)
|
|
.SendAsync("ClientDisconnected",
|
|
It.IsAny<object>(),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task JoinDeviceGroupAsync_ValidConnection_JoinsGroup()
|
|
{
|
|
// Arrange
|
|
var connectionId = "test-connection-id";
|
|
var deviceId = 1;
|
|
var deviceStatus = new DeviceCurrentStatus {
|
|
DeviceId = deviceId,
|
|
Status = DeviceStatus.Online,
|
|
CurrentProgram = "Test Program"
|
|
};
|
|
|
|
_mockDeviceCollectionService.Setup(service => service.GetDeviceCurrentStatusAsync(deviceId))
|
|
.ReturnsAsync(deviceStatus);
|
|
|
|
// Act
|
|
await _realTimeService.JoinDeviceGroupAsync(connectionId, deviceId);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Groups.AddToGroupAsync(connectionId, $"device_{deviceId}", It.IsAny<CancellationToken>()), Times.Once);
|
|
_mockHubContext.Verify(hub => hub.Clients.Client(connectionId)
|
|
.SendAsync("DeviceStatusUpdated",
|
|
It.Is<object>(o =>
|
|
dynamic obj = o &&
|
|
obj.GetType().GetProperty("DeviceId").GetValue(obj) == deviceId),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LeaveDeviceGroupAsync_ValidConnection_LeavesGroup()
|
|
{
|
|
// Arrange
|
|
var connectionId = "test-connection-id";
|
|
var deviceId = 1;
|
|
|
|
// Act
|
|
await _realTimeService.LeaveDeviceGroupAsync(connectionId, deviceId);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Groups.RemoveFromGroupAsync(connectionId, $"device_{deviceId}", It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task JoinDashboardGroupAsync_ValidConnection_JoinsGroup()
|
|
{
|
|
// Arrange
|
|
var connectionId = "test-connection-id";
|
|
var dashboardId = "dashboard-1";
|
|
var dashboardUpdate = new DashboardUpdate {
|
|
Timestamp = DateTime.UtcNow,
|
|
TotalDevices = 10,
|
|
ActiveDevices = 8,
|
|
TotalProductionToday = 1000
|
|
};
|
|
|
|
_mockCacheService.Setup(cache => cache.GetOrSetDashboardSummaryAsync(It.IsAny<DateTime>(), It.IsAny<Func<Task<DashboardSummary>>>()))
|
|
.ReturnsAsync(new DashboardSummary {
|
|
TotalDevices = 10,
|
|
ActiveDevices = 8,
|
|
TotalProductionToday = 1000
|
|
});
|
|
|
|
// Act
|
|
await _realTimeService.JoinDashboardGroupAsync(connectionId, dashboardId);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Groups.AddToGroupAsync(connectionId, $"dashboard_{dashboardId}", It.IsAny<CancellationToken>()), Times.Once);
|
|
_mockHubContext.Verify(hub => hub.Clients.Client(connectionId)
|
|
.SendAsync("DashboardUpdated",
|
|
It.Is<object>(o =>
|
|
dynamic obj = o &&
|
|
obj.GetType().GetProperty("DashboardId").GetValue(obj)?.ToString() == dashboardId),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LeaveDashboardGroupAsync_ValidConnection_LeavesGroup()
|
|
{
|
|
// Arrange
|
|
var connectionId = "test-connection-id";
|
|
var dashboardId = "dashboard-1";
|
|
|
|
// Act
|
|
await _realTimeService.LeaveDashboardGroupAsync(connectionId, dashboardId);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Groups.RemoveFromGroupAsync(connectionId, $"dashboard_{dashboardId}", It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BroadcastDeviceStatusAsync_ValidStatus_BroadcastsToGroups()
|
|
{
|
|
// Arrange
|
|
var statusUpdate = new DeviceStatusUpdate
|
|
{
|
|
DeviceId = 1,
|
|
DeviceName = "Test Device",
|
|
Status = DeviceStatus.Running,
|
|
CurrentProgram = "Test Program",
|
|
Runtime = TimeSpan.FromHours(1),
|
|
Timestamp = DateTime.UtcNow
|
|
};
|
|
|
|
// Act
|
|
await _realTimeService.BroadcastDeviceStatusAsync(statusUpdate);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Clients.Group($"device_1")
|
|
.SendAsync("DeviceStatusUpdated",
|
|
It.Is<DeviceStatusUpdate>(s => s.DeviceId == 1 && s.Status == DeviceStatus.Running),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
|
|
_mockHubContext.Verify(hub => hub.Clients.Group("dashboard")
|
|
.SendAsync("DeviceStatusUpdated",
|
|
It.Is<DeviceStatusUpdate>(s => s.DeviceId == 1 && s.Status == DeviceStatus.Running),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BroadcastProductionUpdateAsync_ValidUpdate_BroadcastsToGroups()
|
|
{
|
|
// Arrange
|
|
var productionUpdate = new ProductionUpdate
|
|
{
|
|
DeviceId = 1,
|
|
DeviceName = "Test Device",
|
|
Quantity = 100,
|
|
Timestamp = DateTime.UtcNow
|
|
};
|
|
|
|
// Act
|
|
await _realTimeService.BroadcastProductionUpdateAsync(productionUpdate);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Clients.Group($"device_1")
|
|
.SendAsync("ProductionUpdated",
|
|
It.Is<ProductionUpdate>(p => p.DeviceId == 1 && p.Quantity == 100),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
|
|
_mockHubContext.Verify(hub => hub.Clients.Group("dashboard")
|
|
.SendAsync("ProductionUpdated",
|
|
It.Is<ProductionUpdate>(p => p.DeviceId == 1 && p.Quantity == 100),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BroadcastAlertAsync_ValidAlert_BroadcastsToRelevantGroups()
|
|
{
|
|
// Arrange
|
|
var alertUpdate = new AlertUpdate
|
|
{
|
|
DeviceId = 1,
|
|
DeviceName = "Test Device",
|
|
AlertType = "DeviceError",
|
|
Message = "Device error occurred",
|
|
Timestamp = DateTime.UtcNow,
|
|
IsResolved = false
|
|
};
|
|
|
|
// Act
|
|
await _realTimeService.BroadcastAlertAsync(alertUpdate);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Clients.Group("dashboard")
|
|
.SendAsync("AlertUpdated",
|
|
It.Is<AlertUpdate>(a => a.DeviceId == 1 && a.AlertType == "DeviceError"),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
|
|
_mockHubContext.Verify(hub => hub.Clients.Group("alerts")
|
|
.SendAsync("AlertUpdated",
|
|
It.Is<AlertUpdate>(a => a.DeviceId == 1 && a.AlertType == "DeviceError"),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
|
|
_mockHubContext.Verify(hub => hub.Clients.Group($"device_1")
|
|
.SendAsync("AlertUpdated",
|
|
It.Is<AlertUpdate>(a => a.DeviceId == 1 && a.AlertType == "DeviceError"),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SendSystemNotificationAsync_ValidNotification_SendsToNotificationGroup()
|
|
{
|
|
// Arrange
|
|
var notification = new SystemNotification
|
|
{
|
|
NotificationType = "Info",
|
|
Title = "System Update",
|
|
Message = "System maintenance scheduled",
|
|
Timestamp = DateTime.UtcNow
|
|
};
|
|
|
|
// Act
|
|
await _realTimeService.SendSystemNotificationAsync(notification);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Clients.Group("notifications")
|
|
.SendAsync("SystemNotification",
|
|
It.Is<SystemNotification>(n =>
|
|
n.NotificationType == "Info" &&
|
|
n.Title == "System Update"),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SendDashboardUpdateAsync_ValidUpdate_SendsToDashboardGroup()
|
|
{
|
|
// Arrange
|
|
var dashboardUpdate = new DashboardUpdate
|
|
{
|
|
Timestamp = DateTime.UtcNow,
|
|
TotalDevices = 10,
|
|
ActiveDevices = 8,
|
|
TotalProductionToday = 1000
|
|
};
|
|
|
|
// Act
|
|
await _realTimeService.SendDashboardUpdateAsync(dashboardUpdate);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Clients.Group("dashboard")
|
|
.SendAsync("DashboardUpdated",
|
|
It.Is<DashboardUpdate>(d =>
|
|
d.TotalDevices == 10 &&
|
|
d.ActiveDevices == 8),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SendCommandToClientAsync_ValidCommand_SendsToClient()
|
|
{
|
|
// Arrange
|
|
var connectionId = "test-connection-id";
|
|
var command = new RealTimeCommand
|
|
{
|
|
Command = "RefreshData",
|
|
Parameters = new { Interval = 5000 },
|
|
Timestamp = DateTime.UtcNow
|
|
};
|
|
|
|
// Act
|
|
await _realTimeService.SendCommandToClientAsync(connectionId, command);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Clients.Client(connectionId)
|
|
.SendAsync("Command",
|
|
It.Is<RealTimeCommand>(c =>
|
|
c.Command == "RefreshData" &&
|
|
c.Parameters.ToString().Contains("Interval")),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BroadcastCommandAsync_ValidCommand_BroadcastsToAllClients()
|
|
{
|
|
// Arrange
|
|
var command = new RealTimeCommand
|
|
{
|
|
Command = "SystemShutdown",
|
|
Parameters = new { DelayMinutes = 5 },
|
|
Timestamp = DateTime.UtcNow
|
|
};
|
|
|
|
// Act
|
|
await _realTimeService.BroadcastCommandAsync(command);
|
|
|
|
// Assert
|
|
_mockHubContext.Verify(hub => hub.Clients.All
|
|
.SendAsync("Command",
|
|
It.Is<RealTimeCommand>(c =>
|
|
c.Command == "SystemShutdown"),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetConnectedClientsCountAsync_ValidClients_ReturnsCount()
|
|
{
|
|
// Arrange
|
|
// This test would need to mock the internal client tracking
|
|
// For now, we'll verify the method exists and doesn't throw
|
|
var result = await _realTimeService.GetConnectedClientsCountAsync();
|
|
|
|
// Assert
|
|
Assert.True(result >= 0); // Should return a non-negative number
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetConnectedClientsByTypeAsync_ValidType_ReturnsClients()
|
|
{
|
|
// Arrange
|
|
var clientType = "web";
|
|
|
|
// This test would need to mock the internal client tracking
|
|
// For now, we'll verify the method exists and doesn't throw
|
|
var result = await _realTimeService.GetConnectedClientsByTypeAsync(clientType);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetDeviceMonitoringStatusAsync_ValidDevice_ReturnsStatus()
|
|
{
|
|
// Arrange
|
|
var deviceId = 1;
|
|
var streamingInfo = new DeviceStreamingInfo
|
|
{
|
|
DeviceId = deviceId,
|
|
IntervalMs = 1000,
|
|
StartedAt = DateTime.UtcNow.AddMinutes(-5),
|
|
LastUpdate = DateTime.UtcNow.AddMinutes(-1),
|
|
IsRunning = true
|
|
};
|
|
|
|
// This test would need to mock the internal device streaming tracking
|
|
// For now, we'll verify the method exists and doesn't throw
|
|
var result = await _realTimeService.GetDeviceMonitoringStatusAsync(deviceId);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Equal(deviceId, result.DeviceId);
|
|
Assert.True(result.IsStreaming);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task StartDeviceStreamingAsync_ValidDevice_StartsStreaming()
|
|
{
|
|
// Arrange
|
|
var deviceId = 1;
|
|
var intervalMs = 1000;
|
|
|
|
// This test would need to mock the internal device streaming tracking
|
|
// and verify that streaming starts
|
|
// For now, we'll verify the method exists and doesn't throw
|
|
await _realTimeService.StartDeviceStreamingAsync(deviceId, intervalMs);
|
|
|
|
// Assert - would need to verify streaming started
|
|
Assert.True(true); // Placeholder assertion
|
|
}
|
|
|
|
[Fact]
|
|
public async Task StopDeviceStreamingAsync_ValidDevice_StopsStreaming()
|
|
{
|
|
// Arrange
|
|
var deviceId = 1;
|
|
|
|
// This test would need to mock the internal device streaming tracking
|
|
// and verify that streaming stops
|
|
// For now, we'll verify the method exists and doesn't throw
|
|
await _realTimeService.StopDeviceStreamingAsync(deviceId);
|
|
|
|
// Assert - would need to verify streaming stopped
|
|
Assert.True(true); // Placeholder assertion
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetActiveStreamingDevicesAsync_ReturnsStreamingDevices()
|
|
{
|
|
// This test would need to mock the internal device streaming tracking
|
|
// For now, we'll verify the method exists and doesn't throw
|
|
var result = await _realTimeService.GetActiveStreamingDevicesAsync();
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.IsType<List<int>>(result);
|
|
}
|
|
}
|
|
} |