feat(api): add CSV upload import endpoint
Build MoneyMap image / build-and-push (push) Successful in 31s
Build MoneyMap image / build-and-push (push) Successful in 31s
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using MoneyMap.Controllers;
|
||||
using MoneyMap.Models;
|
||||
using MoneyMap.Services;
|
||||
using MoneyMap.Tests.TestHelpers;
|
||||
|
||||
namespace MoneyMap.Tests.Controllers;
|
||||
|
||||
public class ImportsControllerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ImportCsv_AutoMode_SkipsDuplicates_AssignsCardAndCategory()
|
||||
{
|
||||
using var context = DbContextHelper.CreateInMemoryContext();
|
||||
SeedAutoImportData(context);
|
||||
|
||||
var controller = CreateController(context);
|
||||
var csv = CreateCsvFormFile(
|
||||
"transactions.csv",
|
||||
"Date,Transaction,Name,Memo,Amount\n" +
|
||||
"2025-01-15,DEBIT,AMAZON MKTPL,existing usbank.com.1234,-50.00\n" +
|
||||
"2025-01-16,DEBIT,AMAZON MKTPL,new purchase usbank.com.1234,-25.00\n");
|
||||
|
||||
var result = await controller.ImportCsv(new CsvImportRequest
|
||||
{
|
||||
Csv = csv,
|
||||
PaymentMode = MoneyMap.Models.Import.PaymentSelectMode.Auto
|
||||
});
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result);
|
||||
using var json = JsonDocument.Parse(JsonSerializer.Serialize(ok.Value));
|
||||
|
||||
Assert.Equal(2, json.RootElement.GetProperty("TotalRows").GetInt32());
|
||||
Assert.Equal(1, json.RootElement.GetProperty("Inserted").GetInt32());
|
||||
Assert.Equal(1, json.RootElement.GetProperty("SkippedDuplicates").GetInt32());
|
||||
Assert.Equal(2, json.RootElement.GetProperty("AutoCategorized").GetInt32());
|
||||
|
||||
Assert.Equal(2, context.Transactions.Count());
|
||||
|
||||
var imported = context.Transactions.Single(t => t.Memo == "new purchase usbank.com.1234");
|
||||
Assert.Equal(1, imported.AccountId);
|
||||
Assert.Equal(1, imported.CardId);
|
||||
Assert.Equal("Shopping", imported.Category);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ImportCsv_AccountMode_ImportsWhenAutoDetectionIsUnavailable()
|
||||
{
|
||||
using var context = DbContextHelper.CreateInMemoryContext();
|
||||
context.Accounts.Add(new Account
|
||||
{
|
||||
Id = 7,
|
||||
Institution = "Manual Bank",
|
||||
Last4 = "4321",
|
||||
Owner = "Test Owner",
|
||||
AccountType = AccountType.Checking
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var controller = CreateController(context);
|
||||
var csv = CreateCsvFormFile(
|
||||
"manual-import.csv",
|
||||
"Date,Transaction,Name,Memo,Amount\n" +
|
||||
"2025-02-01,DEBIT,LOCAL STORE,no card info here,-10.00\n");
|
||||
|
||||
var result = await controller.ImportCsv(new CsvImportRequest
|
||||
{
|
||||
Csv = csv,
|
||||
PaymentMode = MoneyMap.Models.Import.PaymentSelectMode.Account,
|
||||
SelectedAccountId = 7
|
||||
});
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result);
|
||||
using var json = JsonDocument.Parse(JsonSerializer.Serialize(ok.Value));
|
||||
|
||||
Assert.Equal(1, json.RootElement.GetProperty("Inserted").GetInt32());
|
||||
Assert.Equal(0, json.RootElement.GetProperty("SkippedDuplicates").GetInt32());
|
||||
|
||||
var imported = Assert.Single(context.Transactions);
|
||||
Assert.Equal(7, imported.AccountId);
|
||||
Assert.Null(imported.CardId);
|
||||
}
|
||||
|
||||
private static ImportsController CreateController(MoneyMap.Data.MoneyMapContext context)
|
||||
{
|
||||
var cardResolver = new CardResolver(context);
|
||||
var importer = new TransactionImporter(context, cardResolver);
|
||||
var categorizer = new TransactionCategorizer(context, new MemoryCache(new MemoryCacheOptions()));
|
||||
|
||||
return new ImportsController(context, importer, categorizer);
|
||||
}
|
||||
|
||||
private static void SeedAutoImportData(MoneyMap.Data.MoneyMapContext context)
|
||||
{
|
||||
context.Accounts.Add(new Account
|
||||
{
|
||||
Id = 1,
|
||||
Institution = "Test Bank",
|
||||
Last4 = "1234",
|
||||
Owner = "Test Owner",
|
||||
AccountType = AccountType.Checking
|
||||
});
|
||||
|
||||
context.Cards.Add(new Card
|
||||
{
|
||||
Id = 1,
|
||||
Issuer = "Visa",
|
||||
Last4 = "1234",
|
||||
Owner = "Test Owner",
|
||||
AccountId = 1
|
||||
});
|
||||
|
||||
context.CategoryMappings.Add(new CategoryMapping
|
||||
{
|
||||
Category = "Shopping",
|
||||
Pattern = "AMAZON",
|
||||
Priority = 10
|
||||
});
|
||||
|
||||
context.Transactions.Add(new Transaction
|
||||
{
|
||||
Date = new DateTime(2025, 1, 15),
|
||||
TransactionType = "DEBIT",
|
||||
Name = "AMAZON MKTPL",
|
||||
Memo = "existing usbank.com.1234",
|
||||
Amount = -50.00m,
|
||||
Category = "Shopping",
|
||||
AccountId = 1,
|
||||
CardId = 1,
|
||||
Last4 = "1234"
|
||||
});
|
||||
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
private static IFormFile CreateCsvFormFile(string fileName, string content)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(content);
|
||||
var stream = new MemoryStream(bytes);
|
||||
return new FormFile(stream, 0, bytes.Length, "csv", fileName)
|
||||
{
|
||||
Headers = new HeaderDictionary(),
|
||||
ContentType = "text/csv"
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user