176 lines
6.7 KiB
C#
176 lines
6.7 KiB
C#
using FinalFactory.Rendezvous.Contracts;
|
|
using FinalFactory.Rendezvous.Server.ConnectionOutcomes;
|
|
using FinalFactory.Rendezvous.Tests.JoinAttempts;
|
|
|
|
namespace FinalFactory.Rendezvous.Tests.ConnectionOutcomes;
|
|
|
|
public sealed class ConnectionOutcomeServiceTests
|
|
{
|
|
[Fact]
|
|
public void ReportRemainsAuthenticatedAfterAttemptExpiryAndCountsOnlyOnce()
|
|
{
|
|
using JoinAttemptFixture fixture = new();
|
|
(RegisterSessionResponse registration, _) = fixture.CreateHost();
|
|
CreateJoinAttemptResponse attempt = fixture.Create(registration.ListingId);
|
|
ConnectionOutcomeMetrics metrics = new();
|
|
ConnectionOutcomeService service = new(
|
|
fixture.Sessions.Store,
|
|
fixture.Sessions.Capabilities,
|
|
metrics);
|
|
ReportConnectionOutcomeRequest report = new()
|
|
{
|
|
Outcome = ConnectionOutcomeKind.PunchTimedOut,
|
|
ElapsedBucket = ConnectionElapsedBucket.FiveToFifteenSeconds,
|
|
};
|
|
fixture.Sessions.Clock.Advance(fixture.Sessions.StoreOptions.JoinAttemptLifetime);
|
|
|
|
ConnectionOutcomeServiceResult first = service.Report(
|
|
attempt.AttemptId,
|
|
attempt.ClientPunchCapability,
|
|
report);
|
|
ConnectionOutcomeServiceResult duplicate = service.Report(
|
|
attempt.AttemptId,
|
|
attempt.ClientPunchCapability,
|
|
report);
|
|
ConnectionOutcomeServiceResult conflict = service.Report(
|
|
attempt.AttemptId,
|
|
attempt.ClientPunchCapability,
|
|
new ReportConnectionOutcomeRequest
|
|
{
|
|
Outcome = ConnectionOutcomeKind.Connected,
|
|
ElapsedBucket = ConnectionElapsedBucket.FiveToFifteenSeconds,
|
|
});
|
|
|
|
Assert.True(first.Succeeded);
|
|
Assert.False(first.Value!.IsDuplicate);
|
|
Assert.True(duplicate.Succeeded);
|
|
Assert.True(duplicate.Value!.IsDuplicate);
|
|
Assert.Equal(RendezvousErrorCode.ReplayRejected, conflict.Error);
|
|
Assert.Equal(
|
|
1,
|
|
metrics.GetCount(
|
|
ConnectionOutcomeKind.PunchTimedOut,
|
|
ConnectionElapsedBucket.FiveToFifteenSeconds));
|
|
Assert.Equal(
|
|
RendezvousErrorCode.NotFound,
|
|
service.Report(
|
|
attempt.AttemptId,
|
|
new string('X', ContractLimits.DerivedCredentialCharacters),
|
|
report).Error);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(ConnectionOutcomeKind.DirectoryNotFound)]
|
|
[InlineData(ConnectionOutcomeKind.IncompatibleProtocol)]
|
|
[InlineData(ConnectionOutcomeKind.Unauthorized)]
|
|
[InlineData(ConnectionOutcomeKind.RateLimited)]
|
|
public void ReportRejectsOutcomesThatCouldNotHaveAnIssuedAttempt(
|
|
ConnectionOutcomeKind outcome)
|
|
{
|
|
using JoinAttemptFixture fixture = new();
|
|
(RegisterSessionResponse registration, _) = fixture.CreateHost();
|
|
CreateJoinAttemptResponse attempt = fixture.Create(registration.ListingId);
|
|
ConnectionOutcomeService service = new(
|
|
fixture.Sessions.Store,
|
|
fixture.Sessions.Capabilities,
|
|
new ConnectionOutcomeMetrics());
|
|
|
|
ConnectionOutcomeServiceResult result = service.Report(
|
|
attempt.AttemptId,
|
|
attempt.ClientPunchCapability,
|
|
new ReportConnectionOutcomeRequest
|
|
{
|
|
Outcome = outcome,
|
|
ElapsedBucket = ConnectionElapsedBucket.UnderOneSecond,
|
|
});
|
|
|
|
Assert.Equal(RendezvousErrorCode.InvalidRequest, result.Error);
|
|
}
|
|
|
|
[Fact]
|
|
public void ListingDeletionRemovesRetainedOutcomeAuthorization()
|
|
{
|
|
using JoinAttemptFixture fixture = new();
|
|
(RegisterSessionResponse registration, _) = fixture.CreateHost();
|
|
CreateJoinAttemptResponse attempt = fixture.Create(registration.ListingId);
|
|
ConnectionOutcomeService service = new(
|
|
fixture.Sessions.Store,
|
|
fixture.Sessions.Capabilities,
|
|
new ConnectionOutcomeMetrics());
|
|
|
|
Assert.True(fixture.Sessions.Store.RevokeListing(registration.ListingId).Succeeded);
|
|
|
|
Assert.Equal(
|
|
RendezvousErrorCode.NotFound,
|
|
service.Report(
|
|
attempt.AttemptId,
|
|
attempt.ClientPunchCapability,
|
|
new ReportConnectionOutcomeRequest
|
|
{
|
|
Outcome = ConnectionOutcomeKind.Cancelled,
|
|
ElapsedBucket = ConnectionElapsedBucket.UnderOneSecond,
|
|
}).Error);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrincipalRevocationRemovesReportAuthorizationAfterAttemptExpiry()
|
|
{
|
|
using JoinAttemptFixture fixture = new();
|
|
(RegisterSessionResponse registration, _) = fixture.CreateHost();
|
|
CreateJoinAttemptResponse attempt = fixture.Create(registration.ListingId);
|
|
ConnectionOutcomeService service = new(
|
|
fixture.Sessions.Store,
|
|
fixture.Sessions.Capabilities,
|
|
new ConnectionOutcomeMetrics());
|
|
fixture.Sessions.Clock.Advance(fixture.Sessions.StoreOptions.JoinAttemptLifetime);
|
|
|
|
Assert.True(fixture.Sessions.Store.RevokePrincipal(
|
|
fixture.ClientSubject,
|
|
TimeSpan.FromMinutes(1)).Succeeded);
|
|
|
|
Assert.Equal(
|
|
RendezvousErrorCode.NotFound,
|
|
service.Report(
|
|
attempt.AttemptId,
|
|
attempt.ClientPunchCapability,
|
|
new ReportConnectionOutcomeRequest
|
|
{
|
|
Outcome = ConnectionOutcomeKind.Cancelled,
|
|
ElapsedBucket = ConnectionElapsedBucket.UnderOneSecond,
|
|
}).Error);
|
|
}
|
|
|
|
[Fact]
|
|
public void FrozenV1ReportFieldsAreAcceptedButNormalizedBeforeRetention()
|
|
{
|
|
using JoinAttemptFixture fixture = new();
|
|
(RegisterSessionResponse registration, _) = fixture.CreateHost();
|
|
CreateJoinAttemptResponse attempt = fixture.Create(registration.ListingId);
|
|
ConnectionOutcomeMetrics metrics = new();
|
|
ConnectionOutcomeService service = new(
|
|
fixture.Sessions.Store,
|
|
fixture.Sessions.Capabilities,
|
|
metrics);
|
|
#pragma warning disable CS0618 // Deliberately exercises the frozen legacy input surface.
|
|
ReportConnectionOutcomeRequest legacy = new()
|
|
{
|
|
Outcome = ConnectionOutcomeKind.TimedOut,
|
|
ElapsedMilliseconds = 6_000,
|
|
DiagnosticCode = "legacy-text-is-discarded",
|
|
};
|
|
#pragma warning restore CS0618
|
|
|
|
ConnectionOutcomeServiceResult result = service.Report(
|
|
attempt.AttemptId,
|
|
attempt.ClientPunchCapability,
|
|
legacy);
|
|
|
|
Assert.True(result.Succeeded);
|
|
Assert.Equal(
|
|
1,
|
|
metrics.GetCount(
|
|
ConnectionOutcomeKind.PunchTimedOut,
|
|
ConnectionElapsedBucket.FiveToFifteenSeconds));
|
|
}
|
|
}
|