using FinalFactory.Rendezvous.Contracts; using FinalFactory.Rendezvous.Server.State; namespace FinalFactory.Rendezvous.Tests.State; internal sealed class ManualRendezvousClock : IWallClock, IMonotonicClock { public ManualRendezvousClock(DateTimeOffset? utcNow = null) => UtcNow = utcNow ?? new DateTimeOffset(2026, 7, 16, 0, 0, 0, TimeSpan.Zero); public DateTimeOffset UtcNow { get; private set; } public TimeSpan Elapsed { get; private set; } public void Advance(TimeSpan duration) { Elapsed += duration; UtcNow += duration; } public void MoveWall(TimeSpan duration) => UtcNow += duration; } internal sealed class EphemeralStateFixture { private int _sequence; public EphemeralStateFixture(EphemeralStoreOptions? options = null) { Clock = new(); Store = new(options ?? new EphemeralStoreOptions(), Clock, Clock); } public ManualRendezvousClock Clock { get; } public InMemoryEphemeralRendezvousStore Store { get; } public TenantScope Scope { get; } = new(new GameId("space-game"), new EnvironmentId("test")); public CreateListingCommand ListingCommand( string owner = "publisher-1", string? idempotencyKey = null, string? requestFingerprint = null) { int sequence = Interlocked.Increment(ref _sequence); return new( idempotencyKey ?? $"register-{sequence}", requestFingerprint ?? $"request-{sequence}", new ListingDefinition { ListingId = NewListingId(), LeaseId = NewLeaseId(), Scope = Scope, OwnerSubject = owner, RegionId = new RegionId("eu-central"), ProtocolVersion = 7, BuildVersion = "1.2.3", DisplayName = "Test host", Visibility = ListingVisibility.Public, TrustMode = PublisherTrustMode.ManagedDedicated, CurrentPlayers = 1, MaximumPlayers = 8, Metadata = new Dictionary(StringComparer.Ordinal) { ["mode"] = "coop" }, LeaseFingerprint = Fingerprint($"lease-{sequence}"), HostPresenceHandle = NewHandle(), HostPresenceFingerprint = Fingerprint($"presence-{sequence}"), CapabilityDerivationSalt = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", }); } public StoredListing CreateVisibleListing(out CreateListingCommand command) { command = ListingCommand(); StoreResult created = Store.CreateListing(command); Assert.True(created.Succeeded); StoreResult bound = Store.BindHostPresence(new( command.Listing.HostPresenceHandle, command.Listing.HostPresenceFingerprint, PublicEndpoint(40_000), LocalEndpoint(40_000))); Assert.True(bound.Succeeded); return bound.Value!; } public CreateJoinAttemptCommand AttemptCommand(StoredListing listing, string owner = "client-1") { int sequence = Interlocked.Increment(ref _sequence); return new() { IdempotencyOwner = owner, IdempotencyKey = $"join-{sequence}", RequestFingerprint = $"join-request-{sequence}", ClientSubject = owner, AttemptId = NewAttemptId(), MediationHandle = NewHandle(), Scope = listing.Definition.Scope, ListingId = listing.Definition.ListingId, ProtocolVersion = listing.Definition.ProtocolVersion, HostCapabilityFingerprint = Fingerprint($"host-{sequence}"), ClientCapabilityFingerprint = Fingerprint($"client-{sequence}"), }; } public static SecretFingerprint Fingerprint(string value) => new(value); public static ObservedEndpoint PublicEndpoint(int port) => new(AddressFamilyKind.Ipv4, "203.0.113.10", port); public static ObservedEndpoint OtherPublicEndpoint(int port) => new(AddressFamilyKind.Ipv4, "198.51.100.20", port); public static ObservedEndpoint LocalEndpoint(int port) => new(AddressFamilyKind.Ipv4, "192.168.1.20", port); public static SessionListingId NewListingId() => new(Guid.NewGuid()); public static LeaseId NewLeaseId() => new(Guid.NewGuid()); public static JoinAttemptId NewAttemptId() => new(Guid.NewGuid()); public static MediationHandle NewHandle() => new(Guid.NewGuid()); }