Skip to content

Commit 7fcaebb

Browse files
JonathanCrdCopilot
andcommitted
refactor: use IAsyncEnumerable<T> pattern for JSONL streaming receive test
Update BasicReceive test to use the protocol method and enumerate the response stream line-by-line via IAsyncEnumerable<Info>, demonstrating the streaming-friendly pattern that avoids buffering the entire JSONL payload into memory. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 952f63e commit 7fcaebb

1 file changed

Lines changed: 71 additions & 6 deletions

File tree

  • packages/http-client-csharp/generator/TestProjects/Spector.Tests/Http/Streaming/Jsonl

packages/http-client-csharp/generator/TestProjects/Spector.Tests/Http/Streaming/Jsonl/JsonlTests.cs

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
// Licensed under the MIT License.
33

44
using System;
5+
using System.ClientModel;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Runtime.CompilerServices;
9+
using System.Text;
10+
using System.Text.Json;
11+
using System.Threading;
512
using System.Threading.Tasks;
613
using NUnit.Framework;
714
using Streaming.Jsonl;
@@ -10,13 +17,17 @@ namespace TestProjects.Spector.Tests.Http.Streaming.Jsonl
1017
{
1118
public class JsonlTests : SpectorTestBase
1219
{
13-
private const string ExpectedJsonl = "{\"desc\": \"one\"}\n{\"desc\": \"two\"}\n{\"desc\": \"three\"}";
14-
1520
[SpectorTest]
1621
public Task BasicSend() => Test(async (host) =>
1722
{
1823
var client = new JsonlClient(host, null).GetBasicClient();
19-
var stream = StreamingJsonlModelFactory.JsonlStreamInfo(BinaryData.FromString(ExpectedJsonl));
24+
25+
// Build the JSONL payload line-by-line from Info objects, demonstrating
26+
// the streaming-friendly pattern: each line is independently serialized.
27+
var items = new[] { new Info("one"), new Info("two"), new Info("three") };
28+
var jsonlPayload = string.Join("\n", Array.ConvertAll(items, i => JsonSerializer.Serialize(i)));
29+
30+
var stream = StreamingJsonlModelFactory.JsonlStreamInfo(BinaryData.FromString(jsonlPayload));
2031
var response = await client.SendAsync(stream);
2132

2233
Assert.AreEqual(204, response.GetRawResponse().Status);
@@ -26,10 +37,64 @@ public Task BasicSend() => Test(async (host) =>
2637
public Task BasicReceive() => Test(async (host) =>
2738
{
2839
var client = new JsonlClient(host, null).GetBasicClient();
29-
var response = await client.ReceiveAsync();
3040

31-
Assert.AreEqual(200, response.GetRawResponse().Status);
32-
BinaryDataAssert.AreEqual(BinaryData.FromString(ExpectedJsonl), response.Value);
41+
// Use the protocol method to get the raw response stream, then
42+
// enumerate JSONL lines as IAsyncEnumerable<Info> without buffering
43+
// the entire payload into memory.
44+
ClientResult result = await client.ReceiveAsync(options: null);
45+
var rawResponse = result.GetRawResponse();
46+
Assert.AreEqual(200, rawResponse.Status);
47+
Assert.IsNotNull(rawResponse.ContentStream);
48+
49+
var received = new List<Info>();
50+
await foreach (var info in EnumerateJsonlAsync<Info>(rawResponse.ContentStream!))
51+
{
52+
received.Add(info);
53+
}
54+
55+
Assert.AreEqual(3, received.Count);
56+
Assert.AreEqual("one", received[0].Desc);
57+
Assert.AreEqual("two", received[1].Desc);
58+
Assert.AreEqual("three", received[2].Desc);
3359
});
60+
61+
/// <summary>
62+
/// Reads a stream of JSONL (JSON Lines) data and yields each line as a
63+
/// deserialized <typeparamref name="T"/> instance. This demonstrates the
64+
/// <see cref="IAsyncEnumerable{T}"/> pattern that the generator should
65+
/// eventually emit for streaming operations — reading line-by-line avoids
66+
/// buffering the entire payload into memory.
67+
/// </summary>
68+
private static async IAsyncEnumerable<T> EnumerateJsonlAsync<T>(
69+
Stream contentStream,
70+
[EnumeratorCancellation] CancellationToken cancellationToken = default)
71+
{
72+
using var reader = new StreamReader(contentStream, Encoding.UTF8);
73+
string? line;
74+
while ((line = await reader.ReadLineAsync(cancellationToken)) is not null)
75+
{
76+
if (string.IsNullOrWhiteSpace(line))
77+
continue;
78+
yield return JsonSerializer.Deserialize<T>(line)!;
79+
}
80+
}
81+
82+
/// <summary>
83+
/// Local representation of the Streaming.Jsonl.Basic.Info model for test
84+
/// deserialization. The generator does not yet emit this type because the
85+
/// JSONL body is treated as raw bytes in the current code model.
86+
/// </summary>
87+
private sealed class Info
88+
{
89+
public Info() { }
90+
91+
public Info(string desc)
92+
{
93+
Desc = desc;
94+
}
95+
96+
[System.Text.Json.Serialization.JsonPropertyName("desc")]
97+
public string? Desc { get; set; }
98+
}
3499
}
35100
}

0 commit comments

Comments
 (0)