From 0b8123ad1e7dfe5b0e495f69e6a43a46b7afaee6 Mon Sep 17 00:00:00 2001 From: Jared Goodwin Date: Thu, 22 Jun 2023 13:40:05 -0700 Subject: [PATCH] Add unit tests. --- Server/API/AgentUpdateController.cs | 4 +- Server/API/RemoteControlController.cs | 4 +- Server/API/ScriptingController.cs | 4 +- Server/Components/Devices/DeviceCard.razor.cs | 2 +- Server/Components/Scripts/RunScript.razor.cs | 2 +- Server/Hubs/AgentHub.cs | 4 +- Server/Hubs/CircuitConnection.cs | 41 +- Server/Pages/ServerConfig.razor.cs | 2 +- Server/Program.cs | 2 +- Server/Properties/AssemblyInfo.cs | 3 + ...ectionCache.cs => AgentHubSessionCache.cs} | 4 +- Server/Services/DataService.cs | 28 +- Server/Services/ScriptScheduleDispatcher.cs | 4 +- Tests/Server.Tests/AgentHubTests.cs | 19 +- Tests/Server.Tests/CircuitConnectionTests.cs | 442 ++++++++++++++++++ Tests/Server.Tests/DataServiceTests.cs | 155 +++--- Tests/Server.Tests/Mocks/HubContextFixture.cs | 46 ++ .../ScriptScheduleDispatcherTests.cs | 25 +- Tests/Server.Tests/TestData.cs | 139 ++++-- 19 files changed, 761 insertions(+), 169 deletions(-) create mode 100644 Server/Properties/AssemblyInfo.cs rename Server/Services/{ServiceHubConnectionCache.cs => AgentHubSessionCache.cs} (95%) create mode 100644 Tests/Server.Tests/CircuitConnectionTests.cs create mode 100644 Tests/Server.Tests/Mocks/HubContextFixture.cs diff --git a/Server/API/AgentUpdateController.cs b/Server/API/AgentUpdateController.cs index 6aeb7523..e5e3db29 100644 --- a/Server/API/AgentUpdateController.cs +++ b/Server/API/AgentUpdateController.cs @@ -30,11 +30,11 @@ namespace Remotely.Server.API private readonly ILogger _logger; private readonly IApplicationConfig _appConfig; private readonly IWebHostEnvironment _hostEnv; - private readonly IServiceHubSessionCache _serviceSessionCache; + private readonly IAgentHubSessionCache _serviceSessionCache; public AgentUpdateController(IWebHostEnvironment hostingEnv, IApplicationConfig appConfig, - IServiceHubSessionCache serviceSessionCache, + IAgentHubSessionCache serviceSessionCache, IHubContext agentHubContext, ILogger logger) { diff --git a/Server/API/RemoteControlController.cs b/Server/API/RemoteControlController.cs index 69e0e901..b33fbde9 100644 --- a/Server/API/RemoteControlController.cs +++ b/Server/API/RemoteControlController.cs @@ -28,7 +28,7 @@ namespace Remotely.Server.API { private readonly IHubContext _serviceHub; private readonly IDesktopHubSessionCache _desktopSessionCache; - private readonly IServiceHubSessionCache _serviceSessionCache; + private readonly IAgentHubSessionCache _serviceSessionCache; private readonly IApplicationConfig _appConfig; private readonly IOtpProvider _otpProvider; private readonly IHubEventHandler _hubEvents; @@ -41,7 +41,7 @@ namespace Remotely.Server.API IDataService dataService, IDesktopHubSessionCache desktopSessionCache, IHubContext serviceHub, - IServiceHubSessionCache serviceSessionCache, + IAgentHubSessionCache serviceSessionCache, IOtpProvider otpProvider, IHubEventHandler hubEvents, IApplicationConfig appConfig, diff --git a/Server/API/ScriptingController.cs b/Server/API/ScriptingController.cs index c9958ea1..f9564295 100644 --- a/Server/API/ScriptingController.cs +++ b/Server/API/ScriptingController.cs @@ -25,14 +25,14 @@ namespace Remotely.Server.API private readonly IHubContext _agentHubContext; private readonly IDataService _dataService; - private readonly IServiceHubSessionCache _serviceSessionCache; + private readonly IAgentHubSessionCache _serviceSessionCache; private readonly IExpiringTokenService _expiringTokenService; private readonly UserManager _userManager; public ScriptingController(UserManager userManager, IDataService dataService, - IServiceHubSessionCache serviceSessionCache, + IAgentHubSessionCache serviceSessionCache, IExpiringTokenService expiringTokenService, IHubContext agentHub) { diff --git a/Server/Components/Devices/DeviceCard.razor.cs b/Server/Components/Devices/DeviceCard.razor.cs index 6bbb7fef..fb6dacb7 100644 --- a/Server/Components/Devices/DeviceCard.razor.cs +++ b/Server/Components/Devices/DeviceCard.razor.cs @@ -59,7 +59,7 @@ namespace Remotely.Server.Components.Devices private IModalService ModalService { get; set; } [Inject] - private IServiceHubSessionCache ServiceSessionCache { get; init; } + private IAgentHubSessionCache ServiceSessionCache { get; init; } [Inject] private IToastService ToastService { get; set; } diff --git a/Server/Components/Scripts/RunScript.razor.cs b/Server/Components/Scripts/RunScript.razor.cs index ccd49c25..2f78b3fa 100644 --- a/Server/Components/Scripts/RunScript.razor.cs +++ b/Server/Components/Scripts/RunScript.razor.cs @@ -43,7 +43,7 @@ namespace Remotely.Server.Components.Scripts private IToastService ToastService { get; set; } [Inject] - private IServiceHubSessionCache ServiceSessionCache { get; init; } + private IAgentHubSessionCache ServiceSessionCache { get; init; } [Inject] private ICircuitConnection CircuitConnection { get; set; } diff --git a/Server/Hubs/AgentHub.cs b/Server/Hubs/AgentHub.cs index 0eb7fb8c..84e57a40 100644 --- a/Server/Hubs/AgentHub.cs +++ b/Server/Hubs/AgentHub.cs @@ -24,12 +24,12 @@ namespace Remotely.Server.Hubs private readonly IDataService _dataService; private readonly IExpiringTokenService _expiringTokenService; private readonly ILogger _logger; - private readonly IServiceHubSessionCache _serviceSessionCache; + private readonly IAgentHubSessionCache _serviceSessionCache; private readonly IHubContext _viewerHubContext; public AgentHub(IDataService dataService, IApplicationConfig appConfig, - IServiceHubSessionCache serviceSessionCache, + IAgentHubSessionCache serviceSessionCache, IHubContext viewerHubContext, ICircuitManager circuitManager, IExpiringTokenService expiringTokenService, diff --git a/Server/Hubs/CircuitConnection.cs b/Server/Hubs/CircuitConnection.cs index 73155ac0..d8fc3183 100644 --- a/Server/Hubs/CircuitConnection.cs +++ b/Server/Hubs/CircuitConnection.cs @@ -83,7 +83,7 @@ namespace Remotely.Server.Hubs private readonly ConcurrentQueue _eventQueue = new(); private readonly IExpiringTokenService _expiringTokenService; private readonly ILogger _logger; - private readonly IServiceHubSessionCache _serviceSessionCache; + private readonly IAgentHubSessionCache _agentSessionCache; private readonly IToastService _toastService; public CircuitConnection( IAuthService authService, @@ -95,7 +95,7 @@ namespace Remotely.Server.Hubs IToastService toastService, IExpiringTokenService expiringTokenService, IDesktopHubSessionCache desktopSessionCache, - IServiceHubSessionCache serviceSessionCache, + IAgentHubSessionCache agentSessionCache, ILogger logger) { _dataService = dataService; @@ -107,7 +107,7 @@ namespace Remotely.Server.Hubs _toastService = toastService; _expiringTokenService = expiringTokenService; _desktopSessionCache = desktopSessionCache; - _serviceSessionCache = serviceSessionCache; + _agentSessionCache = agentSessionCache; _logger = logger; } @@ -115,7 +115,7 @@ namespace Remotely.Server.Hubs public event EventHandler MessageReceived; public string ConnectionId { get; set; } - public RemotelyUser User { get; set; } + public RemotelyUser User { get; internal set; } public Task DeleteRemoteLogs(string deviceId) @@ -223,7 +223,7 @@ namespace Remotely.Server.Hubs public async Task> RemoteControl(string deviceId, bool viewOnly) { - if (!_serviceSessionCache.TryGetByDeviceId(deviceId, out var targetDevice)) + if (!_agentSessionCache.TryGetByDeviceId(deviceId, out var targetDevice)) { MessageReceived?.Invoke(this, new CircuitEvent(CircuitEventName.DisplayMessage, "The selected device is not online.", @@ -257,7 +257,7 @@ namespace Remotely.Server.Hubs return Result.Fail("Max number of concurrent sessions reached."); } - if (!_serviceSessionCache.TryGetConnectionId(targetDevice.ID, out var serviceConnectionId)) + if (!_agentSessionCache.TryGetConnectionId(targetDevice.ID, out var serviceConnectionId)) { MessageReceived?.Invoke(this, new CircuitEvent(CircuitEventName.DisplayMessage, "Service connection not found.", @@ -317,7 +317,7 @@ namespace Remotely.Server.Hubs var authToken = _expiringTokenService.GetToken(Time.Now.AddMinutes(AppConstants.ScriptRunExpirationMinutes)); - var connectionIds = _serviceSessionCache.GetConnectionIdsByDeviceIds(deviceIds).ToArray(); + var connectionIds = _agentSessionCache.GetConnectionIdsByDeviceIds(deviceIds).ToArray(); if (connectionIds.Any()) { @@ -333,8 +333,8 @@ namespace Remotely.Server.Hubs return Task.CompletedTask; } - if (!_serviceSessionCache.TryGetByDeviceId(deviceId, out var device) || - !_serviceSessionCache.TryGetConnectionId(deviceId, out var connectionId)) + if (!_agentSessionCache.TryGetByDeviceId(deviceId, out var device) || + !_agentSessionCache.TryGetConnectionId(deviceId, out var connectionId)) { _toastService.ShowToast("Device not found."); return Task.CompletedTask; @@ -359,7 +359,7 @@ namespace Remotely.Server.Hubs public async Task TransferFileFromBrowserToAgent(string deviceId, string transferId, string[] fileIds) { - if (!_serviceSessionCache.TryGetConnectionId(deviceId, out var connectionId)) + if (!_agentSessionCache.TryGetConnectionId(deviceId, out var connectionId)) { return false; } @@ -454,11 +454,11 @@ namespace Remotely.Server.Hubs return Result.Fail("Unauthorized.") ; } - var availableDevices = _serviceSessionCache + var availableDevices = _agentSessionCache .GetAllDevices() .Where(x => x.OrganizationID == User.OrganizationID && - (x.DeviceGroup == device.DeviceGroup || x.PublicIP == device.PublicIP)) + (x.DeviceGroupID == device.DeviceGroupID || x.PublicIP == device.PublicIP)) .ToArray(); await SendWakeCommand(device, availableDevices); @@ -480,22 +480,21 @@ namespace Remotely.Server.Hubs var filteredIds = _dataService.FilterDeviceIDsByUserPermission(deviceIds, User); var filteredDevices = devices.Where(x => filteredIds.Contains(x.ID)).ToArray(); - var availableDevices = _serviceSessionCache + var availableDevices = _agentSessionCache .GetAllDevices() .Where(x => x.OrganizationID == User.OrganizationID); - var devicesById = new ConcurrentDictionary(); var devicesByGroupId = new ConcurrentDictionary>(); var devicesByPublicIp = new ConcurrentDictionary>(); foreach (var device in availableDevices) { - devicesById.AddOrUpdate(device.ID, device, (k, v) => device); - if (!string.IsNullOrWhiteSpace(device.DeviceGroupID)) { var group = devicesByGroupId.GetOrAdd(device.DeviceGroupID, key => new()); group.Add(device); + // We only need the device in one group. + break; } if (!string.IsNullOrWhiteSpace(device.PublicIP)) @@ -536,9 +535,9 @@ namespace Remotely.Server.Hubs return (false, string.Empty); } - if (!_serviceSessionCache.TryGetByDeviceId(deviceId, out var device) || + if (!_agentSessionCache.TryGetByDeviceId(deviceId, out var device) || !_dataService.DoesUserHaveAccessToDevice(device.ID, User) || - !_serviceSessionCache.TryGetConnectionId(device.ID, out var connectionId)) + !_agentSessionCache.TryGetConnectionId(device.ID, out var connectionId)) { return (false, string.Empty); } @@ -552,7 +551,7 @@ namespace Remotely.Server.Hubs foreach (var deviceId in deviceIds) { - if (!_serviceSessionCache.TryGetByDeviceId(deviceId, out var device)) + if (!_agentSessionCache.TryGetByDeviceId(deviceId, out var device)) { continue; } @@ -562,7 +561,7 @@ namespace Remotely.Server.Hubs continue; } - if (_serviceSessionCache.TryGetConnectionId(device.ID, out var connectionId)) + if (_agentSessionCache.TryGetConnectionId(device.ID, out var connectionId)) { yield return connectionId; } @@ -593,7 +592,7 @@ namespace Remotely.Server.Hubs { foreach (var mac in deviceToWake.MacAddresses ?? Array.Empty()) { - if (_serviceSessionCache.TryGetConnectionId(peerDevice.ID, out var connectionId)) + if (_agentSessionCache.TryGetConnectionId(peerDevice.ID, out var connectionId)) { _logger.LogInformation( "Sending wake command for device {deviceName} ({deviceId}) to " + diff --git a/Server/Pages/ServerConfig.razor.cs b/Server/Pages/ServerConfig.razor.cs index 88d7a647..a78ce72e 100644 --- a/Server/Pages/ServerConfig.razor.cs +++ b/Server/Pages/ServerConfig.razor.cs @@ -162,7 +162,7 @@ namespace Remotely.Server.Pages private ILogger Logger { get; set; } [Inject] - private IServiceHubSessionCache ServiceSessionCache { get; init; } + private IAgentHubSessionCache ServiceSessionCache { get; init; } private AppSettingsModel Input { get; } = new(); diff --git a/Server/Program.cs b/Server/Program.cs index 37fb6418..7e68bb5f 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -228,7 +228,7 @@ services.AddRemoteControlServer(config => config.AddViewerPageDataProvider(); }); -services.AddSingleton(); +services.AddSingleton(); var app = builder.Build(); var appConfig = app.Services.GetRequiredService(); diff --git a/Server/Properties/AssemblyInfo.cs b/Server/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..d6663dea --- /dev/null +++ b/Server/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Remotely.Server.Tests")] \ No newline at end of file diff --git a/Server/Services/ServiceHubConnectionCache.cs b/Server/Services/AgentHubSessionCache.cs similarity index 95% rename from Server/Services/ServiceHubConnectionCache.cs rename to Server/Services/AgentHubSessionCache.cs index d610e381..127a0c2a 100644 --- a/Server/Services/ServiceHubConnectionCache.cs +++ b/Server/Services/AgentHubSessionCache.cs @@ -6,7 +6,7 @@ using System.Linq; namespace Remotely.Server.Services { - public interface IServiceHubSessionCache + public interface IAgentHubSessionCache { void AddOrUpdateByConnectionId(string connectionId, Device device); ICollection GetAllDevices(); @@ -16,7 +16,7 @@ namespace Remotely.Server.Services bool TryRemoveByConnectionId(string connectionId, out Device device); } - public class ServiceHubSessionCache : IServiceHubSessionCache + public class AgentHubSessionCache : IAgentHubSessionCache { private readonly ConcurrentDictionary _connectionIdToDeviceLookup = new(); diff --git a/Server/Services/DataService.cs b/Server/Services/DataService.cs index ec434ecb..721b900e 100644 --- a/Server/Services/DataService.cs +++ b/Server/Services/DataService.cs @@ -124,7 +124,11 @@ namespace Remotely.Server.Services int GetDeviceCount(RemotelyUser user); - Task GetDeviceGroup(string deviceGroupID); + Task GetDeviceGroup( + string deviceGroupID, + bool includeDevices = false, + bool includeUsers = false); + DeviceGroup[] GetDeviceGroups(string username); DeviceGroup[] GetDeviceGroupsForOrganization(string organizationId); @@ -348,6 +352,7 @@ namespace Remotely.Server.Services resultDevice.TotalStorage = device.TotalStorage; resultDevice.AgentVersion = device.AgentVersion; resultDevice.MacAddresses = device.MacAddresses ?? Array.Empty(); + resultDevice.DeviceGroupID = device.DeviceGroupID; resultDevice.LastOnline = DateTimeOffset.Now; } else @@ -922,7 +927,6 @@ namespace Remotely.Server.Services device.ID == deviceID && ( remotelyUser.IsAdministrator || - string.IsNullOrWhiteSpace(device.DeviceGroupID) || device.DeviceGroup.Users.Any(user => user.Id == remotelyUser.Id ))); } @@ -948,7 +952,6 @@ namespace Remotely.Server.Services deviceIDs.Contains(device.ID) && ( remotelyUser.IsAdministrator || - string.IsNullOrWhiteSpace(device.DeviceGroupID) || device.DeviceGroup.Users.Any(user => user.Id == remotelyUser.Id ))) .Select(x => x.ID) @@ -1168,10 +1171,25 @@ namespace Remotely.Server.Services )); } - public async Task GetDeviceGroup(string deviceGroupID) + public async Task GetDeviceGroup( + string deviceGroupID, + bool includeDevices = false, + bool includeUsers = false) { using var dbContext = _appDbFactory.GetContext(); - return await dbContext.DeviceGroups.FindAsync(deviceGroupID); + + var query = dbContext.DeviceGroups.AsQueryable(); + + if (includeDevices) + { + query = query.Include(x => x.Devices); + } + if (includeUsers) + { + query = query.Include(x => x.Users); + } + + return await query.FirstOrDefaultAsync(x => x.ID == deviceGroupID); } public DeviceGroup[] GetDeviceGroups(string username) diff --git a/Server/Services/ScriptScheduleDispatcher.cs b/Server/Services/ScriptScheduleDispatcher.cs index 7705820d..d2773029 100644 --- a/Server/Services/ScriptScheduleDispatcher.cs +++ b/Server/Services/ScriptScheduleDispatcher.cs @@ -19,12 +19,12 @@ namespace Remotely.Server.Services public class ScriptScheduleDispatcher : IScriptScheduleDispatcher { private readonly IDataService _dataService; - private readonly IServiceHubSessionCache _serviceSessionCache; + private readonly IAgentHubSessionCache _serviceSessionCache; private readonly ICircuitConnection _circuitConnection; private readonly ILogger _logger; public ScriptScheduleDispatcher(IDataService dataService, - IServiceHubSessionCache serviceSessionCache, + IAgentHubSessionCache serviceSessionCache, ICircuitConnection circuitConnection, ILogger logger) { diff --git a/Tests/Server.Tests/AgentHubTests.cs b/Tests/Server.Tests/AgentHubTests.cs index d096fbb4..278d8af4 100644 --- a/Tests/Server.Tests/AgentHubTests.cs +++ b/Tests/Server.Tests/AgentHubTests.cs @@ -34,14 +34,14 @@ namespace Remotely.Tests var circuitManager = new Mock(); var circuitConnection = new Mock(); circuitManager.Setup(x => x.Connections).Returns(new[] { circuitConnection.Object }); - circuitConnection.Setup(x => x.User).Returns(_testData.Admin1); + circuitConnection.Setup(x => x.User).Returns(_testData.Org1Admin1); var appConfig = new Mock(); var viewerHub = new Mock>(); var expiringTokenService = new Mock(); - var serviceSessionCache = new Mock(); + var serviceSessionCache = new Mock(); var logger = new Mock>(); - appConfig.Setup(x => x.BannedDevices).Returns(new string[] { _testData.Device1.DeviceName }); + appConfig.Setup(x => x.BannedDevices).Returns(new string[] { _testData.Org1Device1.DeviceName }); var hub = new AgentHub( DataService, @@ -57,7 +57,7 @@ namespace Remotely.Tests hubClients.Setup(x => x.Caller).Returns(caller.Object); hub.Clients = hubClients.Object; - Assert.IsFalse(await hub.DeviceCameOnline(_testData.Device1)); + Assert.IsFalse(await hub.DeviceCameOnline(_testData.Org1Device1)); hubClients.Verify(x => x.Caller, Times.Once); caller.Verify(x => x.SendCoreAsync("UninstallAgent", It.IsAny(), It.IsAny()), Times.Once); } @@ -71,14 +71,14 @@ namespace Remotely.Tests var circuitManager = new Mock(); var circuitConnection = new Mock(); circuitManager.Setup(x => x.Connections).Returns(new[] { circuitConnection.Object }); - circuitConnection.Setup(x => x.User).Returns(_testData.Admin1); + circuitConnection.Setup(x => x.User).Returns(_testData.Org1Admin1); var appConfig = new Mock(); var viewerHub = new Mock>(); var expiringTokenService = new Mock(); - var serviceSessionCache = new Mock(); + var serviceSessionCache = new Mock(); var logger = new Mock>(); - appConfig.Setup(x => x.BannedDevices).Returns(new string[] { _testData.Device1.ID }); + appConfig.Setup(x => x.BannedDevices).Returns(new string[] { _testData.Org1Device1.ID }); var hub = new AgentHub( DataService, @@ -94,7 +94,7 @@ namespace Remotely.Tests hubClients.Setup(x => x.Caller).Returns(caller.Object); hub.Clients = hubClients.Object; - Assert.IsFalse(await hub.DeviceCameOnline(_testData.Device1)); + Assert.IsFalse(await hub.DeviceCameOnline(_testData.Org1Device1)); hubClients.Verify(x => x.Caller, Times.Once); caller.Verify(x => x.SendCoreAsync("UninstallAgent", It.IsAny(), It.IsAny()), Times.Once); } @@ -106,9 +106,10 @@ namespace Remotely.Tests } [TestInitialize] - public void TestInit() + public async Task TestInit() { _testData = new TestData(); + await _testData.Init(); DataService = IoCActivator.ServiceProvider.GetRequiredService(); } diff --git a/Tests/Server.Tests/CircuitConnectionTests.cs b/Tests/Server.Tests/CircuitConnectionTests.cs new file mode 100644 index 00000000..21007cfd --- /dev/null +++ b/Tests/Server.Tests/CircuitConnectionTests.cs @@ -0,0 +1,442 @@ +#nullable enable +using Castle.Core.Logging; +using Immense.RemoteControl.Server.Services; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Remotely.Server.Hubs; +using Remotely.Server.Services; +using Remotely.Server.Tests.Mocks; +using Remotely.Shared.Models; +using Remotely.Tests; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Remotely.Server.Tests +{ + [TestClass] + public class CircuitConnectionTests + { +#nullable disable + private TestData _testData; + private IDataService _dataService; + private Mock _authService; + private Mock _clientAppState; + private HubContextFixture _agentHubContextFixture; + private Mock _appConfig; + private Mock _circuitManager; + private Mock _toastService; + private Mock _expiringTokenService; + private Mock _desktopSessionCache; + private Mock _agentSessionCache; + private Mock> _logger; + private CircuitConnection _circuitConnection; +#nullable enable + + [TestInitialize] + public async Task Init() + { + _testData = new TestData(); + await _testData.Init(); + + _dataService = IoCActivator.ServiceProvider.GetRequiredService(); + _authService = new Mock(); + _clientAppState = new Mock(); + _agentHubContextFixture = new HubContextFixture(); + _appConfig = new Mock(); + _circuitManager = new Mock(); + _toastService = new Mock(); + _expiringTokenService = new Mock(); + _desktopSessionCache = new Mock(); + _agentSessionCache = new Mock(); + _logger = new Mock>(); + + _circuitConnection = new CircuitConnection( + _authService.Object, + _dataService, + _clientAppState.Object, + _agentHubContextFixture.HubContextMock.Object, + _appConfig.Object, + _circuitManager.Object, + _toastService.Object, + _expiringTokenService.Object, + _desktopSessionCache.Object, + _agentSessionCache.Object, + _logger.Object); + } + + [TestMethod] + public async Task WakeDevice_GivenUserIsUnauthorized_Fails() + { + // A standard user won't have access if they aren't in the same + // group as the device. + _circuitConnection.User = _testData.Org1User1; + + // Offline device. + _testData.Org1Device1.PublicIP = "142.251.33.110"; + _testData.Org1Device1.MacAddresses = new[] { "78E3B5A1E45B" }; + _testData.Org1Device1.DeviceGroupID = _testData.Org1Group1.ID; + // Online device. + _testData.Org1Device2.PublicIP = "142.251.33.110"; + // Device in another org that shouldn't receive the command. + _testData.Org2Device1.PublicIP = "142.251.33.110"; + + + var updateResult = await _dataService.AddOrUpdateDevice(_testData.Org1Device1); + Assert.IsTrue(updateResult.IsSuccess); + updateResult = await _dataService.AddOrUpdateDevice(_testData.Org1Device2); + Assert.IsTrue(updateResult.IsSuccess); + updateResult = await _dataService.AddOrUpdateDevice(_testData.Org2Device1); + Assert.IsTrue(updateResult.IsSuccess); + + var wakeResult = await _circuitConnection.WakeDevice(_testData.Org1Device1); + Assert.IsFalse(wakeResult.IsSuccess); + + _agentSessionCache.VerifyNoOtherCalls(); + _agentHubContextFixture.HubContextMock.VerifyNoOtherCalls(); + } + + [TestMethod] + public async Task WakeDevice_GivenMatchingPeerByIp_UsesCorrectPeer() + { + _circuitConnection.User = _testData.Org1User1; + + var macAddress = "78E3B5A1E45B"; + + // Offline device. + _testData.Org1Device1.PublicIP = "142.251.33.110"; + _testData.Org1Device1.MacAddresses = new[] { macAddress }; + _testData.Org1Device1.DeviceGroupID = _testData.Org1Group1.ID; + // Online device. + _testData.Org1Device2.PublicIP = "142.251.33.110"; + // Device in another org that shouldn't receive the command. + _testData.Org2Device1.PublicIP = "142.251.33.110"; + + var addToGroupResult = _dataService.AddUserToDeviceGroup( + _testData.Org1Id, + _testData.Org1Group1.ID, + _testData.Org1User1.UserName, + out _); + + var updateResult = await _dataService.AddOrUpdateDevice(_testData.Org1Device1); + Assert.IsTrue(updateResult.IsSuccess); + updateResult = await _dataService.AddOrUpdateDevice(_testData.Org1Device2); + Assert.IsTrue(updateResult.IsSuccess); + updateResult = await _dataService.AddOrUpdateDevice(_testData.Org2Device1); + Assert.IsTrue(updateResult.IsSuccess); + + _agentSessionCache + .Setup(x => x.GetAllDevices()) + .Returns(new[] + { + _testData.Org1Device2, + _testData.Org2Device1 + }); + + var connectionId = "HQUSIBxiOwNokVH_mYgGyg"; + + _agentSessionCache + .Setup(x => x.TryGetConnectionId(_testData.Org1Device2.ID, out connectionId)) + .Returns(true); + + var wakeResult = await _circuitConnection.WakeDevice(_testData.Org1Device1); + + Assert.IsTrue(addToGroupResult); + Assert.IsTrue(wakeResult.IsSuccess); + + + _agentSessionCache + .Verify(x => x.GetAllDevices(), Times.Once); + + _agentSessionCache + .Verify(x => x.TryGetConnectionId(_testData.Org1Device2.ID, out connectionId), Times.Once); + + _agentHubContextFixture.HubClientsMock + .Verify(x => x.Client(connectionId), Times.Once); + + _agentHubContextFixture.SingleClientProxyMock + .Verify(x => + x.SendCoreAsync( + "WakeDevice", + new object[] { macAddress }, + default), + Times.Once); + + _agentHubContextFixture.SingleClientProxyMock.VerifyNoOtherCalls(); + _agentHubContextFixture.HubContextMock.VerifyNoOtherCalls(); + _agentSessionCache.VerifyNoOtherCalls(); + } + + [TestMethod] + public async Task WakeDevice_GivenMatchingPeerByGroupId_UsesCorrectPeer() + { + _circuitConnection.User = _testData.Org1User1; + + var macAddress = "78E3B5A1E45B"; + + // Offline device. + _testData.Org1Device1.PublicIP = "142.251.33.110"; + _testData.Org1Device1.MacAddresses = new[] { macAddress }; + _testData.Org1Device1.DeviceGroupID = _testData.Org1Group1.ID; + // Online device. + _testData.Org1Device2.DeviceGroupID = _testData.Org1Group1.ID; + // Device in another org that shouldn't receive the command. + _testData.Org2Device1.DeviceGroupID = _testData.Org2Group1.ID; + + var addToGroupResult = _dataService.AddUserToDeviceGroup( + _testData.Org1Id, + _testData.Org1Group1.ID, + _testData.Org1User1.UserName, + out _); + + var updateResult = await _dataService.AddOrUpdateDevice(_testData.Org1Device1); + Assert.IsTrue(updateResult.IsSuccess); + updateResult = await _dataService.AddOrUpdateDevice(_testData.Org1Device2); + Assert.IsTrue(updateResult.IsSuccess); + updateResult = await _dataService.AddOrUpdateDevice(_testData.Org2Device1); + Assert.IsTrue(updateResult.IsSuccess); + + _agentSessionCache + .Setup(x => x.GetAllDevices()) + .Returns(new[] + { + _testData.Org1Device2, + _testData.Org2Device1 + }); + + var connectionId = "HQUSIBxiOwNokVH_mYgGyg"; + + _agentSessionCache + .Setup(x => x.TryGetConnectionId(_testData.Org1Device2.ID, out connectionId)) + .Returns(true); + + var wakeResult = await _circuitConnection.WakeDevice(_testData.Org1Device1); + + Assert.IsTrue(addToGroupResult); + Assert.IsTrue(wakeResult.IsSuccess); + + + _agentSessionCache + .Verify(x => x.GetAllDevices(), Times.Once); + + _agentSessionCache + .Verify(x => x.TryGetConnectionId(_testData.Org1Device2.ID, out connectionId), Times.Once); + + _agentHubContextFixture.HubClientsMock + .Verify(x => x.Client(connectionId), Times.Once); + + _agentHubContextFixture.SingleClientProxyMock + .Verify(x => + x.SendCoreAsync( + "WakeDevice", + new object[] { macAddress }, + default), + Times.Once); + + _agentHubContextFixture.SingleClientProxyMock.VerifyNoOtherCalls(); + _agentHubContextFixture.HubContextMock.VerifyNoOtherCalls(); + _agentSessionCache.VerifyNoOtherCalls(); + } + + + [TestMethod] + public async Task WakeDevice_GivenNoMatchingGroupOrIp_DoesNotSend() + { + _circuitConnection.User = _testData.Org1User1; + + var macAddress = "78E3B5A1E45B"; + + // Offline device. + _testData.Org1Device1.PublicIP = "142.251.33.110"; + _testData.Org1Device1.MacAddresses = new[] { macAddress }; + _testData.Org1Device1.DeviceGroupID = _testData.Org1Group1.ID; + // Online device, but in a different group. + _testData.Org1Device2.DeviceGroupID = _testData.Org1Group2.ID; + // Device in another org that shouldn't receive the command. + _testData.Org2Device1.DeviceGroupID = _testData.Org2Group1.ID; + + var addToGroupResult = _dataService.AddUserToDeviceGroup( + _testData.Org1Id, + _testData.Org1Group1.ID, + _testData.Org1User1.UserName, + out _); + + var updateResult = await _dataService.AddOrUpdateDevice(_testData.Org1Device1); + Assert.IsTrue(updateResult.IsSuccess); + updateResult = await _dataService.AddOrUpdateDevice(_testData.Org1Device2); + Assert.IsTrue(updateResult.IsSuccess); + updateResult = await _dataService.AddOrUpdateDevice(_testData.Org2Device1); + Assert.IsTrue(updateResult.IsSuccess); + + _agentSessionCache + .Setup(x => x.GetAllDevices()) + .Returns(new[] + { + _testData.Org1Device2, + _testData.Org2Device1 + }); + + var wakeResult = await _circuitConnection.WakeDevice(_testData.Org1Device1); + + Assert.IsTrue(addToGroupResult); + Assert.IsTrue(wakeResult.IsSuccess); + + + _agentSessionCache + .Verify(x => x.GetAllDevices(), Times.Once); + + _agentHubContextFixture.SingleClientProxyMock.VerifyNoOtherCalls(); + _agentHubContextFixture.HubContextMock.VerifyNoOtherCalls(); + _agentSessionCache.VerifyNoOtherCalls(); + } + + [TestMethod] + public async Task WakeDevices_GivenPeerIpMatches_UsesCorrectPeer() + { + _circuitConnection.User = _testData.Org1User1; + + var macAddress = "78E3B5A1E45B"; + + // Offline device. + _testData.Org1Device1.PublicIP = "142.251.33.110"; + _testData.Org1Device1.MacAddresses = new[] { macAddress }; + _testData.Org1Device1.DeviceGroupID = _testData.Org1Group1.ID; + // Online device. + _testData.Org1Device2.PublicIP = "142.251.33.110"; + // Device in another org that shouldn't receive the command. + _testData.Org2Device1.PublicIP = "142.251.33.110"; + + var addToGroupResult = _dataService.AddUserToDeviceGroup( + _testData.Org1Id, + _testData.Org1Group1.ID, + _testData.Org1User1.UserName, + out _); + + var updateResult = await _dataService.AddOrUpdateDevice(_testData.Org1Device1); + Assert.IsTrue(updateResult.IsSuccess); + updateResult = await _dataService.AddOrUpdateDevice(_testData.Org1Device2); + Assert.IsTrue(updateResult.IsSuccess); + updateResult = await _dataService.AddOrUpdateDevice(_testData.Org2Device1); + Assert.IsTrue(updateResult.IsSuccess); + + _agentSessionCache + .Setup(x => x.GetAllDevices()) + .Returns(new[] + { + _testData.Org1Device2, + _testData.Org2Device1 + }); + + var connectionId = "HQUSIBxiOwNokVH_mYgGyg"; + + _agentSessionCache + .Setup(x => x.TryGetConnectionId(_testData.Org1Device2.ID, out connectionId)) + .Returns(true); + + var wakeResult = await _circuitConnection.WakeDevices(new[] { _testData.Org1Device1 }); + + Assert.IsTrue(addToGroupResult); + Assert.IsTrue(wakeResult.IsSuccess); + + + _agentSessionCache + .Verify(x => x.GetAllDevices(), Times.Once); + + _agentSessionCache + .Verify(x => x.TryGetConnectionId(_testData.Org1Device2.ID, out connectionId), Times.Once); + + _agentHubContextFixture.HubClientsMock + .Verify(x => x.Client(connectionId), Times.Once); + + _agentHubContextFixture.SingleClientProxyMock + .Verify(x => + x.SendCoreAsync( + "WakeDevice", + new object[] { macAddress }, + default), + Times.Once); + + _agentHubContextFixture.SingleClientProxyMock.VerifyNoOtherCalls(); + _agentHubContextFixture.HubContextMock.VerifyNoOtherCalls(); + _agentSessionCache.VerifyNoOtherCalls(); + } + + [TestMethod] + public async Task WakeDevices_GivenMatchingPeerByGroupId_UsesCorrectPeer() + { + _circuitConnection.User = _testData.Org1User1; + + var macAddress = "78E3B5A1E45B"; + + // Offline device. + _testData.Org1Device1.PublicIP = "142.251.33.110"; + _testData.Org1Device1.MacAddresses = new[] { macAddress }; + _testData.Org1Device1.DeviceGroupID = _testData.Org1Group1.ID; + // Online device. + _testData.Org1Device2.DeviceGroupID = _testData.Org1Group1.ID; + // Device in another org that shouldn't receive the command. + _testData.Org2Device1.DeviceGroupID = _testData.Org2Group1.ID; + + var addToGroupResult = _dataService.AddUserToDeviceGroup( + _testData.Org1Id, + _testData.Org1Group1.ID, + _testData.Org1User1.UserName, + out _); + + var updateResult = await _dataService.AddOrUpdateDevice(_testData.Org1Device1); + Assert.IsTrue(updateResult.IsSuccess); + updateResult = await _dataService.AddOrUpdateDevice(_testData.Org1Device2); + Assert.IsTrue(updateResult.IsSuccess); + updateResult = await _dataService.AddOrUpdateDevice(_testData.Org2Device1); + Assert.IsTrue(updateResult.IsSuccess); + + _agentSessionCache + .Setup(x => x.GetAllDevices()) + .Returns(new[] + { + _testData.Org1Device2, + _testData.Org2Device1 + }); + + var connectionId = "HQUSIBxiOwNokVH_mYgGyg"; + + _agentSessionCache + .Setup(x => x.TryGetConnectionId(_testData.Org1Device2.ID, out connectionId)) + .Returns(true); + + var wakeResult = await _circuitConnection.WakeDevices(new[] { _testData.Org1Device1 }); + + Assert.IsTrue(addToGroupResult); + Assert.IsTrue(wakeResult.IsSuccess); + + + _agentSessionCache + .Verify(x => x.GetAllDevices(), Times.Once); + + _agentSessionCache + .Verify(x => x.TryGetConnectionId(_testData.Org1Device2.ID, out connectionId), Times.Once); + + _agentHubContextFixture.HubClientsMock + .Verify(x => x.Client(connectionId), Times.Once); + + _agentHubContextFixture.SingleClientProxyMock + .Verify(x => + x.SendCoreAsync( + "WakeDevice", + new object[] { macAddress }, + default), + Times.Once); + + _agentHubContextFixture.SingleClientProxyMock.VerifyNoOtherCalls(); + _agentHubContextFixture.HubContextMock.VerifyNoOtherCalls(); + _agentSessionCache.VerifyNoOtherCalls(); + } + + } +} diff --git a/Tests/Server.Tests/DataServiceTests.cs b/Tests/Server.Tests/DataServiceTests.cs index dc944d94..2dbd23f1 100644 --- a/Tests/Server.Tests/DataServiceTests.cs +++ b/Tests/Server.Tests/DataServiceTests.cs @@ -21,9 +21,9 @@ namespace Remotely.Tests [TestMethod] public async Task AddAlert() { - await _dataService.AddAlert(_testData.Device1.ID, _testData.OrganizationID, "Test Message"); + await _dataService.AddAlert(_testData.Org1Device1.ID, _testData.Org1Id, "Test Message"); - var alerts = _dataService.GetAlerts(_testData.Admin1.Id); + var alerts = _dataService.GetAlerts(_testData.Org1Admin1.Id); Assert.AreEqual("Test Message", alerts.First().Message); } @@ -38,7 +38,7 @@ namespace Remotely.Tests var newDevice = new Device() { ID = _newDeviceID, - OrganizationID = _testData.OrganizationID, + OrganizationID = _testData.Org1Id, DeviceName = Environment.MachineName, Is64Bit = Environment.Is64BitOperatingSystem }; @@ -60,7 +60,7 @@ namespace Remotely.Tests { DeviceID = Guid.NewGuid().ToString(), DeviceAlias = "Spare Laptop", - OrganizationID = _testData.OrganizationID + OrganizationID = _testData.Org1Id }; // First call should create and return device. @@ -75,32 +75,32 @@ namespace Remotely.Tests [TestMethod] public void DeviceGroupPermissions() { - Assert.IsTrue(_dataService.GetDevicesForUser(_testData.Admin1.UserName).Count() == 2); - Assert.IsTrue(_dataService.GetDevicesForUser(_testData.Admin2.UserName).Count() == 2); - Assert.IsTrue(_dataService.GetDevicesForUser(_testData.User1.UserName).Count() == 2); - Assert.IsTrue(_dataService.GetDevicesForUser(_testData.User2.UserName).Count() == 2); + Assert.IsTrue(_dataService.GetDevicesForUser(_testData.Org1Admin1.UserName).Count() == 2); + Assert.IsTrue(_dataService.GetDevicesForUser(_testData.Org1Admin2.UserName).Count() == 2); + Assert.IsTrue(_dataService.GetDevicesForUser(_testData.Org1User1.UserName).Count() == 2); + Assert.IsTrue(_dataService.GetDevicesForUser(_testData.Org1User2.UserName).Count() == 2); - var groupID = _dataService.GetDeviceGroups(_testData.Admin1.UserName).First().ID; + var groupID = _dataService.GetDeviceGroups(_testData.Org1Admin1.UserName).First().ID; - _dataService.UpdateDevice(_testData.Device1.ID, "", "", groupID, ""); - _dataService.AddUserToDeviceGroup(_testData.OrganizationID, groupID, _testData.User1.UserName, out _); + _dataService.UpdateDevice(_testData.Org1Device1.ID, "", "", groupID, ""); + _dataService.AddUserToDeviceGroup(_testData.Org1Id, groupID, _testData.Org1User1.UserName, out _); - Assert.IsTrue(_dataService.GetDevicesForUser(_testData.Admin1.UserName).Count() == 2); - Assert.IsTrue(_dataService.GetDevicesForUser(_testData.Admin2.UserName).Count() == 2); - Assert.IsTrue(_dataService.GetDevicesForUser(_testData.User1.UserName).Count() == 2); - Assert.IsTrue(_dataService.GetDevicesForUser(_testData.User2.UserName).Count() == 1); + Assert.IsTrue(_dataService.GetDevicesForUser(_testData.Org1Admin1.UserName).Count() == 2); + Assert.IsTrue(_dataService.GetDevicesForUser(_testData.Org1Admin2.UserName).Count() == 2); + Assert.IsTrue(_dataService.GetDevicesForUser(_testData.Org1User1.UserName).Count() == 2); + Assert.IsTrue(_dataService.GetDevicesForUser(_testData.Org1User2.UserName).Count() == 1); - Assert.IsTrue(_dataService.DoesUserHaveAccessToDevice(_testData.Device1.ID, _testData.Admin1)); - Assert.IsTrue(_dataService.DoesUserHaveAccessToDevice(_testData.Device1.ID, _testData.Admin2)); - Assert.IsTrue(_dataService.DoesUserHaveAccessToDevice(_testData.Device1.ID, _testData.User1)); - Assert.IsFalse(_dataService.DoesUserHaveAccessToDevice(_testData.Device1.ID, _testData.User2)); + Assert.IsTrue(_dataService.DoesUserHaveAccessToDevice(_testData.Org1Device1.ID, _testData.Org1Admin1)); + Assert.IsTrue(_dataService.DoesUserHaveAccessToDevice(_testData.Org1Device1.ID, _testData.Org1Admin2)); + Assert.IsTrue(_dataService.DoesUserHaveAccessToDevice(_testData.Org1Device1.ID, _testData.Org1User1)); + Assert.IsFalse(_dataService.DoesUserHaveAccessToDevice(_testData.Org1Device1.ID, _testData.Org1User2)); - var allDevices = _dataService.GetAllDevices(_testData.OrganizationID).Select(x => x.ID).ToArray(); + var allDevices = _dataService.GetAllDevices(_testData.Org1Id).Select(x => x.ID).ToArray(); - Assert.AreEqual(2, _dataService.FilterDeviceIDsByUserPermission(allDevices, _testData.Admin1).Length); - Assert.AreEqual(2, _dataService.FilterDeviceIDsByUserPermission(allDevices, _testData.Admin2).Length); - Assert.AreEqual(2, _dataService.FilterDeviceIDsByUserPermission(allDevices, _testData.User1).Length); - Assert.AreEqual(1, _dataService.FilterDeviceIDsByUserPermission(allDevices, _testData.User2).Length); + Assert.AreEqual(2, _dataService.FilterDeviceIDsByUserPermission(allDevices, _testData.Org1Admin1).Length); + Assert.AreEqual(2, _dataService.FilterDeviceIDsByUserPermission(allDevices, _testData.Org1Admin2).Length); + Assert.AreEqual(2, _dataService.FilterDeviceIDsByUserPermission(allDevices, _testData.Org1User1).Length); + Assert.AreEqual(1, _dataService.FilterDeviceIDsByUserPermission(allDevices, _testData.Org1User2).Length); } [TestMethod] @@ -111,25 +111,25 @@ namespace Remotely.Tests var savedScript = new SavedScript() { Content = "Get-ChildItem", - Creator = _testData.Admin1, - CreatorId = _testData.Admin1.Id, + Creator = _testData.Org1Admin1, + CreatorId = _testData.Org1Admin1.Id, Name = "GCI", - Organization = _testData.Admin1.Organization, - OrganizationID = _testData.OrganizationID, + Organization = _testData.Org1Admin1.Organization, + OrganizationID = _testData.Org1Id, Shell = Shared.Enums.ScriptingShell.PSCore }; - await _dataService.AddOrUpdateSavedScript(savedScript, _testData.Admin1.Id); + await _dataService.AddOrUpdateSavedScript(savedScript, _testData.Org1Admin1.Id); var scriptRun = new ScriptRun() { - Devices = new() { _testData.Device1 }, + Devices = new() { _testData.Org1Device1 }, InputType = Shared.Enums.ScriptInputType.ScheduledScript, SavedScriptId = savedScript.Id, - Initiator = _testData.Admin1.UserName, + Initiator = _testData.Org1Admin1.UserName, RunAt = now, - OrganizationID = _testData.OrganizationID, - Organization = _testData.Admin1.Organization, + OrganizationID = _testData.Org1Id, + Organization = _testData.Org1Admin1.Organization, RunOnNextConnect = true }; @@ -142,16 +142,16 @@ namespace Remotely.Tests Time.Adjust(TimeSpan.FromMinutes(2)); - var pendingRuns = await _dataService.GetPendingScriptRuns(_testData.Device1.ID); + var pendingRuns = await _dataService.GetPendingScriptRuns(_testData.Org1Device1.ID); Assert.AreEqual(1, pendingRuns.Count); Assert.AreEqual(2, pendingRuns[0].Id); var scriptResult = new ScriptResult() { - DeviceID = _testData.Device1.ID, + DeviceID = _testData.Org1Device1.ID, InputType = Shared.Enums.ScriptInputType.ScheduledScript, - OrganizationID = _testData.OrganizationID, + OrganizationID = _testData.Org1Id, SavedScriptId = savedScript.Id, ScriptRunId = scriptRun.Id, Shell = Shared.Enums.ScriptingShell.PSCore @@ -161,7 +161,7 @@ namespace Remotely.Tests await _dataService.AddScriptResultToScriptRun(scriptResult.ID, scriptRun.Id); - pendingRuns = await _dataService.GetPendingScriptRuns(_testData.Device1.ID); + pendingRuns = await _dataService.GetPendingScriptRuns(_testData.Org1Device1.ID); Assert.AreEqual(0, pendingRuns.Count); } @@ -174,9 +174,10 @@ namespace Remotely.Tests } [TestInitialize] - public void TestInit() + public async Task TestInit() { _testData = new TestData(); + await _testData.Init(); _dataService = IoCActivator.ServiceProvider.GetRequiredService(); var newDevice = new Device() @@ -184,16 +185,16 @@ namespace Remotely.Tests ID = _newDeviceID, DeviceName = Environment.MachineName, Is64Bit = Environment.Is64BitOperatingSystem, - OrganizationID = _testData.OrganizationID + OrganizationID = _testData.Org1Id }; } [TestMethod] public void UpdateOrganizationName() { - Assert.IsTrue(string.IsNullOrWhiteSpace(_testData.Admin1.Organization.OrganizationName)); - _dataService.UpdateOrganizationName(_testData.OrganizationID, "Test Org"); - var updatedOrg = _dataService.GetOrganizationById(_testData.OrganizationID); + Assert.IsTrue(string.IsNullOrWhiteSpace(_testData.Org1Admin1.Organization.OrganizationName)); + _dataService.UpdateOrganizationName(_testData.Org1Id, "Test Org"); + var updatedOrg = _dataService.GetOrganizationById(_testData.Org1Id); Assert.AreEqual("Test Org", updatedOrg.OrganizationName); } @@ -204,57 +205,75 @@ namespace Remotely.Tests Assert.AreEqual(1, currentAdmins.Count); - await _dataService.SetIsServerAdmin(_testData.Admin2.Id, true, _testData.Admin1.Id); + await _dataService.SetIsServerAdmin(_testData.Org1Admin2.Id, true, _testData.Org1Admin1.Id); currentAdmins = _dataService.GetServerAdmins(); Assert.AreEqual(2, currentAdmins.Count); - Assert.IsTrue(currentAdmins.Contains(_testData.Admin1.UserName)); - Assert.IsTrue(currentAdmins.Contains(_testData.Admin2.UserName)); + Assert.IsTrue(currentAdmins.Contains(_testData.Org1Admin1.UserName)); + Assert.IsTrue(currentAdmins.Contains(_testData.Org1Admin2.UserName)); // Shouldn't be able to change themselves. - await _dataService.SetIsServerAdmin(_testData.Admin2.Id, false, _testData.Admin2.Id); + await _dataService.SetIsServerAdmin(_testData.Org1Admin2.Id, false, _testData.Org1Admin2.Id); currentAdmins = _dataService.GetServerAdmins(); Assert.AreEqual(2, currentAdmins.Count); // Non-admins shouldn't be able to change admins. - await _dataService.SetIsServerAdmin(_testData.User1.Id, false, _testData.Admin2.Id); + await _dataService.SetIsServerAdmin(_testData.Org1User1.Id, false, _testData.Org1Admin2.Id); currentAdmins = _dataService.GetServerAdmins(); Assert.AreEqual(2, currentAdmins.Count); - await _dataService.SetIsServerAdmin(_testData.Admin2.Id, false, _testData.Admin1.Id); + await _dataService.SetIsServerAdmin(_testData.Org1Admin2.Id, false, _testData.Org1Admin1.Id); currentAdmins = _dataService.GetServerAdmins(); Assert.AreEqual(1, currentAdmins.Count); - Assert.AreEqual(_testData.Admin1.UserName, currentAdmins[0]); + Assert.AreEqual(_testData.Org1Admin1.UserName, currentAdmins[0]); } [TestMethod] public void VerifyInitialData() { - Assert.IsNotNull(_dataService.GetUserByNameWithOrg(_testData.Admin1.UserName)); - Assert.IsNotNull(_dataService.GetUserByNameWithOrg(_testData.Admin2.UserName)); - Assert.IsNotNull(_dataService.GetUserByNameWithOrg(_testData.User1.UserName)); - Assert.IsNotNull(_dataService.GetUserByNameWithOrg(_testData.User2.UserName)); - Assert.AreEqual(1, _dataService.GetOrganizationCount()); + Assert.IsNotNull(_dataService.GetUserByNameWithOrg(_testData.Org1Admin1.UserName)); + Assert.IsNotNull(_dataService.GetUserByNameWithOrg(_testData.Org1Admin2.UserName)); + Assert.IsNotNull(_dataService.GetUserByNameWithOrg(_testData.Org1User1.UserName)); + Assert.IsNotNull(_dataService.GetUserByNameWithOrg(_testData.Org1User2.UserName)); + Assert.AreEqual(2, _dataService.GetOrganizationCount()); - var devices = _dataService.GetAllDevices(_testData.OrganizationID); + var devices1 = _dataService.GetAllDevices(_testData.Org1Id); + Assert.AreEqual(2, devices1.Length); + Assert.IsTrue(devices1.Any(x => x.ID == "Org1Device1")); + Assert.IsTrue(devices1.Any(x => x.ID == "Org1Device2")); - Assert.AreEqual(2, devices.Count()); - Assert.IsTrue(devices.Any(x => x.ID == "Device1")); - Assert.IsTrue(devices.Any(x => x.ID == "Device2")); + var devices2 = _dataService.GetAllDevices(_testData.Org2Id); + Assert.AreEqual(2, devices2.Length); + Assert.IsTrue(devices2.Any(x => x.ID == "Org2Device1")); + Assert.IsTrue(devices2.Any(x => x.ID == "Org2Device2")); - var orgIDs = new string[] + var org1Ids = new string[] { - _testData.Group1.OrganizationID, - _testData.Group2.OrganizationID, - _testData.Admin1.OrganizationID, - _testData.Admin2.OrganizationID, - _testData.User1.OrganizationID, - _testData.User2.OrganizationID, - _testData.Device1.OrganizationID, - _testData.Device2.OrganizationID + _testData.Org1Group1.OrganizationID, + _testData.Org1Group2.OrganizationID, + _testData.Org1Admin1.OrganizationID, + _testData.Org1Admin2.OrganizationID, + _testData.Org1User1.OrganizationID, + _testData.Org1User2.OrganizationID, + _testData.Org1Device1.OrganizationID, + _testData.Org1Device2.OrganizationID }; - Assert.IsTrue(orgIDs.All(x => x == _testData.OrganizationID)); + Assert.IsTrue(org1Ids.All(x => x == _testData.Org1Id)); + + var org2Ids = new string[] +{ + _testData.Org2Group1.OrganizationID, + _testData.Org2Group2.OrganizationID, + _testData.Org2Admin1.OrganizationID, + _testData.Org2Admin2.OrganizationID, + _testData.Org2User1.OrganizationID, + _testData.Org2User2.OrganizationID, + _testData.Org2Device1.OrganizationID, + _testData.Org2Device2.OrganizationID +}; + + Assert.IsTrue(org2Ids.All(x => x == _testData.Org2Id)); } } } diff --git a/Tests/Server.Tests/Mocks/HubContextFixture.cs b/Tests/Server.Tests/Mocks/HubContextFixture.cs new file mode 100644 index 00000000..c5451c5f --- /dev/null +++ b/Tests/Server.Tests/Mocks/HubContextFixture.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.SignalR; +using Moq; +using Remotely.Server.Hubs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Remotely.Server.Tests.Mocks +{ + public class HubContextFixture + where T : Hub + { + public HubContextFixture() + { + HubContextMock = new Mock>(); + HubClientsMock = new Mock(); + GroupManagerMock = new Mock(); + SingleClientProxyMock = new Mock(); + ClientProxyMock = new Mock(); + + HubContextMock + .Setup(x => x.Clients) + .Returns(HubClientsMock.Object); + + HubContextMock + .Setup(x => x.Groups) + .Returns(GroupManagerMock.Object); + + HubClientsMock + .Setup(x => x.Client(It.IsAny())) + .Returns(SingleClientProxyMock.Object); + + HubClientsMock + .Setup(x => x.Group(It.IsAny())) + .Returns(ClientProxyMock.Object); + } + + public Mock> HubContextMock { get; } + public Mock HubClientsMock { get; } + public Mock GroupManagerMock { get; } + public Mock SingleClientProxyMock { get; } + public Mock ClientProxyMock { get; } + } +} diff --git a/Tests/Server.Tests/ScriptScheduleDispatcherTests.cs b/Tests/Server.Tests/ScriptScheduleDispatcherTests.cs index 976f1b65..ca5d64d4 100644 --- a/Tests/Server.Tests/ScriptScheduleDispatcherTests.cs +++ b/Tests/Server.Tests/ScriptScheduleDispatcherTests.cs @@ -22,16 +22,17 @@ namespace Remotely.Tests private ScriptSchedule _schedule1; private Mock _dataService; private Mock _circuitConnection; - private Mock _serviceSessionCache; + private Mock _serviceSessionCache; private Mock> _logger; private ScriptScheduleDispatcher _dispatcher; private TestData _testData; private SavedScript _savedScript; [TestInitialize] - public void Init() + public async Task Init() { _testData = new TestData(); + await _testData.Init(); _savedScript = new SavedScript() { @@ -41,10 +42,10 @@ namespace Remotely.Tests _schedule1 = new() { CreatedAt = Time.Now, - CreatorId = _testData.User1.Id, + CreatorId = _testData.Org1User1.Id, Devices = new List() { - _testData.Device1 + _testData.Org1Device1 }, DeviceGroups = new List() { @@ -52,7 +53,7 @@ namespace Remotely.Tests { Devices = new List() { - _testData.Device2 + _testData.Org1Device2 } } }, @@ -60,7 +61,7 @@ namespace Remotely.Tests Name = "_scheduleName", Id = 5, NextRun = Time.Now.AddMinutes(1), - OrganizationID = _testData.User1.OrganizationID, + OrganizationID = _testData.Org1User1.OrganizationID, SavedScriptId = _savedScript.Id }; @@ -72,12 +73,12 @@ namespace Remotely.Tests _dataService = new Mock(); _dataService.Setup(x => x.GetScriptSchedulesDue()).Returns(Task.FromResult(scriptSchedules)); _dataService.Setup(x => x.GetDevices(It.Is>(x => - x.Contains(_testData.Device1.ID) && - x.Contains(_testData.Device2.ID) - ))).Returns(new List() { _testData.Device1, _testData.Device2 }); + x.Contains(_testData.Org1Device1.ID) && + x.Contains(_testData.Org1Device2.ID) + ))).Returns(new List() { _testData.Org1Device1, _testData.Org1Device2 }); _circuitConnection = new Mock(); - _serviceSessionCache = new Mock(); + _serviceSessionCache = new Mock(); _logger = new Mock>(); _dispatcher = new ScriptScheduleDispatcher(_dataService.Object, _serviceSessionCache.Object, _circuitConnection.Object, _logger.Object); } @@ -109,8 +110,8 @@ namespace Remotely.Tests x.Contains(_schedule1.Devices.First().ID)))); _dataService.Verify(x => x.AddScriptRun(It.Is(x => x.ScheduleId == _schedule1.Id && - x.Devices.Exists(d => d.ID == _testData.Device1.ID) && - x.Devices.Exists(d => d.ID == _testData.Device2.ID)))); + x.Devices.Exists(d => d.ID == _testData.Org1Device1.ID) && + x.Devices.Exists(d => d.ID == _testData.Org1Device2.ID)))); _dataService.VerifyNoOtherCalls(); _circuitConnection.Verify(x => x.RunScript( diff --git a/Tests/Server.Tests/TestData.cs b/Tests/Server.Tests/TestData.cs index 22120046..0b161d60 100644 --- a/Tests/Server.Tests/TestData.cs +++ b/Tests/Server.Tests/TestData.cs @@ -14,49 +14,89 @@ namespace Remotely.Tests { public class TestData { - public TestData() - { - Init().Wait(); - } + #region Organization1 + public Organization Org1 => Org1Admin1.Organization; - public RemotelyUser Admin1 { get; } = new RemotelyUser() + public RemotelyUser Org1Admin1 { get; } = new() { - UserName = "admin1@test.com", + UserName = "org1admin1@test.com", IsAdministrator = true, IsServerAdmin = true, Organization = new Organization(), UserOptions = new RemotelyUserOptions() }; - public RemotelyUser Admin2 { get; private set; } + public RemotelyUser Org1Admin2 { get; private set; } - public Device Device1 { get; private set; } = new Device() + public Device Org1Device1 { get; private set; } = new Device() { - ID = "Device1", - DeviceName = "Device1Name" + ID = "Org1Device1", + DeviceName = "Org1Device1Name" }; - public Device Device2 { get; private set; } = new Device() + public Device Org1Device2 { get; private set; } = new Device() { - ID = "Device2", - DeviceName = "Device2Name" + ID = "Org1Device2", + DeviceName = "Org1Device2Name" }; - public DeviceGroup Group1 { get; private set; } = new DeviceGroup() + public DeviceGroup Org1Group1 { get; private set; } = new DeviceGroup() { - Name = "Group1" + Name = "Org1Group1" }; - public DeviceGroup Group2 { get; private set; } = new DeviceGroup() + public DeviceGroup Org1Group2 { get; private set; } = new DeviceGroup() { - Name = "Group2" + Name = "Org1Group2" }; - public string OrganizationID { get; private set; } + public string Org1Id => Org1.ID; + public RemotelyUser Org1User1 { get; private set; } + public RemotelyUser Org1User2 { get; private set; } + #endregion - public RemotelyUser User1 { get; private set; } - public RemotelyUser User2 { get; private set; } + + #region Organization2 + public Organization Org2 => Org2Admin1.Organization; + + public RemotelyUser Org2Admin1 { get; } = new() + { + UserName = "org2admin1@test.com", + IsAdministrator = true, + IsServerAdmin = false, + Organization = new Organization(), + UserOptions = new RemotelyUserOptions() + }; + + public RemotelyUser Org2Admin2 { get; private set; } + + public Device Org2Device1 { get; private set; } = new Device() + { + ID = "Org2Device1", + DeviceName = "Org2Device1Name" + }; + + public Device Org2Device2 { get; private set; } = new Device() + { + ID = "Org2Device2", + DeviceName = "Org2Device2Name" + }; + + public DeviceGroup Org2Group1 { get; private set; } = new DeviceGroup() + { + Name = "Org2Group1" + }; + + public DeviceGroup Org2Group2 { get; private set; } = new DeviceGroup() + { + Name = "Org2Group2" + }; + + public string Org2Id => Org2.ID; + public RemotelyUser Org2User1 { get; private set; } + public RemotelyUser Org2User2 { get; private set; } + #endregion public void ClearData() { @@ -74,7 +114,7 @@ namespace Remotely.Tests } - private async Task Init() + public async Task Init() { ClearData(); @@ -82,29 +122,52 @@ namespace Remotely.Tests var userManager = IoCActivator.ServiceProvider.GetRequiredService>(); var emailSender = IoCActivator.ServiceProvider.GetRequiredService(); - await userManager.CreateAsync(Admin1); + // Organization 1 + await userManager.CreateAsync(Org1Admin1); - await dataService.CreateUser("admin2@test.com", true, Admin1.OrganizationID); - Admin2 = dataService.GetUserByNameWithOrg("admin2@test.com"); + await dataService.CreateUser("org1admin2@test.com", true, Org1Admin1.OrganizationID); + Org1Admin2 = dataService.GetUserByNameWithOrg("org1admin2@test.com"); - await dataService.CreateUser("testuser1@test.com", false, Admin1.OrganizationID); - User1 = dataService.GetUserByNameWithOrg("testuser1@test.com"); + await dataService.CreateUser("org1testuser1@test.com", false, Org1Admin1.OrganizationID); + Org1User1 = dataService.GetUserByNameWithOrg("org1testuser1@test.com"); - await dataService.CreateUser("testuser2@test.com", false, Admin1.OrganizationID); - User2 = dataService.GetUserByNameWithOrg("testuser2@test.com"); + await dataService.CreateUser("org1testuser2@test.com", false, Org1Admin1.OrganizationID); + Org1User2 = dataService.GetUserByNameWithOrg("org1testuser2@test.com"); - Device1.OrganizationID = Admin1.OrganizationID; - await dataService.AddOrUpdateDevice(Device1); ; - Device2.OrganizationID = Admin1.OrganizationID; - await dataService.AddOrUpdateDevice(Device2); + Org1Device1.OrganizationID = Org1Admin1.OrganizationID; + await dataService.AddOrUpdateDevice(Org1Device1); ; + Org1Device2.OrganizationID = Org1Admin1.OrganizationID; + await dataService.AddOrUpdateDevice(Org1Device2); - dataService.AddDeviceGroup(Admin1.OrganizationID, Group1, out _, out _); - dataService.AddDeviceGroup(Admin1.OrganizationID, Group2, out _, out _); - var deviceGroups = dataService.GetDeviceGroups(Admin1.UserName); - Group1 = deviceGroups.First(x => x.Name == Group1.Name); - Group2 = deviceGroups.First(x => x.Name == Group2.Name); + dataService.AddDeviceGroup(Org1Admin1.OrganizationID, Org1Group1, out _, out _); + dataService.AddDeviceGroup(Org1Admin1.OrganizationID, Org1Group2, out _, out _); + var deviceGroups1 = dataService.GetDeviceGroups(Org1Admin1.UserName); + Org1Group1 = deviceGroups1.First(x => x.Name == Org1Group1.Name); + Org1Group2 = deviceGroups1.First(x => x.Name == Org1Group2.Name); - OrganizationID = Admin1.OrganizationID; + + // Organization 2 + await userManager.CreateAsync(Org2Admin1); + + await dataService.CreateUser("org2admin2@test.com", true, Org2Admin1.OrganizationID); + Org2Admin2 = dataService.GetUserByNameWithOrg("org2admin2@test.com"); + + await dataService.CreateUser("org2testuser1@test.com", false, Org2Admin1.OrganizationID); + Org2User1 = dataService.GetUserByNameWithOrg("org2testuser1@test.com"); + + await dataService.CreateUser("org2testuser2@test.com", false, Org2Admin1.OrganizationID); + Org2User2 = dataService.GetUserByNameWithOrg("org2testuser2@test.com"); + + Org2Device1.OrganizationID = Org2Admin1.OrganizationID; + await dataService.AddOrUpdateDevice(Org2Device1); ; + Org2Device2.OrganizationID = Org2Admin1.OrganizationID; + await dataService.AddOrUpdateDevice(Org2Device2); + + dataService.AddDeviceGroup(Org2Admin1.OrganizationID, Org2Group1, out _, out _); + dataService.AddDeviceGroup(Org2Admin1.OrganizationID, Org2Group2, out _, out _); + var deviceGroups2 = dataService.GetDeviceGroups(Org2Admin1.UserName); + Org2Group1 = deviceGroups2.First(x => x.Name == Org2Group1.Name); + Org2Group2 = deviceGroups2.First(x => x.Name == Org2Group2.Name); } } }