119 lines
4.1 KiB
C#
119 lines
4.1 KiB
C#
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Text;
|
|
using game.Account;
|
|
using game.PlayerDataStorage;
|
|
|
|
static class ReviewTests
|
|
{
|
|
static async Task Main()
|
|
{
|
|
try
|
|
{
|
|
await QueuedRefreshCloseReturnsStructuredResult().WaitAsync(TimeSpan.FromSeconds(5));
|
|
Console.WriteLine("PASS queued-refresh-close-returns-structured-result");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"FAIL queued-refresh-close-returns-structured-result {ex.GetType().Name}: {ex.Message}");
|
|
Environment.ExitCode = 1;
|
|
}
|
|
}
|
|
|
|
static async Task QueuedRefreshCloseReturnsStructuredResult()
|
|
{
|
|
var entered = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
var release = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
var handler = new BlockingMutationHandler(entered, release);
|
|
var session = await Login("101");
|
|
var opened = await PlayerKVSessionOwner.CreateAsync(
|
|
session,
|
|
Root(),
|
|
"101",
|
|
new Uri("http://unit.test/"),
|
|
1,
|
|
epoch => epoch == 1,
|
|
default,
|
|
_ => handler);
|
|
Check(opened.Success, "owner open failed");
|
|
|
|
opened.Owner.Update("review", "value");
|
|
var flush = opened.Owner.FlushAsync();
|
|
await entered.Task;
|
|
|
|
var refresh = opened.Owner.RefreshAsync();
|
|
var close = opened.Owner.CloseAsync();
|
|
release.TrySetResult(true);
|
|
|
|
PlayerKVResult refreshResult;
|
|
try
|
|
{
|
|
refreshResult = await refresh;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception("RefreshAsync leaked an exception instead of returning PlayerKVResult", ex);
|
|
}
|
|
|
|
Check(!refreshResult.Success, "refresh should not succeed after owner close");
|
|
await flush;
|
|
await close;
|
|
session.Dispose();
|
|
}
|
|
|
|
static async Task<S101AccountSession> Login(string id)
|
|
{
|
|
var session = new S101AccountSession(new Uri("http://unit.test/"), jar => new DelegateHandler(_ =>
|
|
{
|
|
jar.Add(new Uri("http://unit.test/"), new Cookie("sid", id));
|
|
return Json(HttpStatusCode.OK, "{\"state\":0,\"internalId\":1,\"newCreate\":false,\"createTime\":\"2026-01-01T00:00:00Z\",\"svrTime\":\"2026-01-01T00:00:00Z\"}");
|
|
}));
|
|
Check((await session.LoginAsync(id)).Success, "login failed");
|
|
return session;
|
|
}
|
|
|
|
static string Root()
|
|
{
|
|
var path = Path.Combine(Path.GetTempPath(), "kv3b-review-" + Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(path);
|
|
return path;
|
|
}
|
|
|
|
static HttpResponseMessage Json(HttpStatusCode code, string json) =>
|
|
new(code) { Content = new StringContent(json, Encoding.UTF8, "application/json") };
|
|
|
|
static void Check(bool value, string message)
|
|
{
|
|
if (!value) throw new Exception(message);
|
|
}
|
|
|
|
sealed class DelegateHandler : HttpMessageHandler
|
|
{
|
|
readonly Func<HttpRequestMessage, HttpResponseMessage> action;
|
|
internal DelegateHandler(Func<HttpRequestMessage, HttpResponseMessage> action) => this.action = action;
|
|
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken token) =>
|
|
Task.FromResult(action(request));
|
|
}
|
|
|
|
sealed class BlockingMutationHandler : HttpMessageHandler
|
|
{
|
|
readonly TaskCompletionSource<bool> entered;
|
|
readonly TaskCompletionSource<bool> release;
|
|
internal BlockingMutationHandler(TaskCompletionSource<bool> entered, TaskCompletionSource<bool> release)
|
|
{
|
|
this.entered = entered;
|
|
this.release = release;
|
|
}
|
|
|
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken token)
|
|
{
|
|
if (request.RequestUri.AbsolutePath.EndsWith("/Get"))
|
|
return Json(HttpStatusCode.OK, "{\"data\":{}}");
|
|
|
|
entered.TrySetResult(true);
|
|
await release.Task;
|
|
return Json(HttpStatusCode.OK, "{}");
|
|
}
|
|
}
|
|
}
|