feat(api,web): retrieve and display transactions

This commit is contained in:
Stevan Freeborn
2026-03-10 07:42:51 -05:00
parent e2d2058960
commit 3408f6ea4f
25 changed files with 595 additions and 100 deletions
@@ -22,6 +22,7 @@
<ProjectReference Include="..\..\src\FiscalOS.API\FiscalOS.API.csproj" />
<ProjectReference Include="..\..\src\FiscalOS.Core\FiscalOS.Core.csproj" />
<ProjectReference Include="..\..\src\FiscalOS.Infra\FiscalOS.Infra.csproj" />
<ProjectReference Include="..\FiscalOS.Tests.Common\FiscalOS.Tests.Common.csproj" />
</ItemGroup>
</Project>
@@ -1,3 +1,5 @@
using Microsoft.AspNetCore.WebUtilities;
namespace FiscalOS.API.Tests.Infra;
internal sealed class HttpRequestBuilder
@@ -8,6 +10,7 @@ internal sealed class HttpRequestBuilder
private string? _bearerToken;
private readonly Dictionary<string, string> _cookies = [];
private readonly Dictionary<string, string> _headers = [];
private readonly Dictionary<string, string?> _queryParameters = [];
private HttpRequestBuilder()
{
@@ -68,6 +71,12 @@ internal sealed class HttpRequestBuilder
return this;
}
public HttpRequestBuilder WithQueryParameter(string name, string value)
{
_queryParameters[name] = value;
return this;
}
public HttpRequestBuilder Post(Uri uri)
{
_method = HttpMethod.Post;
@@ -103,7 +112,11 @@ internal sealed class HttpRequestBuilder
throw new InvalidOperationException("URI must be set before building the request.");
}
var request = new HttpRequestMessage(_method, _uri);
var uri = _queryParameters.Count > 0
? QueryHelpers.AddQueryString(_uri.ToString(), _queryParameters)
: _uri.ToString();
var request = new HttpRequestMessage(_method, uri);
if (_body is not null)
{
@@ -0,0 +1,101 @@
using FiscalOS.API.Transactions;
namespace FiscalOS.API.Tests.Integration.Transactions;
public class GetTests(TestApi testApi) : IntegrationTest(testApi)
{
private static readonly Uri GetUri = new("/transactions", UriKind.Relative);
[Fact]
public async Task Get_WhenNotLoggedIn_ItShouldReturn401WithProblemDetails()
{
using var request = HttpRequestBuilder.New()
.Get(GetUri)
.Build();
var response = await Client.SendAsync(request, TestContext.Current.CancellationToken);
await response.Should().BeProblemDetails(HttpStatusCode.Unauthorized);
}
[Fact]
public async Task Get_WhenCalledWithInvalidPageNumber_ItShouldReturn400WithProblemDetails()
{
using var request = HttpRequestBuilder.New()
.Get(GetUri)
.WithQueryParameter("pageNumber", "-1")
.WithUserId(Guid.NewGuid())
.Build();
var response = await Client.SendAsync(request, TestContext.Current.CancellationToken);
await response.Should().BeValidationProblemDetails(new Dictionary<string, string[]>()
{
["PageNumber"] = ["PageNumber must be greater than 0."],
});
}
[Theory]
[InlineData(0)]
[InlineData(1001)]
public async Task Get_WhenCalledWithInvalidPageSize_ItShouldReturn400WithProblemDetails(int pageSize)
{
using var request = HttpRequestBuilder.New()
.Get(GetUri)
.WithQueryParameter("pageSize", pageSize.ToString(CultureInfo.InvariantCulture))
.WithUserId(Guid.NewGuid())
.Build();
var response = await Client.SendAsync(request, TestContext.Current.CancellationToken);
await response.Should().BeValidationProblemDetails(new Dictionary<string, string[]>()
{
["PageSize"] = ["PageSize must be between 1 and 1000."],
});
}
[Fact]
public async Task Get_WhenCalled_ItShouldReturn200WithPagedResponseOfTransactions()
{
var user = await Api.ExecuteAsync(static async (context, ct, sp) =>
{
var passwordHasher = sp.GetRequiredService<IPasswordHasher>();
var encryptor = sp.GetRequiredService<IEncryptor>();
var user = UserBuilder.Create()
.WithInstitution(static ib =>
{
ib.WithMetadata();
ib.WithAccount(static ab =>
{
ab.WithMetadata();
ab.WithTransaction(static tb =>
{
tb.WithMetadata();
});
});
})
.Build();
await context.AddAsync(user, ct);
await context.SaveChangesAsync(ct);
return user;
}, TestContext.Current.CancellationToken);
using var request = HttpRequestBuilder.New()
.Get(GetUri)
.WithUserId(user.Id)
.Build();
var response = await Client.SendAsync(request, TestContext.Current.CancellationToken);
(await response.Should()
.BeJsonContentOfType<PagedResponse<TransactionDto>>(HttpStatusCode.OK))
.Which
.Items
.Should()
.BeEquivalentTo(
user.Transactions.Select(TransactionDto.From)
);
}
}
@@ -0,0 +1,21 @@
namespace FiscalOS.API.Tests.Unit;
public class PagedResponseTests
{
[Fact]
public void From_WhenCalled_ItShouldReturnPageWithCorrectValues()
{
var pageNumber = 1;
var pageSize = 10;
var totalItems = 25;
string[] items = ["test"];
var results = PagedResponse<string>.From(pageNumber, pageSize, totalItems, items);
results.PageNumber.Should().Be(pageNumber);
results.PageSize.Should().Be(pageSize);
results.TotalItems.Should().Be(totalItems);
results.TotalPages.Should().Be(3);
results.Items.Should().BeEquivalentTo(items);
}
}
+3
View File
@@ -1,3 +1,4 @@
global using System.Globalization;
global using System.IdentityModel.Tokens.Jwt;
global using System.Net;
global using System.Net.Http.Headers;
@@ -10,6 +11,7 @@ global using System.Text.Json;
global using AwesomeAssertions.Execution;
global using AwesomeAssertions.Primitives;
global using FiscalOS.API.Common;
global using FiscalOS.API.Tests.Assertions;
global using FiscalOS.API.Tests.Infra;
global using FiscalOS.Core.Authentication;
@@ -18,6 +20,7 @@ global using FiscalOS.Core.Security;
global using FiscalOS.Infra.Accounts.Plaid;
global using FiscalOS.Infra.Authentication;
global using FiscalOS.Infra.Data;
global using FiscalOS.Tests.Common.Data;
global using Going.Plaid;
global using Going.Plaid.Entity;