package service_test import ( "context" "strings" "testing" "financeiro-carvalho/internal/model" "financeiro-carvalho/internal/service" ) // mockImportRepo implements repository.TransactionRepository for import tests. type mockImportRepo struct { catByDesc map[string]int // normalized desc → category_id } func (m *mockImportRepo) IsDuplicate(_ context.Context, _, _ string, _ float64) (bool, error) { return false, nil } func (m *mockImportRepo) IsExternalIDKnown(_ context.Context, _ string) (bool, error) { return false, nil } func (m *mockImportRepo) BulkInsert(_ context.Context, rows []model.ImportRow) (int, error) { return len(rows), nil } func (m *mockImportRepo) SaveImportLog(_ context.Context, _, _ string, _, _, _ int) error { return nil } func (m *mockImportRepo) CategoryByDescription(_ context.Context, descs []string) (map[string]int, error) { out := map[string]int{} for _, d := range descs { if id, ok := m.catByDesc[d]; ok { out[d] = id } } return out, nil } var csvMapping = model.CSVMapping{ DateColumn: 0, AmountColumn: 1, DescriptionColumn: 2, DateFormat: "02/01/2006", HasHeader: false, DecimalSeparator: ",", FieldSeparator: ";", } func TestPreview_AutoCategorize(t *testing.T) { repo := &mockImportRepo{ catByDesc: map[string]int{ "padaria do ze": 3, "uber *trip": 7, }, } svc := service.NewImportService(repo) csv := "01/03/2024;-45,00;PADARIA DO ZE\n02/03/2024;-22,50;UBER *TRIP\n03/03/2024;-99,00;LOJA NOVA XYZ\n" rows, _, err := svc.Preview(context.Background(), "fatura.csv", strings.NewReader(csv), &csvMapping) if err != nil { t.Fatalf("preview error: %v", err) } if len(rows) != 3 { t.Fatalf("expected 3 rows, got %d", len(rows)) } // PADARIA DO ZE → category 3 if rows[0].CategoryID == nil || *rows[0].CategoryID != 3 { t.Errorf("row[0] (PADARIA DO ZE) expected category 3, got %v", rows[0].CategoryID) } // UBER *TRIP → category 7 if rows[1].CategoryID == nil || *rows[1].CategoryID != 7 { t.Errorf("row[1] (UBER *TRIP) expected category 7, got %v", rows[1].CategoryID) } // LOJA NOVA XYZ → no suggestion if rows[2].CategoryID != nil { t.Errorf("row[2] (LOJA NOVA XYZ) expected no category, got %v", rows[2].CategoryID) } } func TestPreview_DuplicateNotAutoCategorizated(t *testing.T) { // row that IS a duplicate should not get category pre-filled repo := &mockImportRepoDup{ catByDesc: map[string]int{"padaria do ze": 3}, } svc := service.NewImportService(repo) csv := "01/03/2024;-45,00;PADARIA DO ZE\n" rows, _, err := svc.Preview(context.Background(), "fatura.csv", strings.NewReader(csv), &csvMapping) if err != nil { t.Fatalf("preview error: %v", err) } if len(rows) != 1 { t.Fatalf("expected 1 row, got %d", len(rows)) } if !rows[0].IsDuplicate { t.Error("expected row to be marked duplicate") } if rows[0].CategoryID != nil { t.Errorf("duplicate should not get category, got %v", rows[0].CategoryID) } } // mockImportRepoDup marks all rows as duplicate (by date+amount+desc). type mockImportRepoDup struct { catByDesc map[string]int } func (m *mockImportRepoDup) IsDuplicate(_ context.Context, _, _ string, _ float64) (bool, error) { return true, nil // always duplicate } func (m *mockImportRepoDup) IsExternalIDKnown(_ context.Context, _ string) (bool, error) { return false, nil } func (m *mockImportRepoDup) BulkInsert(_ context.Context, rows []model.ImportRow) (int, error) { return len(rows), nil } func (m *mockImportRepoDup) SaveImportLog(_ context.Context, _, _ string, _, _, _ int) error { return nil } func (m *mockImportRepoDup) CategoryByDescription(_ context.Context, descs []string) (map[string]int, error) { out := map[string]int{} for _, d := range descs { if id, ok := m.catByDesc[d]; ok { out[d] = id } } return out, nil }