From 962162e7c63e97a96b9b6ea829bf2a42c99f98fb Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 23 Sep 2026 15:06:15 +0200 Subject: [PATCH] Rename and add the C# sides of Cut 5's external-provider lane NativeConnectorReceivesFromExternalManagedProvider -> NativeConnectorReceivesFromExternalProvider: CULTMESH_NATIVE_EXTERNAL_ENDPOINT's peer is no longer necessarily the C# managed provider now that Cut 5 adds a TypeScript one on the same lane; the env var name stays, since it names the endpoint, not the peer. Add ManagedConnectorReceivesFromExternalProvider, gated by the same env var: dials an external provider with the plain managed connector and no ValidateProviderCertificate (so acceptance runs on the endpoint's own cert-sha256 pin alone, like an ordinary consumer), and prints the received frame's CultMeshRealtimeWireProtocol re-encoding as hex for the harness's golden-bytes comparison against the TypeScript peer's own encoding. Co-Authored-By: Claude Sonnet 5 --- ...ultMeshNativeQuicRealtimeTransportTests.cs | 7 ++- .../CultMeshQuicRealtimeTransportTests.cs | 43 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/tests/GameCult.Mesh.Quic.Native.Tests/CultMeshNativeQuicRealtimeTransportTests.cs b/tests/GameCult.Mesh.Quic.Native.Tests/CultMeshNativeQuicRealtimeTransportTests.cs index 7eb99edc..e56b7c2e 100644 --- a/tests/GameCult.Mesh.Quic.Native.Tests/CultMeshNativeQuicRealtimeTransportTests.cs +++ b/tests/GameCult.Mesh.Quic.Native.Tests/CultMeshNativeQuicRealtimeTransportTests.cs @@ -13,11 +13,14 @@ namespace GameCult.Mesh.Quic.Native.Tests; public sealed class CultMeshNativeQuicRealtimeTransportTests { [Test] - public async Task NativeConnectorReceivesFromExternalManagedProvider() + public async Task NativeConnectorReceivesFromExternalProvider() { + // The provider on the other end is no longer necessarily the C# + // managed one (Cut 5 adds a TypeScript provider on this same lane); + // the env var name stays, since it names the endpoint, not the peer. var endpoint = Environment.GetEnvironmentVariable("CULTMESH_NATIVE_EXTERNAL_ENDPOINT"); if (string.IsNullOrWhiteSpace(endpoint)) - Assert.Ignore("Set CULTMESH_NATIVE_EXTERNAL_ENDPOINT to exercise cross-process native/managed parity."); + Assert.Ignore("Set CULTMESH_NATIVE_EXTERNAL_ENDPOINT to exercise cross-process native/external-provider parity."); var connector = new CultMeshNativeQuicRealtimeTransportConnector(); using var client = await connector.ConnectAsync( diff --git a/tests/GameCult.Mesh.Quic.Tests/CultMeshQuicRealtimeTransportTests.cs b/tests/GameCult.Mesh.Quic.Tests/CultMeshQuicRealtimeTransportTests.cs index ef739411..1ff6fd20 100644 --- a/tests/GameCult.Mesh.Quic.Tests/CultMeshQuicRealtimeTransportTests.cs +++ b/tests/GameCult.Mesh.Quic.Tests/CultMeshQuicRealtimeTransportTests.cs @@ -159,6 +159,49 @@ public sealed class CultMeshQuicRealtimeTransportTests session.State.Status.Should().Be(CultMeshSessionStatus.Offline); } + /// + /// Cut 5's cross-process lane: an external provider (normally the + /// TypeScript `cultmesh-quic-peer.ts serve` peer started by + /// `cultnet-interop.test.ts`) is dialed by the plain managed connector, + /// using only the endpoint's own `cert-sha256` pin — no + /// — + /// exactly as an ordinary consumer would. Prints the received frame's + /// re-encoding as hex so the harness can compare it against the same + /// frame's TypeScript-side encodeRealtimeFrame output (golden + /// bytes across processes, closing "TypeScript encodings decode in C#" + /// by process, not by shared file). + /// + [Test] + public async Task ManagedConnectorReceivesFromExternalProvider() + { + var endpoint = Environment.GetEnvironmentVariable("CULTMESH_NATIVE_EXTERNAL_ENDPOINT"); + if (string.IsNullOrWhiteSpace(endpoint)) + Assert.Ignore("Set CULTMESH_NATIVE_EXTERNAL_ENDPOINT to exercise cross-process managed/external-provider parity."); + + var connector = new CultMeshQuicRealtimeTransportConnector(); + using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var transport = await connector.ConnectAsync( + new CultMeshTransportCandidate(endpoint!), + new CultMeshSessionTarget("interop", "interop.ts-provider"), + timeout.Token); + try + { + var frame = await transport.ReceiveAsync(timeout.Token); + + transport.TransportId.Should().Be("msquic-realtime"); + frame.SchemaId.Should().NotBeNullOrWhiteSpace(); + frame.BodyId.Should().NotBeNullOrWhiteSpace(); + frame.Payload.Should().NotBeEmpty(); + + var hex = Convert.ToHexString(CultMeshRealtimeWireProtocol.EncodeFrame(frame)); + TestContext.Out.WriteLine($"CULTMESH_GOLDEN_HEX={hex}"); + } + finally + { + transport.Dispose(); + } + } + private static CultMeshSessionManager CreateSessions( CultMeshQuicRealtimeServer server, out IdentityObservation validatedIdentity)