8000 test: add socket test snapshots · cnblogs/dashscope-sdk@be1eb9d · GitHub
[go: up one dir, main page]

Skip to content

Commit be1eb9d

Browse files
committed
test: add socket test snapshots
1 parent 1f83e23 commit be1eb9d

20 files changed

+322
-5
lines changed

src/Cnblogs.DashScope.Core/DashScopeClientWebSocket.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public void ResetOutput()
107107
public Task SendMessageAsync<TInput, TParameter>(
108108
DashScopeWebSocketRequest<TInput, TParameter> request,
109109
CancellationToken cancellationToken = default)
110-
where TInput : class
110+
where TInput : class, new()
111111
where TParameter : class
112112
{
113113
if (State == DashScopeWebSocketState.Closed)
@@ -222,6 +222,10 @@ public async Task CloseAsync(CancellationToken cancellationToken = default)
222222
{
223223
await _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", cancellationToken);
224224
State = DashScopeWebSocketState.Closed;
225+
if (_receiveTask != null)
226+
{
227+
await _receiveTask;
228+
}
225229
}
226230

227231
private void Dispose(bool disposing)

src/Cnblogs.DashScope.Core/DashScopeClientWebSocketPool.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ public DashScopeClientWebSocketPool(DashScopeOptions options)
2020
_options = options;
2121
}
2222

23+
internal DashScopeClientWebSocketPool(IEnumerable<DashScopeClientWebSocket> sockets)
24+
{
25+
_options = new DashScopeOptions();
26+
foreach (var socket in sockets)
27+
{
28+
_available.Add(socket);
29+
}
30+
}
31+
2332
internal void ReturnSocketAsync< F438 span class=pl-kos>(DashScopeClientWebSocket socket)
2433
{
2534
if (socket.State != DashScopeWebSocketState.Ready)

src/Cnblogs.DashScope.Core/DashScopeClientWebSocketWrapper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public sealed record DashScopeClientWebSocketWrapper(DashScopeClientWebSocket So
3737
public Task SendMessageAsync<TInput, TParameter>(
3838
DashScopeWebSocketRequest<TInput, TParameter> request,
3939
CancellationToken cancellationToken = default)
40-
where TInput : class
40+
where TInput : class, new()
4141
where TParameter : class
4242
=> Socket.SendMessageAsync(request, cancellationToken);
4343

src/Cnblogs.DashScope.Core/DashScopeWebSocketRequest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
/// <typeparam name="TInput">Type of the input.</typeparam>
77
/// <typeparam name="TParameter">Type of the parameter.</typeparam>
88
public class DashScopeWebSocketRequest<TInput, TParameter>
9-
where TInput : class
9+
where TInput : class, new()
1010
where TParameter : class
1111
{
1212
/// <summary>

src/Cnblogs.DashScope.Core/DashScopeWebSocketRequestPayload.cs

Lines changed: 2 additions & 2 deletions
< 10000 td data-grid-cell-id="diff-85b6fbb0dcd256671703bf30caf92e865c7aecdb1e651b683ed9a59138a61633-11-11-1" data-selected="false" role="gridcell" style="background-color:var(--bgColor-default);text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative diff-line-number-neutral left-side">11
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
/// <typeparam name="TInput">Type of the input.</typeparam>
77
/// <typeparam name="TParameter">Type of the parameter.</typeparam>
88
public class DashScopeWebSocketRequestPayload<TInput, TParameter>
9-
where TInput : class
9+
where TInput : class, new() // Input's default value must be empty object(not null or omitted).
1010
where TParameter : class
11
{
1212
/// <summary>
@@ -37,5 +37,5 @@ public class DashScopeWebSocketRequestPayload<TInput, TParameter>
3737
/// <summary>
3838
/// The input of the request.
3939
/// </summary>
40-
public TInput Input { get; set; } = null!;
40+
public TInput Input { get; set; } = new();
4141
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Runtime.CompilerServices;
22

33
[assembly: InternalsVisibleTo("Cnblogs.DashScope.Sdk.UnitTests")]
4+
[assembly: InternalsVisibleTo("Cnblogs.DashScope.Tests.Shared")]
45
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

src/Cnblogs.DashScope.Core/SpeechSynthesizerParameters.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,9 @@ public class SpeechSynthesizerParameters
3939
/// Pitch of the voice, range between 0.5~2, defaults to 1.0.
4040
/// </summary>
4141
public float? Pitch { get; set; }
42+
43+
/// <summary>
44+
/// Enable SSML, you can only send text once if enabled.
45+
/// </summary>
46+
public bool? EnableSsml { get; set; }
4247
}

test/Cnblogs.DashScope.Sdk.UnitTests/DashScopeClientWebSocketTests.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Reflection;
44
using Cnblogs.DashScope.Core;
55
using Cnblogs.DashScope.Core.Internals;
6+
using Cnblogs.DashScope.Tests.Shared.Utils;
67
using NSubstitute;
78

89
namespace Cnblogs.DashScope.Sdk.UnitTests;
@@ -102,6 +103,56 @@ public async Task ResetOutput_WithInitialOutput_CompleteThenCreateNewOutputAsync
102103
Assert.NotSame(oldSignal, client.TaskStarted);
103104
}
104105

106+
[Fact]
107+
public async Task SendMessageAsync_SocketClosed_ThrowAsync()
108+
{
109+
// Arrange
110+
var socket = Substitute.For<IClientWebSocket>();
111+
var client = new DashScopeClientWebSocket(socket);
112+
var snapshot = Snapshots.SpeechSynthesizer.RunTask;
113+
await client.CloseAsync();
114+
115+
// Act
116+
var act = () => client.SendMessageAsync(snapshot.Message);
117+
118+
// Assert
119+
await Assert.ThrowsAsync<InvalidOperationException>(act);
120+
}
121+
122+
[Fact]
123+
public async Task SendMessageAsync_Connected_SendAsync()
124+
{
125+
// Arrange
126+
var socket = Substitute.For<IClientWebSocket>();
127+
var client = new DashScopeClientWebSocket(socket);
128+
var snapshot = Snapshots.SpeechSynthesizer.RunTask;
129+
130+
// Act
131+
await client.ConnectAsync<SpeechSynthesizerOutput>(new Uri(DashScopeDefaults.WebsocketApiBaseAddress));
132+
await client.SendMessageAsync(snapshot.Message);
133+
134+
// Assert
135+
await socket.Received().SendAsync(
136+
Arg.Is<ArraySegment<byte>>(s => Checkers.IsJsonEquivalent(s, snapshot.GetRequestJson())),
137+
WebSocketMessageType.Text,
138+
true,
139+
Arg.Any<CancellationToken>());
140+
}
141+
142+
[Fact]
143+
public async Task ReceiveMessageAsync_ServerClosed_CloseAsync()
144+
{
145+
// Arrange
146+
var (_, dashScopeClientWebSocket, server) = await Sut.GetSocketTestClientAsync<SpeechSynthesizerOutput>();
147+
148+
// Act
149+
await server.WriteServerCloseAsync();
150+
151+
// Assert
152+
Assert.Equal(DashScopeWebSocketState.Closed, dashScopeClientWebSocket.State);
153+
Assert.Equal(WebSocketCloseStatus.NormalClosure, server.CloseStatus);
154+
}
155+
105156
private static WebHeaderCollection ExtractHeaders(DashScopeClientWebSocket socket)
106157
{
107158
var obj = InnerSocketInfo.GetValue(socket);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
using System.Runtime.CompilerServices;
2+
3+
[assembly: InternalsVisibleTo("Cnblogs.DashScope.Sdk.UnitTests")]
4+
[assembly: InternalsVisibleTo("Cnblogs.DashScope.AI.UnitTests")]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"header": {
3+
"action": "continue-task",
4+
"task_id": "439e0616-2f5b-44e0-8872-0002a066a49c"
5+
},
6+
"payload": {
7+
"input": {
8+
"text": "代码改变世界"
9+
}
10+
}
11+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"header": {
3+
"action": "finish-task",
4+
"task_id": "439e0616-2f5b-44e0-8872-0002a066a49c"
5+
},
6+
"payload": {
7+
"input": {}
8+
}
9+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"header": {
3+
"task_id": "439e0616-2f5b-44e0-8872-0002a066a49c",
4+
"event": "result-generated",
5+
"attributes": {
6+
"request_uuid": "c88301b4-3caa-4f15-94e2-246e84d2e648",
7+
"x-ds-batch-queue-length": "0"
8+
}
9+
},
10+
"payload": {
11+
"output": {
12+
"sentence": {
13+
"words": []
14+
}
15+
}
16+
}
17+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"header": {
3+
"action": "run-task",
4+
"task_id": "439e0616-2f5b-44e0-8872-0002a066a49c",
5+
"streaming": "duplex"
6+
},
7+
"payload": {
8+
"model": "cosyvoice-v1",
9+
"task_group": "audio",
10+
"task": "tts",
11+
"function": "SpeechSynthesizer",
12+
"input": {},
13+
"parameters": {
14+
"voice": "longxiaochun",
15+
"volume": 50,
16+
"text_type": "PlainText",
17+
"sample_rate": 0,
18+
"rate": 1.1,
19+
"format": "mp3",
20+
"pitch": 1.2,
21+
"enable_ssml": true
22+
}
23+
}
24+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"header": {
3+
"task_id": "439e0616-2f5b-44e0-8872-0002a066a49c",
4+
"event": "task-finished",
5+
"attributes": {
6+
"request_uuid": "c88301b4-3caa-4f15-94e2-246e84d2e648",
7+
"x-ds-batch-queue-length": "0"
8+
}
9+
},
10+
"payload": {
11+
"output": {
12+
"sentence": {
13+
"words": []
14+
}
15+
},
16+
"usage": {
17+
"characters": 12
18+
}
19+
}
20+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"header": {
3+
"task_id": "439e0616-2f5b-44e0-8872-0002a066a49c",
4+
"event": "task-started",
5+
"attributes": {}
6+
},
7+
"payload": {}
8+
}

test/Cnblogs.DashScope.Tests.Shared/Utils/Checkers.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ namespace Cnblogs.DashScope.Tests.Shared.Utils;
44

55
public static class Checkers
66
{
7+
public static bool IsJsonEquivalent(ArraySegment<byte> socketBuffer, string requestSnapshot)
8+
{
9+
var actual = JsonNode.Parse(socketBuffer);
10+
var expected = JsonNode.Parse(requestSnapshot);
11+
return JsonNode.DeepEquals(actual, expected);
12+
}
13+
714
public static bool IsJsonEquivalent(HttpContent content, string requestSnapshot)
815
{
916
#pragma warning disable VSTHRD002
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System.Net.WebSockets;
2+
using System.Threading.Channels;
3+
using Cnblogs.DashScope.Core.Internals;
4+
5+
namespace Cnblogs.DashScope.Tests.Shared.Utils;
6+
7+
public sealed class FakeClientWebSocket : IClientWebSocket
8+
{
9+
public List<ArraySegment<byte>> ReceivedMessages { get; } = new();
10+
11+
public Channel<WebSocketReceiveResult> Server { get; } =
12+
Channel.CreateUnbounded<WebSocketReceiveResult>();
13+
14+
public async Task WriteServerCloseAsync()
15+
{
16+
var close = new WebSocketReceiveResult(1, WebSocketMessageType.Close, true);
17+
await Server.Writer.WriteAsync(close);
18+
Server.Writer.Complete();
19+
}
20+
21+
private void Dispose(bool disposing)
22+
{
23+
// nothing to release.
24+
if (disposing)
25+
{
26+
Server.Writer.Complete();
27+
}
28+
}
29+
30+
/// <inheritdoc />
31+
public void Dispose()
32+
{
33+
Dispose(true);
34+
}
35+
36+
/// <inheritdoc />
37+
public ClientWebSocketOptions Options { get; set; } = null!;
38+
39+
/// <inheritdoc />
40+
public WebSocketCloseStatus? CloseStatus { get; set; }
41+
42+
/// <inheritdoc />
43+
public Task ConnectAsync(Uri uri, CancellationToken cancellation)
44+
{
45+
// do nothing.
46+
return Task.CompletedTask;
47+
}
48+
49+
/// <inheritdoc />
50+
public Task SendAsync(
51+
ArraySegment<byte> buffer,
52+
WebSocketMessageType messageType,
53+
bool endOfMessage,
54+
CancellationToken cancellationToken)
55+
{
56+
ReceivedMessages.Add(buffer);
57+
return Task.CompletedTask;
58+
}
59+
60+
/// <inheritdoc />
61+
public async Task<WebSocketReceiveResult> ReceiveAsync(
62+
ArraySegment<byte> buffer,
63+
CancellationToken cancellationToken)
64+
{
65+
await Server.Reader.WaitToReadAsync(cancellationToken);
66+
return await Server.Reader.ReadAsync(cancellationToken);
67+
}
68+
69+
/// <inheritdoc />
70+
public Task CloseAsync(
71+
WebSocketCloseStatus closeStatus,
72+
string? statusDescription,
73+
CancellationToken cancellationToken)
74+
{
75+
CloseStatus = WebSocketCloseStatus.NormalClosure;
76+
return Task.CompletedTask;
77+
}
78+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Cnblogs.DashScope.Core;
2+
3+
namespace Cnblogs.DashScope.Tests.Shared.Utils;
4+
5+
public partial class Snapshots
6+
{
7+
public static class SpeechSynthesizer
8+
{
9+
private const string GroupName = "speech-synthesizer";
10+
11+
public static readonly
12+
SocketMessageSnapshot<DashScopeWebSocketRequest<SpeechSynthesizerInput, SpeechSynthesizerParameters>>
13+
RunTask = new(GroupName, "run-task", new DashScopeWebSocketRequest<SpeechSynthesizerInput, SpeechSynthesizerParameters>()
14+
{
15+
Header = new DashScopeWebSocketRequestHeader()
16+
{
17+
Action = "run-task",
18+
Streaming = "duplex",
19+
TaskId = "439e0616-2f5b-44e0-8872-0002a066a49c"
20+
},
21+
Payload = new DashScopeWebSocketRequestPayload<SpeechSynthesizerInput, SpeechSynthesizerParameters>()
22+
{
23+
Task = "tts",
24+
TaskGroup = "audio",
25+
Function = "SpeechSynthesizer",
26+
Model = "cosyvoice-v1",
27+
Parameters = new SpeechSynthesizerParameters()
28+
{
29+
EnableSsml = true,
30+
Format = "mp3",
31+
Pitch = 1.2f,
32+
Voice = "longxiaochun",
33+
Volume = 50,
34+
SampleRate = 0,
35+
Rate = 1.1f,
36+
}
37+
}
38+
});
39+
}
40+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Cnblogs.DashScope.Tests.Shared.Utils;
2+
3+
public record SocketMessageSnapshot(string GroupName, string MessageName)
4+
{
5+
public string GetRequestJson()
6+
{
7+
return File.ReadAllText(Path.Combine("RawHttpData", $"socket-{GroupName}.{MessageName}.json"));
8+
}
9+
}
10+
11+
public record SocketMessageSnapshot<TMessage>(string GroupName, string MessageName, TMessage Message)
12+
: SocketMessageSnapshot(GroupName, MessageName);

0 commit comments

Comments
 (0)
0