using System.Net; using System.Text; using Decky.Core.Api; using Decky.Core.Configuration; namespace Decky.Core.Tests; public sealed class DeckApiClientTests { [Fact] public void Connection_options_reject_insecure_remote_servers() { var exception = Assert.Throws(() => DeckConnectionOptions.Create("http://cloud.example.test", "elijah", "app-password")); Assert.Contains("HTTPS", exception.Message, StringComparison.Ordinal); } [Fact] public async Task GetBoardsAsync_sends_required_headers_and_deserializes_response() { HttpRequestMessage? capturedRequest = null; var handler = new StubHttpMessageHandler(request => { capturedRequest = CloneRequest(request); return JsonResponse("[{\"id\":10,\"title\":\"Homework\",\"color\":\"ff0000\"}]"); }); using var httpClient = new HttpClient(handler); var options = DeckConnectionOptions.Create("https://cloud.example.test", "elijah", "app-password"); var client = new DeckApiClient( httpClient, options, new Uri("https://cloud.example.test/index.php/apps/deck/api/v1.0/")); var boards = await client.GetBoardsAsync(); Assert.Single(boards); Assert.Equal("Homework", boards[0].Title); Assert.NotNull(capturedRequest); Assert.Equal( "https://cloud.example.test/index.php/apps/deck/api/v1.0/boards", capturedRequest.RequestUri!.ToString()); Assert.Equal("true", capturedRequest.Headers.GetValues("OCS-APIRequest").Single()); Assert.Equal("Basic", capturedRequest.Headers.Authorization!.Scheme); } [Fact] public async Task ConnectAsync_falls_back_to_rewritten_path_after_not_found() { var requestedPaths = new List(); var handler = new StubHttpMessageHandler(request => { requestedPaths.Add(request.RequestUri!.AbsolutePath); return request.RequestUri.AbsolutePath.Contains("/index.php/", StringComparison.Ordinal) ? new HttpResponseMessage(HttpStatusCode.NotFound) : JsonResponse("[]"); }); using var httpClient = new HttpClient(handler); var options = DeckConnectionOptions.Create("https://cloud.example.test", "elijah", "app-password"); var client = await DeckApiClient.ConnectAsync(httpClient, options); Assert.Equal("https://cloud.example.test/apps/deck/api/v1.0/", client.ApiBaseUri.ToString()); Assert.Equal(2, requestedPaths.Count); } private static HttpResponseMessage JsonResponse(string json) => new(HttpStatusCode.OK) { Content = new StringContent(json, Encoding.UTF8, "application/json"), }; private static HttpRequestMessage CloneRequest(HttpRequestMessage request) { var clone = new HttpRequestMessage(request.Method, request.RequestUri); foreach (var header in request.Headers) { clone.Headers.TryAddWithoutValidation(header.Key, header.Value); } return clone; } private sealed class StubHttpMessageHandler( Func responseFactory) : HttpMessageHandler { protected override Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) => Task.FromResult(responseFactory(request)); } }