Skip to content

Commit 2ddec99

Browse files
committed
feat: add client web socket unit tests
1 parent dbc63f8 commit 2ddec99

File tree

8 files changed

+140
-15
lines changed

8 files changed

+140
-15
lines changed

src/Cnblogs.DashScope.Core/DashScopeClientWebSocket.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public Task SendMessageAsync<TInput, TParameter>(
171171
/// <param name="cancellationToken">A cancellation token used to propagate notification that this operation should be canceled.</param>
172172
/// <typeparam name="TOutput">Type of the response content.</typeparam>
173173
/// <exception cref="DashScopeException">The task was failed.</exception>
174-
public async Task ReceiveMessagesAsync<TOutput>(CancellationToken cancellationToken = default)
174+
private async Task ReceiveMessagesAsync<TOutput>(CancellationToken cancellationToken = default)
175175
where TOutput : class
176176
{
177177
while (State != DashScopeWebSocketState.Closed && _socket.CloseStatus == null)
@@ -195,6 +195,7 @@ public async Task ReceiveMessagesAsync<TOutput>(CancellationToken cancellationTo
195195
break;
196196
case "task-failed":
197197
await CloseAsync(cancellationToken);
198+
_binaryOutput?.Writer.Complete();
198199
throw new DashScopeException(
199200
null,
200201
400,
@@ -220,10 +221,6 @@ public async Task CloseAsync(CancellationToken cancellationToken = default)
220221
{
221222
await _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", cancellationToken);
222223
State = DashScopeWebSocketState.Closed;
223-
if (_receiveTask != null)
224-
{
225-
await _receiveTask;
226-
}
227224
}
228225

229226
private void Dispose(bool disposing)

src/Cnblogs.DashScope.Core/TextChatMessage.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ public TextChatMessage(
4444
string? reasoningContent = null,
4545
List<ToolCall>? toolCalls = null)
4646
{
47-
this.Role = role;
48-
this.Content = content;
49-
this.Name = name;
50-
this.Partial = partial;
51-
this.ReasoningContent = reasoningContent;
52-
this.ToolCalls = toolCalls;
47+
Role = role;
48+
Content = content;
49+
Name = name;
50+
Partial = partial;
51+
ReasoningContent = reasoningContent;
52+
ToolCalls = toolCalls;
5353
}
5454

5555
/// <summary>The role of this message.</summary>

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

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,80 @@ public async Task ReceiveMessageAsync_TaskStarted_UpdateStateToRunningAsync()
171171
Assert.Equal(DashScopeWebSocketState.RunningTask, clientWebSocket.State);
172172
}
173173

174+
[Fact]
175+
public async Task ReceiveMessageAsync_TaskFinished_UpdateStateToReadyAsync()
176+
{
177+
// Arrange
178+
var (_, clientWebSocket, server) = await Sut.GetSocketTestClientAsync<SpeechSynthesizerOutput>();
179+
await server.WriteServerMessageAsync(Snapshots.SpeechSynthesizer.TaskStarted.GetMessageJson());
180+
await clientWebSocket.TaskStarted;
181+
var snapshot = Snapshots.SpeechSynthesizer.TaskFinished;
182+
var output = clientWebSocket.BinaryOutput;
183+
184+
// Act
185+
await server.WriteServerMessageAsync(snapshot.GetMessageJson());
186+
187+
// Assert
188+
Assert.True(output.Completion.IsCompleted);
189+
Assert.Equal(DashScopeWebSocketState.Ready, clientWebSocket.State);
190+
}
191+
192+
[Fact]
193+
public async Task ReceiveMessageAsync_TaskFailed_CloseAndThrowAsync()
194+
{
195+
// Arrange
196+
var (_, clientWebSocket, server) = await Sut.GetSocketTestClientAsync<SpeechSynthesizerOutput>();
197+
await server.WriteServerMessageAsync(Snapshots.SpeechSynthesizer.TaskStarted.GetMessageJson());
198+
await clientWebSocket.TaskStarted;
199+
var snapshot = Snapshots.SpeechSynthesizer.TaskFailed;
200+
var output = clientWebSocket.BinaryOutput;
201+
202+
// Act
203+
await server.WriteServerMessageAsync(snapshot.GetMessageJson());
204+
await server.WriteServerCloseAsync();
205+
206+
// Assert
207+
Assert.True(output.Completion.IsCompleted);
208+
Assert.Equal(DashScopeWebSocketState.Closed, clientWebSocket.State);
209+
}
210+
211+
[Fact]
212+
public async Task ReceiveMessageAsync_ReceiveBinary_WriteToBinaryOutputAsync()
213+
{
214+
// Arrange
215+
var (_, clientWebSocket, server) = await Sut.GetSocketTestClientAsync<SpeechSynthesizerOutput>();
216+
await server.WriteServerMessageAsync(Snapshots.SpeechSynthesizer.TaskStarted.GetMessageJson());
217+
await clientWebSocket.TaskStarted;
218+
var expectedAudio = Snapshots.SpeechSynthesizer.AudioTts;
219+
var output = clientWebSocket.BinaryOutput;
220+
var audioTask = output.ReadAllAsync().ToArrayAsync();
221+
222+
// Act
223+
await server.WriteServerMessageAsync(expectedAudio);
224+
await server.WriteServerMessageAsync(Snapshots.SpeechSynthesizer.TaskFinished.GetMessageJson());
225+
var audio = await audioTask;
226+
227+
// Assert
228+
Assert.True(output.Completion.IsCompleted);
229+
Assert.Equal(expectedAudio, audio);
230+
Assert.Equal(DashScopeWebSocketState.Ready, clientWebSocket.State);
231+
}
232+
233+
[Fact]
234+
public async Task Dispose_ManuallyCalled_DisposeSocketAndOutputTogetherAsync()
235+
{
236+
// Arrange
237+
var (_, clientWebSocket, server) = await Sut.GetSocketTestClientAsync<SpeechSynthesizerOutput>();
238+
var output = clientWebSocket.BinaryOutput;
239+
240+
// Act
241+
clientWebSocket.Dispose();
242+
243+
// Assert
244+
Assert.True(output.Completion.IsCompleted);
245+
Assert.True(server.DisposeCalled);
246+
}
247+
174248
private static WebHeaderCollection ExtractHeaders(DashScopeClientWebSocket socket)
175249
{
176250
var obj = InnerSocketInfo.GetValue(socket);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"header": {
3+
"task_id": "439e0616-2f5b-44e0-8872-0002a066a49c",
4+
"event": "task-failed",
5+
"error_code": "InvalidParameter",
6+
"error_message": "[tts:]Engine return error code: 418",
7+
"attributes": {}
8+
},
9+
"payload": {}
10+
}
Binary file not shown.

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ public sealed class FakeClientWebSocket : IClientWebSocket
1414

1515
public Channel<byte[]> ServerBuffer { get; } = Channel.CreateUnbounded<byte[]>();
1616

17+
public bool DisposeCalled { get; private set; }
18+
1719
public async Task WriteServerCloseAsync()
1820
{
1921
var close = new WebSocketReceiveResult(1, WebSocketMessageType.Close, true);
2022
await Server.Writer.WriteAsync(close);
21-
Server.Writer.Complete();
22-
ServerBuffer.Writer.Complete();
23+
await ServerBuffer.Writer.WriteAsync(new byte[] { 1 });
24+
await Server.Reader.WaitToReadAsync();
25+
await ServerBuffer.Reader.WaitToReadAsync();
26+
await Task.Delay(50);
2327
}
2428

2529
public async Task WriteServerMessageAsync(string json)
@@ -30,13 +34,25 @@ public async Task WriteServerMessageAsync(string json)
3034
await ServerBuffer.Writer.WriteAsync(binary);
3135
await Server.Reader.WaitToReadAsync();
3236
await ServerBuffer.Reader.WaitToReadAsync();
37+
await Task.Delay(50);
38+
}
39+
40+
public async Task WriteServerMessageAsync(byte[] binary)
41+
{
42+
await Server.Writer.WriteAsync(new WebSocketReceiveResult(binary.Length, WebSocketMessageType.Binary, true));
43+
44+
await ServerBuffer.Writer.WriteAsync(binary);
45+
await Server.Reader.WaitToReadAsync();
46+
await ServerBuffer.Reader.WaitToReadAsync();
47+
await Task.Delay(50);
3348
}
3449

3550
private void Dispose(bool disposing)
3651
{
3752
// nothing to release.
3853
if (disposing)
3954
{
55+
DisposeCalled = true;
4056
Server.Writer.Complete();
4157
}
4258
}

test/Cnblogs.DashScope.Tests.Shared/Utils/Snapshots.SocketRequests.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,34 @@ public static readonly
106106
new DashScopeWebSocketResponseHeaderAttributes(null)),
107107
new DashScopeWebSocketResponsePayload<SpeechSynthesizerOutput>(null, null)));
108108

109-
public static readonly SocketMessageSnapshot TaskFailed = new(GroupName, "task-failed");
110-
public static readonly SocketMessageSnapshot ResultGenerated = new(GroupName, "result-generated");
109+
public static readonly SocketMessageSnapshot<DashScopeWebSocketResponse<SpeechSynthesizerOutput>> TaskFailed =
110+
new(
111+
GroupName,
112+
"task-failed",
113+
new DashScopeWebSocketResponse<SpeechSynthesizerOutput>(
114+
new DashScopeWebSocketResponseHeader(
115+
"439e0616-2f5b-44e0-8872-0002a066a49c",
116+
"task-failed",
117+
"InvalidParameter",
118+
"[tts:]Engine return error code: 418",
119+
new DashScopeWebSocketResponseHeaderAttributes(null)),
120+
new DashScopeWebSocketResponsePayload<SpeechSynthesizerOutput>(null, null)));
121+
122+
public static readonly SocketMessageSnapshot<DashScopeWebSocketResponse<SpeechSynthesizerOutput>>
123+
ResultGenerated = new(
124+
GroupName,
125+
"result-generated",
126+
new DashScopeWebSocketResponse<SpeechSynthesizerOutput>(
127+
new DashScopeWebSocketResponseHeader(
128+
"439e0616-2f5b-44e0-8872-0002a066a49c",
129+
"result-generated",
130+
null,
131+
null,
132+
new DashScopeWebSocketResponseHeaderAttributes("c88301b4-3caa-4f15-94e2-246e84d2e648")),
133+
new DashScopeWebSocketResponsePayload<SpeechSynthesizerOutput>(
134+
new SpeechSynthesizerOutput(new SpeechSynthesizerOutputSentences(Array.Empty<string>())),
135+
null)));
136+
137+
public static readonly byte[] AudioTts = System.IO.File.ReadAllBytes(Path.Combine("RawHttpData", "tts.mp3"))[..1000];
111138
}
112139
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ internal static async
3838
await dsWebSocket.ConnectAsync<TOutput>(
3939
new Uri(DashScopeDefaults.WebsocketApiBaseAddress),
4040
CancellationToken.None);
41+
dsWebSocket.ResetOutput();
4142
var pool = new DashScopeClientWebSocketPool(new List<DashScopeClientWebSocket> { dsWebSocket });
4243
var client = new DashScopeClientCore(new HttpClient(), pool);
4344
return (client, dsWebSocket, socket);

0 commit comments

Comments
 (0)