using System.Net; using System.Net.Http.Json; using System.Text; using ChatAgent.Client.Services; using ChatAgent.Shared.Models; namespace ChatAgent.Client.Tests; public class ChatApiClientTests { [Fact] public async Task SendChatStreamingAsync_ParsesTextDeltas_InOrder() { // Arrange: build a canned SSE response with two text deltas var sse = BuildSSE( ("text", "Hello"), ("text", " world") ); var handler = new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(sse, Encoding.UTF8, "text/event-stream") }); var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") }; var client = new ChatApiClient(httpClient); var request = new ChatRequest { Messages = new List { new() { Role = "user", Content = "Hi" } } }; // Act var deltas = new List(); await foreach (var delta in client.SendChatStreamingAsync(request)) { deltas.Add(delta); } // Assert Assert.Equal(2, deltas.Count); Assert.Equal("Hello", deltas[0]); Assert.Equal(" world", deltas[1]); } [Fact] public async Task SendChatStreamingAsync_ThrowsOnErrorEvent() { // Arrange var sse = "data: {\"error\":\"Something went wrong\"}\n\n" + "data: [DONE]\n\n"; var handler = new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(sse, Encoding.UTF8, "text/event-stream") }); var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") }; var client = new ChatApiClient(httpClient); var request = new ChatRequest { Messages = new List { new() { Role = "user", Content = "Hi" } } }; // Act & Assert var ex = await Assert.ThrowsAsync(async () => { await foreach (var _ in client.SendChatStreamingAsync(request)) { } }); Assert.Contains("Something went wrong", ex.Message); } [Fact] public async Task GetHealthAsync_ReturnsHealthResponse() { // Arrange var healthJson = "{\"status\":\"healthy\",\"timestamp\":\"2026-04-04T12:00:00Z\"}"; var handler = new FakeHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(healthJson, Encoding.UTF8, "application/json") }); var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") }; var client = new ChatApiClient(httpClient); // Act var result = await client.GetHealthAsync(); // Assert Assert.NotNull(result); Assert.Equal("healthy", result.Status); } /// /// Builds simplified SSE content from (type, value) pairs. /// Each pair becomes: data: {"text":"value"}\n\n /// Ends with data: [DONE]\n\n /// private static string BuildSSE(params (string key, string value)[] events) { var sb = new StringBuilder(); foreach (var (key, value) in events) { sb.AppendLine($"data: {{\"{key}\":\"{value}\"}}"); sb.AppendLine(); } sb.AppendLine("data: [DONE]"); sb.AppendLine(); return sb.ToString(); } } /// /// Simple fake HttpMessageHandler for unit testing HttpClient-based services. /// Returns a canned response for any request. /// public class FakeHttpMessageHandler : HttpMessageHandler { private readonly HttpResponseMessage _response; public FakeHttpMessageHandler(HttpResponseMessage response) { _response = response; } protected override Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { return Task.FromResult(_response); } }