feat(client): add rendezvous traversal coordinators (#12)
quality-gate / quality (push) Successful in 56s

This commit is contained in:
KyuubiYoru
2026-07-16 08:39:05 +02:00
parent 6d076c281a
commit b4b6072fe1
28 changed files with 2949 additions and 57 deletions
@@ -56,11 +56,16 @@ public sealed class NatMediationProcessorTests
Assert.Equal(Endpoint("192.168.1.11", 42_000), plan.ClientLocal);
Assert.Equal(Endpoint("203.0.113.20", 51_000), plan.HostPublic);
Assert.Equal(Endpoint("203.0.113.20", 52_000), plan.ClientPublic);
Assert.Equal(43, plan.ConnectionTicket.Length);
Assert.DoesNotContain(plan.ConnectionTicket, plan.ToString(), StringComparison.Ordinal);
Assert.True(NatIntroductionTokenCodec.TryDecode(
plan.IntroductionToken,
out NatIntroductionToken? introduction));
Assert.NotNull(introduction);
Assert.Equal(attempt.AttemptId, introduction.AttemptId);
Assert.Equal(43, introduction.ConnectionTicket.Length);
Assert.DoesNotContain(introduction.ConnectionTicket, plan.ToString(), StringComparison.Ordinal);
Assert.True(fixture.Sessions.Capabilities.TryFingerprint(
plan.ConnectionTicket,
introduction.ConnectionTicket,
out SecretFingerprint ticketFingerprint));
Assert.True(fixture.Sessions.Store.ConsumeConnectionTicket(new(
attempt.AttemptId,
@@ -187,7 +192,7 @@ public sealed class NatMediationProcessorTests
}
[Fact]
public async Task CancellationCannotReportSuccessAfterIntroductionIsConsumed()
public async Task CancellationAfterIntroductionCreatesAHostRevocationTombstone()
{
using JoinAttemptFixture fixture = new();
(RegisterSessionResponse registration, _) = fixture.CreateHost();
@@ -209,9 +214,16 @@ public sealed class NatMediationProcessorTests
attempt.AttemptId,
attempt.ClientCapability);
Assert.Equal(RendezvousErrorCode.Conflict, cancelled.Error);
Assert.True(cancelled.Succeeded);
sink.Release();
Assert.Equal(NatMediationResult.Introduced, await completion);
HostJoinAttempt cancelledAttempt = Assert.Single(fixture.Service.BrowseForHost(
registration.ListingId,
ContractLimits.ContractVersion,
registration.LeaseToken,
ContractLimits.BrowserPageMaxItems,
null).Value!.Items);
Assert.True(cancelledAttempt.IsCancelled);
}
[Fact]
@@ -159,7 +159,12 @@ public sealed class UdpMediatorServiceTests
string hostTicket = Assert.Single(hostTickets.Distinct(StringComparer.Ordinal));
string clientTicket = Assert.Single(clientTickets.Distinct(StringComparer.Ordinal));
Assert.Equal(hostTicket, clientTicket);
Assert.Equal(43, hostTicket.Length);
Assert.True(NatIntroductionTokenCodec.TryDecode(
hostTicket,
out NatIntroductionToken? introduction));
Assert.NotNull(introduction);
Assert.Equal(created.AttemptId, introduction.AttemptId);
Assert.Equal(43, introduction.ConnectionTicket.Length);
}
finally
{
@@ -229,7 +234,9 @@ public sealed class UdpMediatorServiceTests
Assert.True(
hostIntroduction.Buffer.Length + clientIntroduction.Buffer.Length
<= clientDatagram.Length * 2,
"The completing authenticated contribution exceeded the 2.0 response-byte budget.");
$"The completing authenticated contribution exceeded the 2.0 response-byte budget: "
+ $"responses={hostIntroduction.Buffer.Length + clientIntroduction.Buffer.Length}, "
+ $"request={clientDatagram.Length}.");
}
finally
{