test: expand backend coverage

This commit is contained in:
Tiago Yamamoto 2026-01-01 16:01:45 -03:00
parent 38b3342425
commit 9fb4f3e12e
5 changed files with 335 additions and 0 deletions

View file

@ -0,0 +1,114 @@
package notifications
import (
"bytes"
"context"
"io"
"net/http"
"testing"
"github.com/gofrs/uuid/v5"
"github.com/saveinmed/backend-go/internal/domain"
)
type roundTripperFunc func(*http.Request) (*http.Response, error)
func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req)
}
func TestRegisterAndUnregisterToken(t *testing.T) {
svc := NewFCMService("")
ctx := context.Background()
userID := uuid.Must(uuid.NewV7())
if err := svc.RegisterToken(ctx, userID, ""); err == nil {
t.Fatal("expected error for empty token")
}
if err := svc.RegisterToken(ctx, userID, "token-1"); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := svc.RegisterToken(ctx, userID, "token-1"); err != nil {
t.Fatalf("unexpected error on duplicate token: %v", err)
}
if len(svc.tokens[userID]) != 1 {
t.Fatalf("expected 1 token, got %d", len(svc.tokens[userID]))
}
if err := svc.UnregisterToken(ctx, userID, "token-1"); err != nil {
t.Fatalf("unexpected error unregistering: %v", err)
}
if len(svc.tokens[userID]) != 0 {
t.Fatalf("expected no tokens after unregister, got %d", len(svc.tokens[userID]))
}
}
func TestSendPushSkipsWhenNoTokens(t *testing.T) {
svc := NewFCMService("")
ctx := context.Background()
if err := svc.SendPush(ctx, uuid.Must(uuid.NewV7()), "title", "body", nil); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestSendToFCMWithServerKey(t *testing.T) {
svc := NewFCMService("server-key")
ctx := context.Background()
var capturedAuth string
svc.httpClient = &http.Client{
Transport: roundTripperFunc(func(req *http.Request) (*http.Response, error) {
capturedAuth = req.Header.Get("Authorization")
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString("ok"))}, nil
}),
}
err := svc.sendToFCM(ctx, FCMMessage{
To: "token",
Notification: &FCMNotification{
Title: "Hello",
Body: "World",
},
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if capturedAuth != "key=server-key" {
t.Fatalf("expected auth header to include server key, got %q", capturedAuth)
}
}
func TestSendToFCMRejectsNonOK(t *testing.T) {
svc := NewFCMService("server-key")
ctx := context.Background()
svc.httpClient = &http.Client{
Transport: roundTripperFunc(func(req *http.Request) (*http.Response, error) {
return &http.Response{StatusCode: http.StatusBadRequest, Body: io.NopCloser(bytes.NewBufferString("bad"))}, nil
}),
}
if err := svc.sendToFCM(ctx, FCMMessage{To: "token"}); err == nil {
t.Fatal("expected error for non-OK response")
}
}
func TestNotifyOrderStatusChangedUsesDefaultEmoji(t *testing.T) {
svc := NewFCMService("")
ctx := context.Background()
buyerID := uuid.Must(uuid.NewV7())
_ = svc.RegisterToken(ctx, buyerID, "token-1")
order := &domain.Order{ID: uuid.Must(uuid.NewV7()), Status: domain.OrderStatus("Em análise")}
buyer := &domain.User{ID: buyerID}
if err := svc.NotifyOrderStatusChanged(ctx, order, buyer); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}

View file

@ -0,0 +1,40 @@
package notifications
import (
"bytes"
"context"
"log"
"strings"
"testing"
"github.com/gofrs/uuid/v5"
"github.com/saveinmed/backend-go/internal/domain"
)
func TestLoggerNotificationService(t *testing.T) {
buffer := &bytes.Buffer{}
original := log.Writer()
log.SetOutput(buffer)
defer log.SetOutput(original)
svc := NewLoggerNotificationService()
ctx := context.Background()
order := &domain.Order{ID: uuid.Must(uuid.NewV7()), TotalCents: 12345, Status: domain.OrderStatusPaid}
buyer := &domain.User{Email: "buyer@example.com", Name: "Buyer"}
seller := &domain.User{Email: "seller@example.com", Name: "Seller"}
if err := svc.NotifyOrderCreated(ctx, order, buyer, seller); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := svc.NotifyOrderStatusChanged(ctx, order, buyer); err != nil {
t.Fatalf("unexpected error: %v", err)
}
output := buffer.String()
if !strings.Contains(output, "Novo Pedido") {
t.Fatalf("expected output to include order created log, got %q", output)
}
if !strings.Contains(output, "Atualização do Pedido") {
t.Fatalf("expected output to include status change log, got %q", output)
}
}

View file

@ -0,0 +1,110 @@
package usecase
import (
"context"
"testing"
"github.com/gofrs/uuid/v5"
"github.com/saveinmed/backend-go/internal/domain"
)
func TestCheckCreditLineErrors(t *testing.T) {
svc, _ := newTestService()
ctx := context.Background()
_, err := svc.CheckCreditLine(ctx, uuid.Must(uuid.NewV7()), 100)
if err == nil {
t.Fatal("expected error for missing company")
}
company := domain.Company{ID: uuid.Must(uuid.NewV7())}
svc.repo.(*MockRepository).companies = append(svc.repo.(*MockRepository).companies, company)
_, err = svc.CheckCreditLine(ctx, company.ID, 100)
if err == nil {
t.Fatal("expected error when credit line not enabled")
}
}
func TestCheckCreditLineAvailable(t *testing.T) {
svc, repo := newTestService()
ctx := context.Background()
company := domain.Company{
ID: uuid.Must(uuid.NewV7()),
CreditLimitCents: 10000,
CreditUsedCents: 2500,
}
repo.companies = append(repo.companies, company)
ok, err := svc.CheckCreditLine(ctx, company.ID, 5000)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !ok {
t.Fatal("expected credit line to be available")
}
}
func TestUseAndReleaseCreditLine(t *testing.T) {
svc, repo := newTestService()
ctx := context.Background()
company := domain.Company{
ID: uuid.Must(uuid.NewV7()),
CreditLimitCents: 9000,
CreditUsedCents: 1000,
}
repo.companies = append(repo.companies, company)
if err := svc.UseCreditLine(ctx, company.ID, 3000); err != nil {
t.Fatalf("unexpected error using credit line: %v", err)
}
updated, _ := repo.GetCompany(ctx, company.ID)
if updated.CreditUsedCents != 4000 {
t.Fatalf("expected credit used to be 4000, got %d", updated.CreditUsedCents)
}
if err := svc.ReleaseCreditLine(ctx, company.ID, 5000); err != nil {
t.Fatalf("unexpected error releasing credit: %v", err)
}
updated, _ = repo.GetCompany(ctx, company.ID)
if updated.CreditUsedCents != 0 {
t.Fatalf("expected credit used to floor at 0, got %d", updated.CreditUsedCents)
}
}
func TestUseCreditLineInsufficient(t *testing.T) {
svc, repo := newTestService()
ctx := context.Background()
company := domain.Company{
ID: uuid.Must(uuid.NewV7()),
CreditLimitCents: 5000,
CreditUsedCents: 4500,
}
repo.companies = append(repo.companies, company)
if err := svc.UseCreditLine(ctx, company.ID, 1000); err != ErrInsufficientCredit {
t.Fatalf("expected ErrInsufficientCredit, got %v", err)
}
}
func TestSetCreditLimit(t *testing.T) {
svc, repo := newTestService()
ctx := context.Background()
company := domain.Company{ID: uuid.Must(uuid.NewV7())}
repo.companies = append(repo.companies, company)
if err := svc.SetCreditLimit(ctx, company.ID, 12000); err != nil {
t.Fatalf("unexpected error: %v", err)
}
updated, _ := repo.GetCompany(ctx, company.ID)
if updated.CreditLimitCents != 12000 {
t.Fatalf("expected credit limit to be 12000, got %d", updated.CreditLimitCents)
}
}

View file

@ -0,0 +1,53 @@
package usecase
import (
"context"
"testing"
"github.com/gofrs/uuid/v5"
"github.com/saveinmed/backend-go/internal/domain"
)
func TestUpsertAndGetPaymentGatewayConfig(t *testing.T) {
svc, _ := newTestService()
ctx := context.Background()
config := &domain.PaymentGatewayConfig{
Provider: "stripe",
Active: true,
Credentials: "{\"secret\":\"secret\"}",
Environment: "sandbox",
Commission: 2.9,
}
if err := svc.UpsertPaymentGatewayConfig(ctx, config); err != nil {
t.Fatalf("unexpected error: %v", err)
}
stored, err := svc.GetPaymentGatewayConfig(ctx, "stripe")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if stored == nil || stored.Provider != "stripe" {
t.Fatal("expected stored config to be returned")
}
}
func TestOnboardSellerStoresAccount(t *testing.T) {
svc, repo := newTestService()
ctx := context.Background()
sellerID := uuid.Must(uuid.NewV7())
link, err := svc.OnboardSeller(ctx, sellerID, "stripe")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if link == "" {
t.Fatal("expected onboarding link")
}
stored, _ := repo.GetSellerPaymentAccount(ctx, sellerID)
if stored == nil || stored.Status != "pending" {
t.Fatal("expected seller payment account to be stored as pending")
}
}

View file

@ -19,6 +19,8 @@ type MockRepository struct {
reviews []domain.Review
shipping []domain.ShippingMethod
shippingSettings map[uuid.UUID]domain.ShippingSettings
paymentConfigs map[string]domain.PaymentGatewayConfig
sellerAccounts map[uuid.UUID]domain.SellerPaymentAccount
}
func NewMockRepository() *MockRepository {
@ -31,6 +33,8 @@ func NewMockRepository() *MockRepository {
reviews: make([]domain.Review, 0),
shipping: make([]domain.ShippingMethod, 0),
shippingSettings: make(map[uuid.UUID]domain.ShippingSettings),
paymentConfigs: make(map[string]domain.PaymentGatewayConfig),
sellerAccounts: make(map[uuid.UUID]domain.SellerPaymentAccount),
}
}
@ -355,18 +359,32 @@ func (m *MockRepository) ListWithdrawals(ctx context.Context, companyID uuid.UUI
// Payment Config methods
func (m *MockRepository) GetPaymentGatewayConfig(ctx context.Context, provider string) (*domain.PaymentGatewayConfig, error) {
if config, ok := m.paymentConfigs[provider]; ok {
copied := config
return &copied, nil
}
return nil, nil
}
func (m *MockRepository) UpsertPaymentGatewayConfig(ctx context.Context, config *domain.PaymentGatewayConfig) error {
if config != nil {
m.paymentConfigs[config.Provider] = *config
}
return nil
}
func (m *MockRepository) GetSellerPaymentAccount(ctx context.Context, sellerID uuid.UUID) (*domain.SellerPaymentAccount, error) {
if account, ok := m.sellerAccounts[sellerID]; ok {
copied := account
return &copied, nil
}
return nil, nil
}
func (m *MockRepository) UpsertSellerPaymentAccount(ctx context.Context, account *domain.SellerPaymentAccount) error {
if account != nil {
m.sellerAccounts[account.SellerID] = *account
}
return nil
}