Skip to content

Commit 881d5e6

Browse files
committed
Creacion de test y legger
1 parent 2ac5ac5 commit 881d5e6

File tree

9 files changed

+301
-33
lines changed

9 files changed

+301
-33
lines changed

api/handler.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ import (
1010
"go.uber.org/zap"
1111
)
1212

13-
type ventaHandler struct {
13+
type VentaHandler struct {
1414
ventaService *internal.Service
1515
logger *zap.Logger
1616
}
1717

18-
func NewVentaHandler(service *internal.Service, logger *zap.Logger) *ventaHandler {
19-
return &ventaHandler{
18+
func NewVentaHandler(service *internal.Service, logger *zap.Logger) *VentaHandler {
19+
return &VentaHandler{
2020
ventaService: service,
2121
logger: logger,
2222
}
2323
}
2424

25-
func (ventaHandler *ventaHandler) HandleCreate(ginContext *gin.Context) {
25+
func (ventaHandler *VentaHandler) HandleCreate(ginContext *gin.Context) {
2626
var venta internal.Venta
2727
if err := ginContext.ShouldBindJSON(&venta); err != nil {
2828
ginContext.JSON(http.StatusBadRequest, gin.H{"error": "Entrada invalida"})
@@ -48,7 +48,21 @@ func (ventaHandler *ventaHandler) HandleCreate(ginContext *gin.Context) {
4848
ginContext.JSON(http.StatusCreated, venta)
4949
}
5050

51-
func (ventaHandler *ventaHandler) HandleUpdate(ginContext *gin.Context) {
51+
func (ventaHandler *VentaHandler) HandleGet(ginContext *gin.Context) {
52+
ventaId := ginContext.Param("id")
53+
54+
venta, err := ventaHandler.ventaService.GetVenta(ventaId)
55+
if err != nil {
56+
ginContext.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
57+
return
58+
}
59+
60+
ventaHandler.logger.Info("venta encontrada", zap.Any("venta", venta))
61+
ginContext.JSON(http.StatusOK, venta)
62+
63+
}
64+
65+
func (ventaHandler *VentaHandler) HandleUpdate(ginContext *gin.Context) {
5266
id := ginContext.Param("id")
5367

5468
var input struct {
@@ -100,13 +114,13 @@ func (ventaHandler *ventaHandler) HandleUpdate(ginContext *gin.Context) {
100114
ginContext.JSON(http.StatusOK, response)
101115
}
102116

103-
func (ventaHandler *ventaHandler) HandleSearch(ginContext *gin.Context) {
117+
func (ventaHandler *VentaHandler) HandleSearch(ginContext *gin.Context) {
104118
userID := ginContext.Query("user_id")
105119
status := ginContext.Query("status")
106120

107121
if userID == "" {
108122
ventaHandler.logger.Warn("falta user_id en búsqueda de ventas")
109-
ginContext.JSON(http.StatusBadRequest, gin.H{"error": "user_id i es requerido"})
123+
ginContext.JSON(http.StatusBadRequest, gin.H{"error": "user_id es requerido"})
110124
return
111125
}
112126

api/router.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,16 @@ import (
88
"go.uber.org/zap"
99
)
1010

11-
func InitRoutes(router *gin.Engine) {
11+
func InitRoutes(router *gin.Engine, httpClient *resty.Client, localStorage *internal.LocalStorage) {
1212
logger, _ := zap.NewProduction()
1313
defer logger.Sync()
1414

15-
localStorage := internal.NewLocalStorage()
16-
httpClient := resty.New()
17-
18-
ventaService := internal.NewService(localStorage, httpClient, logger)
15+
userService := internal.NewUserService(httpClient)
16+
ventaService := internal.NewService(localStorage, userService, logger)
1917
salesHandler := NewVentaHandler(ventaService, logger)
2018

2119
router.POST("/sales", salesHandler.HandleCreate)
2220
router.PATCH("/sales/:id", salesHandler.HandleUpdate)
2321
router.GET("/sales", salesHandler.HandleSearch)
24-
22+
router.GET("/sales/:id", salesHandler.HandleGet)
2523
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/bytedance/sonic/loader v0.1.1 // indirect
88
github.com/cloudwego/base64x v0.1.4 // indirect
99
github.com/cloudwego/iasm v0.2.0 // indirect
10+
github.com/davecgh/go-spew v1.1.1 // indirect
1011
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
1112
github.com/gin-contrib/sse v0.1.0 // indirect
1213
github.com/gin-gonic/gin v1.10.0 // indirect
@@ -23,6 +24,8 @@ require (
2324
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
2425
github.com/modern-go/reflect2 v1.0.2 // indirect
2526
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
27+
github.com/pmezard/go-difflib v1.0.0 // indirect
28+
github.com/stretchr/testify v1.10.0 // indirect
2629
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
2730
github.com/ugorji/go/codec v1.2.12 // indirect
2831
go.uber.org/multierr v1.11.0 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJ
77
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
88
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
99
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1011
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1112
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
1213
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
@@ -44,6 +45,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
4445
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
4546
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
4647
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
48+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4749
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4850
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
4951
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -56,6 +58,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
5658
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
5759
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
5860
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
61+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
62+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
5963
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
6064
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
6165
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=

internal/service.go

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ package internal
22

33
import (
44
"errors"
5-
"fmt"
65
"math/rand"
76
"time"
87

9-
"github.com/go-resty/resty/v2"
108
"github.com/google/uuid"
119
"go.uber.org/zap"
1210
)
@@ -17,26 +15,32 @@ var (
1715
ErrVentaNotFound = errors.New("la venta no existe")
1816
ErrInvalidTransition = errors.New("solo se puede cambiar el estado si está en 'pending'")
1917
ErrInvalidStatus = errors.New("estado inválido")
18+
ErrInternalError = errors.New("error interno")
2019
)
2120

22-
type Service struct {
23-
storage *LocalStorage
24-
client *resty.Client
25-
logger *zap.Logger
21+
// probando para test
22+
type Storage interface {
23+
Set(*Venta) error
24+
Read(string) (*Venta, error)
25+
Search(userID string, status string) ([]*Venta, error)
2626
}
2727

28-
func NewService(storage *LocalStorage, client *resty.Client, logger *zap.Logger) *Service {
29-
client.SetBaseURL("http://localhost:8081")
28+
type Service struct {
29+
storage Storage // 👈 antes era *LocalStorage
30+
userService UserService
31+
logger *zap.Logger
32+
}
3033

34+
func NewService(storage Storage, userService UserService, logger *zap.Logger) *Service {
3135
if logger == nil {
3236
logger, _ = zap.NewProduction()
3337
defer logger.Sync()
3438
}
3539

3640
return &Service{
37-
storage: storage,
38-
client: client,
39-
logger: logger,
41+
storage: storage,
42+
userService: userService,
43+
logger: logger,
4044
}
4145
}
4246

@@ -45,17 +49,12 @@ func (service *Service) Create(venta *Venta) error {
4549
return ErrInvalidAmount
4650
}
4751

48-
resp, err := service.client.R().
49-
SetHeader("Content-Type", "application/json").
50-
Get("http://localhost:8080/users/" + venta.UserID)
51-
52+
userExist, err := service.userService.userExist(venta.UserID)
5253
if err != nil {
53-
return err
54+
return ErrInternalError
5455
}
5556

56-
if resp.StatusCode() < 200 || resp.StatusCode() >= 300 {
57-
fmt.Println("Error en la respuesta de usuarios:", resp.Status())
58-
fmt.Println("Cuerpo de la respuesta:", resp.Body())
57+
if !userExist {
5958
return ErrUserNotFound
6059
}
6160

@@ -111,6 +110,10 @@ func (service *Service) UpdateStatus(id string, status string) (*Venta, error) {
111110
return venta, nil
112111
}
113112

113+
func (service *Service) GetVenta(ventaID string) (*Venta, error) {
114+
return service.storage.Read(ventaID)
115+
}
116+
114117
func (service *Service) SearchVentas(userID string, status string) ([]*Venta, error) {
115118
return service.storage.Search(userID, status)
116119
}

internal/service_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package internal
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
8+
"go.uber.org/zap"
9+
)
10+
11+
// --- mockStorage que cumple con la interfaz Storage ---
12+
type mockStorage struct {
13+
SetFunc func(*Venta) error
14+
ReadFunc func(string) (*Venta, error)
15+
SearchFunc func(string, string) ([]*Venta, error)
16+
}
17+
18+
func (m *mockStorage) Set(v *Venta) error {
19+
if m.SetFunc != nil {
20+
return m.SetFunc(v)
21+
}
22+
return nil
23+
}
24+
25+
func (m *mockStorage) Read(id string) (*Venta, error) {
26+
if m.ReadFunc != nil {
27+
return m.ReadFunc(id)
28+
}
29+
return nil, nil
30+
}
31+
32+
func (m *mockStorage) Search(userID string, status string) ([]*Venta, error) {
33+
if m.SearchFunc != nil {
34+
return m.SearchFunc(userID, status)
35+
}
36+
return nil, nil
37+
}
38+
39+
// --- TEST: Create devuelve error si usuario no existe ---
40+
func TestService_Create_UserNotFound(t *testing.T) {
41+
// logger falso
42+
logger := zap.NewNop()
43+
44+
// storage mock
45+
storage := &mockStorage{
46+
SetFunc: func(v *Venta) error {
47+
t.Fatal("Set no debería ser llamado si el usuario no existe")
48+
return nil
49+
},
50+
}
51+
52+
userService := NewMockUserService(false, nil)
53+
54+
service := NewService(storage, userService, logger)
55+
56+
venta := &Venta{
57+
UserID: "1234",
58+
Amount: 100,
59+
}
60+
61+
err := service.Create(venta)
62+
63+
require.ErrorIs(t, err, ErrUserNotFound)
64+
}
65+
66+
// NewMockUserService devuelve una implementación de UserService que siempre retorna
67+
// los valores pasados como parámetros.
68+
func NewMockUserService(exists bool, err error) UserService {
69+
return &mockUserServiceImpl{
70+
exists: exists,
71+
err: err,
72+
}
73+
}
74+
75+
// Implementación privada del UserService
76+
type mockUserServiceImpl struct {
77+
exists bool
78+
err error
79+
}
80+
81+
func (m *mockUserServiceImpl) userExist(userID string) (bool, error) {
82+
return m.exists, m.err
83+
}

internal/users_service.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package internal
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/go-resty/resty/v2"
7+
)
8+
9+
type UserService interface {
10+
userExist(userID string) (bool, error)
11+
}
12+
13+
type userService struct {
14+
client *resty.Client
15+
}
16+
17+
func NewUserService(client *resty.Client) *userService {
18+
return &userService{
19+
client: client,
20+
}
21+
}
22+
23+
func (userService *userService) userExist(userID string) (bool, error) {
24+
resp, err := userService.client.R().
25+
SetHeader("Content-Type", "application/json").
26+
Get("http://localhost:8080/users/" + userID)
27+
28+
if err != nil {
29+
return false, err
30+
}
31+
32+
if resp.StatusCode() < 200 || resp.StatusCode() >= 300 {
33+
fmt.Println("Error en la respuesta de usuarios:", resp.Status())
34+
fmt.Println("Cuerpo de la respuesta:", resp.Body())
35+
return false, nil
36+
}
37+
38+
return true, nil
39+
}

main.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@ package main
22

33
import (
44
"sales-api/api"
5+
"sales-api/internal"
56

67
"github.com/gin-gonic/gin"
8+
"github.com/go-resty/resty/v2"
79
)
810

911
func main() {
1012
r := gin.Default()
1113

12-
api.InitRoutes(r)
14+
httpClient := resty.New()
15+
localStorage := internal.NewLocalStorage()
16+
api.InitRoutes(r, httpClient, localStorage)
1317

1418
r.Run(":8081")
1519

0 commit comments

Comments
 (0)