public async Task ConnectAsync_InvokesCircuitHandlers_DisposesCircuitOnFailure() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = CreateRegistry(circuitIdFactory); var handler = new Mock <CircuitHandler> { CallBase = true }; handler.Setup(h => h.OnConnectionUpAsync(It.IsAny <Circuit>(), It.IsAny <CancellationToken>())).Throws(new InvalidTimeZoneException()); var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId(), handlers: new[] { handler.Object }); registry.Register(circuitHost); var newClient = Mock.Of <IClientProxy>(); var newConnectionId = "new-id"; // Act var result = await registry.ConnectAsync(circuitHost.CircuitId, newClient, newConnectionId, default); // Assert Assert.Null(result); Assert.Null(circuitHost.Handle.CircuitHost); // Will be null if disposed. Assert.Empty(registry.ConnectedCircuits); Assert.Equal(0, registry.DisconnectedCircuits.Count); }
public async Task ExtractsUriFromHttpContext_EmptyPathBase() { // Arrange var circuitFactory = new TestCircuitFactory(); var circuitRegistry = new CircuitRegistry( Options.Create(new CircuitOptions()), Mock.Of <ILogger <CircuitRegistry> >(), TestCircuitIdFactory.CreateTestFactory()); var circuitPrerenderer = new CircuitPrerenderer(circuitFactory, circuitRegistry); var httpContext = new DefaultHttpContext(); var httpRequest = httpContext.Request; httpRequest.Scheme = "https"; httpRequest.Host = new HostString("example.com", 1234); httpRequest.Path = "/some/path"; var prerenderingContext = new ComponentPrerenderingContext { ComponentType = typeof(UriDisplayComponent), Parameters = ParameterCollection.Empty, Context = httpContext }; // Act var result = await circuitPrerenderer.PrerenderComponentAsync(prerenderingContext); // Assert Assert.Equal(string.Join("", new[] { "The current URI is ", "https://example.com:1234/some/path", " within base URI ", "https://example.com:1234/" }), GetUnwrappedContent(result)); }
public async Task ConnectAsync_MakesInactiveCircuitActive() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = CreateRegistry(circuitIdFactory); var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId()); registry.RegisterDisconnectedCircuit(circuitHost); var newClient = Mock.Of <IClientProxy>(); var newConnectionId = "new-id"; // Act var result = await registry.ConnectAsync(circuitHost.CircuitId, newClient, newConnectionId, default); // Assert Assert.Same(circuitHost, result); Assert.Same(newClient, circuitHost.Client.Client); Assert.Same(newConnectionId, circuitHost.Client.ConnectionId); var actual = Assert.Single(registry.ConnectedCircuits.Values); Assert.Same(circuitHost, actual); Assert.False(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId, out _)); }
public async Task Returns400BadRequest_IfNoCircuitIdOnForm() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = new CircuitRegistry( Options.Create(new CircuitOptions()), NullLogger <CircuitRegistry> .Instance, circuitIdFactory); var middleware = new CircuitDisconnectMiddleware( NullLogger <CircuitDisconnectMiddleware> .Instance, registry, circuitIdFactory, (ctx) => Task.CompletedTask); var context = new DefaultHttpContext(); context.Request.Method = HttpMethods.Post; context.Request.ContentType = "application/x-www-form-urlencoded"; // Act await middleware.Invoke(context); // Assert Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode); }
public async Task DisconnectMiddleware_OnlyAccepts_PostRequests(string httpMethod) { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = new CircuitRegistry( Options.Create(new CircuitOptions()), NullLogger <CircuitRegistry> .Instance, circuitIdFactory); var middleware = new CircuitDisconnectMiddleware( NullLogger <CircuitDisconnectMiddleware> .Instance, registry, circuitIdFactory, (ctx) => Task.CompletedTask); var context = new DefaultHttpContext(); context.Request.Method = httpMethod; // Act await middleware.Invoke(context); // Assert Assert.Equal(StatusCodes.Status405MethodNotAllowed, context.Response.StatusCode); }
public async Task ReplacesDashesWithDots_WhenTheyAppearInPairs() { // Arrange var circuitFactory = new TestCircuitFactory(() => "--1234--"); var circuitRegistry = new CircuitRegistry( Options.Create(new CircuitOptions()), Mock.Of <ILogger <CircuitRegistry> >(), TestCircuitIdFactory.CreateTestFactory()); var circuitPrerenderer = new CircuitPrerenderer(circuitFactory, circuitRegistry); var httpContext = new DefaultHttpContext(); var httpRequest = httpContext.Request; httpRequest.Scheme = "https"; httpRequest.Host = new HostString("example.com", 1234); httpRequest.Path = "/some/path"; var prerenderingContext = new ComponentPrerenderingContext { ComponentType = typeof(UriDisplayComponent), Parameters = ParameterView.Empty, Context = httpContext }; // Act var result = await circuitPrerenderer.PrerenderComponentAsync(prerenderingContext); // Assert Assert.Equal("..1234..", GetUnwrappedCircuitInfo(result).RootElement.GetProperty("circuitId").GetString()); }
private static CircuitRegistry CreateRegistry(CircuitIdFactory factory = null) { return(new CircuitRegistry( Options.Create(new CircuitOptions()), NullLogger <CircuitRegistry> .Instance, factory ?? TestCircuitIdFactory.CreateTestFactory())); }
public async Task Returns200OK_NonExistingCircuit() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var circuitId = circuitIdFactory.CreateCircuitId(); var registry = new CircuitRegistry( Options.Create(new CircuitOptions()), NullLogger <CircuitRegistry> .Instance, circuitIdFactory); var middleware = new CircuitDisconnectMiddleware( NullLogger <CircuitDisconnectMiddleware> .Instance, registry, circuitIdFactory, (ctx) => Task.CompletedTask); using var memory = new MemoryStream(); await new FormUrlEncodedContent(new Dictionary <string, string> { ["circuitId"] = circuitId.Secret, }).CopyToAsync(memory); memory.Seek(0, SeekOrigin.Begin); var context = new DefaultHttpContext(); context.Request.Method = HttpMethods.Post; context.Request.ContentType = "application/x-www-form-urlencoded"; context.Request.Body = memory; // Act await middleware.Invoke(context); // Assert Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); }
public async Task ConnectAsync_InvokesCircuitHandlers_WhenCircuitWasPreviouslyDisconnected() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = CreateRegistry(circuitIdFactory); var handler = new Mock <CircuitHandler> { CallBase = true }; var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId(), handlers: new[] { handler.Object }); registry.RegisterDisconnectedCircuit(circuitHost); var newClient = Mock.Of <IClientProxy>(); var newConnectionId = "new-id"; // Act var result = await registry.ConnectAsync(circuitHost.CircuitId, newClient, newConnectionId, default); // Assert Assert.NotNull(result); handler.Verify(v => v.OnCircuitOpenedAsync(It.IsAny <Circuit>(), It.IsAny <CancellationToken>()), Times.Never()); handler.Verify(v => v.OnConnectionUpAsync(It.IsAny <Circuit>(), It.IsAny <CancellationToken>()), Times.Once()); handler.Verify(v => v.OnConnectionDownAsync(It.IsAny <Circuit>(), It.IsAny <CancellationToken>()), Times.Never()); handler.Verify(v => v.OnCircuitClosedAsync(It.IsAny <Circuit>(), It.IsAny <CancellationToken>()), Times.Never()); }
public async Task ReconnectBeforeTimeoutDoesNotGetEntryToBeEvicted() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var circuitOptions = new CircuitOptions { DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(8), }; var registry = new TestCircuitRegistry(circuitIdFactory, circuitOptions); var tcs = new TaskCompletionSource <object>(); registry.OnAfterEntryEvicted = () => { tcs.TrySetResult(new object()); }; var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId()); registry.RegisterDisconnectedCircuit(circuitHost); await registry.ConnectAsync(circuitHost.CircuitId, Mock.Of <IClientProxy>(), "new-connection", default); // Act await Task.Run(() => tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10))); // Verify it's still connected Assert.True(registry.ConnectedCircuits.TryGetValue(circuitHost.CircuitId, out var cacheValue)); Assert.Same(circuitHost, cacheValue); // Nothing should be disconnected. Assert.False(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId.Secret, out var _)); }
public async Task DisconnectedCircuitIsRemovedAfterConfiguredTimeout() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var circuitOptions = new CircuitOptions { DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(3), }; var registry = new TestCircuitRegistry(circuitIdFactory, circuitOptions); var tcs = new TaskCompletionSource <object>(); registry.OnAfterEntryEvicted = () => { tcs.TrySetResult(new object()); }; var circuitHost = TestCircuitHost.Create(); registry.RegisterDisconnectedCircuit(circuitHost); // Act // Verify it's present in the dictionary. Assert.True(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId.Secret, out var _)); await Task.Run(() => tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10))); Assert.False(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId.Secret, out var _)); }
public async Task DisconnectWhenAConnectIsInProgress() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = new TestCircuitRegistry(circuitIdFactory); registry.BeforeConnect = new ManualResetEventSlim(); var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId()); registry.Register(circuitHost); var client = Mock.Of <IClientProxy>(); var oldId = circuitHost.Client.ConnectionId; var newId = "new-connection"; // Act var connect = Task.Run(() => registry.ConnectAsync(circuitHost.CircuitId, client, newId, default)); var disconnect = Task.Run(() => registry.DisconnectAsync(circuitHost, oldId)); registry.BeforeConnect.Set(); await Task.WhenAll(connect, disconnect); // Assert // We expect the disconnect to fail since the client identifier has changed. var actual = Assert.Single(registry.ConnectedCircuits.Values); Assert.Same(circuitHost, actual); Assert.Same(client, circuitHost.Client.Client); Assert.Equal(newId, circuitHost.Client.ConnectionId); Assert.False(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId.Secret, out _)); }
public void ValidateCircuitId_ReturnsFalseForMalformedPayloads() { // Arrange var factory = TestCircuitIdFactory.CreateTestFactory(); // Act var isValid = factory.TryParseCircuitId("$%@&==", out _); // Assert Assert.False(isValid, "Accepted an invalid payload"); }
public void CreateCircuitId_Generates_GeneratesDifferentIds_ForSuccessiveCalls() { // Arrange var factory = TestCircuitIdFactory.CreateTestFactory(); // Act var secrets = Enumerable.Range(0, 100).Select(i => factory.CreateCircuitId()).Select(s => s.Secret).ToArray(); // Assert Assert.All(secrets, secret => Assert.NotNull(secret)); Assert.Equal(100, secrets.Distinct(StringComparer.Ordinal).Count()); }
public void CreateCircuitId_Generates_NewRandomId() { var factory = TestCircuitIdFactory.CreateTestFactory(); // Act var secret = factory.CreateCircuitId(); // Assert Assert.NotNull(secret.Secret); // This is the magic data protection header that validates its protected Assert.StartsWith("CfDJ", secret.Secret); }
public void CircuitIds_Roundtrip() { // Arrange var factory = TestCircuitIdFactory.CreateTestFactory(); var id = factory.CreateCircuitId(); // Act var isValid = factory.TryParseCircuitId(id.Secret, out var parsed); // Assert Assert.True(isValid, "Failed to validate id"); Assert.Equal(id, parsed); Assert.Equal(id.Secret, parsed.Secret); Assert.Equal(id.Id, parsed.Id); }
public void ValidateCircuitId_ReturnsFalseForPotentiallyTamperedPayloads() { // Arrange var factory = TestCircuitIdFactory.CreateTestFactory(); var secret = factory.CreateCircuitId(); var protectedBytes = Base64UrlTextEncoder.Decode(secret.Secret); for (int i = protectedBytes.Length - 10; i < protectedBytes.Length; i++) { protectedBytes[i] = 0; } var tampered = Base64UrlTextEncoder.Encode(protectedBytes); // Act var isValid = factory.TryParseCircuitId(tampered, out _); // Assert Assert.False(isValid, "Accepted a tampered payload"); }
public async Task Connect_WhileDisconnectIsInProgress() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = new TestCircuitRegistry(circuitIdFactory); registry.BeforeDisconnect = new ManualResetEventSlim(); var tcs = new TaskCompletionSource <int>(); var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId()); registry.Register(circuitHost); var client = Mock.Of <IClientProxy>(); var newId = "new-connection"; // Act var disconnect = Task.Run(() => { var task = registry.DisconnectAsync(circuitHost, circuitHost.Client.ConnectionId); tcs.SetResult(0); return(task); }); var connect = Task.Run(async() => { registry.BeforeDisconnect.Set(); await tcs.Task; await registry.ConnectAsync(circuitHost.CircuitId, client, newId, default); }); registry.BeforeDisconnect.Set(); await Task.WhenAll(disconnect, connect); // Assert // We expect the disconnect to finish followed by a reconnect var actual = Assert.Single(registry.ConnectedCircuits.Values); Assert.Same(circuitHost, actual); Assert.Same(client, circuitHost.Client.Client); Assert.Equal(newId, circuitHost.Client.ConnectionId); Assert.False(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId.Secret, out _)); }
public async Task DisposesCircuitScopeEvenIfPrerenderingThrows() { // Arrange var circuitFactory = new MockServiceScopeCircuitFactory(); var circuitRegistry = new CircuitRegistry( Options.Create(new CircuitOptions()), Mock.Of <ILogger <CircuitRegistry> >(), TestCircuitIdFactory.CreateTestFactory()); var httpContext = new DefaultHttpContext(); var prerenderer = new CircuitPrerenderer(circuitFactory, circuitRegistry); var prerenderingContext = new ComponentPrerenderingContext { ComponentType = typeof(ThrowExceptionComponent), Parameters = ParameterCollection.Empty, Context = httpContext }; // Act await Assert.ThrowsAsync <InvalidTimeZoneException>(async() => await prerenderer.PrerenderComponentAsync(prerenderingContext)); // Assert circuitFactory.MockServiceScope.Verify(scope => scope.Dispose(), Times.Once()); }