Skip to content

Commit ab0e826

Browse files
committed
add unit tests for DefaultSessionFactory and enhance SocketIO options handling
1 parent 7c7b2d0 commit ab0e826

File tree

5 files changed

+143
-11
lines changed

5 files changed

+143
-11
lines changed

src/SocketIOClient/V2/Session/ISession.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ public interface ISession : IMyObserver<ProtocolMessage>, IMyObservable<IMessage
1212
int PendingDeliveryCount { get; }
1313
Task SendAsync(object[] data, CancellationToken cancellationToken);
1414
Task ConnectAsync(CancellationToken cancellationToken);
15+
// Task DisconnectAsync(CancellationToken cancellationToken);
1516
}

src/SocketIOClient/V2/SocketIO.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.Threading;
45
using System.Threading.Tasks;
56
using SocketIOClient.Core.Messages;
@@ -27,6 +28,10 @@ public SocketIO(Uri uri, SocketIOOptions options)
2728
{
2829
}
2930

31+
public SocketIO(string uri, SocketIOOptions options) : this(new Uri(uri), options)
32+
{
33+
}
34+
3035
public IHttpClient HttpClient { get; set; }
3136
public ISessionFactory SessionFactory { get; set; }
3237
private ISession _session;
@@ -57,8 +62,11 @@ private Uri ServerUri
5762

5863

5964
private readonly Dictionary<int, Action<IAckMessage>> _ackHandlers = new();
65+
6066
private readonly Dictionary<int, Func<IAckMessage, Task>> _funcHandlers = new();
61-
private TaskCompletionSource<Exception> _connCompletionSource = new();
67+
68+
// private TaskCompletionSource<bool> _openedCompletionSource = new();
69+
private TaskCompletionSource<bool> _sessionCompletionSource;
6270
private TaskCompletionSource<Exception> _connCompletionSource;
6371
public SocketIOOptions Options { get; }
6472
public event EventHandler<Exception> OnReconnectError;
@@ -72,7 +80,9 @@ public async Task ConnectAsync()
7280

7381
public async Task ConnectAsync(CancellationToken cancellationToken)
7482
{
83+
// TODO: concurrent connect
7584
_connCompletionSource = new TaskCompletionSource<Exception>();
85+
_sessionCompletionSource = new TaskCompletionSource<bool>();
7686
_ = ConnectCoreAsync(cancellationToken).ConfigureAwait(false);
7787
var task = Task.Run(async () => await _connCompletionSource.Task.ConfigureAwait(false), cancellationToken);
7888
var ex = await task.ConfigureAwait(false);
@@ -90,15 +100,19 @@ private async Task ConnectCoreAsync(CancellationToken cancellationToken)
90100
cancellationToken.ThrowIfCancellationRequested();
91101
var session = SessionFactory.New(Options.EIO, new SessionOptions
92102
{
93-
103+
ServerUri = ServerUri,
104+
Path = Options.Path,
105+
Query = Options.Query,
106+
Timeout = Options.ConnectionTimeout,
94107
});
95108
using var cts = new CancellationTokenSource(Options.ConnectionTimeout);
96109
try
97110
{
98111
await session.ConnectAsync(cts.Token).ConfigureAwait(false);
99112
_session = session;
100113
_session.Subscribe(this);
101-
// _sessionCompletionSource.SetResult(true);
114+
_sessionCompletionSource.SetResult(true);
115+
break;
102116
}
103117
catch (Exception e)
104118
{
@@ -181,6 +195,7 @@ private async Task HandleAckMessage(IMessage message)
181195

182196
private async Task HandleConnectedMessage(IMessage message)
183197
{
198+
await _sessionCompletionSource.Task.ConfigureAwait(false);
184199
var connectedMessage = (ConnectedMessage)message;
185200
Id = connectedMessage.Sid;
186201
Connected = true;
@@ -193,10 +208,12 @@ private void HandlePongMessage(IMessage message)
193208
OnPong?.Invoke(this, pong.Duration);
194209
}
195210

196-
public async Task DisconnectAsync()
211+
public Task DisconnectAsync()
197212
{
213+
// await _session.DisconnectAsync(CancellationToken.None).ConfigureAwait(false);
198214
_session?.Dispose();
199215
Connected = false;
200216
Id = null;
217+
return Task.CompletedTask;
201218
}
202219
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
using System;
2+
using System.Collections.Generic;
23

34
namespace SocketIOClient.V2;
45

56
public class SocketIOOptions
67
{
8+
// TODO: what will happen if user set an invalid value?
79
public EngineIO EIO { get; set; }
810
public TimeSpan ConnectionTimeout { get; set; }
911
public bool Reconnection { get; set; } = true;
10-
public int ReconnectionAttempts { get; set; } = int.MaxValue;
12+
public int ReconnectionAttempts { get; set; } = 10;
1113
public int ReconnectionDelayMax { get; set; } = 5000;
14+
public string Path { get; set; } = "/socket.io";
15+
public IEnumerable<KeyValuePair<string, string>> Query { get; set; }
1216
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using FluentAssertions;
2+
using SocketIOClient.V2;
3+
using SocketIOClient.V2.Session;
4+
5+
namespace SocketIOClient.UnitTests.V2;
6+
7+
public class DefaultSessionFactoryTests
8+
{
9+
public DefaultSessionFactoryTests()
10+
{
11+
_sessionFactory = new DefaultSessionFactory();
12+
}
13+
14+
private readonly DefaultSessionFactory _sessionFactory;
15+
16+
[Fact]
17+
public void New_OptionIsNull_ThrowNullReferenceException()
18+
{
19+
_sessionFactory.Invoking(x => x.New(EngineIO.V3, null))
20+
.Should()
21+
.ThrowExactly<NullReferenceException>();
22+
}
23+
24+
[Fact]
25+
public void New_WhenCalled_AlwaysReturnHttpSession()
26+
{
27+
var session = _sessionFactory.New(EngineIO.V3, new SessionOptions());
28+
session.Should().BeOfType<HttpSession>();
29+
}
30+
}

tests/SocketIOClient.UnitTests/V2/SocketIOTests.cs

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ public class SocketIOTests
1515
public SocketIOTests()
1616
{
1717
_session = Substitute.For<ISession>();
18-
var sessionFactory = Substitute.For<ISessionFactory>();
19-
sessionFactory.New(Arg.Any<EngineIO>(), Arg.Any<SessionOptions>()).Returns(_session);
18+
_sessionFactory = Substitute.For<ISessionFactory>();
19+
_sessionFactory.New(Arg.Any<EngineIO>(), Arg.Any<SessionOptions>()).Returns(_session);
2020
_random = Substitute.For<IRandom>();
2121
_io = new SocketIOClient.V2.SocketIO("http://localhost:3000")
2222
{
23-
SessionFactory = sessionFactory,
23+
SessionFactory = _sessionFactory,
2424
Random = _random,
2525
Options =
2626
{
@@ -32,6 +32,7 @@ public SocketIOTests()
3232
private readonly SocketIOClient.V2.SocketIO _io;
3333
private readonly ISession _session;
3434
private readonly IRandom _random;
35+
private readonly ISessionFactory _sessionFactory;
3536

3637
[Fact]
3738
public void NothingCalled_DefaultValues()
@@ -113,20 +114,30 @@ public async Task ConnectAsync_SessionSuccessfullyConnected_SessionSubscribeIO()
113114

114115
private async Task ConnectAsync()
115116
{
116-
await ConnectAsync(0);
117+
await ConnectAsync(_io);
117118
}
118119

119120
private async Task ConnectAsync(int ms)
121+
{
122+
await ConnectAsync(_io, ms);
123+
}
124+
125+
private static async Task ConnectAsync(SocketIOClient.V2.SocketIO io)
126+
{
127+
await ConnectAsync(io, 0);
128+
}
129+
130+
private static async Task ConnectAsync(SocketIOClient.V2.SocketIO io, int ms)
120131
{
121132
_ = Task.Run(async () =>
122133
{
123134
await Task.Delay(ms);
124-
await _io.OnNextAsync(new ConnectedMessage
135+
await io.OnNextAsync(new ConnectedMessage
125136
{
126137
Sid = "123",
127138
});
128139
});
129-
await _io.ConnectAsync();
140+
await io.ConnectAsync();
130141
}
131142

132143
[Fact]
@@ -137,6 +148,18 @@ public async Task ConnectAsync_ConnectedMessageReceived_ConnectedIsTrueIdHasValu
137148
_io.Id.Should().Be("123");
138149
}
139150

151+
[Fact]
152+
public async Task ConnectAsync_FirstSuccess_ConnectAsyncOfSessionIsCalled1Time()
153+
{
154+
_io.Options.Reconnection = true;
155+
156+
await ConnectAsync();
157+
await Task.Delay(2000);
158+
159+
await _session.Received(1).ConnectAsync(Arg.Any<CancellationToken>());
160+
_sessionFactory.Received(1).New(Arg.Any<EngineIO>(), Arg.Any<SessionOptions>());
161+
}
162+
140163
[Fact]
141164
public async Task ConnectAsync_ConnectedMessageDelay_ConnectAsyncIsSync()
142165
{
@@ -321,4 +344,61 @@ await _io
321344
.Should()
322345
.ThrowAsync<ConnectionException>();
323346
}
347+
348+
[Theory]
349+
[InlineData(EngineIO.V3)]
350+
[InlineData(EngineIO.V4)]
351+
public async Task ConnectAsync_DifferentEngineIO_PassCorrectValueToSessionFactory(EngineIO eio)
352+
{
353+
var options = new SocketIOClient.V2.SocketIOOptions
354+
{
355+
EIO = eio,
356+
};
357+
var io = new SocketIOClient.V2.SocketIO("http://localhost:3000", options)
358+
{
359+
SessionFactory = _sessionFactory,
360+
Random = _random,
361+
};
362+
363+
await ConnectAsync(io);
364+
365+
_sessionFactory.Received(1).New(eio, Arg.Any<SessionOptions>());
366+
}
367+
368+
[Fact]
369+
public async Task ConnectAsync_CustomValues_PassCorrectValuesToSessionFactory()
370+
{
371+
var options = new SocketIOClient.V2.SocketIOOptions
372+
{
373+
Path = "/chat",
374+
ConnectionTimeout = TimeSpan.FromSeconds(30),
375+
Query =
376+
[
377+
new KeyValuePair<string, string>("id", "abc"),
378+
],
379+
};
380+
var io = new SocketIOClient.V2.SocketIO("http://localhost:3000", options)
381+
{
382+
SessionFactory = _sessionFactory,
383+
Random = _random,
384+
};
385+
386+
SessionOptions receivedSessionOptions = null!;
387+
_sessionFactory.When(x => x.New(Arg.Any<EngineIO>(), Arg.Any<SessionOptions>()))
388+
.Do(info => { receivedSessionOptions = info.Arg<SessionOptions>(); });
389+
390+
await ConnectAsync(io);
391+
392+
receivedSessionOptions.Should()
393+
.BeEquivalentTo(new SessionOptions
394+
{
395+
ServerUri = new Uri("http://localhost:3000"),
396+
Path = "/chat",
397+
Query =
398+
[
399+
new KeyValuePair<string, string>("id", "abc"),
400+
],
401+
Timeout = TimeSpan.FromSeconds(30),
402+
});
403+
}
324404
}

0 commit comments

Comments
 (0)