Пример #1
0
    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));
        }
Пример #3
0
    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());
        }
Пример #7
0
 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);
        }
Пример #9
0
    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());
    }
Пример #10
0
    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 _));
    }
Пример #11
0
    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 _));
    }
Пример #12
0
    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 _));
    }
Пример #13
0
    public void ValidateCircuitId_ReturnsFalseForMalformedPayloads()
    {
        // Arrange
        var factory = TestCircuitIdFactory.CreateTestFactory();

        // Act
        var isValid = factory.TryParseCircuitId("$%@&==", out _);

        // Assert
        Assert.False(isValid, "Accepted an invalid payload");
    }
Пример #14
0
    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());
    }
Пример #15
0
    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);
    }
Пример #16
0
    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);
    }
Пример #17
0
    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");
    }
Пример #18
0
    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());
        }