172 lines
6.2 KiB
C#
172 lines
6.2 KiB
C#
using System.Net;
|
|
using System.Text;
|
|
using Decky.Core.Api;
|
|
using Decky.Core.Api.Requests;
|
|
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<ArgumentException>(() =>
|
|
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<string>();
|
|
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);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReorderCardAsync_fetches_destination_card_after_boolean_acknowledgement()
|
|
{
|
|
var handler = new StubHttpMessageHandler(request =>
|
|
{
|
|
if (request.Method == HttpMethod.Put)
|
|
{
|
|
return JsonResponse("true");
|
|
}
|
|
|
|
Assert.Equal(
|
|
"/index.php/apps/deck/api/v1.0/boards/2/stacks/8/cards/11",
|
|
request.RequestUri!.AbsolutePath);
|
|
return JsonResponse("{\"id\":11,\"stackId\":8,\"title\":\"Moved\",\"owner\":\"admin\"}");
|
|
});
|
|
using var httpClient = new HttpClient(handler);
|
|
var client = CreateClient(httpClient);
|
|
|
|
var card = await client.ReorderCardAsync(
|
|
2,
|
|
7,
|
|
11,
|
|
new ReorderCardRequest { Order = 0, StackId = 8 });
|
|
|
|
Assert.Equal(8, card.StackId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReorderCardAsync_retries_with_destination_route_when_body_stack_was_ignored()
|
|
{
|
|
var putCount = 0;
|
|
var handler = new StubHttpMessageHandler(request =>
|
|
{
|
|
if (request.Method == HttpMethod.Put)
|
|
{
|
|
putCount++;
|
|
if (putCount == 2)
|
|
{
|
|
Assert.Contains("/stacks/8/", request.RequestUri!.AbsolutePath, StringComparison.Ordinal);
|
|
}
|
|
|
|
return JsonResponse("true");
|
|
}
|
|
|
|
if (request.RequestUri!.AbsolutePath.Contains("/stacks/8/", StringComparison.Ordinal))
|
|
{
|
|
if (putCount == 2)
|
|
{
|
|
return JsonResponse("{\"id\":11,\"stackId\":8,\"title\":\"Moved\",\"owner\":\"admin\"}");
|
|
}
|
|
|
|
return new HttpResponseMessage(HttpStatusCode.NotFound)
|
|
{
|
|
Content = new StringContent("{\"status\":404}", Encoding.UTF8, "application/json"),
|
|
};
|
|
}
|
|
|
|
return JsonResponse("{\"id\":11,\"stackId\":7,\"title\":\"Not moved\",\"owner\":\"admin\"}");
|
|
});
|
|
using var httpClient = new HttpClient(handler);
|
|
var client = CreateClient(httpClient);
|
|
|
|
var card = await client.ReorderCardAsync(
|
|
2,
|
|
7,
|
|
11,
|
|
new ReorderCardRequest { Order = 0, StackId = 8 });
|
|
|
|
Assert.Equal(8, card.StackId);
|
|
Assert.Equal(2, putCount);
|
|
}
|
|
|
|
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 static DeckApiClient CreateClient(HttpClient httpClient)
|
|
{
|
|
var options = DeckConnectionOptions.Create("https://cloud.example.test", "elijah", "app-password");
|
|
return new DeckApiClient(
|
|
httpClient,
|
|
options,
|
|
new Uri("https://cloud.example.test/index.php/apps/deck/api/v1.0/"));
|
|
}
|
|
|
|
private sealed class StubHttpMessageHandler(
|
|
Func<HttpRequestMessage, HttpResponseMessage> responseFactory) : HttpMessageHandler
|
|
{
|
|
protected override Task<HttpResponseMessage> SendAsync(
|
|
HttpRequestMessage request,
|
|
CancellationToken cancellationToken) => Task.FromResult(responseFactory(request));
|
|
}
|
|
}
|