Backend:
- Refatoração crítica em [DeleteOrder](cci:1://file:///c:/Projetos/saveinmed/backend-old/internal/usecase/usecase.go:46:1-46:53): agora devolve o estoque fisicamente para a tabela `products` antes de deletar o pedido, corrigindo o "vazamento" de estoque em pedidos pendentes/cancelados.
- Novo Handler [UpdateInventoryItem](cci:1://file:///c:/Projetos/saveinmed/backend-old/internal/http/handler/product_handler.go:513:0-573:1): implementada lógica para resolver o ID de Inventário (frontend) para o ID de Produto (backend) e atualizar ambas as tabelas (`products` e `inventory_items`) simultaneamente, garantindo consistência entre a visualização e o checkout.
- Compatibilidade Frontend (DTOs):
- Adicionado suporte aos campos `qtdade_estoque` e `preco_venda` (float) no payload de update.
- Removida a validação estrita de JSON (`DisallowUnknownFields`) para evitar erros 400 em payloads com campos extras.
- Registrada rota alias `PUT /api/v1/produtos-venda/{id}` apontando para o manipulador correto.
- Repositório & Testes:
- Implementação de [GetInventoryItem](cci:1://file:///c:/Projetos/saveinmed/backend-old/internal/usecase/usecase_test.go:189:0-191:1) e [UpdateInventoryItem](cci:1://file:///c:/Projetos/saveinmed/backend-old/internal/http/handler/product_handler.go:513:0-573:1) no PostgresRepo e Interfaces de Serviço.
- Correção de erro de sintaxe (declaração duplicada) em [postgres.go](cci:7://file:///c:/Projetos/saveinmed/backend-old/internal/repository/postgres/postgres.go:0:0-0:0).
- Atualização dos Mocks ([handler_test.go](cci:7://file:///c:/Projetos/saveinmed/backend-old/internal/http/handler/handler_test.go:0:0-0:0), [usecase_test.go](cci:7://file:///c:/Projetos/saveinmed/backend-old/internal/usecase/usecase_test.go:0:0-0:0), [product_service_test.go](cci:7://file:///c:/Projetos/saveinmed/backend-old/internal/usecase/product_service_test.go:0:0-0:0)) para refletir as novas assinaturas de interface e corrigir o build.
Frontend:
- Ajustes de integração nos serviços de carrinho, pedidos e gestão de produtos para suportar o fluxo corrigido.
140 lines
4.4 KiB
Go
140 lines
4.4 KiB
Go
package usecase
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gofrs/uuid/v5"
|
|
"github.com/saveinmed/backend-go/internal/domain"
|
|
)
|
|
|
|
type failingBatchRepo struct {
|
|
*MockRepository
|
|
}
|
|
|
|
func (f *failingBatchRepo) BatchCreateProducts(ctx context.Context, products []domain.Product) error {
|
|
return errors.New("boom")
|
|
}
|
|
|
|
func (f *failingBatchRepo) CreateInventoryItem(ctx context.Context, item *domain.InventoryItem) error {
|
|
return errors.New("boom")
|
|
}
|
|
|
|
func (f *failingBatchRepo) GetInventoryItem(ctx context.Context, id uuid.UUID) (*domain.InventoryItem, error) {
|
|
return nil, errors.New("boom")
|
|
}
|
|
|
|
func (f *failingBatchRepo) UpdateInventoryItem(ctx context.Context, item *domain.InventoryItem) error {
|
|
return errors.New("boom")
|
|
}
|
|
|
|
func TestImportProductsSuccess(t *testing.T) {
|
|
repo := NewMockRepository()
|
|
svc := NewService(repo, &MockPaymentGateway{}, &MockNotificationService{}, 2.5, 0.12, "secret", time.Hour, "pepper")
|
|
|
|
csvData := strings.NewReader("name,price,stock,description,ean\nAspirin,12.5,5,Anti-inflammatory,123\nIbuprofen,10,0,,\n")
|
|
sellerID := uuid.Must(uuid.NewV7())
|
|
|
|
report, err := svc.ImportProducts(context.Background(), sellerID, csvData)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if report.TotalProcessed != 2 {
|
|
t.Fatalf("expected total processed 2, got %d", report.TotalProcessed)
|
|
}
|
|
if report.SuccessCount != 2 {
|
|
t.Fatalf("expected success count 2, got %d", report.SuccessCount)
|
|
}
|
|
if report.FailedCount != 0 {
|
|
t.Fatalf("expected failed count 0, got %d", report.FailedCount)
|
|
}
|
|
|
|
if len(repo.products) != 2 {
|
|
t.Fatalf("expected 2 products, got %d", len(repo.products))
|
|
}
|
|
|
|
if repo.products[0].SellerID != sellerID {
|
|
t.Errorf("expected seller ID %s, got %s", sellerID, repo.products[0].SellerID)
|
|
}
|
|
if repo.products[0].PriceCents != 1250 {
|
|
t.Errorf("expected price cents 1250, got %d", repo.products[0].PriceCents)
|
|
}
|
|
// Stock check removed (Dictionary Mode)
|
|
}
|
|
|
|
func TestImportProductsMissingHeaders(t *testing.T) {
|
|
repo := NewMockRepository()
|
|
svc := NewService(repo, &MockPaymentGateway{}, &MockNotificationService{}, 2.5, 0.12, "secret", time.Hour, "pepper")
|
|
|
|
csvData := strings.NewReader("ean,stock\n123,5\n")
|
|
_, err := svc.ImportProducts(context.Background(), uuid.Must(uuid.NewV7()), csvData)
|
|
if err == nil {
|
|
t.Fatal("expected error for missing headers")
|
|
}
|
|
if !strings.Contains(err.Error(), "missing required header") {
|
|
t.Fatalf("expected missing header error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestImportProductsEmptyCSV(t *testing.T) {
|
|
repo := NewMockRepository()
|
|
svc := NewService(repo, &MockPaymentGateway{}, &MockNotificationService{}, 2.5, 0.12, "secret", time.Hour, "pepper")
|
|
|
|
csvData := strings.NewReader("name,price\n")
|
|
_, err := svc.ImportProducts(context.Background(), uuid.Must(uuid.NewV7()), csvData)
|
|
if err == nil {
|
|
t.Fatal("expected error for empty CSV")
|
|
}
|
|
if !strings.Contains(err.Error(), "csv file is empty") {
|
|
t.Fatalf("expected empty csv error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestImportProductsInvalidRows(t *testing.T) {
|
|
repo := NewMockRepository()
|
|
svc := NewService(repo, &MockPaymentGateway{}, &MockNotificationService{}, 2.5, 0.12, "secret", time.Hour, "pepper")
|
|
|
|
csvData := strings.NewReader("name,price,stock\n,12.5,5\nValid,abc,2\nGood,5,1\n")
|
|
sellerID := uuid.Must(uuid.NewV7())
|
|
|
|
report, err := svc.ImportProducts(context.Background(), sellerID, csvData)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if report.TotalProcessed != 3 {
|
|
t.Fatalf("expected total processed 3, got %d", report.TotalProcessed)
|
|
}
|
|
if report.FailedCount != 2 {
|
|
t.Fatalf("expected failed count 2, got %d", report.FailedCount)
|
|
}
|
|
if report.SuccessCount != 1 {
|
|
t.Fatalf("expected success count 1, got %d", report.SuccessCount)
|
|
}
|
|
if len(report.Errors) != 2 {
|
|
t.Fatalf("expected 2 errors, got %d", len(report.Errors))
|
|
}
|
|
|
|
if len(repo.products) != 1 {
|
|
t.Fatalf("expected 1 product, got %d", len(repo.products))
|
|
}
|
|
}
|
|
|
|
func TestImportProductsBatchInsertFailure(t *testing.T) {
|
|
baseRepo := NewMockRepository()
|
|
repo := &failingBatchRepo{MockRepository: baseRepo}
|
|
svc := NewService(repo, &MockPaymentGateway{}, &MockNotificationService{}, 2.5, 0.12, "secret", time.Hour, "pepper")
|
|
|
|
csvData := strings.NewReader("name,price\nItem,12.5\n")
|
|
_, err := svc.ImportProducts(context.Background(), uuid.Must(uuid.NewV7()), csvData)
|
|
if err == nil {
|
|
t.Fatal("expected batch insert error")
|
|
}
|
|
if !strings.Contains(err.Error(), "batch insert failed") {
|
|
t.Fatalf("expected batch insert error, got %v", err)
|
|
}
|
|
}
|