- TransactionRepository.CategoryByDescription: busca a categoria mais recente para cada descrição normalizada no histórico do perfil - ImportService.Preview: pré-preenche category_id em linhas não-duplicatas cuja descrição já foi categorizada antes (case-insensitive) - Duplicatas não recebem sugestão - 2 novos testes: auto-categorize + duplicate-not-categorized Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
134 lines
4.1 KiB
Go
134 lines
4.1 KiB
Go
package service_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"financeiro-carvalho/internal/model"
|
|
"financeiro-carvalho/internal/service"
|
|
)
|
|
|
|
type mockImportTxRepo struct{}
|
|
|
|
func (m *mockImportTxRepo) IsDuplicate(_ context.Context, _, _ string, _ float64) (bool, error) {
|
|
return false, nil
|
|
}
|
|
func (m *mockImportTxRepo) IsExternalIDKnown(_ context.Context, _ string) (bool, error) {
|
|
return false, nil
|
|
}
|
|
func (m *mockImportTxRepo) BulkInsert(_ context.Context, rows []model.ImportRow) (int, error) {
|
|
return len(rows), nil
|
|
}
|
|
func (m *mockImportTxRepo) SaveImportLog(_ context.Context, _, _ string, _, _, _ int) error {
|
|
return nil
|
|
}
|
|
func (m *mockImportTxRepo) CategoryByDescription(_ context.Context, _ []string) (map[string]int, error) {
|
|
return map[string]int{}, nil
|
|
}
|
|
|
|
type mockDashboardRepo struct {
|
|
income float64
|
|
expenses float64
|
|
}
|
|
|
|
func (m *mockDashboardRepo) MonthlySummary(_ context.Context, _ string) (float64, float64, error) {
|
|
return m.income, m.expenses, nil
|
|
}
|
|
func (m *mockDashboardRepo) TotalTaxes(_ context.Context, _ string) (float64, error) {
|
|
return 0, nil
|
|
}
|
|
func (m *mockDashboardRepo) ByCategory(_ context.Context, _ string) ([]model.CategoryTotal, error) {
|
|
return nil, nil
|
|
}
|
|
func (m *mockDashboardRepo) MonthlyEvolution(_ context.Context, _ string) ([]model.MonthEvolution, error) {
|
|
return nil, nil
|
|
}
|
|
func (m *mockDashboardRepo) RecentTransactions(_ context.Context, _ string) ([]model.RecentTransaction, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
type mockPatrimony struct{}
|
|
|
|
func (m *mockPatrimony) TotalPatrimony(_ context.Context) (float64, error) { return 0, nil }
|
|
|
|
type mockPendingBillRepo struct{}
|
|
|
|
func (m *mockPendingBillRepo) List(_ context.Context) ([]model.PendingBillImport, error) {
|
|
return nil, nil
|
|
}
|
|
func (m *mockPendingBillRepo) Create(_ context.Context, _, _ string, _ float64, _ []model.ImportRow) (*model.PendingBillImport, error) {
|
|
return &model.PendingBillImport{}, nil
|
|
}
|
|
func (m *mockPendingBillRepo) GetByID(_ context.Context, _ int) (*model.PendingBillImport, error) {
|
|
return nil, nil
|
|
}
|
|
func (m *mockPendingBillRepo) Delete(_ context.Context, _ int) error { return nil }
|
|
|
|
type mockSettingsRepo struct{}
|
|
|
|
func (m *mockSettingsRepo) Get(_ context.Context) (*model.UserSettings, error) {
|
|
return &model.UserSettings{SavingsGoalPct: 40}, nil
|
|
}
|
|
func (m *mockSettingsRepo) Upsert(_ context.Context, s model.UserSettings) (*model.UserSettings, error) {
|
|
return &s, nil
|
|
}
|
|
|
|
func newDashboardSvc(income, expenses float64) *service.DashboardService {
|
|
repo := &mockAccountRepo{}
|
|
recurrSvc := service.NewRecurringService(newMockRecurring(nil), &mockTxRepo{})
|
|
billSvc := service.NewCreditBillService(&mockCreditBillRepo{}, repo)
|
|
pendingBillSvc := service.NewPendingBillService(&mockPendingBillRepo{}, &mockImportTxRepo{})
|
|
settingsSvc := service.NewSettingsService(&mockSettingsRepo{})
|
|
return service.NewDashboardService(&mockDashboardRepo{income: income, expenses: expenses}, recurrSvc, &mockPatrimony{}, billSvc, pendingBillSvc, settingsSvc)
|
|
}
|
|
|
|
func TestDashboard_SavingsPct_40(t *testing.T) {
|
|
svc := newDashboardSvc(10000, 6000)
|
|
data, err := svc.Get(context.Background(), "2024-03")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if data.SavingsPct != 40.0 {
|
|
t.Errorf("expected 40.0, got %.2f", data.SavingsPct)
|
|
}
|
|
}
|
|
|
|
func TestDashboard_SavingsPct_NoIncome(t *testing.T) {
|
|
svc := newDashboardSvc(0, 500)
|
|
data, err := svc.Get(context.Background(), "2024-03")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if data.SavingsPct != 0 {
|
|
t.Errorf("expected 0.0 when no income, got %.2f", data.SavingsPct)
|
|
}
|
|
}
|
|
|
|
func TestDashboard_SavingsPct_Above40(t *testing.T) {
|
|
svc := newDashboardSvc(10000, 5000)
|
|
data, err := svc.Get(context.Background(), "2024-03")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if data.SavingsPct != 50.0 {
|
|
t.Errorf("expected 50.0, got %.2f", data.SavingsPct)
|
|
}
|
|
}
|
|
|
|
func TestDashboard_EmptySlices(t *testing.T) {
|
|
svc := newDashboardSvc(0, 0)
|
|
data, err := svc.Get(context.Background(), "2024-03")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if data.ByCategory == nil {
|
|
t.Error("ByCategory should not be nil")
|
|
}
|
|
if data.MonthlyEvolution == nil {
|
|
t.Error("MonthlyEvolution should not be nil")
|
|
}
|
|
if data.RecentTransactions == nil {
|
|
t.Error("RecentTransactions should not be nil")
|
|
}
|
|
}
|