using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using XFEExtension.NetCore.XUnit.Attributes;
using XFEEDToolBox.Core.Execution;
namespace XFEEDToolBox.Test;
[TestFixture]
public class NativeCommunityGoalClientTests
{
private sealed class Channel:IOriginalCommerceChannel
{
public string Identity=>"test-session";
public int Reads,Polls,Refusals=2;
public string? Failure;
public bool CancelAfterRefusal;
public CancellationTokenSource? Stop;
public Task<JsonElement> CallAsync(object request,CancellationToken token)
{
token.ThrowIfCancellationRequested();var command=JsonSerializer.SerializeToElement(request);
var op=command.GetProperty("op").GetString();
if(op=="cg.read.mine")
{
Reads++;
if(Failure is not null)throw new InvalidOperationException(Failure);
if(Reads<=Refusals){if(CancelAfterRefusal)Stop!.Cancel();throw new InvalidOperationException("Fresh healthy docking at the requested CG market required");}
return Task.FromResult(JsonSerializer.SerializeToElement(new{generation=17UL}));
}
if(op!="cg.status")throw new InvalidOperationException("Unexpected mutation or replay");
Check.Equal(17UL,command.GetProperty("generation").GetUInt64());Polls++;
if(Polls==1)throw new IOException("Transient status transport failure");
const string body="{\"initiatives\":[]}";
return Task.FromResult(JsonSerializer.SerializeToElement(new{state=3,kind=2,mutationsSent=0,fresh=true,responseBody=body,
proof=Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes(body)))}));
}
}
[Test] public async Task DockingTransitionWaitsThenPollsOnlyTheAcceptedRead()
{
var channel=new Channel();var result=await new NativeCommunityGoalClient(channel).ReadAsync(true);
Check.Equal(3,channel.Reads);Check.Equal(2,channel.Polls);Check.Equal(17UL,result.Generation);Check.Equal(0,result.Goals.Count);
}
[Test] public async Task UnrelatedRefusalRemainsTerminal()
{
var channel=new Channel{Failure="Wrong game identity"};var rejected=false;
try{await new NativeCommunityGoalClient(channel).ReadAsync(true);}catch(InvalidOperationException error){rejected=error.Message==channel.Failure;}
Check.That(rejected);Check.Equal(1,channel.Reads);Check.Equal(0,channel.Polls);
}
[Test] public async Task CancellationDuringDockingWaitDoesNotQueueAnotherRequest()
{
using var stop=new CancellationTokenSource();var channel=new Channel{Stop=stop,CancelAfterRefusal=true};var cancelled=false;
try{await new NativeCommunityGoalClient(channel).ReadAsync(true,stop.Token);}catch(OperationCanceledException){cancelled=true;}
Check.That(cancelled);Check.Equal(1,channel.Reads);Check.Equal(0,channel.Polls);
}
}
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using XFEExtension.NetCore.XUnit.Attributes;
using XFEEDToolBox.Core.Execution;
namespace XFEEDToolBox.Test;
[TestFixture]
public class NativeCommunityGoalClientTests
{
private sealed class Channel:IOriginalCommerceChannel
{
public string Identity=>"test-session";
public int Reads,Polls,Refusals=2;
public string? Failure;
public bool CancelAfterRefusal;
public CancellationTokenSource? Stop;
public Task<JsonElement> CallAsync(object request,CancellationToken token)
{
token.ThrowIfCancellationRequested();var command=JsonSerializer.SerializeToElement(request);
var op=command.GetProperty("op").GetString();
if(op=="cg.read.mine")
{
Reads++;
if(Failure is not null)throw new InvalidOperationException(Failure);
if(Reads<=Refusals){if(CancelAfterRefusal)Stop!.Cancel();throw new InvalidOperationException("Fresh healthy docking at the requested CG market required");}
return Task.FromResult(JsonSerializer.SerializeToElement(new{generation=17UL}));
}
if(op!="cg.status")throw new InvalidOperationException("Unexpected mutation or replay");
Check.Equal(17UL,command.GetProperty("generation").GetUInt64());Polls++;
if(Polls==1)throw new IOException("Transient status transport failure");
const string body="{\"initiatives\":[]}";
return Task.FromResult(JsonSerializer.SerializeToElement(new{state=3,kind=2,mutationsSent=0,fresh=true,responseBody=body,
proof=Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes(body)))}));
}
}
[Test] public async Task DockingTransitionWaitsThenPollsOnlyTheAcceptedRead()
{
var channel=new Channel();var result=await new NativeCommunityGoalClient(channel).ReadAsync(true);
Check.Equal(3,channel.Reads);Check.Equal(2,channel.Polls);Check.Equal(17UL,result.Generation);Check.Equal(0,result.Goals.Count);
}
[Test] public async Task UnrelatedRefusalRemainsTerminal()
{
var channel=new Channel{Failure="Wrong game identity"};var rejected=false;
try{await new NativeCommunityGoalClient(channel).ReadAsync(true);}catch(InvalidOperationException error){rejected=error.Message==channel.Failure;}
Check.That(rejected);Check.Equal(1,channel.Reads);Check.Equal(0,channel.Polls);
}
[Test] public async Task CancellationDuringDockingWaitDoesNotQueueAnotherRequest()
{
using var stop=new CancellationTokenSource();var channel=new Channel{Stop=stop,CancelAfterRefusal=true};var cancelled=false;
try{await new NativeCommunityGoalClient(channel).ReadAsync(true,stop.Token);}catch(OperationCanceledException){cancelled=true;}
Check.That(cancelled);Check.Equal(1,channel.Reads);Check.Equal(0,channel.Polls);
}
}