Skip to content

Commit e5c19f7

Browse files
authored
Merge pull request #77 from cdauphinee/no-dynamic-2
Adding alternative methods to avoid dynamic usage
2 parents 76ca471 + 0d452c9 commit e5c19f7

File tree

10 files changed

+358
-87
lines changed

10 files changed

+358
-87
lines changed

PusherClient.Tests.Utilities/FakeAuthoriser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public string Authorize(string channelName, string socketId)
2121
{
2222
var channelData = new PresenceChannelData();
2323
channelData.user_id = socketId;
24-
channelData.user_info = new { name = _userName };
24+
channelData.user_info = new FakeUserInfo { name = _userName };
2525

2626
authData = provider.Authenticate(channelName, socketId, channelData).ToJson();
2727
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using PusherServer;
2+
3+
namespace PusherClient.Tests.Utilities
4+
{
5+
public class FakeUserInfo
6+
{
7+
public string name { get; set; }
8+
}
9+
}

PusherClient.Tests.Utilities/PusherClient.Tests.Utilities.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
<ItemGroup>
7070
<Compile Include="ChannelNameFactory.cs" />
7171
<Compile Include="Config.cs" />
72+
<Compile Include="FakeUserInfo.cs" />
7273
<Compile Include="FakeAuthoriser.cs" />
7374
<Compile Include="Properties\AssemblyInfo.cs" />
7475
<Compile Include="PusherFactory.cs" />

PusherClient.Tests/AcceptanceTests/PresenceChannel.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,49 @@ public void PresenceChannelShouldAddAMemberWhenGivenAMemberAsync()
9292
Assert.IsTrue(channel.IsSubscribed);
9393
Assert.IsTrue(channelSubscribed);
9494
}
95+
96+
[Test]
97+
public void PresenceChannelShouldAddATypedMemberWhenGivenAMemberAsync()
98+
{
99+
// Arrange
100+
var stubOptions = new PusherOptions
101+
{
102+
Authorizer = new FakeAuthoriser(UserNameFactory.CreateUniqueUserName())
103+
};
104+
105+
var pusher = PusherFactory.GetPusher(stubOptions);
106+
AutoResetEvent reset = new AutoResetEvent(false);
107+
108+
pusher.Connected += sender =>
109+
{
110+
reset.Set();
111+
};
112+
113+
AsyncContext.Run(() => pusher.ConnectAsync());
114+
reset.WaitOne(TimeSpan.FromSeconds(5));
115+
reset.Reset();
116+
117+
var mockChannelName = ChannelNameFactory.CreateUniqueChannelName(presenceChannel: true);
118+
119+
var channelSubscribed = false;
120+
121+
// Act
122+
var channel = AsyncContext.Run(() => pusher.SubscribePresenceAsync<FakeUserInfo>(mockChannelName));
123+
channel.Subscribed += sender =>
124+
{
125+
channelSubscribed = true;
126+
reset.Set();
127+
};
128+
129+
reset.WaitOne(TimeSpan.FromSeconds(10));
130+
131+
// Assert
132+
Assert.IsNotNull(channel);
133+
StringAssert.Contains(mockChannelName, channel.Name);
134+
Assert.IsTrue(channel.IsSubscribed);
135+
Assert.IsTrue(channelSubscribed);
136+
137+
CollectionAssert.IsNotEmpty(channel.Members);
138+
}
95139
}
96140
}

PusherClient.Tests/UnitTests/PusherTests.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using Nito.AsyncEx;
33
using NUnit.Framework;
4+
using PusherClient.Tests.Utilities;
45

56
namespace PusherClient.Tests.UnitTests
67
{
@@ -147,5 +148,75 @@ public void PusherShouldThrowAnExceptionWhenSubscribeIsCalledWithANullStringForA
147148
Assert.IsNotNull(caughtException);
148149
StringAssert.Contains("The channel name cannot be null or whitespace", caughtException.Message);
149150
}
151+
152+
[Test]
153+
public void PusherShouldThrowAnExceptionWhenSubscribePresenceIsCalledWithANonPresenceChannelAsync()
154+
{
155+
// Arrange
156+
ArgumentException caughtException = null;
157+
158+
// Act
159+
try
160+
{
161+
var pusher = new Pusher("FakeAppKey");
162+
var channel = AsyncContext.Run(() => pusher.SubscribePresenceAsync<string>("private-123"));
163+
}
164+
catch (ArgumentException ex)
165+
{
166+
caughtException = ex;
167+
}
168+
169+
// Assert
170+
Assert.IsNotNull(caughtException);
171+
StringAssert.Contains("The channel name must be refer to a presence channel", caughtException.Message);
172+
}
173+
174+
[Test]
175+
public void PusherShouldThrowAnExceptionWhenSubscribePresenceIsCalledWithADifferentTypeAsync()
176+
{
177+
// Arrange
178+
InvalidOperationException caughtException = null;
179+
180+
// Act
181+
var pusher = new Pusher("FakeAppKey", new PusherOptions { Authorizer = new FakeAuthoriser("test") });
182+
AsyncContext.Run(() => pusher.SubscribePresenceAsync<string>("presence-123"));
183+
184+
try
185+
{
186+
AsyncContext.Run(() => pusher.SubscribePresenceAsync<int>("presence-123"));
187+
}
188+
catch (InvalidOperationException ex)
189+
{
190+
caughtException = ex;
191+
}
192+
193+
// Assert
194+
Assert.IsNotNull(caughtException);
195+
StringAssert.Contains("Cannot change channel member type; was previously defined as", caughtException.Message);
196+
}
197+
198+
[Test]
199+
public void PusherShouldThrowAnExceptionWhenSubscribePresenceIsCalledAfterSubscribe()
200+
{
201+
// Arrange
202+
InvalidOperationException caughtException = null;
203+
204+
// Act
205+
var pusher = new Pusher("FakeAppKey", new PusherOptions { Authorizer = new FakeAuthoriser("test") });
206+
AsyncContext.Run(() => pusher.SubscribeAsync("presence-123"));
207+
208+
try
209+
{
210+
AsyncContext.Run(() => pusher.SubscribePresenceAsync<int>("presence-123"));
211+
}
212+
catch (InvalidOperationException ex)
213+
{
214+
caughtException = ex;
215+
}
216+
217+
// Assert
218+
Assert.IsNotNull(caughtException);
219+
StringAssert.Contains("This presence channel has already been created without specifying the member info type", caughtException.Message);
220+
}
150221
}
151222
}

PusherClient/EventEmitter.cs

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ public class EventEmitter
1212
private readonly Dictionary<string, List<Action<dynamic>>> _eventListeners = new Dictionary<string, List<Action<dynamic>>>();
1313
private readonly List<Action<string, dynamic>> _generalListeners = new List<Action<string, dynamic>>();
1414

15+
private readonly Dictionary<string, List<Action<string>>> _rawEventListeners = new Dictionary<string, List<Action<string>>>();
16+
private readonly List<Action<string, string>> _rawGeneralListeners = new List<Action<string, string>>();
17+
1518
/// <summary>
1619
/// Binds to a given event name
1720
/// </summary>
@@ -30,6 +33,24 @@ public void Bind(string eventName, Action<dynamic> listener)
3033
}
3134
}
3235

36+
/// <summary>
37+
/// Binds to a given event name. The listener will receive the raw JSON message.
38+
/// </summary>
39+
/// <param name="eventName">The Event Name to listen for</param>
40+
/// <param name="listener">The action to perform when the event occurs</param>
41+
public void Bind(string eventName, Action<string> listener)
42+
{
43+
if (_rawEventListeners.ContainsKey(eventName))
44+
{
45+
_rawEventListeners[eventName].Add(listener);
46+
}
47+
else
48+
{
49+
var listeners = new List<Action<string>> { listener };
50+
_rawEventListeners.Add(eventName, listeners);
51+
}
52+
}
53+
3354
/// <summary>
3455
/// Binds to ALL event
3556
/// </summary>
@@ -39,13 +60,23 @@ public void BindAll(Action<string, dynamic> listener)
3960
_generalListeners.Add(listener);
4061
}
4162

63+
/// <summary>
64+
/// Binds to ALL event. The listener will receive the raw JSON message.
65+
/// </summary>
66+
/// <param name="listener">The action to perform when the any event occurs</param>
67+
public void BindAll(Action<string, string> listener)
68+
{
69+
_rawGeneralListeners.Add(listener);
70+
}
71+
4272
/// <summary>
4373
/// Removes the binding for the given event name
4474
/// </summary>
4575
/// <param name="eventName">The name of the event to unbind</param>
4676
public void Unbind(string eventName)
4777
{
4878
_eventListeners.Remove(eventName);
79+
_rawEventListeners.Remove(eventName);
4980
}
5081

5182
/// <summary>
@@ -55,36 +86,68 @@ public void Unbind(string eventName)
5586
/// <param name="listener">The action to remove</param>
5687
public void Unbind(string eventName, Action<dynamic> listener)
5788
{
58-
if(_eventListeners.ContainsKey(eventName))
89+
if (_eventListeners.ContainsKey(eventName))
5990
{
6091
_eventListeners[eventName].Remove(listener);
6192
}
6293
}
6394

95+
/// <summary>
96+
/// Remove the action for the event name
97+
/// </summary>
98+
/// <param name="eventName">The name of the event to unbind</param>
99+
/// <param name="listener">The action to remove</param>
100+
public void Unbind(string eventName, Action<string> listener)
101+
{
102+
if (_rawEventListeners.ContainsKey(eventName))
103+
{
104+
_rawEventListeners[eventName].Remove(listener);
105+
}
106+
}
107+
64108
/// <summary>
65109
/// Remove All bindings
66110
/// </summary>
67111
public void UnbindAll()
68112
{
69113
_eventListeners.Clear();
70114
_generalListeners.Clear();
115+
116+
_rawEventListeners.Clear();
117+
_rawGeneralListeners.Clear();
71118
}
72119

73120
internal void EmitEvent(string eventName, string data)
74121
{
75-
var obj = JsonConvert.DeserializeObject<dynamic>(data);
122+
foreach (var action in _rawGeneralListeners)
123+
{
124+
action(eventName, data);
125+
}
76126

77-
// Emit to general listeners regardless of event type
78-
foreach (var action in _generalListeners)
127+
if (_rawEventListeners.ContainsKey(eventName))
79128
{
80-
action(eventName, obj);
129+
foreach (var action in _rawEventListeners[eventName])
130+
{
131+
action(data);
132+
}
81133
}
82134

83-
if (_eventListeners.ContainsKey(eventName))
135+
// Don't bother with deserialization if there are no dynamic listeners
136+
if (_generalListeners.Count > 0 || _eventListeners.Count > 0)
84137
{
85-
foreach (var action in _eventListeners[eventName])
138+
var obj = JsonConvert.DeserializeObject<dynamic>(data);
139+
140+
foreach (var action in _generalListeners)
141+
{
142+
action(eventName, obj);
143+
}
144+
145+
if (_eventListeners.ContainsKey(eventName))
86146
{
87-
action(obj);
147+
foreach (var action in _eventListeners[eventName])
148+
{
149+
action(obj);
150+
}
88151
}
89152
}
90153
}

0 commit comments

Comments
 (0)