Initial commit

This commit is contained in:
BillyCool
2026-04-21 01:10:25 +10:00
commit c5595ea083
1752 changed files with 45767 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
using Grpc.Core;
namespace MariesWonderland.Tests.Infrastructure;
/// <summary>
/// Minimal <see cref="ServerCallContext"/> stub that pre-populates the
/// <c>x-apb-user-id</c> request header so service methods can call
/// <c>context.GetUserId()</c> without a real gRPC channel.
/// </summary>
public sealed class FakeServerCallContext : ServerCallContext
{
private readonly Metadata _requestHeaders;
private readonly Metadata _responseTrailers = [];
private readonly CancellationToken _cancellationToken;
private FakeServerCallContext(long userId, CancellationToken cancellationToken = default)
{
_requestHeaders = [new Metadata.Entry("x-apb-user-id", userId.ToString())];
_cancellationToken = cancellationToken;
}
public static FakeServerCallContext For(long userId, CancellationToken cancellationToken = default)
=> new(userId, cancellationToken);
protected override string MethodCore => string.Empty;
protected override string HostCore => string.Empty;
protected override string PeerCore => string.Empty;
protected override DateTime DeadlineCore => DateTime.MaxValue;
protected override Metadata RequestHeadersCore => _requestHeaders;
protected override CancellationToken CancellationTokenCore => _cancellationToken;
protected override Metadata ResponseTrailersCore => _responseTrailers;
protected override Status StatusCore { get; set; }
protected override WriteOptions? WriteOptionsCore { get; set; }
protected override AuthContext AuthContextCore => new(string.Empty, []);
protected override ContextPropagationToken CreatePropagationTokenCore(ContextPropagationOptions? options) =>
throw new NotImplementedException();
protected override Task WriteResponseHeadersAsyncCore(Metadata responseHeaders) =>
throw new NotImplementedException();
}

View File

@@ -0,0 +1,32 @@
using MariesWonderland.Configuration;
using MariesWonderland.Data;
using Microsoft.Extensions.Configuration;
namespace MariesWonderland.Tests.Infrastructure;
/// <summary>
/// Shared fixture that loads the master database and game config once per test collection.
/// Use as <c>IClassFixture&lt;MasterDatabaseFixture&gt;</c> on test classes that need master data.
/// </summary>
public sealed class MasterDatabaseFixture : IDisposable
{
public DarkMasterMemoryDatabase MasterDb { get; }
public GameConfig GameConfig { get; }
public MasterDatabaseFixture()
{
var config = new ConfigurationBuilder()
.SetBasePath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "src"))
.AddJsonFile("appsettings.json", optional: false)
.AddJsonFile("appsettings.Development.json", optional: true)
.Build();
var options = config.GetSection(ServerOptions.SectionName).Get<ServerOptions>()!;
var binPath = Path.Combine(options.Paths.MasterDatabase, $"{options.Data.LatestMasterDataVersion}.bin.e");
MasterDb = BinaryMasterDataLoader.Load(binPath);
GameConfig = GameConfig.From(MasterDb.EntityMConfig);
}
public void Dispose() { }
}

View File

@@ -0,0 +1,37 @@
using MariesWonderland.Data;
namespace MariesWonderland.Tests.Infrastructure;
/// <summary>
/// Base class for service-level tests. Concrete test classes should implement
/// <c>IClassFixture&lt;MasterDatabaseFixture&gt;</c> and pass the fixture here.
/// </summary>
public abstract class ServiceTestBase
{
protected DarkMasterMemoryDatabase MasterDb { get; }
protected GameConfig GameConfig { get; }
protected ServiceTestBase(MasterDatabaseFixture fixture)
{
MasterDb = fixture.MasterDb;
GameConfig = fixture.GameConfig;
}
/// <summary>Creates a fresh empty user database.</summary>
protected static DarkUserMemoryDatabase CreateUserDb() => new();
/// <summary>
/// Creates a <see cref="UserDataStore"/> pre-loaded with the given user database
/// so that <c>store.GetOrCreate(userId)</c> returns <paramref name="userDb"/>.
/// </summary>
protected static UserDataStore CreateStore(long userId, DarkUserMemoryDatabase userDb, DarkMasterMemoryDatabase masterDb)
{
var store = new UserDataStore(masterDb);
store.Set(userId, userDb);
return store;
}
/// <summary>Shorthand for <see cref="FakeServerCallContext.For"/>.</summary>
protected static FakeServerCallContext ContextFor(long userId = 1)
=> FakeServerCallContext.For(userId);
}