Skip to content

Commit

Permalink
tests(net): wait for node initialization to complete (#647)
Browse files Browse the repository at this point in the history
  • Loading branch information
DennisInSky authored Nov 15, 2024
1 parent c9c54ba commit fa10414
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 145 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Threading.Tasks;
using Sails.Remoting.Tests._Infra.XUnit.Fixtures;
using Sails.Tests.Shared.XUnit;
using Substrate.Gear.Api.Generated;
using Substrate.NetApi.Model.Extrinsics;
using Xunit;

namespace Sails.Remoting.Tests.Core;
Expand All @@ -17,9 +19,11 @@ public RemotingViaNodeClientTests(SailsFixture sailsFixture)
[Fact]
public async Task Test()
{
var demoIdl = await this.sailsFixture.GetDemoContractIdlAsync();
var demoContractWasm = await this.sailsFixture.GetDemoContractWasmAsync();
var noSvcsProgIdl = await this.sailsFixture.GetNoSvcsProgContractIdlAsync();
var gearNodeWsUrl = this.sailsFixture.GearNodeWsUrl;
var nodeWsUrl = this.sailsFixture.GearNodeWsUrl;

using (var nodeClient = new SubstrateClientExt(nodeWsUrl, ChargeTransactionPayment.Default()))
{
await nodeClient.ConnectAsync();
}
}
}
1 change: 1 addition & 0 deletions net/tests/Sails.Remoting.Tests/Sails.Remoting.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Sails.Remoting\Sails.Remoting.csproj" />
<ProjectReference Include="..\Sails.Tests.Shared\Sails.Tests.Shared.csproj" />
</ItemGroup>

Expand Down
101 changes: 99 additions & 2 deletions net/tests/Sails.Tests.Shared/Containers/GearNodeContainer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers;
using EnsureThat;

Expand All @@ -17,6 +20,7 @@ public GearNodeContainer(string gearNodeVersion, bool reuse)
{
EnsureArg.IsNotNullOrWhiteSpace(gearNodeVersion, nameof(gearNodeVersion));

this.nodeInitializationDetector = new NodeInitializationDetector();
this.container = new ContainerBuilder()
.WithName("gear-node-for-tests")
.WithImage($"ghcr.io/gear-tech/node:v{gearNodeVersion}")
Expand All @@ -28,12 +32,15 @@ public GearNodeContainer(string gearNodeVersion, bool reuse)
"--tmp")
.WithEnvironment("RUST_LOG", "gear=debug,pallet_gear=debug,gwasm=debug")
.WithReuse(reuse)
.WithOutputConsumer(this.nodeInitializationDetector)
.Build();
this.reuse = reuse;
}

private const ushort RpcPort = 9944;
private static readonly TimeSpan NodeInitializationTimeout = TimeSpan.FromSeconds(10);

private readonly NodeInitializationDetector nodeInitializationDetector;
private readonly IContainer container;
private readonly bool reuse;

Expand All @@ -48,6 +55,96 @@ public ValueTask DisposeAsync()
? ValueTask.CompletedTask
: this.container.DisposeAsync();

public Task StartAsync()
=> this.container.StartAsync();
public async Task StartAsync()
{
await this.container.StartAsync();
await this.nodeInitializationDetector.IsInitializedAsync(NodeInitializationTimeout);
}

private sealed class NodeInitializationDetector : IOutputConsumer
{
public NodeInitializationDetector()
{
this.isNodeInitialized = new TaskCompletionSource();
this.nodeStdout = new NodeOutput(this.HandleNodeOutput);
this.nodeStrerr = new NodeOutput(this.HandleNodeOutput);
}

private readonly TaskCompletionSource isNodeInitialized;
private readonly NodeOutput nodeStdout;
private readonly NodeOutput nodeStrerr;

public bool Enabled => !this.isNodeInitialized.Task.IsCompleted;
Stream IOutputConsumer.Stdout => this.nodeStdout;
Stream IOutputConsumer.Stderr => this.nodeStrerr;

public async Task IsInitializedAsync(TimeSpan maxWaitTime)
{
var timeoutTask = Task.Delay(maxWaitTime);
var completedTask = await Task.WhenAny(this.isNodeInitialized.Task, timeoutTask);
if (completedTask == timeoutTask)
{
this.isNodeInitialized.SetException(
new TimeoutException($"Node initialization timed out after {maxWaitTime}."));
await this.isNodeInitialized.Task;
}
}

public void Dispose()
{
this.isNodeInitialized.SetCanceled();
this.nodeStrerr.Dispose();
this.nodeStdout.Dispose();
GC.SuppressFinalize(this);
}

private void HandleNodeOutput(string output)
{
if (this.Enabled && output.Contains("Initialization of block #"))
{
this.isNodeInitialized.SetResult();
}
}

private sealed class NodeOutput : Stream
{
public NodeOutput(Action<string> output)
{
this.output = output;
this.length = 0;
}

private readonly Action<string> output;
private long length;

public override bool CanRead => false;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length => this.length;
public override long Position
{
get => this.Length;
set => throw new NotImplementedException();
}

public override void Write(byte[] buffer, int offset, int count)
{
var message = Encoding.UTF8.GetString(buffer, offset, count);
this.output(message);
this.length += count;
}

public override void Flush()
=> throw new NotImplementedException();

public override int Read(byte[] buffer, int offset, int count)
=> throw new NotImplementedException();

public override long Seek(long offset, SeekOrigin origin)
=> throw new NotImplementedException();

public override void SetLength(long value)
=> throw new NotImplementedException();
}
}
}
139 changes: 0 additions & 139 deletions net/tests/Sails.Tests.Shared/XUnit/Fixtures/SailsFixture.cs

This file was deleted.

0 comments on commit fa10414

Please sign in to comment.