using System.Net; using System.Text.Json; using Microsoft.Extensions.Configuration; using ReC.Application.Common.Dto; using ReC.Application.Common.Procedures.UpdateProcedure.Dto; using ReC.Application.RecActions.Commands; using ReC.Client; using ReC.Client.Api; using ClientInvokeReferences = ReC.Client.Api.InvokeReferences; namespace ReC.Tests.Client; [TestFixture] public class RecActionApiTests : RecClientTestBase { [Test] public void ReCClient_is_resolvable_through_dependency_injection() { var (client, scope) = CreateScopedClient(); using var _ = scope; Assert.That(client, Is.Not.Null); Assert.That(client.RecActions, Is.Not.Null); } [Test] public async Task GetAsync_without_filters_returns_deserialized_result_or_throws_not_found() { var (client, scope) = CreateScopedClient(); using var _ = scope; try { var actions = await client.RecActions.GetAsync(); Assert.That(actions, Is.Not.Null); } catch (ReCApiException ex) { Assert.That(ex.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); Assert.That(ex.Method, Is.EqualTo("GET")); Assert.That(ex.RequestUri!.AbsolutePath, Does.EndWith("api/RecAction")); } } [Test] public async Task GetDynamicAsync_without_filters_returns_dynamic_payload_or_throws_not_found() { var (client, scope) = CreateScopedClient(); using var _ = scope; try { dynamic? actions = await client.RecActions.GetDynamicAsync(); Assert.That(actions, Is.Not.Null); Assert.That(actions, Is.TypeOf()); Assert.That(((JsonElement)actions).ValueKind, Is.EqualTo(JsonValueKind.Array)); } catch (ReCApiException ex) { Assert.That(ex.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); Assert.That(ex.Method, Is.EqualTo("GET")); Assert.That(ex.RequestUri!.AbsolutePath, Does.EndWith("api/RecAction")); } } [Test] public async Task GetAsync_with_profile_filter_returns_only_matching_actions() { var profileId = Configuration.GetValue("FakeProfileId"); if (profileId is null or <= 0) Assert.Ignore("FakeProfileId must be configured in appsettings.json for this test."); var (client, scope) = CreateScopedClient(); using var _ = scope; RecActionViewDto[]? actions; try { actions = await client.RecActions.GetAsync(profileId: profileId); } catch (ReCApiException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { Assert.Pass("NotFound is acceptable when test data is unavailable."); return; } Assert.That(actions, Is.Not.Null.And.Not.Empty); Assert.That(actions, Has.All.Matches(a => a.ProfileId == profileId)); } [Test] public void GetAsync_with_unknown_profile_throws_not_found() { var (client, scope) = CreateScopedClient(); using var _ = scope; var ex = Assert.ThrowsAsync(async () => await client.RecActions.GetAsync(profileId: long.MaxValue)); Assert.That(ex, Is.Not.Null); Assert.That(ex!.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); Assert.That(ex.Method, Is.EqualTo("GET")); Assert.That(ex.RequestUri!.Query, Does.Contain("ProfileId=")); } [Test] public void GetDynamicAsync_with_unknown_profile_throws_not_found() { var (client, scope) = CreateScopedClient(); using var _ = scope; var ex = Assert.ThrowsAsync(async () => await client.RecActions.GetDynamicAsync(profileId: long.MaxValue)); Assert.That(ex, Is.Not.Null); Assert.That(ex!.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); Assert.That(ex.Method, Is.EqualTo("GET")); Assert.That(ex.RequestUri!.Query, Does.Contain("ProfileId=")); } [Test] public void CreateAsync_with_invalid_foreign_key_throws_ReCApiException() { var (client, scope) = CreateScopedClient(); using var _ = scope; var payload = new InsertActionCommand { ProfileId = long.MaxValue, EndpointId = long.MaxValue, Active = true, Sequence = 1 }; var ex = Assert.ThrowsAsync(async () => await client.RecActions.CreateAsync(payload)); Assert.That(ex, Is.Not.Null); Assert.That((int)ex!.StatusCode, Is.GreaterThanOrEqualTo(400)); Assert.That(ex.Method, Is.EqualTo("POST")); } [Test] public void UpdateAsync_with_unknown_id_throws_ReCApiException_with_method_PUT() { var (client, scope) = CreateScopedClient(); using var _ = scope; var unknownId = long.MaxValue; var payload = new UpdateActionDto { ProfileId = 1, Active = false, Sequence = 1 }; var ex = Assert.ThrowsAsync(async () => await client.RecActions.UpdateAsync(unknownId, payload)); Assert.That(ex, Is.Not.Null); Assert.That(ex!.Method, Is.EqualTo("PUT")); } [Test] public void DeleteAsync_sends_payload_as_query_string_not_body() { var (client, scope) = CreateScopedClient(); using var _ = scope; var payload = new DeleteActionCommand { Start = long.MaxValue - 1, End = long.MaxValue, Force = false }; try { client.RecActions.DeleteAsync(payload).GetAwaiter().GetResult(); } catch (ReCApiException ex) { Assert.That(ex.Method, Is.EqualTo("DELETE")); Assert.That(ex.RequestUri!.Query, Does.Contain("Start=").And.Contains("End=").And.Contains("Force=")); } } [Test] public void InvokeAsync_with_unknown_profile_throws_ReCApiException() { var (client, scope) = CreateScopedClient(); using var _ = scope; var ex = Assert.ThrowsAsync(async () => await client.RecActions.InvokeAsync(long.MaxValue, new ClientInvokeReferences { BatchId = "test-batch" })); Assert.That(ex, Is.Not.Null); Assert.That(ex!.Method, Is.EqualTo("POST")); } }