Compare commits

...

1 Commits

Author SHA1 Message Date
KyuubiYoru e626b89909 build: bootstrap solution and quality gates (#3)
quality-gate / quality (push) Successful in 58s
Closes #3
2026-07-16 04:25:54 +02:00
28 changed files with 803 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{cs,csproj,props,targets}]
indent_style = space
indent_size = 4
[*.cs]
dotnet_sort_system_directives_first = true
csharp_new_line_before_open_brace = all
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = false:suggestion
[*.{json,yml,yaml}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
+34
View File
@@ -0,0 +1,34 @@
name: quality-gate
on:
push:
branches:
- main
- codex/**
pull_request:
workflow_dispatch:
jobs:
quality:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Install .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.301
- name: Restore locked dependencies
run: dotnet restore Rendezvous.slnx --locked-mode
- name: Verify formatting and analyzers
run: dotnet format Rendezvous.slnx --verify-no-changes --no-restore
- name: Build
run: dotnet build Rendezvous.slnx --configuration Release --no-restore
- name: Test
run: dotnet test Rendezvous.slnx --configuration Release --no-build
+8
View File
@@ -0,0 +1,8 @@
**/bin/
**/obj/
TestResults/
.idea/
.vs/
*.suo
*.user
*.userosscache
+15
View File
@@ -0,0 +1,15 @@
<Project>
<PropertyGroup>
<AnalysisLevel>latest-recommended</AnalysisLevel>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
<Deterministic>true</Deterministic>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<RestoreLockedMode Condition="'$(CI)' == 'true'">true</RestoreLockedMode>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
+12
View File
@@ -0,0 +1,12 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="LiteNetLib" Version="2.1.4" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
</ItemGroup>
</Project>
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
+18
View File
@@ -79,3 +79,21 @@ Rendezvous is currently in its initial design and bootstrap stage. The first imp
The ratified v1 boundaries, trust decisions, privacy rules, safety budgets, and
threat model are indexed in [the architecture documentation](docs/architecture/README.md).
## Development
The repository pins .NET SDK 10.0.301. From a clean clone, run the same gates as
CI from the repository root:
```bash
dotnet restore Rendezvous.slnx --locked-mode
dotnet format Rendezvous.slnx --verify-no-changes --no-restore
dotnet build Rendezvous.slnx --configuration Release --no-restore
dotnet test Rendezvous.slnx --configuration Release --no-build
```
Run the bootstrap server with
`dotnet run --project src/FinalFactory.Rendezvous.Server`. It serves HTTP health endpoints and binds
the configured UDP mediator port; both stop through normal host cancellation.
The project dependency rules and supported runtime choices are documented in
[project and dependency boundaries](docs/architecture/project-boundaries.md).
+11
View File
@@ -0,0 +1,11 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/FinalFactory.Rendezvous.Client/FinalFactory.Rendezvous.Client.csproj" />
<Project Path="src/FinalFactory.Rendezvous.Contracts/FinalFactory.Rendezvous.Contracts.csproj" />
<Project Path="src/FinalFactory.Rendezvous.Server/FinalFactory.Rendezvous.Server.csproj" />
<Project Path="src/FinalFactory.Rendezvous.TestClient/FinalFactory.Rendezvous.TestClient.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/FinalFactory.Rendezvous.Tests/FinalFactory.Rendezvous.Tests.csproj" />
</Folder>
</Solution>
+45
View File
@@ -0,0 +1,45 @@
# Project and dependency boundaries
Tracking: #3
```text
FinalFactory.Rendezvous.Contracts <- FinalFactory.Rendezvous.Client
^ ^
| |
FinalFactory.Rendezvous.Server FinalFactory.Rendezvous.TestClient
```
- `Contracts` targets `netstandard2.1` and contains only versioned,
transport-neutral IDs and wire contracts. It cannot reference Server,
LiteNetLib, or Godot.
- `Client` targets `netstandard2.1`, references Contracts and the pinned
LiteNetLib package, and contains no Godot or Server dependency.
- `Server` targets .NET 10 LTS, references Contracts and LiteNetLib, and owns
HTTP hosting, UDP mediation, application policy, and ephemeral state.
- `TestClient` targets .NET 8, references only the public Client/Contracts seams
and LiteNetLib, and must never reach into Server internals.
- `Tests` target .NET 10 and may reference every project solely to verify public
behavior and architecture boundaries.
The dependency-boundary tests inspect compiled assembly references. A forbidden
engine, transport, or server dependency therefore fails the normal test gate.
## Supported toolchain
- Build SDK: .NET SDK 10.0.301, pinned by `global.json`.
- Server runtime: .NET 10 LTS.
- Client/contracts compatibility target: .NET Standard 2.1, consumable by the
.NET 8-or-later runtime used by current Godot 4 C# projects.
- TestClient runtime: .NET 8.
- LiteNetLib: 2.1.4, pinned centrally and restored from the lock files.
The repository uses central package versions, per-project lock files,
deterministic compilation, nullable reference types, warnings as errors, current
.NET analyzers, and formatting verification. CI restores in locked mode so a
package graph change must be deliberate and committed.
Primary compatibility references:
- [.NET support policy](https://dotnet.microsoft.com/en-us/platform/support/policy)
- [Godot stable C# prerequisites](https://docs.godotengine.org/en/stable/tutorials/scripting/c_sharp/c_sharp_basics.html)
- [LiteNetLib 2.1.4 on NuGet](https://www.nuget.org/packages/LiteNetLib/2.1.4)
+7
View File
@@ -0,0 +1,7 @@
{
"sdk": {
"version": "10.0.301",
"rollForward": "disable",
"allowPrerelease": false
}
}
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<AssemblyName>FinalFactory.Rendezvous.Client</AssemblyName>
<RootNamespace>FinalFactory.Rendezvous.Client</RootNamespace>
<IsPackable>true</IsPackable>
<PackageId>FinalFactory.Rendezvous.Client</PackageId>
<Description>Godot-independent client SDK for Final Factory Rendezvous.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../FinalFactory.Rendezvous.Contracts/FinalFactory.Rendezvous.Contracts.csproj" />
<PackageReference Include="LiteNetLib" />
</ItemGroup>
</Project>
@@ -0,0 +1,16 @@
{
"version": 2,
"dependencies": {
".NETStandard,Version=v2.1": {
"LiteNetLib": {
"type": "Direct",
"requested": "[2.1.4, )",
"resolved": "2.1.4",
"contentHash": "KWlxvMw3Urpqj9joD96LRiK+LC62pQNs/zkXRJc+rHnxgkGp+vV703xzDrxRmv+V1YhCFfIGzs5nrVWtREIlyA=="
},
"finalfactory.rendezvous.contracts": {
"type": "Project"
}
}
}
}
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<AssemblyName>FinalFactory.Rendezvous.Contracts</AssemblyName>
<RootNamespace>FinalFactory.Rendezvous.Contracts</RootNamespace>
<IsPackable>true</IsPackable>
<PackageId>FinalFactory.Rendezvous.Contracts</PackageId>
<Description>Versioned transport-neutral contracts for Final Factory Rendezvous.</Description>
</PropertyGroup>
</Project>
@@ -0,0 +1,6 @@
{
"version": 2,
"dependencies": {
".NETStandard,Version=v2.1": {}
}
}
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<AssemblyName>FinalFactory.Rendezvous.Server</AssemblyName>
<RootNamespace>FinalFactory.Rendezvous.Server</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../FinalFactory.Rendezvous.Contracts/FinalFactory.Rendezvous.Contracts.csproj" />
<PackageReference Include="LiteNetLib" />
</ItemGroup>
</Project>
@@ -0,0 +1,31 @@
using System.Net;
using FinalFactory.Rendezvous.Server.Transport;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services
.AddOptions<UdpMediatorOptions>()
.BindConfiguration(UdpMediatorOptions.SectionName)
.ValidateDataAnnotations()
.Validate(
options => IPAddress.TryParse(options.ListenAddress, out _),
$"{UdpMediatorOptions.SectionName}:ListenAddress must be an IP address.")
.ValidateOnStart();
builder.Services.AddSingleton<UdpMediatorService>();
builder.Services.AddHostedService(static services => services.GetRequiredService<UdpMediatorService>());
WebApplication app = builder.Build();
app.MapGet("/health/live", static () => Results.Ok(new { status = "live" }));
app.MapGet(
"/health/ready",
static (UdpMediatorService mediator) => mediator.LocalEndpoint is null
? Results.StatusCode(StatusCodes.Status503ServiceUnavailable)
: Results.Ok(new { status = "ready" }));
await app.RunAsync();
/// <summary>
/// Entry point marker used by integration-test hosts.
/// </summary>
public partial class Program;
@@ -0,0 +1,26 @@
using System.ComponentModel.DataAnnotations;
namespace FinalFactory.Rendezvous.Server.Transport;
/// <summary>
/// Configures the UDP endpoint reserved for the Rendezvous mediator.
/// </summary>
public sealed class UdpMediatorOptions
{
/// <summary>
/// Configuration section containing UDP mediator settings.
/// </summary>
public const string SectionName = "Rendezvous:Udp";
/// <summary>
/// Gets or sets the numeric IP address to bind.
/// </summary>
[Required]
public string ListenAddress { get; set; } = "0.0.0.0";
/// <summary>
/// Gets or sets the UDP port. Zero requests an ephemeral port for tests.
/// </summary>
[Range(0, 65_535)]
public int Port { get; set; } = 9050;
}
@@ -0,0 +1,116 @@
using System.Net;
using System.Net.Sockets;
using Microsoft.Extensions.Options;
namespace FinalFactory.Rendezvous.Server.Transport;
/// <summary>
/// Owns the cancellable UDP socket used by the future NAT mediator.
/// </summary>
public sealed partial class UdpMediatorService : BackgroundService
{
private readonly ILogger<UdpMediatorService> _logger;
private readonly UdpMediatorOptions _options;
private UdpClient? _udpClient;
/// <summary>
/// Initializes a new UDP mediator service.
/// </summary>
public UdpMediatorService(
IOptions<UdpMediatorOptions> options,
ILogger<UdpMediatorService> logger)
{
_options = options.Value;
_logger = logger;
}
/// <summary>
/// Gets the bound endpoint after startup completes.
/// </summary>
public IPEndPoint? LocalEndpoint { get; private set; }
/// <inheritdoc />
public override Task StartAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (_udpClient is not null)
{
throw new InvalidOperationException("The UDP mediator is already running.");
}
IPAddress listenAddress = IPAddress.Parse(_options.ListenAddress);
UdpClient udpClient = new(new IPEndPoint(listenAddress, _options.Port));
_udpClient = udpClient;
IPEndPoint localEndpoint =
(IPEndPoint?)udpClient.Client.LocalEndPoint
?? throw new InvalidOperationException("The UDP socket did not expose its bound endpoint.");
LocalEndpoint = localEndpoint;
LogMediatorListening(_logger, localEndpoint.Address, localEndpoint.Port);
return base.StartAsync(cancellationToken);
}
/// <inheritdoc />
public override async Task StopAsync(CancellationToken cancellationToken)
{
await base.StopAsync(cancellationToken).ConfigureAwait(false);
_udpClient?.Dispose();
_udpClient = null;
LocalEndpoint = null;
LogMediatorStopped(_logger);
}
/// <inheritdoc />
public override void Dispose()
{
_udpClient?.Dispose();
_udpClient = null;
LocalEndpoint = null;
base.Dispose();
}
/// <inheritdoc />
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
UdpClient udpClient = _udpClient
?? throw new InvalidOperationException("The UDP mediator socket was not initialized.");
try
{
while (!stoppingToken.IsCancellationRequested)
{
_ = await udpClient.ReceiveAsync(stoppingToken).ConfigureAwait(false);
// Bootstrap deliberately emits no UDP response. Protocol handling lands in #11.
}
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
// Expected during normal shutdown.
}
catch (ObjectDisposedException) when (stoppingToken.IsCancellationRequested)
{
// Disposing the socket is the fallback that releases a blocked receive.
}
finally
{
LocalEndpoint = null;
}
}
[LoggerMessage(
EventId = 1,
Level = LogLevel.Information,
Message = "UDP mediator listening on {ListenAddress}:{ListenPort}")]
private static partial void LogMediatorListening(
ILogger logger,
IPAddress listenAddress,
int listenPort);
[LoggerMessage(
EventId = 2,
Level = LogLevel.Information,
Message = "UDP mediator stopped")]
private static partial void LogMediatorStopped(ILogger logger);
}
@@ -0,0 +1,15 @@
{
"Rendezvous": {
"Udp": {
"ListenAddress": "0.0.0.0",
"Port": 9050
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
@@ -0,0 +1,16 @@
{
"version": 2,
"dependencies": {
"net10.0": {
"LiteNetLib": {
"type": "Direct",
"requested": "[2.1.4, )",
"resolved": "2.1.4",
"contentHash": "KWlxvMw3Urpqj9joD96LRiK+LC62pQNs/zkXRJc+rHnxgkGp+vV703xzDrxRmv+V1YhCFfIGzs5nrVWtREIlyA=="
},
"finalfactory.rendezvous.contracts": {
"type": "Project"
}
}
}
}
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>FinalFactory.Rendezvous.TestClient</AssemblyName>
<RootNamespace>FinalFactory.Rendezvous.TestClient</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../FinalFactory.Rendezvous.Client/FinalFactory.Rendezvous.Client.csproj" />
<ProjectReference Include="../FinalFactory.Rendezvous.Contracts/FinalFactory.Rendezvous.Contracts.csproj" />
<PackageReference Include="LiteNetLib" />
</ItemGroup>
</Project>
@@ -0,0 +1,16 @@
namespace FinalFactory.Rendezvous.TestClient;
/// <summary>
/// Bootstrap entry point for the public-SDK-only diagnostic client.
/// </summary>
public static class Program
{
/// <summary>
/// Runs the bootstrap diagnostic.
/// </summary>
public static int Main()
{
Console.WriteLine("Rendezvous TestClient bootstrap is ready.");
return 0;
}
}
@@ -0,0 +1,23 @@
{
"version": 2,
"dependencies": {
"net8.0": {
"LiteNetLib": {
"type": "Direct",
"requested": "[2.1.4, )",
"resolved": "2.1.4",
"contentHash": "KWlxvMw3Urpqj9joD96LRiK+LC62pQNs/zkXRJc+rHnxgkGp+vV703xzDrxRmv+V1YhCFfIGzs5nrVWtREIlyA=="
},
"finalfactory.rendezvous.client": {
"type": "Project",
"dependencies": {
"FinalFactory.Rendezvous.Contracts": "[1.0.0, )",
"LiteNetLib": "[2.1.4, )"
}
},
"finalfactory.rendezvous.contracts": {
"type": "Project"
}
}
}
}
@@ -0,0 +1,113 @@
using System.Reflection;
using System.Xml.Linq;
namespace FinalFactory.Rendezvous.Tests.Architecture;
public sealed class DependencyBoundaryTests
{
[Fact]
public void ContractsAreTransportAndEngineIndependent()
{
string[] references = GetReferences("FinalFactory.Rendezvous.Contracts");
Assert.DoesNotContain(references, IsGodotAssembly);
Assert.DoesNotContain(references, IsServerAssembly);
Assert.DoesNotContain(references, IsLiteNetLibAssembly);
}
[Fact]
public void ClientIsGodotAndServerIndependent()
{
string[] references = GetReferences("FinalFactory.Rendezvous.Client");
Assert.DoesNotContain(references, IsGodotAssembly);
Assert.DoesNotContain(references, IsServerAssembly);
}
[Fact]
public void TestClientUsesOnlyPublicRendezvousDependencies()
{
string[] references = GetReferences("FinalFactory.Rendezvous.TestClient");
Assert.DoesNotContain(references, IsGodotAssembly);
Assert.DoesNotContain(references, IsServerAssembly);
}
[Fact]
public void DeclaredDependencyGraphMatchesTheArchitecture()
{
AssertDeclaredDependencies(
"src/FinalFactory.Rendezvous.Contracts/FinalFactory.Rendezvous.Contracts.csproj",
[],
[]);
AssertDeclaredDependencies(
"src/FinalFactory.Rendezvous.Client/FinalFactory.Rendezvous.Client.csproj",
["FinalFactory.Rendezvous.Contracts"],
["LiteNetLib"]);
AssertDeclaredDependencies(
"src/FinalFactory.Rendezvous.Server/FinalFactory.Rendezvous.Server.csproj",
["FinalFactory.Rendezvous.Contracts"],
["LiteNetLib"]);
AssertDeclaredDependencies(
"src/FinalFactory.Rendezvous.TestClient/FinalFactory.Rendezvous.TestClient.csproj",
["FinalFactory.Rendezvous.Client", "FinalFactory.Rendezvous.Contracts"],
["LiteNetLib"]);
}
private static void AssertDeclaredDependencies(
string projectPath,
string[] expectedProjects,
string[] expectedPackages)
{
XDocument project = XDocument.Load(Path.Combine(FindRepositoryRoot(), projectPath));
string[] projects = project
.Descendants("ProjectReference")
.Select(static element =>
Path.GetFileNameWithoutExtension(element.Attribute("Include")?.Value) ?? string.Empty)
.Order(StringComparer.Ordinal)
.ToArray();
string[] packages = project
.Descendants("PackageReference")
.Select(static element => element.Attribute("Include")?.Value ?? string.Empty)
.Order(StringComparer.Ordinal)
.ToArray();
Assert.Equal(expectedProjects.Order(StringComparer.Ordinal), projects);
Assert.Equal(expectedPackages.Order(StringComparer.Ordinal), packages);
}
private static string FindRepositoryRoot()
{
DirectoryInfo? directory = new(AppContext.BaseDirectory);
while (directory is not null)
{
if (File.Exists(Path.Combine(directory.FullName, "Rendezvous.slnx")))
{
return directory.FullName;
}
directory = directory.Parent;
}
throw new DirectoryNotFoundException("Could not locate the Rendezvous repository root.");
}
private static string[] GetReferences(string assemblyName) => Assembly
.Load(assemblyName)
.GetReferencedAssemblies()
.Select(static reference => reference.Name ?? string.Empty)
.ToArray();
private static bool IsGodotAssembly(string assemblyName) =>
assemblyName.StartsWith("Godot", StringComparison.OrdinalIgnoreCase);
private static bool IsLiteNetLibAssembly(string assemblyName) =>
string.Equals(assemblyName, "LiteNetLib", StringComparison.Ordinal);
private static bool IsServerAssembly(string assemblyName) =>
string.Equals(
assemblyName,
"FinalFactory.Rendezvous.Server",
StringComparison.Ordinal);
}
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<AssemblyName>FinalFactory.Rendezvous.Tests</AssemblyName>
<RootNamespace>FinalFactory.Rendezvous.Tests</RootNamespace>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../src/FinalFactory.Rendezvous.Client/FinalFactory.Rendezvous.Client.csproj" />
<ProjectReference Include="../../src/FinalFactory.Rendezvous.Contracts/FinalFactory.Rendezvous.Contracts.csproj" />
<ProjectReference Include="../../src/FinalFactory.Rendezvous.Server/FinalFactory.Rendezvous.Server.csproj" />
<ProjectReference Include="../../src/FinalFactory.Rendezvous.TestClient/FinalFactory.Rendezvous.TestClient.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1 @@
global using Xunit;
@@ -0,0 +1,34 @@
using System.Net;
using FinalFactory.Rendezvous.Server.Transport;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
namespace FinalFactory.Rendezvous.Tests.Server;
public sealed class UdpMediatorServiceTests
{
[Fact]
public async Task ServiceBindsAnEphemeralUdpPortAndStopsCleanly()
{
using CancellationTokenSource timeout = new(TimeSpan.FromSeconds(5));
UdpMediatorOptions options = new()
{
ListenAddress = IPAddress.Loopback.ToString(),
Port = 0,
};
using UdpMediatorService service = new(
Options.Create(options),
NullLogger<UdpMediatorService>.Instance);
await service.StartAsync(timeout.Token);
IPEndPoint? boundEndpoint = service.LocalEndpoint;
Assert.NotNull(boundEndpoint);
Assert.Equal(IPAddress.Loopback, boundEndpoint.Address);
Assert.InRange(boundEndpoint.Port, 1, 65_535);
await service.StopAsync(timeout.Token);
Assert.Null(service.LocalEndpoint);
}
}
@@ -0,0 +1,129 @@
{
"version": 2,
"dependencies": {
"net10.0": {
"Microsoft.NET.Test.Sdk": {
"type": "Direct",
"requested": "[18.4.0, )",
"resolved": "18.4.0",
"contentHash": "w49iZdL4HL6V25l41NVQLXWQ+e71GvSkKVteMrOL02gP/PUkcnO/1yEb2s9FntU4wGmJWfKnyrRAhcMHd9ZZNA==",
"dependencies": {
"Microsoft.CodeCoverage": "18.4.0",
"Microsoft.TestPlatform.TestHost": "18.4.0"
}
},
"xunit": {
"type": "Direct",
"requested": "[2.9.3, )",
"resolved": "2.9.3",
"contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
"dependencies": {
"xunit.analyzers": "1.18.0",
"xunit.assert": "2.9.3",
"xunit.core": "[2.9.3]"
}
},
"xunit.runner.visualstudio": {
"type": "Direct",
"requested": "[3.1.5, )",
"resolved": "3.1.5",
"contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA=="
},
"Microsoft.CodeCoverage": {
"type": "Transitive",
"resolved": "18.4.0",
"contentHash": "9O0BtCfzCWrkAmK187ugKdq72HHOXoOUjuWFDVc2LsZZ0pOnA9bTt+Sg9q4cF+MoAaUU+MuWtvBuFsnduviJow=="
},
"Microsoft.TestPlatform.ObjectModel": {
"type": "Transitive",
"resolved": "18.4.0",
"contentHash": "4L6m2kS2pY5uJ9cpeRxzW22opr6ttScIRqsOpMDQpgENp/ZwxkkQCcmc6LRSURo2dFaaSW5KVflQZvroiJ7Wzg=="
},
"Microsoft.TestPlatform.TestHost": {
"type": "Transitive",
"resolved": "18.4.0",
"contentHash": "gZsCHI+zOmZCcKZieIL4Jg14qKD2OGZOmX5DehuIk1EA9BN6Crm0+taXQNEuajOH1G9CCyBxw8VWR4t5tumcng==",
"dependencies": {
"Microsoft.TestPlatform.ObjectModel": "18.4.0",
"Newtonsoft.Json": "13.0.3"
}
},
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.3",
"contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
},
"xunit.abstractions": {
"type": "Transitive",
"resolved": "2.0.3",
"contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
},
"xunit.analyzers": {
"type": "Transitive",
"resolved": "1.18.0",
"contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
},
"xunit.assert": {
"type": "Transitive",
"resolved": "2.9.3",
"contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
},
"xunit.core": {
"type": "Transitive",
"resolved": "2.9.3",
"contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
"dependencies": {
"xunit.extensibility.core": "[2.9.3]",
"xunit.extensibility.execution": "[2.9.3]"
}
},
"xunit.extensibility.core": {
"type": "Transitive",
"resolved": "2.9.3",
"contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
"dependencies": {
"xunit.abstractions": "2.0.3"
}
},
"xunit.extensibility.execution": {
"type": "Transitive",
"resolved": "2.9.3",
"contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
"dependencies": {
"xunit.extensibility.core": "[2.9.3]"
}
},
"finalfactory.rendezvous.client": {
"type": "Project",
"dependencies": {
"FinalFactory.Rendezvous.Contracts": "[1.0.0, )",
"LiteNetLib": "[2.1.4, )"
}
},
"finalfactory.rendezvous.contracts": {
"type": "Project"
},
"finalfactory.rendezvous.server": {
"type": "Project",
"dependencies": {
"FinalFactory.Rendezvous.Contracts": "[1.0.0, )",
"LiteNetLib": "[2.1.4, )"
}
},
"finalfactory.rendezvous.testclient": {
"type": "Project",
"dependencies": {
"FinalFactory.Rendezvous.Client": "[1.0.0, )",
"FinalFactory.Rendezvous.Contracts": "[1.0.0, )",
"LiteNetLib": "[2.1.4, )"
}
},
"LiteNetLib": {
"type": "CentralTransitive",
"requested": "[2.1.4, )",
"resolved": "2.1.4",
"contentHash": "KWlxvMw3Urpqj9joD96LRiK+LC62pQNs/zkXRJc+rHnxgkGp+vV703xzDrxRmv+V1YhCFfIGzs5nrVWtREIlyA=="
}
}
}
}