Skip to content

Commit 5f32544

Browse files
committed
add EngineIO4Adapter and corresponding unit tests
1 parent c6deda8 commit 5f32544

File tree

2 files changed

+225
-0
lines changed

2 files changed

+225
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using SocketIOClient.V2.Protocol;
5+
using SocketIOClient.V2.Protocol.Http;
6+
7+
namespace SocketIOClient.V2.Session.EngineIOHttpAdapter;
8+
9+
public class EngineIO4Adapter : IEngineIOAdapter
10+
{
11+
private const string Delimiter = "\u001E";
12+
13+
public IHttpRequest ToHttpRequest(ICollection<byte[]> bytes)
14+
{
15+
if (!bytes.Any())
16+
{
17+
throw new ArgumentException("The array cannot be empty");
18+
}
19+
var req = new HttpRequest
20+
{
21+
Method = RequestMethod.Post,
22+
BodyType = RequestBodyType.Text,
23+
};
24+
25+
var base64Strings = bytes.Select(b => $"b{Convert.ToBase64String(b)}");
26+
req.BodyText = string.Join(Delimiter, base64Strings);
27+
return req;
28+
}
29+
30+
public IHttpRequest ToHttpRequest(string content)
31+
{
32+
if (string.IsNullOrEmpty(content))
33+
{
34+
throw new ArgumentException("The content cannot be null or empty");
35+
}
36+
return new HttpRequest
37+
{
38+
Method = RequestMethod.Post,
39+
BodyType = RequestBodyType.Text,
40+
BodyText = content,
41+
};
42+
}
43+
44+
public IEnumerable<ProtocolMessage> GetMessages(string text)
45+
{
46+
var items = text.Split([Delimiter], StringSplitOptions.RemoveEmptyEntries);
47+
foreach (var item in items)
48+
{
49+
if (item[0] == 'b')
50+
{
51+
var bytes = Convert.FromBase64String(item.Substring(1));
52+
yield return new ProtocolMessage
53+
{
54+
Type = ProtocolMessageType.Bytes,
55+
Bytes = bytes,
56+
};
57+
}
58+
else
59+
{
60+
yield return new ProtocolMessage
61+
{
62+
Type = ProtocolMessageType.Text,
63+
Text = item,
64+
};
65+
}
66+
}
67+
}
68+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using FluentAssertions;
6+
using JetBrains.Annotations;
7+
using SocketIOClient.V2.Protocol;
8+
using SocketIOClient.V2.Protocol.Http;
9+
using SocketIOClient.V2.Session.EngineIOHttpAdapter;
10+
using Xunit;
11+
12+
namespace SocketIOClient.UnitTests.V2.Session.EngineIOHttpAdapter;
13+
14+
public class EngineIO4AdapterTests
15+
{
16+
private readonly EngineIO4Adapter _adapter = new();
17+
18+
[Fact]
19+
public void ToHttpRequest_GivenAnEmptyArray_ThrowException()
20+
{
21+
_adapter
22+
.Invoking(x => x.ToHttpRequest(new List<byte[]>()))
23+
.Should()
24+
.Throw<ArgumentException>()
25+
.WithMessage("The array cannot be empty");
26+
}
27+
28+
private static readonly (ICollection<byte[]> bytes, IHttpRequest req) OneItem1Byte = new(new List<byte[]>
29+
{
30+
new byte[] { 1 },
31+
}, new HttpRequest
32+
{
33+
BodyText = "bAQ==",
34+
Method = RequestMethod.Post,
35+
BodyType = RequestBodyType.Text,
36+
});
37+
38+
private static readonly (ICollection<byte[]> bytes, IHttpRequest req) OneItem10Bytes = new(new List<byte[]>
39+
{
40+
Enumerable.Range(0, 10).Select(x => (byte)x).ToArray(),
41+
}, new HttpRequest
42+
{
43+
BodyText = "bAAECAwQFBgcICQ==",
44+
Method = RequestMethod.Post,
45+
BodyType = RequestBodyType.Text,
46+
});
47+
48+
private static readonly (ICollection<byte[]> bytes, IHttpRequest req) TwoItems1Byte10Bytes = new(new List<byte[]>
49+
{
50+
new byte[] { 1 },
51+
Enumerable.Range(0, 10).Select(x => (byte)x).ToArray(),
52+
}, new HttpRequest
53+
{
54+
BodyText = "bAQ==\u001EbAAECAwQFBgcICQ==",
55+
Method = RequestMethod.Post,
56+
BodyType = RequestBodyType.Text,
57+
});
58+
59+
private static IEnumerable<(ICollection<byte[]> bytes, IHttpRequest req)> ToHttpRequestStrongTypeCases
60+
{
61+
get
62+
{
63+
yield return OneItem1Byte;
64+
yield return OneItem10Bytes;
65+
yield return TwoItems1Byte10Bytes;
66+
}
67+
}
68+
69+
public static IEnumerable<object[]> ToHttpRequestCases =>
70+
ToHttpRequestStrongTypeCases.Select(x => new object[] { x.bytes, x.req });
71+
72+
[Theory]
73+
[MemberData(nameof(ToHttpRequestCases))]
74+
public void ToHttpRequest_WhenCalled_AlwaysPass(ICollection<byte[]> bytes, IHttpRequest result)
75+
{
76+
var req = _adapter.ToHttpRequest(bytes);
77+
req.Should().BeEquivalentTo(result);
78+
}
79+
80+
private static readonly (string raw, IEnumerable<ProtocolMessage> messages) GetMessagesSingleHelloWorld = new(
81+
"hello world!",
82+
[
83+
new ProtocolMessage { Type = ProtocolMessageType.Text, Text = "hello world!" },
84+
]);
85+
86+
private static readonly (string raw, IEnumerable<ProtocolMessage> messages) GetMessagesPingAndHelloWorld = new(
87+
"2\u001Ehello world!",
88+
[
89+
new ProtocolMessage { Type = ProtocolMessageType.Text, Text = "2" },
90+
new ProtocolMessage { Type = ProtocolMessageType.Text, Text = "hello world!" },
91+
]);
92+
93+
private static readonly (string raw, IEnumerable<ProtocolMessage> messages) GetMessagesPingAndBytes = new(
94+
"2\u001EbAQ==",
95+
[
96+
new ProtocolMessage { Type = ProtocolMessageType.Text, Text = "2" },
97+
new ProtocolMessage { Type = ProtocolMessageType.Bytes, Bytes = [1] },
98+
]);
99+
100+
private static readonly (string raw, IEnumerable<ProtocolMessage> messages) Get2Bytes = new(
101+
"bAA==\u001EbAQ==",
102+
[
103+
new ProtocolMessage { Type = ProtocolMessageType.Bytes, Bytes = [0] },
104+
new ProtocolMessage { Type = ProtocolMessageType.Bytes, Bytes = [1] },
105+
]);
106+
107+
public static TheoryData<string, IEnumerable<ProtocolMessage>> GetMessagesCases =>
108+
new()
109+
{
110+
{
111+
GetMessagesSingleHelloWorld.raw,
112+
GetMessagesSingleHelloWorld.messages
113+
},
114+
{
115+
GetMessagesPingAndHelloWorld.raw,
116+
GetMessagesPingAndHelloWorld.messages
117+
},
118+
{
119+
GetMessagesPingAndBytes.raw,
120+
GetMessagesPingAndBytes.messages
121+
},
122+
{
123+
Get2Bytes.raw,
124+
Get2Bytes.messages
125+
},
126+
};
127+
128+
[Theory]
129+
[MemberData(nameof(GetMessagesCases))]
130+
public void GetMessages_WhenCalled_AlwaysPass(string raw, IEnumerable<ProtocolMessage> messages)
131+
{
132+
_adapter.GetMessages(raw)
133+
.Should()
134+
.BeEquivalentTo(messages);
135+
}
136+
137+
[Theory]
138+
[InlineData(null)]
139+
[InlineData("")]
140+
public void ToHttpRequest_GivenAnInvalidContent_ThrowException([CanBeNull] string content)
141+
{
142+
_adapter
143+
.Invoking(x => x.ToHttpRequest(content))
144+
.Should()
145+
.Throw<ArgumentException>()
146+
.WithMessage("The content cannot be null or empty");
147+
}
148+
149+
[Theory]
150+
[InlineData(" ", " ")]
151+
[InlineData("hello, world!", "hello, world!")]
152+
public void ToHttpRequest_GivenValidContent_ReturnSameBodyText(string content, string expected)
153+
{
154+
var req = _adapter.ToHttpRequest(content);
155+
req.BodyText.Should().Be(expected);
156+
}
157+
}

0 commit comments

Comments
 (0)