Skip to content

Commit dbe2503

Browse files
committed
Address feedback
1 parent a288a60 commit dbe2503

File tree

9 files changed

+108
-66
lines changed

9 files changed

+108
-66
lines changed

src/Components/test/E2ETest/ServerExecutionTests/ServerReconnectionTest.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
14
using System;
2-
using System.Collections.Generic;
3-
using System.Text;
45
using BasicTestApp;
56
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
67
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
7-
using Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests;
88
using Microsoft.AspNetCore.E2ETesting;
99
using OpenQA.Selenium;
1010
using OpenQA.Selenium.Support.UI;
@@ -23,8 +23,6 @@ public ServerReconnectionTest(
2323
{
2424
}
2525

26-
public List<(Extensions.Logging.LogLevel level, string eventIdName)> Messages { get; private set; }
27-
2826
public string SessionIdentifier { get; set; } = Guid.NewGuid().ToString();
2927

3028
protected override void InitializeAsyncCore()
@@ -99,7 +97,8 @@ public void RendersContinueAfterReconnect()
9997
private void Disconnect()
10098
{
10199
var javascript = (IJavaScriptExecutor)Browser;
102-
javascript.ExecuteScript($"fetch('/WebSockets/Interrupt?WebSockets.Identifier={SessionIdentifier}')");
100+
Browser.ExecuteScript($"fetch('/WebSockets/Interrupt?WebSockets.Identifier={SessionIdentifier}').then(r => window['WebSockets.{SessionIdentifier}'] = r.ok)");
101+
Assert.True(Browser.HasJavaScriptValue($"window['WebSockets.{ SessionIdentifier}']", (r) => r != null));
103102
}
104103

105104
private IWebElement WaitUntilReconnectionDialogExists()

src/Components/test/E2ETest/ServerExecutionTests/ServerSideAppTest.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ public ServerSideAppTest(
2828

2929
public DateTime LastLogTimeStamp { get; set; } = DateTime.MinValue;
3030

31-
public string SessionIdentifier { get; } = Guid.NewGuid().ToString();
32-
3331
public override async Task InitializeAsync()
3432
{
3533
await base.InitializeAsync();
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Threading.Tasks;
4+
using Microsoft.AspNetCore.Http;
5+
using Microsoft.AspNetCore.Http.Features;
6+
7+
namespace Components.TestServer
8+
{
9+
public class InterruptibleSocketMiddleware
10+
{
11+
public InterruptibleSocketMiddleware(
12+
RequestDelegate next,
13+
InterruptibleWebSocketOptions options)
14+
{
15+
Next = next;
16+
Options = options;
17+
}
18+
19+
public RequestDelegate Next { get; }
20+
public ConcurrentDictionary<string, InterruptibleWebSocket> Registry => Options.Registry;
21+
public InterruptibleWebSocketOptions Options { get; }
22+
23+
public async Task Invoke(HttpContext context)
24+
{
25+
if (context.Request.Path.Equals(Options.InterruptPath) && context.Request.Query.TryGetValue(Options.WebSocketIdParameterName,out var currentIdentifier))
26+
{
27+
if (Registry.TryGetValue(currentIdentifier, out var webSocket))
28+
{
29+
webSocket.Disable();
30+
return;
31+
}
32+
else
33+
{
34+
context.Response.StatusCode = 400;
35+
return;
36+
}
37+
}
38+
39+
if (context.Request.Path.Equals(Options.WebSocketPath, StringComparison.OrdinalIgnoreCase) &&
40+
context.Request.Cookies.TryGetValue(Options.WebSocketIdParameterName, out var identifier))
41+
{
42+
context.Features.Set<IHttpWebSocketFeature>(new InterruptibleWebSocketFeature(context, identifier, Registry));
43+
}
44+
45+
await Next(context);
46+
}
47+
}
48+
}

src/Components/test/testassets/TestServer/Infrastructure/InterruptibleWebSocket.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
using System.Threading;
55
using System.Threading.Tasks;
66

7-
namespace Components.TestServer.Infrastructure
7+
namespace Components.TestServer
88
{
9+
// A socket used in testing that wraps the underlying websocket and that has built-in hooks to allow
10+
// tests to start generating exceptions at will to test scenarios where the websocket connection gets
11+
// closed in a non-graceful way.
912
public class InterruptibleWebSocket : WebSocket
1013
{
1114
private TaskCompletionSource<WebSocketReceiveResult> _disabledReceiveTask =
@@ -53,6 +56,11 @@ public override void Dispose()
5356
Socket.Dispose();
5457
}
5558

59+
// Consumers will call ReceiveAsync to wait for data from the network to come through.
60+
// We have setup this websocket so that we can trigger errors from outside. When we get
61+
// notified that we need to create errors, we simply return an exception to the caller
62+
// on the current call and successive calls so that the server believes the connection
63+
// got disconnected abruptly.
5664
public override async Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken)
5765
{
5866
if (_disabledReceiveTask.Task.IsCompleted)
@@ -61,9 +69,9 @@ public override async Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byt
6169
}
6270
else
6371
{
64-
return await await Task.WhenAny(
72+
return await Task.WhenAny(
6573
_disabledReceiveTask.Task,
66-
Socket.ReceiveAsync(buffer, cancellationToken));
74+
Socket.ReceiveAsync(buffer, cancellationToken)).Unwrap();
6775
}
6876
}
6977

Lines changed: 2 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
1-
using System;
2-
using System.Collections.Concurrent;
3-
using System.Threading.Tasks;
4-
using Microsoft.AspNetCore.Builder;
5-
using Microsoft.AspNetCore.Http;
6-
using Microsoft.AspNetCore.Http.Features;
7-
using Microsoft.Extensions.Options;
1+
using Components.TestServer;
82

9-
namespace Components.TestServer.Infrastructure
3+
namespace Microsoft.AspNetCore.Builder
104
{
115
public static class InterruptibleWebSocketAppBuilderExtensions
126
{
@@ -18,44 +12,4 @@ public static IApplicationBuilder UseInterruptibleWebSockets(
1812
return builder;
1913
}
2014
}
21-
22-
public class InterruptibleSocketMiddleware
23-
{
24-
public InterruptibleSocketMiddleware(
25-
RequestDelegate next,
26-
InterruptibleWebSocketOptions options)
27-
{
28-
Next = next;
29-
Options = options;
30-
}
31-
32-
public RequestDelegate Next { get; }
33-
public ConcurrentDictionary<string, InterruptibleWebSocket> Registry => Options.Registry;
34-
public InterruptibleWebSocketOptions Options { get; }
35-
36-
public async Task Invoke(HttpContext context)
37-
{
38-
if (context.Request.Path.Equals(Options.InterruptPath) && context.Request.Query.TryGetValue(Options.WebSocketIdParameterName,out var currentIdentifier))
39-
{
40-
if (Registry.TryGetValue(currentIdentifier, out var webSocket))
41-
{
42-
webSocket.Disable();
43-
return;
44-
}
45-
else
46-
{
47-
context.Response.StatusCode = 400;
48-
return;
49-
}
50-
}
51-
52-
if (context.Request.Path.Equals(Options.WebSocketPath, StringComparison.OrdinalIgnoreCase) &&
53-
context.Request.Cookies.TryGetValue(Options.WebSocketIdParameterName, out var identifier))
54-
{
55-
context.Features.Set<IHttpWebSocketFeature>(new InterruptibleWebSocketFeature(context, identifier, Registry));
56-
}
57-
58-
await Next(context);
59-
}
60-
}
6115
}

src/Components/test/testassets/TestServer/Infrastructure/InterruptibleWebSocketFeature.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
using Microsoft.Extensions.Options;
1515
using Microsoft.Net.Http.Headers;
1616

17-
namespace Components.TestServer.Infrastructure
17+
namespace Components.TestServer
1818
{
1919
public class InterruptibleWebSocketFeature : IHttpWebSocketFeature
2020
{

src/Components/test/testassets/TestServer/Infrastructure/InterruptibleWebSocketOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System.Collections.Concurrent;
22
using Microsoft.AspNetCore.Http;
33

4-
namespace Components.TestServer.Infrastructure
4+
namespace Components.TestServer
55
{
66
public class InterruptibleWebSocketOptions
77
{

src/Components/test/testassets/TestServer/Startup.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
using System.Threading.Tasks;
22
using BasicTestApp;
33
using BasicTestApp.RouterTest;
4-
using Components.TestServer.Infrastructure;
4+
using Components.TestServer;
55
using Microsoft.AspNetCore.Authentication.Cookies;
66
using Microsoft.AspNetCore.Builder;
77
using Microsoft.AspNetCore.Hosting;
8-
using Microsoft.AspNetCore.Http;
9-
using Microsoft.AspNetCore.Http.Features;
108
using Microsoft.AspNetCore.Localization;
119
using Microsoft.Extensions.Configuration;
1210
using Microsoft.Extensions.DependencyInjection;

src/Shared/E2ETesting/WaitAssert.cs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,35 @@ public static void Equal<T>(this IWebDriver driver, T expected, Func<T> actual)
2323
public static void True(this IWebDriver driver, Func<bool> actual)
2424
=> WaitAssertCore(driver, () => Assert.True(actual()));
2525

26+
public static void ExecuteScript(this IWebDriver driver, string script)
27+
{
28+
if (!(driver is IJavaScriptExecutor javaScript))
29+
{
30+
Assert.False(true, "The driver can't execute JavaScript.");
31+
return;
32+
}
33+
else
34+
{
35+
javaScript.ExecuteScript(script);
36+
}
37+
}
38+
39+
public static T HasJavaScriptValue<T>(
40+
this IWebDriver driver,
41+
string script, Func<object,T> converter = null)
42+
{
43+
converter ??= (v) => (T)v;
44+
if(!(driver is IJavaScriptExecutor javaScript))
45+
{
46+
Assert.False(true, "The driver can't execute JavaScript.");
47+
return default;
48+
}
49+
else
50+
{
51+
return WaitAssertCore(driver, () => converter(javaScript.ExecuteScript(script)));
52+
}
53+
}
54+
2655
public static void True(this IWebDriver driver, Func<bool> actual, TimeSpan timeout)
2756
=> WaitAssertCore(driver, () => Assert.True(actual()), timeout);
2857

@@ -45,23 +74,29 @@ public static void Exists(this IWebDriver driver, By finder)
4574
=> WaitAssertCore(driver, () => Assert.NotEmpty(driver.FindElements(finder)));
4675

4776
private static void WaitAssertCore(IWebDriver driver, Action assertion, TimeSpan timeout = default)
77+
{
78+
_ = WaitAssertCore<object>(driver, () => assertion, timeout);
79+
}
80+
81+
private static T WaitAssertCore<T>(IWebDriver driver, Func<T> assertion, TimeSpan timeout = default)
4882
{
4983
if (timeout == default)
5084
{
5185
timeout = DefaultTimeout;
5286
}
5387

5488
Exception lastException = null;
89+
T result = default;
5590
try
5691
{
5792
new WebDriverWait(driver, timeout).Until(_ =>
5893
{
5994
try
6095
{
61-
assertion();
96+
result = assertion();
6297
return true;
6398
}
64-
catch(Exception e)
99+
catch (Exception e)
65100
{
66101
lastException = e;
67102
return false;
@@ -80,6 +115,8 @@ private static void WaitAssertCore(IWebDriver driver, Action assertion, TimeSpan
80115
assertion();
81116
}
82117
}
118+
119+
return result;
83120
}
84121
}
85122
}

0 commit comments

Comments
 (0)