acdream/tests/AcDream.Headless.Tests/HeadlessEntryPointTests.cs

202 lines
6.8 KiB
C#

namespace AcDream.Headless.Tests;
public sealed class HeadlessEntryPointTests
{
[Theory]
[InlineData("--help")]
[InlineData("-h")]
public void HelpIsPresentationFreeAndSuccessful(string argument)
{
using var output = new StringWriter();
using var error = new StringWriter();
int exitCode = HeadlessEntryPoint.Run(
[argument],
output,
error);
Assert.Equal(0, exitCode);
Assert.Contains("acdream-headless", output.ToString());
Assert.Contains("Secrets are never accepted", output.ToString());
Assert.Equal(string.Empty, error.ToString());
}
[Fact]
public void ValidateAcceptsStrictEmptyNoConnectConfiguration()
{
using var file = TemporaryConfiguration.Create(
"""{"version":1,"sessions":[]}""");
using var output = new StringWriter();
using var error = new StringWriter();
int exitCode = HeadlessEntryPoint.Run(
["validate", "--config", file.Path],
output,
error);
Assert.Equal(0, exitCode);
Assert.Contains("0 session(s)", output.ToString());
Assert.Equal(string.Empty, error.ToString());
}
[Theory]
[InlineData("""{"version":2,"sessions":[]}""", "Unsupported configuration version")]
[InlineData("""{"version":1,"sessions":[],"password":"secret"}""", "JSON document is invalid")]
[InlineData("""{"version":1,"sessions":[null]}""", "non-empty id")]
[InlineData("""{"version":1}""", "JSON document is invalid")]
public void ValidateRejectsInvalidOrSecretShapedConfiguration(
string json,
string expectedError)
{
using var file = TemporaryConfiguration.Create(json);
using var output = new StringWriter();
using var error = new StringWriter();
int exitCode = HeadlessEntryPoint.Run(
["validate", "--config", file.Path],
output,
error);
Assert.Equal(3, exitCode);
Assert.Contains(
expectedError,
error.ToString(),
StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("secret", error.ToString());
Assert.Equal(string.Empty, output.ToString());
}
[Fact]
public void ValidateAcceptsCompleteSingleSessionConfiguration()
{
using var file = TemporaryConfiguration.Create(
ConfigurationWith(Session("bot", "BOT_PASSWORD")));
using var output = new StringWriter();
using var error = new StringWriter();
int exitCode = HeadlessEntryPoint.Run(
["validate", "--config", file.Path],
output,
error);
Assert.Equal(0, exitCode);
Assert.Contains("1 session(s)", output.ToString());
Assert.Equal(string.Empty, error.ToString());
}
[Fact]
public void ValidateAcceptsCompleteProcessContentDescriptor()
{
using var file = TemporaryConfiguration.Create(
"""{"version":1,"process":{"content":{"datDirectory":"C:\\AC","preparedAssetPath":"C:\\AC\\acdream.pak"}},"sessions":[]}""");
using var output = new StringWriter();
using var error = new StringWriter();
int exitCode = HeadlessEntryPoint.Run(
["validate", "--config", file.Path],
output,
error);
Assert.Equal(0, exitCode);
Assert.Contains("0 session(s)", output.ToString());
Assert.Equal(string.Empty, error.ToString());
}
[Theory]
[InlineData("""{"version":1,"process":{"content":{"datDirectory":"","preparedAssetPath":"package.pak"}},"sessions":[]}""")]
[InlineData("""{"version":1,"process":{"content":{"datDirectory":"dats","preparedAssetPath":""}},"sessions":[]}""")]
public void ValidateRejectsIncompleteProcessContentDescriptor(string json)
{
using var file = TemporaryConfiguration.Create(json);
using var output = new StringWriter();
using var error = new StringWriter();
int exitCode = HeadlessEntryPoint.Run(
["validate", "--config", file.Path],
output,
error);
Assert.Equal(3, exitCode);
Assert.Contains(
"process.content",
error.ToString(),
StringComparison.Ordinal);
Assert.Equal(string.Empty, output.ToString());
}
[Theory]
[InlineData("duplicate-id")]
[InlineData("duplicate-credential")]
public void ValidateRejectsAmbiguousSessionOwnership(string scenario)
{
string second = scenario == "duplicate-id"
? Session("bot", "SECOND_PASSWORD")
: Session("second", "BOT_PASSWORD");
string expected = scenario == "duplicate-id"
? "Duplicate session id"
: "already in use";
using var file = TemporaryConfiguration.Create(
ConfigurationWith(
Session("bot", "BOT_PASSWORD"),
second));
using var output = new StringWriter();
using var error = new StringWriter();
int exitCode = HeadlessEntryPoint.Run(
["validate", "--config", file.Path],
output,
error);
Assert.Equal(3, exitCode);
Assert.Contains(expected, error.ToString());
}
[Fact]
public void UnknownCommandReturnsUsageError()
{
using var output = new StringWriter();
using var error = new StringWriter();
int exitCode = HeadlessEntryPoint.Run(
["connect", "--password", "secret"],
output,
error);
Assert.Equal(2, exitCode);
Assert.DoesNotContain("secret", error.ToString());
Assert.Equal(string.Empty, output.ToString());
}
private static string ConfigurationWith(params string[] sessions) =>
$$"""{"version":1,"sessions":[{{string.Join(",", sessions)}}]}""";
private static string Session(string id, string credentialReference) =>
$"{{\"id\":\"{id}\",\"endpoint\":{{\"host\":\"127.0.0.1\",\"port\":9000}},"
+ "\"account\":\"account\",\"character\":{\"index\":0},"
+ "\"policy\":{\"id\":\"idle\"},\"credential\":"
+ $"{{\"provider\":\"environment\",\"reference\":\"{credentialReference}\"}}}}";
private sealed class TemporaryConfiguration : IDisposable
{
private TemporaryConfiguration(string path)
{
Path = path;
}
internal string Path { get; }
internal static TemporaryConfiguration Create(string json)
{
string path = System.IO.Path.Combine(
System.IO.Path.GetTempPath(),
$"acdream-headless-{Guid.NewGuid():N}.json");
File.WriteAllText(path, json);
return new TemporaryConfiguration(path);
}
public void Dispose()
{
File.Delete(Path);
}
}
}