Closes #6
This commit is contained in:
@@ -0,0 +1,447 @@
|
||||
using FinalFactory.Rendezvous.Contracts;
|
||||
using FinalFactory.Rendezvous.Server.State;
|
||||
|
||||
namespace FinalFactory.Rendezvous.Tests.State;
|
||||
|
||||
public sealed class InMemoryEphemeralRendezvousStoreTests
|
||||
{
|
||||
[Fact]
|
||||
public void DuplicateRegistrationIsIdempotentButChangedRequestConflicts()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
CreateListingCommand command = fixture.ListingCommand();
|
||||
|
||||
StoreResult<StoredListing> first = fixture.Store.CreateListing(command);
|
||||
StoreResult<StoredListing> duplicate = fixture.Store.CreateListing(command);
|
||||
StoreResult<StoredListing> changed = fixture.Store.CreateListing(command with { RequestFingerprint = "different" });
|
||||
|
||||
Assert.True(first.Succeeded);
|
||||
Assert.True(duplicate.Succeeded);
|
||||
Assert.True(duplicate.IsIdempotentReplay);
|
||||
Assert.Equal(first.Value!.Definition.ListingId, duplicate.Value!.Definition.ListingId);
|
||||
Assert.Equal(StoreResultCode.Conflict, changed.Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LeaseAndPresenceExpiryUseMonotonicTime()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
StoredListing listing = fixture.CreateVisibleListing(out _);
|
||||
|
||||
fixture.Clock.Advance(TimeSpan.FromSeconds(20));
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.GetListing(listing.Definition.ListingId, true).Code);
|
||||
Assert.True(fixture.Store.GetListing(listing.Definition.ListingId, false).Succeeded);
|
||||
|
||||
fixture.Clock.Advance(TimeSpan.FromSeconds(40));
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.GetListing(listing.Definition.ListingId, false).Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WallClockMovementDoesNotExpireOrExtendLease()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
StoredListing listing = fixture.CreateVisibleListing(out CreateListingCommand command);
|
||||
|
||||
fixture.Clock.MoveWall(TimeSpan.FromDays(30));
|
||||
Assert.True(fixture.Store.GetListing(listing.Definition.ListingId, false).Succeeded);
|
||||
fixture.Clock.Advance(TimeSpan.FromSeconds(1));
|
||||
StoreResult<StoredListing> renewed = fixture.Store.RenewLease(new(
|
||||
listing.Definition.ListingId,
|
||||
listing.Definition.LeaseId,
|
||||
listing.Definition.LeaseFingerprint,
|
||||
listing.Version));
|
||||
Assert.Equal(new DateTimeOffset(2026, 7, 16, 0, 1, 1, TimeSpan.Zero), renewed.Value!.LeaseExpiresAt);
|
||||
fixture.Clock.MoveWall(TimeSpan.FromDays(-60));
|
||||
fixture.Clock.Advance(TimeSpan.FromSeconds(59));
|
||||
Assert.True(fixture.Store.GetListing(listing.Definition.ListingId, false).Succeeded);
|
||||
fixture.Clock.Advance(TimeSpan.FromSeconds(1));
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.GetListing(command.Listing.ListingId, false).Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenewDeleteRaceIsAtomicAndDeleteAlwaysWinsEventually()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
StoredListing listing = fixture.CreateVisibleListing(out CreateListingCommand command);
|
||||
using ManualResetEventSlim start = new(false);
|
||||
|
||||
Task<StoreResult<StoredListing>> renew = Task.Run(() =>
|
||||
{
|
||||
start.Wait();
|
||||
return fixture.Store.RenewLease(new(
|
||||
listing.Definition.ListingId,
|
||||
listing.Definition.LeaseId,
|
||||
listing.Definition.LeaseFingerprint,
|
||||
listing.Version));
|
||||
});
|
||||
Task<StoreResult<bool>> delete = Task.Run(() =>
|
||||
{
|
||||
start.Wait();
|
||||
return fixture.Store.DeleteListing(new(
|
||||
command.Listing.ListingId,
|
||||
command.Listing.LeaseId,
|
||||
command.Listing.LeaseFingerprint));
|
||||
});
|
||||
|
||||
start.Set();
|
||||
await Task.WhenAll(renew, delete);
|
||||
StoreResult<StoredListing> renewResult = await renew;
|
||||
StoreResult<bool> deleteResult = await delete;
|
||||
|
||||
Assert.True(deleteResult.Succeeded);
|
||||
Assert.Contains(renewResult.Code, new[] { StoreResultCode.Success, StoreResultCode.NotFound });
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.GetListing(command.Listing.ListingId, false).Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareAndSwapPreventsStaleRenewal()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
StoredListing listing = fixture.CreateVisibleListing(out _);
|
||||
RenewLeaseCommand command = new(
|
||||
listing.Definition.ListingId,
|
||||
listing.Definition.LeaseId,
|
||||
listing.Definition.LeaseFingerprint,
|
||||
listing.Version);
|
||||
|
||||
StoreResult<StoredListing> first = fixture.Store.RenewLease(command);
|
||||
StoreResult<StoredListing> stale = fixture.Store.RenewLease(command);
|
||||
|
||||
Assert.Equal(2, first.Value!.Version);
|
||||
Assert.Equal(StoreResultCode.Conflict, stale.Code);
|
||||
Assert.Equal(2, stale.Value!.Version);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JoinRequiresExactScopeProtocolAndFreshHostPresence()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
CreateListingCommand listingCommand = fixture.ListingCommand();
|
||||
StoredListing listing = fixture.Store.CreateListing(listingCommand).Value!;
|
||||
CreateJoinAttemptCommand attempt = fixture.AttemptCommand(listing);
|
||||
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.CreateJoinAttempt(attempt).Code);
|
||||
fixture.Store.BindHostPresence(new(
|
||||
listingCommand.Listing.HostPresenceHandle,
|
||||
listingCommand.Listing.HostPresenceFingerprint,
|
||||
EphemeralStateFixture.PublicEndpoint(40_000),
|
||||
null));
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.CreateJoinAttempt(attempt with { ProtocolVersion = 8 }).Code);
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.CreateJoinAttempt(attempt with
|
||||
{
|
||||
Scope = new(new("other-game"), new("test")),
|
||||
}).Code);
|
||||
Assert.True(fixture.Store.CreateJoinAttempt(attempt).Succeeded);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateJoinIsIdempotentAndDoesNotAllocateTwice()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
StoredListing listing = fixture.CreateVisibleListing(out _);
|
||||
CreateJoinAttemptCommand command = fixture.AttemptCommand(listing);
|
||||
|
||||
StoreResult<StoredJoinAttempt> first = fixture.Store.CreateJoinAttempt(command);
|
||||
StoreResult<StoredJoinAttempt> duplicate = fixture.Store.CreateJoinAttempt(command);
|
||||
|
||||
Assert.True(first.Succeeded);
|
||||
Assert.True(duplicate.Succeeded);
|
||||
Assert.True(duplicate.IsIdempotentReplay);
|
||||
Assert.Equal(first.Value!.AttemptId, duplicate.Value!.AttemptId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BrowseReturnsOnlyFreshPublicCompatibleListingsInStableOrder()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
StoredListing visible = fixture.CreateVisibleListing(out _);
|
||||
CreateListingCommand staleCommand = fixture.ListingCommand();
|
||||
fixture.Store.CreateListing(staleCommand);
|
||||
CreateListingCommand unlistedCommand = fixture.ListingCommand();
|
||||
unlistedCommand = unlistedCommand with
|
||||
{
|
||||
Listing = unlistedCommand.Listing with { Visibility = ListingVisibility.Unlisted },
|
||||
};
|
||||
fixture.Store.CreateListing(unlistedCommand);
|
||||
fixture.Store.BindHostPresence(new(
|
||||
unlistedCommand.Listing.HostPresenceHandle,
|
||||
unlistedCommand.Listing.HostPresenceFingerprint,
|
||||
EphemeralStateFixture.PublicEndpoint(40_099),
|
||||
null));
|
||||
|
||||
StoreResult<IReadOnlyList<StoredListing>> result = fixture.Store.BrowseVisibleListings(new(
|
||||
fixture.Scope,
|
||||
visible.Definition.ProtocolVersion,
|
||||
visible.Definition.RegionId));
|
||||
|
||||
Assert.True(result.Succeeded);
|
||||
Assert.Collection(result.Value!, item => Assert.Equal(visible.Definition.ListingId, item.Definition.ListingId));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConcurrentEndpointBindingAcceptsOneCompleteEndpointOnly()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
StoredListing listing = fixture.CreateVisibleListing(out _);
|
||||
CreateJoinAttemptCommand command = fixture.AttemptCommand(listing);
|
||||
fixture.Store.CreateJoinAttempt(command);
|
||||
BindAttemptEndpointCommand first = new(
|
||||
command.MediationHandle,
|
||||
AttemptPeerRole.Client,
|
||||
command.ClientCapabilityFingerprint,
|
||||
EphemeralStateFixture.PublicEndpoint(40_001),
|
||||
EphemeralStateFixture.LocalEndpoint(40_001));
|
||||
BindAttemptEndpointCommand second = first with
|
||||
{
|
||||
PublicEndpoint = EphemeralStateFixture.OtherPublicEndpoint(50_001),
|
||||
LocalEndpoint = null,
|
||||
};
|
||||
using ManualResetEventSlim start = new(false);
|
||||
|
||||
Task<StoreResult<StoredJoinAttempt>> left = Task.Run(() => { start.Wait(); return fixture.Store.BindAttemptEndpoint(first); });
|
||||
Task<StoreResult<StoredJoinAttempt>> right = Task.Run(() => { start.Wait(); return fixture.Store.BindAttemptEndpoint(second); });
|
||||
start.Set();
|
||||
await Task.WhenAll(left, right);
|
||||
StoreResult<StoredJoinAttempt> leftResult = await left;
|
||||
StoreResult<StoredJoinAttempt> rightResult = await right;
|
||||
|
||||
Assert.Equal(1, new[] { leftResult, rightResult }.Count(static result => result.Succeeded));
|
||||
Assert.Equal(1, new[] { leftResult, rightResult }.Count(static result => result.Code == StoreResultCode.ReplayRejected));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AttemptCapabilitiesAndIntroductionAreOneTime()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
StoredListing listing = fixture.CreateVisibleListing(out _);
|
||||
CreateJoinAttemptCommand command = fixture.AttemptCommand(listing);
|
||||
fixture.Store.CreateJoinAttempt(command);
|
||||
BindAttemptEndpointCommand host = new(
|
||||
command.MediationHandle,
|
||||
AttemptPeerRole.Host,
|
||||
command.HostCapabilityFingerprint,
|
||||
EphemeralStateFixture.PublicEndpoint(40_010),
|
||||
null);
|
||||
BindAttemptEndpointCommand client = new(
|
||||
command.MediationHandle,
|
||||
AttemptPeerRole.Client,
|
||||
command.ClientCapabilityFingerprint,
|
||||
EphemeralStateFixture.OtherPublicEndpoint(40_020),
|
||||
null);
|
||||
|
||||
Assert.True(fixture.Store.BindAttemptEndpoint(host).Succeeded);
|
||||
Assert.True(fixture.Store.BindAttemptEndpoint(client).Succeeded);
|
||||
Assert.True(fixture.Store.BindAttemptEndpoint(client).IsIdempotentReplay);
|
||||
Assert.True(fixture.Store.ConsumeIntroduction(command.MediationHandle).Succeeded);
|
||||
Assert.Equal(StoreResultCode.ReplayRejected, fixture.Store.ConsumeIntroduction(command.MediationHandle).Code);
|
||||
Assert.Equal(StoreResultCode.ReplayRejected, fixture.Store.BindAttemptEndpoint(client with
|
||||
{
|
||||
PublicEndpoint = EphemeralStateFixture.OtherPublicEndpoint(40_021),
|
||||
}).Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpiredAttemptCannotBeObservedBoundOrConsumed()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
StoredListing listing = fixture.CreateVisibleListing(out _);
|
||||
CreateJoinAttemptCommand command = fixture.AttemptCommand(listing);
|
||||
fixture.Store.CreateJoinAttempt(command);
|
||||
|
||||
fixture.Clock.Advance(TimeSpan.FromSeconds(30));
|
||||
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.BindAttemptEndpoint(new(
|
||||
command.MediationHandle,
|
||||
AttemptPeerRole.Client,
|
||||
command.ClientCapabilityFingerprint,
|
||||
EphemeralStateFixture.PublicEndpoint(40_050),
|
||||
null)).Code);
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.ConsumeIntroduction(command.MediationHandle).Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenericReplayConsumptionIsBoundedAndExpires()
|
||||
{
|
||||
EphemeralStoreOptions options = new() { MaxReplayEntries = 1 };
|
||||
EphemeralStateFixture fixture = new(options);
|
||||
|
||||
Assert.True(fixture.Store.ConsumeReplay(new("ticket", "one")).Succeeded);
|
||||
Assert.Equal(StoreResultCode.ReplayRejected, fixture.Store.ConsumeReplay(new("ticket", "one")).Code);
|
||||
Assert.Equal(StoreResultCode.CapacityExceeded, fixture.Store.ConsumeReplay(new("ticket", "two")).Code);
|
||||
fixture.Clock.Advance(options.ReplayLifetime);
|
||||
Assert.True(fixture.Store.ConsumeReplay(new("ticket", "two")).Succeeded);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PresenceAttemptAndRevocationPoolsShedWithoutPartialMutation()
|
||||
{
|
||||
EphemeralStoreOptions options = new()
|
||||
{
|
||||
MaxPresenceBindings = 1,
|
||||
MaxJoinAttempts = 1,
|
||||
MaxRevocations = 1,
|
||||
};
|
||||
EphemeralStateFixture fixture = new(options);
|
||||
StoredListing first = fixture.CreateVisibleListing(out CreateListingCommand firstCommand);
|
||||
CreateListingCommand secondCommand = fixture.ListingCommand(owner: "publisher-2");
|
||||
fixture.Store.CreateListing(secondCommand);
|
||||
|
||||
Assert.Equal(StoreResultCode.CapacityExceeded, fixture.Store.BindHostPresence(new(
|
||||
secondCommand.Listing.HostPresenceHandle,
|
||||
secondCommand.Listing.HostPresenceFingerprint,
|
||||
EphemeralStateFixture.OtherPublicEndpoint(42_000),
|
||||
null)).Code);
|
||||
Assert.True(fixture.Store.CreateJoinAttempt(fixture.AttemptCommand(first)).Succeeded);
|
||||
Assert.Equal(StoreResultCode.CapacityExceeded, fixture.Store.CreateJoinAttempt(fixture.AttemptCommand(first, "client-2")).Code);
|
||||
Assert.True(fixture.Store.RevokePrincipal("unrelated", TimeSpan.FromMinutes(1)).Succeeded);
|
||||
Assert.Equal(StoreResultCode.CapacityExceeded, fixture.Store.RevokePrincipal(firstCommand.Listing.OwnerSubject, TimeSpan.FromMinutes(1)).Code);
|
||||
Assert.True(fixture.Store.GetListing(first.Definition.ListingId, true).Succeeded);
|
||||
Assert.True(fixture.Store.GetListing(secondCommand.Listing.ListingId, false).Succeeded);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExhaustionShedsNewListingWithoutMutatingExistingState()
|
||||
{
|
||||
EphemeralStoreOptions options = new() { MaxListings = 1 };
|
||||
EphemeralStateFixture fixture = new(options);
|
||||
CreateListingCommand first = fixture.ListingCommand();
|
||||
CreateListingCommand second = fixture.ListingCommand();
|
||||
|
||||
Assert.True(fixture.Store.CreateListing(first).Succeeded);
|
||||
Assert.Equal(StoreResultCode.CapacityExceeded, fixture.Store.CreateListing(second).Code);
|
||||
Assert.True(fixture.Store.GetListing(first.Listing.ListingId, false).Succeeded);
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.GetListing(second.Listing.ListingId, false).Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IdempotencyPoolExhaustionDoesNotCreateUntrackedResource()
|
||||
{
|
||||
EphemeralStoreOptions options = new() { MaxListings = 2, MaxIdempotencyEntries = 1 };
|
||||
EphemeralStateFixture fixture = new(options);
|
||||
CreateListingCommand first = fixture.ListingCommand();
|
||||
CreateListingCommand second = fixture.ListingCommand();
|
||||
|
||||
Assert.True(fixture.Store.CreateListing(first).Succeeded);
|
||||
Assert.Equal(StoreResultCode.CapacityExceeded, fixture.Store.CreateListing(second).Code);
|
||||
Assert.True(fixture.Store.GetListing(first.Listing.ListingId, false).Succeeded);
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.GetListing(second.Listing.ListingId, false).Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PolicyQuotasAreCheckedInsideAtomicCreation()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
CreateListingCommand first = fixture.ListingCommand(owner: "publisher-quota") with { OwnerListingLimit = 1 };
|
||||
CreateListingCommand second = fixture.ListingCommand(owner: "publisher-quota") with { OwnerListingLimit = 1 };
|
||||
Assert.True(fixture.Store.CreateListing(first).Succeeded);
|
||||
Assert.Equal(StoreResultCode.CapacityExceeded, fixture.Store.CreateListing(second).Code);
|
||||
|
||||
fixture.Store.BindHostPresence(new(
|
||||
first.Listing.HostPresenceHandle,
|
||||
first.Listing.HostPresenceFingerprint,
|
||||
EphemeralStateFixture.PublicEndpoint(42_100),
|
||||
null));
|
||||
StoredListing listing = fixture.Store.GetListing(first.Listing.ListingId, true).Value!;
|
||||
CreateJoinAttemptCommand attempt = fixture.AttemptCommand(listing) with { ScopeAttemptLimit = 1 };
|
||||
Assert.True(fixture.Store.CreateJoinAttempt(attempt).Succeeded);
|
||||
Assert.Equal(StoreResultCode.CapacityExceeded, fixture.Store.CreateJoinAttempt(
|
||||
fixture.AttemptCommand(listing, "client-quota-2") with { ScopeAttemptLimit = 1 }).Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidDefaultSecurityValuesCannotEnterStore()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
CreateListingCommand command = fixture.ListingCommand();
|
||||
|
||||
Assert.Throws<ArgumentException>(() => fixture.Store.CreateListing(command with
|
||||
{
|
||||
Listing = command.Listing with { LeaseFingerprint = default },
|
||||
}));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RevocationRemovesEveryPathAndBlocksNewWorkAtomically()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
StoredListing listing = fixture.CreateVisibleListing(out CreateListingCommand command);
|
||||
CreateJoinAttemptCommand attempt = fixture.AttemptCommand(listing, command.Listing.OwnerSubject);
|
||||
fixture.Store.CreateJoinAttempt(attempt);
|
||||
|
||||
StoreResult<int> revoked = fixture.Store.RevokePrincipal(command.Listing.OwnerSubject, TimeSpan.FromMinutes(1));
|
||||
|
||||
Assert.True(revoked.Succeeded);
|
||||
Assert.Equal(2, revoked.Value);
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.GetListing(listing.Definition.ListingId, false).Code);
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.BindAttemptEndpoint(new(
|
||||
attempt.MediationHandle,
|
||||
AttemptPeerRole.Client,
|
||||
attempt.ClientCapabilityFingerprint,
|
||||
EphemeralStateFixture.PublicEndpoint(40_030),
|
||||
null)).Code);
|
||||
Assert.Equal(StoreResultCode.Revoked, fixture.Store.CreateListing(fixture.ListingCommand(owner: command.Listing.OwnerSubject)).Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RestartHasNewGenerationAndNoEphemeralState()
|
||||
{
|
||||
EphemeralStateFixture before = new();
|
||||
StoredListing listing = before.CreateVisibleListing(out _);
|
||||
EphemeralStateFixture after = new();
|
||||
|
||||
Assert.NotEqual(before.Store.InstanceId, after.Store.InstanceId);
|
||||
Assert.Equal(StoreResultCode.NotFound, after.Store.GetListing(listing.Definition.ListingId, false).Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DrainRejectsNewWorkAllowsInflightCompletionThenClearsState()
|
||||
{
|
||||
EphemeralStoreOptions options = new() { GracefulDrainLifetime = TimeSpan.FromSeconds(5) };
|
||||
EphemeralStateFixture fixture = new(options);
|
||||
StoredListing listing = fixture.CreateVisibleListing(out _);
|
||||
CreateJoinAttemptCommand attempt = fixture.AttemptCommand(listing);
|
||||
fixture.Store.CreateJoinAttempt(attempt);
|
||||
fixture.Store.BeginDrain();
|
||||
|
||||
Assert.Equal(StoreResultCode.Draining, fixture.Store.CreateListing(fixture.ListingCommand()).Code);
|
||||
Assert.Equal(StoreResultCode.Draining, fixture.Store.CreateJoinAttempt(fixture.AttemptCommand(listing)).Code);
|
||||
Assert.True(fixture.Store.BindAttemptEndpoint(new(
|
||||
attempt.MediationHandle,
|
||||
AttemptPeerRole.Client,
|
||||
attempt.ClientCapabilityFingerprint,
|
||||
EphemeralStateFixture.PublicEndpoint(41_000),
|
||||
null)).Succeeded);
|
||||
|
||||
fixture.Clock.Advance(options.GracefulDrainLifetime);
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.GetListing(listing.Definition.ListingId, false).Code);
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.BindAttemptEndpoint(new(
|
||||
attempt.MediationHandle,
|
||||
AttemptPeerRole.Host,
|
||||
attempt.HostCapabilityFingerprint,
|
||||
EphemeralStateFixture.PublicEndpoint(41_001),
|
||||
null)).Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PrecancelledOperationHasNoPartialEffect()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
CreateListingCommand command = fixture.ListingCommand();
|
||||
using CancellationTokenSource cancellation = new();
|
||||
cancellation.Cancel();
|
||||
|
||||
Assert.Throws<OperationCanceledException>(() => fixture.Store.CreateListing(command, cancellation.Token));
|
||||
Assert.Equal(StoreResultCode.NotFound, fixture.Store.GetListing(command.Listing.ListingId, false).Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnavailableStoreFailsNewAuthorizationClosedAndErasesActiveState()
|
||||
{
|
||||
EphemeralStateFixture fixture = new();
|
||||
StoredListing listing = fixture.CreateVisibleListing(out _);
|
||||
fixture.Store.MarkUnavailable();
|
||||
|
||||
Assert.Equal(StoreResultCode.ServiceUnavailable, fixture.Store.GetListing(listing.Definition.ListingId, false).Code);
|
||||
Assert.Equal(StoreResultCode.ServiceUnavailable, fixture.Store.CreateJoinAttempt(fixture.AttemptCommand(listing)).Code);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user