218 lines
6.7 KiB
C#
218 lines
6.7 KiB
C#
using System.Net;
|
|
using AcDream.Core.Net;
|
|
using AcDream.Core.Net.Messages;
|
|
using AcDream.Headless.Configuration;
|
|
using AcDream.Headless.Credentials;
|
|
using AcDream.Headless.Diagnostics;
|
|
using AcDream.Headless.Hosting;
|
|
using AcDream.Headless.Platform;
|
|
using AcDream.Runtime;
|
|
using AcDream.Runtime.Session;
|
|
|
|
namespace AcDream.Headless.Tests;
|
|
|
|
public sealed class HeadlessSessionHostTests
|
|
{
|
|
[Fact]
|
|
public void SingleSessionStartsReconnectsAndConvergesWithoutPresentation()
|
|
{
|
|
var operations = new FixtureSessionOperations();
|
|
using var diagnosticsOutput = new StringWriter();
|
|
using var credential = new HeadlessCredentialSecret(
|
|
"fixture",
|
|
"password");
|
|
var host = new HeadlessSessionHost(
|
|
Descriptor(),
|
|
credential,
|
|
new HeadlessDiagnosticWriter(diagnosticsOutput),
|
|
operations);
|
|
|
|
RuntimeSessionStartResult first = host.Start();
|
|
ulong firstGeneration = host.Runtime.Generation.Value;
|
|
host.Tick(0.015d);
|
|
RuntimeSessionStartResult second = host.Reconnect();
|
|
|
|
Assert.Equal(RuntimeSessionStartStatus.Connected, first.Status);
|
|
Assert.Equal(RuntimeSessionStartStatus.Connected, second.Status);
|
|
Assert.Equal(0x50000002u, first.CharacterId);
|
|
Assert.Equal("Headless", host.ActiveCharacterName);
|
|
Assert.True(host.Runtime.Session.IsInWorld);
|
|
Assert.True(host.Runtime.Generation.Value > firstGeneration);
|
|
Assert.Equal(2, operations.CreatedSessionCount);
|
|
Assert.Equal(1, operations.DisposedSessionCount);
|
|
|
|
host.Dispose();
|
|
|
|
Assert.Equal(2, operations.DisposedSessionCount);
|
|
Assert.True(host.Runtime.CaptureOwnership().IsConverged);
|
|
Assert.True(credential.IsDisposed);
|
|
string diagnostics = diagnosticsOutput.ToString();
|
|
Assert.Contains("\"state\":\"start-result\"", diagnostics);
|
|
Assert.DoesNotContain("password", diagnostics);
|
|
Assert.DoesNotContain("AcDream.App", diagnostics);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ProcessHostRunsUntilCancellationAndReturnsStableExitCode()
|
|
{
|
|
var configuration = new HeadlessConfiguration
|
|
{
|
|
Version = 1,
|
|
Sessions =
|
|
[
|
|
Descriptor(
|
|
HeadlessCredentialProviderKind.StandardInput,
|
|
"stdin-bot"),
|
|
],
|
|
};
|
|
HeadlessPathSet paths = HeadlessPathSet.Resolve(
|
|
new HeadlessPathOverrides());
|
|
using var diagnostics = new StringWriter();
|
|
var operations = new FixtureSessionOperations();
|
|
using var host = new HeadlessProcessHost(
|
|
configuration,
|
|
paths,
|
|
new StringReader("process-password" + Environment.NewLine),
|
|
diagnostics,
|
|
operations);
|
|
using var cancellation = new CancellationTokenSource();
|
|
cancellation.Cancel();
|
|
|
|
HeadlessExitCode result =
|
|
await host.RunAsync(cancellation.Token);
|
|
|
|
Assert.Equal(HeadlessExitCode.Success, result);
|
|
Assert.True(host.Session.Runtime.Session.IsInWorld);
|
|
Assert.DoesNotContain(
|
|
"process-password",
|
|
diagnostics.ToString());
|
|
}
|
|
|
|
[Fact]
|
|
public void TeardownRetriesOnlyTheUnfinishedSuffix()
|
|
{
|
|
var operations = new FixtureSessionOperations();
|
|
var writer = new FailOnceTextWriter();
|
|
using var credential = new HeadlessCredentialSecret(
|
|
"fixture",
|
|
"password");
|
|
var host = new HeadlessSessionHost(
|
|
Descriptor(),
|
|
credential,
|
|
new HeadlessDiagnosticWriter(writer),
|
|
operations);
|
|
Assert.Equal(
|
|
RuntimeSessionStartStatus.Connected,
|
|
host.Start().Status);
|
|
writer.FailNextWrite = true;
|
|
|
|
Assert.Throws<IOException>(host.Dispose);
|
|
Assert.Equal(1, operations.DisposedSessionCount);
|
|
|
|
host.Dispose();
|
|
|
|
Assert.Equal(1, operations.DisposedSessionCount);
|
|
Assert.True(host.Runtime.CaptureOwnership().IsConverged);
|
|
}
|
|
|
|
private static HeadlessSessionDescriptor Descriptor(
|
|
HeadlessCredentialProviderKind provider =
|
|
HeadlessCredentialProviderKind.Environment,
|
|
string credentialReference = "BOT_PASSWORD") => new()
|
|
{
|
|
Id = "bot",
|
|
Endpoint = new HeadlessEndpointDescriptor
|
|
{
|
|
Host = "127.0.0.1",
|
|
Port = 9000,
|
|
},
|
|
Account = "account",
|
|
Character = new HeadlessCharacterSelector
|
|
{
|
|
Name = "headless",
|
|
},
|
|
Policy = new HeadlessBotPolicyDescriptor
|
|
{
|
|
Id = "idle",
|
|
},
|
|
Credential = new HeadlessCredentialReference
|
|
{
|
|
Provider = provider,
|
|
Reference = credentialReference,
|
|
},
|
|
};
|
|
|
|
private sealed class FixtureSessionOperations : ILiveSessionOperations
|
|
{
|
|
public int CreatedSessionCount { get; private set; }
|
|
public int DisposedSessionCount { get; private set; }
|
|
|
|
public IPEndPoint ResolveEndpoint(string host, int port) =>
|
|
new(IPAddress.Loopback, port);
|
|
|
|
public WorldSession CreateSession(IPEndPoint endpoint)
|
|
{
|
|
CreatedSessionCount++;
|
|
return new WorldSession(endpoint);
|
|
}
|
|
|
|
public void Connect(
|
|
WorldSession session,
|
|
string user,
|
|
string password)
|
|
{
|
|
}
|
|
|
|
public CharacterList.Parsed GetCharacters(
|
|
WorldSession session) =>
|
|
new(
|
|
0u,
|
|
[
|
|
new CharacterList.Character(
|
|
0x50000001u,
|
|
"Other",
|
|
0u),
|
|
new CharacterList.Character(
|
|
0x50000002u,
|
|
"Headless",
|
|
0u),
|
|
],
|
|
[],
|
|
11,
|
|
"account",
|
|
true,
|
|
true);
|
|
|
|
public void EnterWorld(
|
|
WorldSession session,
|
|
int activeCharacterIndex)
|
|
{
|
|
}
|
|
|
|
public void Tick(WorldSession session)
|
|
{
|
|
}
|
|
|
|
public void DisposeSession(WorldSession session)
|
|
{
|
|
DisposedSessionCount++;
|
|
session.Dispose();
|
|
}
|
|
}
|
|
|
|
private sealed class FailOnceTextWriter : StringWriter
|
|
{
|
|
public bool FailNextWrite { get; set; }
|
|
|
|
public override void WriteLine(string? value)
|
|
{
|
|
if (FailNextWrite)
|
|
{
|
|
FailNextWrite = false;
|
|
throw new IOException("fixture write failure");
|
|
}
|
|
base.WriteLine(value);
|
|
}
|
|
}
|
|
|
|
}
|