Finish documenting DMAPI constructs. Get tests running

This commit is contained in:
Jordan Brown
2020-04-21 23:55:59 -04:00
parent 68f336cc32
commit 0646fd58af
24 changed files with 227 additions and 97 deletions
+3 -3
View File
@@ -1,7 +1,7 @@
#define DMAPI5_PARAM_RUNTIME_INFORMATION_FILE "tgs_json"
#define DMAPI5_TOPIC_DATA "data"
#define DMAPI5_BRIDGE_COMMAND_NEW_PORT 0
#define DMAPI5_BRIDGE_COMMAND_PORT_UPDATE 0
#define DMAPI5_BRIDGE_COMMAND_VALIDATE 1
#define DMAPI5_BRIDGE_COMMAND_PRIME 2
#define DMAPI5_BRIDGE_COMMAND_REBOOT 3
@@ -10,8 +10,8 @@
#define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier"
#define DMAPI5_BRIDGE_PARAMETER_COMMAND "commandType"
#define DMAPI5_BRIDGE_PARAMETER_NEW_PORT "newPort"
#define DMAPI5_BRIDGE_PARAMETER_COMMAND_TYPE "commandType"
#define DMAPI5_BRIDGE_PARAMETER_CURRENT_PORT "currentPort"
#define DMAPI5_BRIDGE_PARAMETER_VERSION "version"
#define DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE "chatMessage"
#define DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL "minimumSecurityLevel"
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using Tgstation.Server.Host.Components.Chat.Commands;
using Tgstation.Server.Host.Components.Chat.Providers;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.IO;
@@ -35,7 +35,7 @@ namespace Tgstation.Server.Host.Components.Chat
public string Mention { get; set; }
/// <summary>
/// The <see cref="Components.Chat.ChatChannel"/> the user spoke from
/// The <see cref="ChatChannel"/> the user spoke from
/// </summary>
public ChatChannel Channel { get; set; }
}
@@ -68,7 +68,7 @@ namespace Tgstation.Server.Host.Components
readonly IByondInstaller byondInstaller;
/// <summary>
/// The <see cref="IProviderFactory"/> for the <see cref="InstanceFactory"/>
/// The <see cref="IChatFactory"/> for the <see cref="InstanceFactory"/>
/// </summary>
readonly IChatFactory chatFactory;
@@ -1,12 +1,38 @@
namespace Tgstation.Server.Host.Components.Interop.Bridge
{
/// <summary>
/// Represents the <see cref="BridgeParameters.CommandType"/>.
/// </summary>
public enum BridgeCommandType
{
NewPort,
/// <summary>
/// DreamDaemon notifying us of its current port and requesting a change if necessary.
/// </summary>
PortUpdate,
/// <summary>
/// DreamDaemon responding to an API validation request.
/// </summary>
Validate,
/// <summary>
/// DreamDaemon notifying the server is primed
/// </summary>
Prime,
/// <summary>
/// DreamDaemon notifiying the server is calling /world/Reboot().
/// </summary>
Reboot,
/// <summary>
/// DreamDaemon requesting the process be terminated.
/// </summary>
Kill,
/// <summary>
/// DreamDaemon requesting a <see cref="ChatMessage"/> be sent.
/// </summary>
ChatSend
}
}
@@ -3,16 +3,34 @@ using Tgstation.Server.Api.Models;
namespace Tgstation.Server.Host.Components.Interop.Bridge
{
/// <summary>
/// Parameters for a bridge request.
/// </summary>
public sealed class BridgeParameters : DMApiParameters
{
/// <summary>
/// The <see cref="BridgeCommandType"/>.
/// </summary>
public BridgeCommandType? CommandType { get; set; }
public ushort? NewPort { get; set; }
/// <summary>
/// The current port for <see cref="BridgeCommandType.PortUpdate"/> requests.
/// </summary>
public ushort? CurrentPort { get; set; }
/// <summary>
/// The DMAPI <see cref="global::System.Version"/> for <see cref="BridgeCommandType.Validate"/> requests.
/// </summary>
public Version Version { get; set; }
public ChatMessage ChatMessage { get; set; }
/// <summary>
/// The minimum required <see cref="DreamDaemonSecurity"/> level for <see cref="BridgeCommandType.Validate"/> requests.
/// </summary>
public DreamDaemonSecurity? MinimumSecurityLevel { get; set; }
/// <summary>
/// The <see cref="Interop.ChatMessage"/> for <see cref="BridgeCommandType.ChatSend"/> requests.
/// </summary>
public ChatMessage ChatMessage { get; set; }
}
}
@@ -1,8 +1,13 @@
namespace Tgstation.Server.Host.Components.Interop.Bridge
{
public sealed class BridgeResponse
/// <summary>
/// A response to a bridge request.
/// </summary>
public sealed class BridgeResponse : DMApiResponse
{
public string ErrorMessage { get; set; }
/// <summary>
/// The new port for <see cref="BridgeCommandType.PortUpdate"/> requests.
/// </summary>
public ushort? NewPort { get; set; }
}
}
@@ -2,10 +2,19 @@
namespace Tgstation.Server.Host.Components.Interop
{
/// <summary>
/// Represents a message to send to one or more <see cref="Chat.ChatChannel"/>s.
/// </summary>
public sealed class ChatMessage
{
/// <summary>
/// The message <see cref="string"/>.
/// </summary>
public string Text { get; set; }
public ICollection<ulong> ChannelIds { get; set; }
/// <summary>
/// The <see cref="ICollection{T}"/> of <see cref="Chat.ChatChannel.Id"/>s to sent the <see cref="Text"/> to. Must be safe to parse as <see cref="ulong"/>s.
/// </summary>
public ICollection<string> ChannelIds { get; set; }
}
}
@@ -1,7 +1,13 @@
namespace Tgstation.Server.Host.Components.Interop
{
public class DMApiParameters
/// <summary>
/// Common base for interop parameters.
/// </summary>
public abstract class DMApiParameters
{
/// <summary>
/// The <see cref="Runtime.RuntimeInformation.AccessIdentifier"/> for interop.
/// </summary>
public string AccessIdentifier { get; set; }
}
}
@@ -0,0 +1,13 @@
namespace Tgstation.Server.Host.Components.Interop
{
/// <summary>
/// Common base for interop responses.
/// </summary>
public abstract class DMApiResponse
{
/// <summary>
/// Any errors in the client's parameters.
/// </summary>
public string ErrorMessage { get; set; }
}
}
@@ -7,7 +7,7 @@ namespace Tgstation.Server.Host.Components.Interop
interface IBridgeHandler : IBridgeHandlerBase
{
/// <summary>
/// The <see cref="RuntimeInformation.AccessIdentifier"/> for the <see cref="IBridgeHandler"/>.
/// The <see cref="Runtime.RuntimeInformation.AccessIdentifier"/> for the <see cref="IBridgeHandler"/>.
/// </summary>
string AccessIdentifier { get; }
@@ -19,7 +19,7 @@ namespace Tgstation.Server.Host.Components.Interop.Topic
public string Params { get; }
/// <summary>
/// The <see cref="Chat.ChatUser"/> that sent the command
/// The <see cref="ChatUser"/> that sent the command
/// </summary>
public ChatUser User { get; }
@@ -28,7 +28,7 @@ namespace Tgstation.Server.Host.Components.Interop.Topic
/// </summary>
/// <param name="user">The value of <see cref="User"/>.</param>
/// <param name="command">The value of <see cref="Name"/>.</param>
/// <param name="parameters">The value of <see cref="Parames"/>.</param>
/// <param name="parameters">The value of <see cref="Params"/>.</param>
public ChatCommand(ChatUser user, string command, string parameters)
{
User = user ?? throw new ArgumentNullException(nameof(user));
@@ -3,16 +3,30 @@ using System.Linq;
namespace Tgstation.Server.Host.Components.Interop.Topic
{
/// <summary>
/// Data structure for <see cref="TopicCommandType.EventNotification"/> requests.
/// </summary>
sealed class EventNotification
{
/// <summary>
/// The <see cref="EventType"/> triggered.
/// </summary>
public EventType Type { get; }
/// <summary>
/// The set of parameters.
/// </summary>
public IReadOnlyCollection<object> Parameters { get; }
/// <summary>
/// Initializes a new instance of the <see cref="EventNotification"/> <see langword="class"/>.
/// </summary>
/// <param name="eventType">The value of <see cref="Type"/>.</param>
/// <param name="parameters">The <see cref="IEnumerable{T}"/> that forms the value of <see cref="Parameters"/>.</param>
public EventNotification(EventType eventType, IEnumerable<object> parameters = null)
{
Type = eventType;
Parameters = parameters?.ToList();
}
}
}
}
@@ -13,7 +13,7 @@
/// <summary>
/// Notification of a TGS event.
/// </summary>
Event,
EventNotification,
/// <summary>
/// Port change request.
@@ -3,48 +3,94 @@ using Tgstation.Server.Host.Components.Watchdog;
namespace Tgstation.Server.Host.Components.Interop.Topic
{
/// <summary>
/// Parameters for a topic request.
/// </summary>
sealed class TopicParameters : DMApiParameters
{
/// <summary>
/// The <see cref="TopicCommandType"/>.
/// </summary>
public TopicCommandType CommandType { get; }
/// <summary>
/// The <see cref="Topic.ChatCommand"/> for <see cref="TopicCommandType.ChatCommand"/> requests.
/// </summary>
public ChatCommand ChatCommand { get; }
/// <summary>
/// The <see cref="Topic.EventNotification"/> for <see cref="TopicCommandType.EventNotification"/> requests.
/// </summary>
public EventNotification EventNotification { get; }
/// <summary>
/// The new port for <see cref="TopicCommandType.ChangePort"/> requests.
/// </summary>
public ushort? NewPort { get; }
/// <summary>
/// The <see cref="RebootState"/> for <see cref="TopicCommandType.ChangeRebootState"/> requests.
/// </summary>
public RebootState? NewRebootState { get; }
/// <summary>
/// The new <see cref="Api.Models.Instance.Name"/> for <see cref="TopicCommandType.InstanceRenamed"/> requests.
/// </summary>
public string NewInstanceName { get; }
/// <summary>
/// Initializes a new instance of the <see cref="TopicParameters"/> <see langword="class"/>.
/// </summary>
/// <param name="commandType">The value of <see cref="CommandType"/>.</param>
private TopicParameters(TopicCommandType commandType)
{
CommandType = commandType;
}
/// <summary>
/// Initializes a new instance of the <see cref="TopicParameters"/> <see langword="class"/>.
/// </summary>
/// <param name="chatCommand">The value of <see cref="ChatCommand"/>.</param>
public TopicParameters(ChatCommand chatCommand)
: this(TopicCommandType.ChatCommand)
{
ChatCommand = chatCommand ?? throw new ArgumentNullException(nameof(chatCommand));
}
/// <summary>
/// Initializes a new instance of the <see cref="TopicParameters"/> <see langword="class"/>.
/// </summary>
/// <param name="eventNotification">The value of <see cref="EventNotification"/>.</param>
public TopicParameters(EventNotification eventNotification)
: this(TopicCommandType.Event)
: this(TopicCommandType.EventNotification)
{
EventNotification = eventNotification ?? throw new ArgumentNullException(nameof(eventNotification));
}
/// <summary>
/// Initializes a new instance of the <see cref="TopicParameters"/> <see langword="class"/>.
/// </summary>
/// <param name="newPort">The value of <see cref="NewPort"/>.</param>
public TopicParameters(ushort newPort)
: this(TopicCommandType.ChangePort)
{
NewPort = newPort;
}
/// <summary>
/// Initializes a new instance of the <see cref="TopicParameters"/> <see langword="class"/>.
/// </summary>
/// <param name="newRebootState">The value of <see cref="NewRebootState"/>.</param>
public TopicParameters(RebootState newRebootState)
: this(TopicCommandType.ChangeRebootState)
{
NewRebootState = newRebootState;
}
/// <summary>
/// Initializes a new instance of the <see cref="TopicParameters"/> <see langword="class"/>.
/// </summary>
/// <param name="newInstanceName">The value of <see cref="NewInstanceName"/>.</param>
public TopicParameters(string newInstanceName)
: this(TopicCommandType.InstanceRenamed)
{
@@ -2,12 +2,19 @@
namespace Tgstation.Server.Host.Components.Interop.Topic
{
sealed class TopicResponse
/// <summary>
/// A response to a topic request.
/// </summary>
sealed class TopicResponse : DMApiResponse
{
public string ErrorMessage { get; set; }
/// <summary>
/// The text to reply with as the result of a <see cref="TopicCommandType.ChatCommand"/> request, if any.
/// </summary>
public string CommandResponseMessage { get; set; }
/// <summary>
/// The <see cref="ChatMessage"/>s to send as the result of a <see cref="TopicCommandType.EventNotification"/> request, if any.
/// </summary>
public ICollection<ChatMessage> ChatResponses { get; set; }
}
}
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
@@ -316,6 +317,12 @@ namespace Tgstation.Server.Host.Components.Watchdog
ErrorMessage = "Missing channelIds field in chatMessage!"
};
if(parameters.ChatMessage.ChannelIds.Any(channelIdString => !UInt64.TryParse(channelIdString, out var _)))
return new BridgeResponse
{
ErrorMessage = "Invalid channelIds in chatMessage!"
};
if (parameters.ChatMessage.Text == null)
return new BridgeResponse
{
@@ -324,7 +331,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
await chat.SendMessage(
parameters.ChatMessage.Text,
parameters.ChatMessage.ChannelIds,
parameters.ChatMessage.ChannelIds.Select(UInt64.Parse),
cancellationToken).ConfigureAwait(false);
break;
case BridgeCommandType.Prime:
@@ -334,10 +341,10 @@ namespace Tgstation.Server.Host.Components.Watchdog
TerminationWasRequested = true;
process.Terminate();
break;
case BridgeCommandType.NewPort:
case BridgeCommandType.PortUpdate:
lock (this)
{
if (!parameters.NewPort.HasValue)
if (!parameters.CurrentPort.HasValue)
{
/////UHHHH
logger.LogWarning("DreamDaemon sent new port command without providing it's own!");
@@ -347,9 +354,9 @@ namespace Tgstation.Server.Host.Components.Watchdog
};
}
var currentPort = parameters.NewPort.Value;
var currentPort = parameters.CurrentPort.Value;
if (!nextPort.HasValue)
reattachInformation.Port = parameters.NewPort.Value; // not ready yet, so what we'll do is accept the random port DD opened on for now and change it later when we decide to
reattachInformation.Port = parameters.CurrentPort.Value; // not ready yet, so what we'll do is accept the random port DD opened on for now and change it later when we decide to
else
{
// nextPort is ready, tell DD to switch to that
@@ -220,7 +220,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
JsonFile("chat_channels"),
JsonFile("chat_commands"),
securityLevelToUse,
await serverPortProvider.HttpApiPort.ConfigureAwait(false));
serverPortProvider.HttpApiPort);
var interopJsonFile = JsonFile("interop");
@@ -447,13 +447,27 @@ namespace Tgstation.Server.Host.Components.Watchdog
if (result?.ChatResponses == null)
return true;
await Task.WhenAll(result.ChatResponses.Select(x => Chat.SendMessage(x.Text, x.ChannelIds, cancellationToken))).ConfigureAwait(false);
await Task.WhenAll(
result.ChatResponses.Select(
x => Chat.SendMessage(
x.Text,
x.ChannelIds
.Select(channelIdString =>
{
if (UInt64.TryParse(channelIdString, out var channelId))
return (ulong?)channelId;
return null;
})
.Where(nullableChannelId => nullableChannelId.HasValue)
.Select(nullableChannelId => nullableChannelId.Value),
cancellationToken))).ConfigureAwait(false);
return true;
}
/// <inheritdoc />
public async Task<string> HandleChatCommand(string commandName, string arguments, Chat.ChatUser sender, CancellationToken cancellationToken)
public async Task<string> HandleChatCommand(string commandName, string arguments, ChatUser sender, CancellationToken cancellationToken)
{
using (await SemaphoreSlimContext.Lock(Semaphore, cancellationToken).ConfigureAwait(false))
{
@@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
@@ -376,7 +375,6 @@ namespace Tgstation.Server.Host.Core
IApplicationBuilder applicationBuilder,
IServerControl serverControl,
ITokenFactory tokenFactory,
IServerPortProvider serverPortProvider,
IOptions<ControlPanelConfiguration> controlPanelConfigurationOptions,
IOptions<GeneralConfiguration> generalConfigurationOptions,
ILogger<Application> logger)
@@ -388,12 +386,6 @@ namespace Tgstation.Server.Host.Core
this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory));
if (serverPortProvider == null)
throw new ArgumentNullException(nameof(serverPortProvider));
var addressFeature = applicationBuilder?.ServerFeatures.Get<IServerAddressesFeature>();
serverPortProvider.Configure(addressFeature);
var controlPanelConfiguration = controlPanelConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(controlPanelConfigurationOptions));
var generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
@@ -1,7 +1,4 @@
using Microsoft.AspNetCore.Hosting.Server.Features;
using System.Threading.Tasks;
namespace Tgstation.Server.Host.Core
namespace Tgstation.Server.Host.Core
{
/// <summary>
/// Provides access to the server's <see cref="HttpApiPort"/>.
@@ -9,14 +6,8 @@ namespace Tgstation.Server.Host.Core
interface IServerPortProvider
{
/// <summary>
/// A <see cref="Task{TResult}"/> resulting in the port the server listens on.
/// The port the server listens on.
/// </summary>
Task<ushort> HttpApiPort { get; }
/// <summary>
/// Configures the <see cref="ServerPortProivder"/>.
/// </summary>
/// <param name="addressFeature">The <see cref="IServerAddressesFeature"/> to use.</param>
void Configure(IServerAddressesFeature addressFeature);
ushort HttpApiPort { get; }
}
}
@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.Extensions.Configuration;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Tgstation.Server.Host.Core
{
@@ -9,38 +8,25 @@ namespace Tgstation.Server.Host.Core
sealed class ServerPortProivder : IServerPortProvider
{
/// <inheritdoc />
public Task<ushort> HttpApiPort => taskCompletionSource.Task;
public ushort HttpApiPort { get; }
/// <summary>
/// Backing <see cref="TaskCompletionSource{TResult}"/> field for <see cref="HttpApiPort"/>/
/// Initializes a new instance of the <see cref="ServerPortProivder"/> <see langword="class"/>.
/// </summary>
readonly TaskCompletionSource<ushort> taskCompletionSource;
/// <summary>
/// In
/// </summary>
public ServerPortProivder()
/// <param name="configuration">The <see cref="IConfiguration"/> to use.</param>
public ServerPortProivder(IConfiguration configuration)
{
taskCompletionSource = new TaskCompletionSource<ushort>();
}
if (configuration == null)
throw new ArgumentNullException(nameof(configuration));
/// <inheritdoc />
public void Configure(IServerAddressesFeature addressFeature)
{
if (addressFeature == null)
throw new ArgumentNullException(nameof(addressFeature));
var httpEndpoint = configuration
.GetSection("Kestrel")
.GetSection("Endpoints")
.GetSection("Http")
.GetSection("Url")
.Value;
var enumerator = addressFeature.Addresses.Select(GetPortFromAddress);
var newPort = enumerator.FirstOrDefault(x => x.HasValue);
if(!newPort.HasValue)
throw new InvalidOperationException("At least one plain HTTP endpoint must be configured. Neded for BYOND -> Server communications!");
if (!addressFeature.Addresses.Select(GetPortFromAddress).All(x => !x.HasValue || x == newPort))
throw new InvalidOperationException("All configured HTTP server addresses must use the same port!");
// Will fail if set twice
taskCompletionSource.SetResult(newPort.Value);
HttpApiPort = GetPortFromAddress(httpEndpoint);
}
/// <summary>
@@ -48,18 +34,14 @@ namespace Tgstation.Server.Host.Core
/// </summary>
/// <param name="address">The address <see cref="string"/>.</param>
/// <returns>The parsed port.</returns>
static ushort? GetPortFromAddress(string address)
static ushort GetPortFromAddress(string address)
{
var splits = address.Split(":", StringSplitOptions.RemoveEmptyEntries);
if (splits.First().Equals("https", StringComparison.OrdinalIgnoreCase))
return null;
var portString = splits.Last();
portString = portString.TrimEnd('/');
if (UInt16.TryParse(portString, out var result))
return result;
return null;
if (!UInt16.TryParse(portString, out var result))
throw new InvalidOperationException("Failed to parse HTTP API port!");
return result;
}
}
}
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@@ -40,27 +41,24 @@ namespace Tgstation.Server.Host.Core.Tests
var app = new Application(mockConfiguration.Object, mockAssemblyInfo.Object, mockHostingEnvironment.Object, Mock.Of<IIOManager>());
Assert.ThrowsException<ArgumentNullException>(() => app.ConfigureServices(null));
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(null, null, null, null, null, null, null));
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(null, null, null, null, null, null));
var mockAppBuilder = new Mock<IApplicationBuilder>();
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(mockAppBuilder.Object, null, null, null, null, null, null));
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(mockAppBuilder.Object, null, null, null, null, null));
var mockServerControl = new Mock<IServerControl>();
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, null, null, null, null, null));
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, null, null, null, null));
var mockTokenFactory = new Mock<ITokenFactory>();
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, null, null, null, null));
var mockServerPortProvider = new Mock<IServerPortProvider>();
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, mockServerPortProvider.Object, null, null, null));
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, null, null, null));
var mockControlPanelOptions = new Mock<IOptions<ControlPanelConfiguration>>();
mockControlPanelOptions.SetupGet(x => x.Value).Returns(new ControlPanelConfiguration()).Verifiable();
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, mockServerPortProvider.Object, mockControlPanelOptions.Object, null, null));
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, mockControlPanelOptions.Object, null, null));
var mockGeneralOptions = new Mock<IOptions<GeneralConfiguration>>();
mockGeneralOptions.SetupGet(x => x.Value).Returns(new GeneralConfiguration()).Verifiable();
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, mockServerPortProvider.Object, mockControlPanelOptions.Object, mockGeneralOptions.Object, null));
Assert.ThrowsException<ArgumentNullException>(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, mockControlPanelOptions.Object, mockGeneralOptions.Object, null));
mockControlPanelOptions.VerifyAll();
mockGeneralOptions.VerifyAll();
}
@@ -34,7 +34,8 @@ namespace Tgstation.Server.Tests
Directory = Path.GetTempFileName();
File.Delete(Directory);
System.IO.Directory.CreateDirectory(Directory);
Url = new Uri("http://localhost:5001");
const string UrlString = "http://localhost:5001";
Url = new Uri(UrlString);
//so we need a db
//we have to rely on env vars
@@ -56,7 +57,7 @@ namespace Tgstation.Server.Tests
var args = new List<string>()
{
String.Format(CultureInfo.InvariantCulture, "Kestrel:EndPoints:Http:Url={0}", Url),
String.Format(CultureInfo.InvariantCulture, "Kestrel:EndPoints:Http:Url={0}", UrlString),
String.Format(CultureInfo.InvariantCulture, "Database:DatabaseType={0}", databaseType),
String.Format(CultureInfo.InvariantCulture, "Database:ConnectionString={0}", connectionString),
String.Format(CultureInfo.InvariantCulture, "Database:DropDatabase={0}", true),