Skip to content

Commit 029984c

Browse files
committed
Update multicall response handler to prevent out of order responses and handle unitless sizes
1 parent b000451 commit 029984c

15 files changed

Lines changed: 229 additions & 37 deletions

Aria2.JsonRpcClient.Test/Client/Aria2ClientSystemMulticallTests.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,5 +114,59 @@ public async Task GIVEN_RequestWithVoidResponse_WHEN_SystemMulticall_THEN_Should
114114
Mock.Get(_requestHandler)
115115
.Verify(x => x.SendRequest<IReadOnlyList<object>>(It.Is<MultiCall>(r => r != null)), Times.Once());
116116
}
117+
118+
[Fact]
119+
public async Task GIVEN_RequestWithNonJsonResponse_WHEN_SystemMulticall_THEN_ShouldPassMultiCallRequestToHandler()
120+
{
121+
var jsonResponse = new JsonRpcResponse<IReadOnlyList<object>>
122+
{
123+
Result = new List<object>
124+
{
125+
""
126+
}.AsReadOnly(),
127+
Error = null,
128+
Id = "Id",
129+
JsonRpc = "JsonRpc"
130+
};
131+
var methods = new JsonRpcRequest[] { new TellActive() };
132+
133+
Mock.Get(_requestHandler)
134+
.Setup(x => x.SendRequest<IReadOnlyList<object>>(It.IsAny<JsonRpcRequest>()))
135+
.ReturnsAsync(jsonResponse);
136+
137+
var result = await _target.SystemMulticall(methods);
138+
139+
result[0].Should().BeOfType<JsonRpcError>();
140+
141+
Mock.Get(_requestHandler)
142+
.Verify(x => x.SendRequest<IReadOnlyList<object>>(It.Is<MultiCall>(r => r != null)), Times.Once());
143+
}
144+
145+
[Fact]
146+
public async Task GIVEN_RequestWithErrorResponse_WHEN_SystemMulticall_THEN_ShouldPassMultiCallRequestToHandler()
147+
{
148+
var jsonResponse = new JsonRpcResponse<IReadOnlyList<object>>
149+
{
150+
Result = new List<object>
151+
{
152+
JsonDocument.Parse("{\"code\":-32600,\"message\":\"Invalid Request\",\"customKey\":\"customValue\"}").RootElement,
153+
}.AsReadOnly(),
154+
Error = null,
155+
Id = "Id",
156+
JsonRpc = "JsonRpc"
157+
};
158+
var methods = new JsonRpcRequest[] { new TellActive() };
159+
160+
Mock.Get(_requestHandler)
161+
.Setup(x => x.SendRequest<IReadOnlyList<object>>(It.IsAny<JsonRpcRequest>()))
162+
.ReturnsAsync(jsonResponse);
163+
164+
var result = await _target.SystemMulticall(methods);
165+
166+
result[0].Should().BeOfType<JsonRpcError>();
167+
168+
Mock.Get(_requestHandler)
169+
.Verify(x => x.SendRequest<IReadOnlyList<object>>(It.Is<MultiCall>(r => r != null)), Times.Once());
170+
}
117171
}
118172
}

Aria2.JsonRpcClient.Test/Client/Aria2ClientTellActiveTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,23 @@ public async Task GIVEN_NoKeys_WHEN_TellActive_THEN_ShouldPassTellActiveRequestT
3333
Mock.Get(_requestHandler)
3434
.Verify(x => x.SendRequest<IReadOnlyList<Aria2Status>>(It.Is<TellActive>(r => r != null)), Times.Once());
3535
}
36+
37+
[Fact]
38+
public async Task GIVEN_KeysSelector_WHEN_TellActive_THEN_ShouldPassTellActiveRequestToHandler()
39+
{
40+
var expected = new List<Aria2Status> { new Aria2Status() };
41+
var response = new JsonRpcResponse<IReadOnlyList<Aria2Status>> { Result = expected.AsReadOnly(), Error = null, Id = "Id", JsonRpc = "JsonRpc" };
42+
43+
Mock.Get(_requestHandler)
44+
.Setup(x => x.SendRequest<IReadOnlyList<Aria2Status>>(It.IsAny<JsonRpcRequest>()))
45+
.ReturnsAsync(response);
46+
47+
var result = await _target.TellActive(s => new { s.Gid });
48+
49+
result.Should().BeEquivalentTo(expected);
50+
51+
Mock.Get(_requestHandler)
52+
.Verify(x => x.SendRequest<IReadOnlyList<Aria2Status>>(It.Is<TellActive>(r => r != null)), Times.Once());
53+
}
3654
}
3755
}

Aria2.JsonRpcClient.Test/Client/Aria2ClientTellStatusTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,24 @@ public async Task GIVEN_ValidGid_WHEN_TellStatus_THEN_ShouldPassTellStatusReques
3434
Mock.Get(_requestHandler)
3535
.Verify(x => x.SendRequest<Aria2Status>(It.Is<TellStatus>(r => r != null)), Times.Once());
3636
}
37+
38+
[Fact]
39+
public async Task GIVEN_ValidGidWithKeysSelector_WHEN_TellStatus_THEN_ShouldPassTellStatusRequestToHandler()
40+
{
41+
var expected = new Aria2Status();
42+
var response = new JsonRpcResponse<Aria2Status> { Result = expected, Error = null, Id = "Id", JsonRpc = "JsonRpc" };
43+
var gid = "Gid1";
44+
45+
Mock.Get(_requestHandler)
46+
.Setup(x => x.SendRequest<Aria2Status>(It.IsAny<JsonRpcRequest>()))
47+
.ReturnsAsync(response);
48+
49+
var result = await _target.TellStatus(gid, s => new { s.Gid });
50+
51+
result.Should().BeSameAs(expected);
52+
53+
Mock.Get(_requestHandler)
54+
.Verify(x => x.SendRequest<Aria2Status>(It.Is<TellStatus>(r => r != null)), Times.Once());
55+
}
3756
}
3857
}

Aria2.JsonRpcClient.Test/Client/Aria2ClientTellStoppedTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,25 @@ public async Task GIVEN_ValidOffsetAndNum_WHEN_TellStopped_THEN_ShouldPassTellSt
3535
Mock.Get(_requestHandler)
3636
.Verify(x => x.SendRequest<IReadOnlyList<Aria2Status>>(It.Is<TellStopped>(r => r != null)), Times.Once());
3737
}
38+
39+
[Fact]
40+
public async Task GIVEN_ValidOffsetAndNumAndKeysSelector_WHEN_TellStopped_THEN_ShouldPassTellStoppedRequestToHandler()
41+
{
42+
var expected = new List<Aria2Status> { new Aria2Status() };
43+
var response = new JsonRpcResponse<IReadOnlyList<Aria2Status>> { Result = expected.AsReadOnly(), Error = null, Id = "Id", JsonRpc = "JsonRpc" };
44+
var offset = 0;
45+
var num = 1;
46+
47+
Mock.Get(_requestHandler)
48+
.Setup(x => x.SendRequest<IReadOnlyList<Aria2Status>>(It.IsAny<JsonRpcRequest>()))
49+
.ReturnsAsync(response);
50+
51+
var result = await _target.TellStopped(offset, num, s => new { s.Gid });
52+
53+
result.Should().BeEquivalentTo(expected);
54+
55+
Mock.Get(_requestHandler)
56+
.Verify(x => x.SendRequest<IReadOnlyList<Aria2Status>>(It.Is<TellStopped>(r => r != null)), Times.Once());
57+
}
3858
}
3959
}

Aria2.JsonRpcClient.Test/Client/Aria2ClientTellWaitingTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,25 @@ public async Task GIVEN_ValidOffsetAndNum_WHEN_TellWaiting_THEN_ShouldPassTellWa
3535
Mock.Get(_requestHandler)
3636
.Verify(x => x.SendRequest<IReadOnlyList<Aria2Status>>(It.Is<TellWaiting>(r => r != null)), Times.Once());
3737
}
38+
39+
[Fact]
40+
public async Task GIVEN_ValidOffsetAndNumAndKeysSelector_WHEN_TellWaiting_THEN_ShouldPassTellWaitingRequestToHandler()
41+
{
42+
var expected = new List<Aria2Status> { new Aria2Status() };
43+
var response = new JsonRpcResponse<IReadOnlyList<Aria2Status>> { Result = expected.AsReadOnly(), Error = null, Id = "Id", JsonRpc = "JsonRpc" };
44+
var offset = 0;
45+
var num = 1;
46+
47+
Mock.Get(_requestHandler)
48+
.Setup(x => x.SendRequest<IReadOnlyList<Aria2Status>>(It.IsAny<JsonRpcRequest>()))
49+
.ReturnsAsync(response);
50+
51+
var result = await _target.TellWaiting(offset, num, s => new { s.Gid });
52+
53+
result.Should().BeEquivalentTo(expected);
54+
55+
Mock.Get(_requestHandler)
56+
.Verify(x => x.SendRequest<IReadOnlyList<Aria2Status>>(It.Is<TellWaiting>(r => r != null)), Times.Once());
57+
}
3858
}
3959
}

Aria2.JsonRpcClient.Test/Converters/SizeConverterTests.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,25 @@ public void GIVEN_NullJson_WHEN_Read_THEN_ShouldReturnDefaultSize()
3333
}
3434

3535
[Fact]
36-
public void GIVEN_ValidSizeStringWithMegabytes_WHEN_Read_THEN_ShouldReturnSizeWithMegabytes()
36+
public void GIVEN_ValidSizeStringWithBytes_WHEN_Read_THEN_ShouldReturnSizeWithBytes()
3737
{
38-
var json = "\"1M\"";
38+
var json = "\"100B\"";
3939

4040
var result = InvokeRead(json);
4141

42-
result.Value.Should().Be(1);
43-
result.SizeType.Should().Be(SizeType.Megabytes);
42+
result.Value.Should().Be(100);
43+
result.SizeType.Should().Be(SizeType.Bytes);
44+
}
45+
46+
[Fact]
47+
public void GIVEN_ValidSizeStringWithNoUnits_WHEN_Read_THEN_ShouldReturnSizeWithBytes()
48+
{
49+
var json = "\"100\"";
50+
51+
var result = InvokeRead(json);
52+
53+
result.Value.Should().Be(100);
54+
result.SizeType.Should().Be(SizeType.Bytes);
4455
}
4556

4657
[Fact]
@@ -54,6 +65,17 @@ public void GIVEN_ValidSizeStringWithKilobytes_WHEN_Read_THEN_ShouldReturnSizeWi
5465
result.SizeType.Should().Be(SizeType.Kilobytes);
5566
}
5667

68+
[Fact]
69+
public void GIVEN_ValidSizeStringWithMegabytes_WHEN_Read_THEN_ShouldReturnSizeWithMegabytes()
70+
{
71+
var json = "\"1M\"";
72+
73+
var result = InvokeRead(json);
74+
75+
result.Value.Should().Be(1);
76+
result.SizeType.Should().Be(SizeType.Megabytes);
77+
}
78+
5779
[Fact]
5880
public void GIVEN_InvalidSizeString_WHEN_Read_THEN_ShouldThrowJsonException()
5981
{

Aria2.JsonRpcClient.Test/Models/Aria2OptionsTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ public void GIVEN_ValidJson_WHEN_Deserializing_THEN_ShouldReturnObject()
2525

2626
result.Should().NotBeNull();
2727

28-
result.Options.Should().NotBeNull();
29-
result.Options.Should().HaveCount(2);
30-
((JsonElement)result.Options["option1"]).GetString().Should().Be("value1");
31-
((JsonElement)result.Options["option2"]).GetString().Should().Be("value2");
28+
result.AdditionalOptions.Should().NotBeNull();
29+
result.AdditionalOptions.Should().HaveCount(2);
30+
((JsonElement)result.AdditionalOptions["option1"]).GetString().Should().Be("value1");
31+
((JsonElement)result.AdditionalOptions["option2"]).GetString().Should().Be("value2");
3232
}
3333
}
3434
}

Aria2.JsonRpcClient.Test/Models/Aria2StatusTests.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,13 @@ public void GIVEN_InputPropertyName_WHEN_Matching_THEN_ShouldReturnCorresponding
110110
result.Should().NotBeNull();
111111
result.Should().Be(key);
112112
}
113+
114+
[Fact]
115+
public void GIVEN_IncorrectPropertyName_WHEN_Matching_THEN_ShouldThrowInvalidOperationException()
116+
{
117+
var act = () => Aria2Status.Keys.Match("notvalid");
118+
119+
act.Should().Throw<InvalidOperationException>();
120+
}
113121
}
114122
}

Aria2.JsonRpcClient.Test/Models/SizeTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,36 @@ public void GIVEN_SizeWithInvalidSizeType_WHEN_CallingToString_THEN_ThrowsArgume
3636
act.Should().Throw<ArgumentOutOfRangeException>();
3737
}
3838

39+
[Fact]
40+
public void GIVEN_StringWithNoUnit_WHEN_TryParseCalled_THEN_ReturnsValidSize()
41+
{
42+
var success = Size.TryParse("1", out var size);
43+
44+
success.Should().BeTrue();
45+
size.Value.Should().Be(1);
46+
size.SizeType.Should().Be(SizeType.Bytes);
47+
}
48+
49+
[Fact]
50+
public void GIVEN_StringWithBytesUpperCase_WHEN_TryParseCalled_THEN_ReturnsValidSize()
51+
{
52+
var success = Size.TryParse("1B", out var size);
53+
54+
success.Should().BeTrue();
55+
size.Value.Should().Be(1);
56+
size.SizeType.Should().Be(SizeType.Bytes);
57+
}
58+
59+
[Fact]
60+
public void GIVEN_StringWithBytesLowerCase_WHEN_TryParseCalled_THEN_ReturnsValidSize()
61+
{
62+
var success = Size.TryParse("1b", out var size);
63+
64+
success.Should().BeTrue();
65+
size.Value.Should().Be(1);
66+
size.SizeType.Should().Be(SizeType.Bytes);
67+
}
68+
3969
[Fact]
4070
public void GIVEN_StringWithMegabytesUpperCase_WHEN_TryParseCalled_THEN_ReturnsValidSize()
4171
{

Aria2.JsonRpcClient/Aria2Client.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -335,17 +335,19 @@ public Task RemoveDownloadResult(string gid, string? id = null)
335335
{
336336
if (response is not JsonElement element)
337337
{
338-
continue;
339-
}
340-
341-
// a valid response will be an array with an item at 0
342-
if (element.ValueKind == JsonValueKind.Array)
343-
{
344-
value = Serializer.Deserialize(element.EnumerateArray().FirstOrDefault(), method.ReturnType);
338+
value = new JsonRpcError { Message = "Response was not JsonElement", Code = -1 };
345339
}
346340
else
347341
{
348-
value = Serializer.Deserialize<JsonRpcError>(element);
342+
// a valid response will be an array with an item at 0
343+
if (element.ValueKind == JsonValueKind.Array)
344+
{
345+
value = Serializer.Deserialize(element.EnumerateArray().FirstOrDefault(), method.ReturnType);
346+
}
347+
else
348+
{
349+
value = Serializer.Deserialize<JsonRpcError>(element);
350+
}
349351
}
350352
}
351353
responses.Add(value);

0 commit comments

Comments
 (0)