[hw-3] loms service

This commit is contained in:
Никита Шубин
2025-06-20 10:11:59 +00:00
parent c8e056bc99
commit b88dfe6db5
73 changed files with 8837 additions and 52 deletions

View File

@@ -1,19 +1,21 @@
FROM golang:1.23.1-alpine as builder
FROM golang:1.23.9-alpine as builder
WORKDIR /build
COPY go.mod go.mod
COPY go.sum go.sum
COPY cart/go.mod go.mod
COPY cart/go.sum go.sum
RUN go mod download
COPY . .
WORKDIR cart
RUN CGO_ENABLED=0 GOOS=linux go build -o /server ./cmd/server/main.go
FROM scratch
COPY --from=builder server /bin/server
COPY configs/values_local.yaml /bin/config/values_local.yaml
COPY cart/configs/values_local.yaml /bin/config/values_local.yaml
ENV CONFIG_FILE=/bin/config/values_local.yaml

View File

@@ -5,6 +5,7 @@ go 1.23.9
require (
github.com/rs/zerolog v1.34.0
github.com/stretchr/testify v1.10.0
google.golang.org/grpc v1.72.0
gopkg.in/yaml.v3 v3.0.1
)

View File

@@ -54,6 +54,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gojuno/minimock/v3 v3.4.5 h1:Jcb0tEYZvVlQNtAAYpg3jCOoSwss2c1/rNugYTzj304=
github.com/gojuno/minimock/v3 v3.4.5/go.mod h1:o9F8i2IT8v3yirA7mmdpNGzh1WNesm6iQakMtQV6KiE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
@@ -147,6 +149,8 @@ go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCRE
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI=

View File

@@ -9,14 +9,19 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"route256/cart/internal/app/server"
"route256/cart/internal/domain/cart/repository"
"route256/cart/internal/domain/cart/service"
product_service "route256/cart/internal/domain/products/service"
loms_service "route256/cart/internal/clients/loms"
product_service "route256/cart/internal/clients/products"
"route256/cart/internal/domain/repository"
"route256/cart/internal/domain/service"
"route256/cart/internal/infra/config"
"route256/cart/internal/infra/http/middlewares"
"route256/cart/internal/infra/http/round_trippers"
pbLOMS "route256/pkg/api/loms/v1"
)
const (
@@ -39,9 +44,9 @@ func NewApp(configPath string) (*App, error) {
zerolog.SetGlobalLevel(zerolog.InfoLevel)
if c.Service.LogLevel != "" {
level, err := zerolog.ParseLevel(c.Service.LogLevel)
if err != nil {
return nil, fmt.Errorf("unknown log level `%s` provided: %w", c.Service.LogLevel, err)
level, logErr := zerolog.ParseLevel(c.Service.LogLevel)
if logErr != nil {
return nil, fmt.Errorf("unknown log level `%s` provided: %w", c.Service.LogLevel, logErr)
}
zerolog.SetGlobalLevel(level)
@@ -52,7 +57,13 @@ func NewApp(configPath string) (*App, error) {
app := &App{
config: c,
}
app.server.Handler = app.BootstrapHandlers(app.setupCartService())
service, err := app.setupCartService()
if err != nil {
return nil, err
}
app.server.Handler = app.BootstrapHandlers(service)
return app, nil
}
@@ -70,7 +81,8 @@ func (app *App) ListenAndServe() error {
return app.server.Serve(l)
}
func (app *App) setupCartService() *service.CartService {
func (app *App) setupCartService() (*service.CartService, error) {
// Product service client
transport := http.DefaultTransport
transport = round_trippers.NewLogRoundTripper(transport)
transport = round_trippers.NewRetryRoundTripper(transport, productsRetryAttemptsDefault, productsInitialDelaySecDefault)
@@ -86,10 +98,21 @@ func (app *App) setupCartService() *service.CartService {
fmt.Sprintf("%s:%s", app.config.ProductService.Host, app.config.ProductService.Port),
)
// LOMS service client
conn, err := grpc.NewClient(
fmt.Sprintf("%s:%s", app.config.LomsService.Host, app.config.LomsService.Port),
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, fmt.Errorf("grpc.NewClient: %w", err)
}
lomsClient := pbLOMS.NewLOMSClient(conn)
lomsService := loms_service.NewLomsService(lomsClient)
const userCartCap = 100
cartRepository := repository.NewInMemoryRepository(userCartCap)
return service.NewCartService(cartRepository, productService)
return service.NewCartService(cartRepository, productService, lomsService), nil
}
func (app *App) BootstrapHandlers(cartService *service.CartService) http.Handler {
@@ -97,6 +120,7 @@ func (app *App) BootstrapHandlers(cartService *service.CartService) http.Handler
mx := http.NewServeMux()
mx.HandleFunc("POST /user/{user_id}/cart/{sku_id}", s.AddItemHandler)
mx.HandleFunc("POST /checkout/{user_id}", s.CheckoutHandler)
mx.HandleFunc("GET /user/{user_id}/cart", s.GetItemsByUserIDHandler)
mx.HandleFunc("DELETE /user/{user_id}/cart/{sku_id}", s.DeleteItemHandler)
mx.HandleFunc("DELETE /user/{user_id}/cart", s.DeleteItemsByUserIDHandler)

View File

@@ -13,12 +13,12 @@ import (
"github.com/rs/zerolog/log"
)
type CreateReviewRequest struct {
type AddItemRequest struct {
Count uint32 `json:"count" validate:"required,gt=0"`
}
func (s *Server) AddItemHandler(w http.ResponseWriter, r *http.Request) {
var request CreateReviewRequest
var request AddItemRequest
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
makeErrorResponse(w, err, http.StatusBadRequest)
@@ -79,7 +79,7 @@ func (s *Server) AddItemHandler(w http.ResponseWriter, r *http.Request) {
if err := s.cartService.AddItem(r.Context(), uid, item); err != nil {
switch {
case errors.Is(err, model.ErrProductNotFound):
case errors.Is(err, model.ErrProductNotFound), errors.Is(err, model.ErrNotEnoughStocks):
makeErrorResponse(w, err, http.StatusPreconditionFailed)
log.Trace().Err(err).Msgf("product does not exist")

View File

@@ -0,0 +1,58 @@
package server
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"route256/cart/internal/domain/entity"
"route256/cart/internal/domain/model"
"github.com/rs/zerolog/log"
)
type CheckoutResponse struct {
OrderID int64 `json:"order_id"`
}
func (s *Server) CheckoutHandler(w http.ResponseWriter, r *http.Request) {
strUserID := r.PathValue("user_id")
userID, err := strconv.ParseInt(strUserID, 10, 64)
if err != nil || userID <= 0 {
if err == nil {
err = fmt.Errorf("user_id must be greater than 0")
}
makeErrorResponse(w, err, http.StatusBadRequest)
log.Trace().Err(err).Msgf("user_id=`%s`", strUserID)
return
}
orderID, err := s.cartService.CheckoutUserCart(r.Context(), entity.UID(userID))
switch {
case errors.Is(err, model.ErrCartNotFound):
makeErrorResponse(w, err, http.StatusNotFound)
return
case err != nil:
makeErrorResponse(w, err, http.StatusInternalServerError)
return
}
resp := CheckoutResponse{
OrderID: orderID,
}
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(resp); err != nil {
makeErrorResponse(w, err, http.StatusInternalServerError)
return
}
}

View File

@@ -12,6 +12,7 @@ type CartService interface {
GetItemsByUserID(ctx context.Context, userID entity.UID) (*model.Cart, error)
DeleteItem(ctx context.Context, userID entity.UID, sku entity.Sku) error
DeleteItemsByUserID(ctx context.Context, userID entity.UID) error
CheckoutUserCart(ctx context.Context, userID entity.UID) (int64, error)
}
type Server struct {

View File

@@ -0,0 +1,56 @@
package loms
import (
"context"
"fmt"
"route256/cart/internal/domain/entity"
"route256/cart/internal/domain/model"
pbLoms "route256/pkg/api/loms/v1"
)
type Service struct {
grpcClient pbLoms.LOMSClient
}
func NewLomsService(grpcClient pbLoms.LOMSClient) *Service {
return &Service{
grpcClient: grpcClient,
}
}
func (s *Service) OrderCreate(ctx context.Context, cart *model.Cart) (int64, error) {
items := make([]*pbLoms.OrderItem, len(cart.Items))
for i, item := range cart.Items {
items[i] = &pbLoms.OrderItem{
Sku: int64(item.Product.Sku),
Count: item.Count,
}
}
req := &pbLoms.OrderCreateRequest{
UserId: int64(cart.UserID),
Items: items,
}
resp, err := s.grpcClient.OrderCreate(ctx, req)
if err != nil {
return 0, fmt.Errorf("grpcClient.OrderCreate: %w", err)
}
return resp.OrderId, nil
}
func (s *Service) StocksInfo(ctx context.Context, sku entity.Sku) (uint32, error) {
req := &pbLoms.StocksInfoRequest{
Sku: int64(sku),
}
resp, err := s.grpcClient.StocksInfo(ctx, req)
if err != nil {
return 0, fmt.Errorf("grpcClient.StocksInfo: %w", err)
}
return resp.Count, nil
}

View File

@@ -1,4 +1,4 @@
package service
package products
import (
"context"

View File

@@ -1,7 +1,7 @@
package entity
type (
UID uint64
UID int64
Sku int64
)

View File

@@ -4,6 +4,7 @@ import "errors"
var (
ErrProductNotFound = errors.New("invalid sku")
ErrNotEnoughStocks = errors.New("not enough stocks")
ErrCartNotFound = errors.New("cart not found")
ErrItemNotFoundInCart = errors.New("item not found in cart")

View File

@@ -6,10 +6,10 @@ import (
"slices"
"sync"
"github.com/rs/zerolog/log"
"route256/cart/internal/domain/entity"
"route256/cart/internal/domain/model"
"github.com/rs/zerolog/log"
)
//go:generate minimock -i Repository -o ./mock -s _mock.go
@@ -24,15 +24,23 @@ type ProductService interface {
GetProductBySku(ctx context.Context, sku entity.Sku) (*model.Product, error)
}
type CartService struct {
repository Repository
productService ProductService
type LomsService interface {
OrderCreate(ctx context.Context, cart *model.Cart) (int64, error)
StocksInfo(ctx context.Context, sku entity.Sku) (uint32, error)
}
func NewCartService(repository Repository, productService ProductService) *CartService {
type CartService struct {
repository Repository
productService ProductService
lomsService LomsService
}
func NewCartService(repository Repository, productService ProductService, lomsService LomsService) *CartService {
return &CartService{
repository: repository,
productService: productService,
lomsService: lomsService,
}
}
@@ -50,6 +58,15 @@ func (s *CartService) AddItem(ctx context.Context, userID entity.UID, item *mode
return fmt.Errorf("productService.GetProductBySku: %w", err)
}
count, err := s.lomsService.StocksInfo(ctx, item.Product.Sku)
if err != nil {
return fmt.Errorf("lomsService.StocksInfo: %w", err)
}
if count < item.Count {
return model.ErrNotEnoughStocks
}
if err := s.repository.AddItem(ctx, userID, item); err != nil {
return fmt.Errorf("repository.AddItemToCart: %w", err)
}
@@ -170,3 +187,25 @@ func (s *CartService) DeleteItemsByUserID(ctx context.Context, userID entity.UID
return nil
}
func (s *CartService) CheckoutUserCart(ctx context.Context, userID entity.UID) (int64, error) {
if userID <= 0 {
return 0, fmt.Errorf("userID invalid")
}
cart, err := s.GetItemsByUserID(ctx, entity.UID(userID))
if err != nil {
return 0, err
}
orderID, err := s.lomsService.OrderCreate(ctx, cart)
if err != nil {
return 0, fmt.Errorf("lomsService.OrderCreate: %w", err)
}
if err := s.DeleteItemsByUserID(ctx, userID); err != nil {
return 0, err
}
return orderID, nil
}

View File

@@ -9,9 +9,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"route256/cart/internal/domain/cart/service/mock"
"route256/cart/internal/domain/entity"
"route256/cart/internal/domain/model"
"route256/cart/internal/domain/service/mock"
)
const (
@@ -19,9 +19,9 @@ const (
validName = "some product name"
)
type ProductServiceFake struct{}
type productServiceFake struct{}
func (f *ProductServiceFake) GetProductBySku(_ context.Context, sku entity.Sku) (*model.Product, error) {
func (f *productServiceFake) GetProductBySku(_ context.Context, sku entity.Sku) (*model.Product, error) {
if sku%2 == 0 {
return nil, errors.New("empty shelf")
}
@@ -33,6 +33,24 @@ func (f *ProductServiceFake) GetProductBySku(_ context.Context, sku entity.Sku)
}, nil
}
type lomsServiceFake struct{}
func (f *lomsServiceFake) StocksInfo(_ context.Context, sku entity.Sku) (uint32, error) {
if sku == 1111 {
return 0, errors.New("stock error")
}
return 100, nil
}
func (f *lomsServiceFake) OrderCreate(_ context.Context, cart *model.Cart) (int64, error) {
if cart.UserID == 1111 {
return 0, errors.New("order create error")
}
return 1234, nil
}
func TestCartService_AddItem(t *testing.T) {
t.Parallel()
@@ -49,9 +67,20 @@ func TestCartService_AddItem(t *testing.T) {
Count: 1,
}
testSKULomsFailing := entity.Sku(1111)
testItemLomsFailing := model.Item{
Product: &model.Product{
Name: validName,
Price: validPrice,
Sku: testSKULomsFailing,
},
Count: 1,
}
type fields struct {
repository Repository
productService ProductService
lomsService LomsService
}
type args struct {
ctx context.Context
@@ -72,7 +101,8 @@ func TestCartService_AddItem(t *testing.T) {
AddItemMock.
Expect(ctx, 1337, &testItem).
Return(nil),
productService: &ProductServiceFake{},
productService: &productServiceFake{},
lomsService: &lomsServiceFake{},
},
args: args{
ctx: ctx,
@@ -118,7 +148,8 @@ func TestCartService_AddItem(t *testing.T) {
name: "product service error",
fields: fields{
repository: nil,
productService: &ProductServiceFake{},
productService: &productServiceFake{},
lomsService: &lomsServiceFake{},
},
args: args{
ctx: ctx,
@@ -141,7 +172,8 @@ func TestCartService_AddItem(t *testing.T) {
AddItemMock.
Expect(ctx, 1337, &testItem).
Return(assert.AnError),
productService: &ProductServiceFake{},
productService: &productServiceFake{},
lomsService: &lomsServiceFake{},
},
args: args{
ctx: ctx,
@@ -150,6 +182,41 @@ func TestCartService_AddItem(t *testing.T) {
},
wantErr: require.Error,
},
{
name: "stocks acquiring error",
fields: fields{
repository: nil,
productService: &productServiceFake{},
lomsService: &lomsServiceFake{},
},
args: args{
ctx: ctx,
item: &testItemLomsFailing,
userID: 1337,
},
wantErr: require.Error,
},
{
name: "not enough stocks",
fields: fields{
repository: nil,
productService: &productServiceFake{},
lomsService: &lomsServiceFake{},
},
args: args{
ctx: ctx,
item: &model.Item{
Product: &model.Product{
Name: validName,
Price: validPrice,
Sku: testSKU,
},
Count: 10000,
},
userID: 1337,
},
wantErr: require.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -158,6 +225,7 @@ func TestCartService_AddItem(t *testing.T) {
s := &CartService{
repository: tt.fields.repository,
productService: tt.fields.productService,
lomsService: tt.fields.lomsService,
}
err := s.AddItem(tt.args.ctx, tt.args.userID, tt.args.item)
@@ -218,7 +286,7 @@ func TestCartService_GetItemsByUserID(t *testing.T) {
GetItemsByUserIDMock.
Expect(ctx, testUID).
Return(testEntityCart, nil),
productService: &ProductServiceFake{},
productService: &productServiceFake{},
},
args: args{
ctx: ctx,
@@ -238,7 +306,7 @@ func TestCartService_GetItemsByUserID(t *testing.T) {
Items: []entity.Sku{testSKU},
ItemCount: map[entity.Sku]uint32{testSKU: 2},
}, nil),
productService: &ProductServiceFake{},
productService: &productServiceFake{},
},
args: args{
ctx: ctx,
@@ -271,7 +339,7 @@ func TestCartService_GetItemsByUserID(t *testing.T) {
Items: []entity.Sku{testSKU, 1},
ItemCount: map[entity.Sku]uint32{testSKU: 1, 1: 1},
}, nil),
productService: &ProductServiceFake{},
productService: &productServiceFake{},
},
args: args{
ctx: ctx,
@@ -357,7 +425,7 @@ func TestCartService_GetItemsByUserID(t *testing.T) {
Items: []entity.Sku{2},
ItemCount: map[entity.Sku]uint32{2: 1},
}, nil),
productService: &ProductServiceFake{},
productService: &productServiceFake{},
},
args: args{
ctx: ctx,
@@ -377,7 +445,7 @@ func TestCartService_GetItemsByUserID(t *testing.T) {
Items: []entity.Sku{2},
ItemCount: map[entity.Sku]uint32{2: 1},
}, nil),
productService: &ProductServiceFake{},
productService: &productServiceFake{},
},
args: args{
ctx: ctx,
@@ -573,3 +641,118 @@ func TestCartService_DeleteItemsByUserID(t *testing.T) {
})
}
}
func TestCartService_CheckoutUserCart(t *testing.T) {
t.Parallel()
ctx := context.Background()
mc := minimock.NewController(t)
testSKU := entity.Sku(199)
testCart := entity.Cart{
Items: []entity.Sku{testSKU},
ItemCount: map[entity.Sku]uint32{testSKU: 1},
}
type fields struct {
repository Repository
productService ProductService
lomsService LomsService
}
type args struct {
ctx context.Context
userID entity.UID
}
tests := []struct {
name string
fields fields
args args
wantOrderID int64
wantErr require.ErrorAssertionFunc
}{
{
name: "success",
fields: fields{
repository: mock.NewRepositoryMock(mc).
GetItemsByUserIDMock.
Expect(ctx, 1337).
Return(testCart, nil).
DeleteItemsByUserIDMock.
Expect(ctx, 1337).
Return(nil),
productService: &productServiceFake{},
lomsService: &lomsServiceFake{},
},
args: args{ctx: ctx, userID: 1337},
wantOrderID: 1234,
wantErr: require.NoError,
},
{
name: "invalid user id",
fields: fields{},
args: args{ctx: ctx, userID: 0},
wantErr: require.Error,
},
{
name: "get cart error",
fields: fields{
repository: mock.NewRepositoryMock(mc).
GetItemsByUserIDMock.
Expect(ctx, 1337).
Return(entity.Cart{}, assert.AnError),
},
args: args{ctx: ctx, userID: 1337},
wantErr: require.Error,
},
{
name: "order create error",
fields: fields{
repository: mock.NewRepositoryMock(mc).
GetItemsByUserIDMock.
Expect(ctx, 1111).
Return(testCart, nil),
productService: &productServiceFake{},
lomsService: &lomsServiceFake{},
},
args: args{ctx: ctx, userID: 1111},
wantErr: require.Error,
},
{
name: "delete order error",
fields: fields{
repository: mock.NewRepositoryMock(mc).
GetItemsByUserIDMock.
Expect(ctx, 1337).
Return(testCart, nil).
DeleteItemsByUserIDMock.
Expect(ctx, 1337).
Return(assert.AnError),
productService: &productServiceFake{},
lomsService: &lomsServiceFake{},
},
args: args{ctx: ctx, userID: 1337},
wantErr: require.Error,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
svc := &CartService{
repository: tt.fields.repository,
productService: tt.fields.productService,
lomsService: tt.fields.lomsService,
}
orderID, err := svc.CheckoutUserCart(tt.args.ctx, tt.args.userID)
tt.wantErr(t, err)
if err == nil {
require.Equal(t, tt.wantOrderID, orderID)
}
})
}
}

View File

@@ -7,6 +7,7 @@ import (
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"net/http/httptest"
"testing"
@@ -16,13 +17,17 @@ import (
"github.com/ozontech/allure-go/pkg/framework/suite"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
"google.golang.org/grpc"
lomsService "route256/cart/internal/adapter/client/loms"
productsService "route256/cart/internal/adapter/client/products"
"route256/cart/internal/app"
"route256/cart/internal/domain/cart/repository"
cartService "route256/cart/internal/domain/cart/service"
"route256/cart/internal/domain/entity"
"route256/cart/internal/domain/model"
productsService "route256/cart/internal/domain/products/service"
"route256/cart/internal/domain/repository"
cartService "route256/cart/internal/domain/service"
pbLoms "route256/pkg/api/loms/v1"
)
const (
@@ -34,12 +39,27 @@ const (
testUID = entity.UID(1337)
)
type lomsServerMock struct {
pbLoms.UnimplementedLOMSServer
}
func (lomsServerMock) OrderCreate(_ context.Context, _ *pbLoms.OrderCreateRequest) (*pbLoms.OrderCreateResponse, error) {
return &pbLoms.OrderCreateResponse{OrderId: 1}, nil
}
func (lomsServerMock) StocksInfo(_ context.Context, _ *pbLoms.StocksInfoRequest) (*pbLoms.StocksInfoResponse, error) {
return &pbLoms.StocksInfoResponse{Count: 1000}, nil
}
type CartHandlerSuite struct {
suite.Suite
psContainer testcontainers.Container
server *httptest.Server
lomsSrv *grpc.Server
lomsConn *grpc.ClientConn
server *httptest.Server
cartSvc *cartService.CartService
}
@@ -72,6 +92,20 @@ func (s *CartHandlerSuite) BeforeAll(t provider.T) {
productURL = endpoint
})
t.WithNewStep("start loms grpc server", func(st provider.StepCtx) {
lis, err := net.Listen("tcp", "127.0.0.1:0")
st.Require().NoError(err)
s.lomsSrv = grpc.NewServer()
pbLoms.RegisterLOMSServer(s.lomsSrv, &lomsServerMock{})
go s.lomsSrv.Serve(lis)
conn, err := grpc.Dial(lis.Addr().String(), grpc.WithInsecure(), grpc.WithBlock())
st.Require().NoError(err)
s.lomsConn = conn
})
t.WithNewStep("init cart-service", func(sCtx provider.StepCtx) {
prodClient := productsService.NewProductService(
*http.DefaultClient,
@@ -79,8 +113,10 @@ func (s *CartHandlerSuite) BeforeAll(t provider.T) {
productURL,
)
lomsClient := pbLoms.NewLOMSClient(s.lomsConn)
repo := repository.NewInMemoryRepository(10)
s.cartSvc = cartService.NewCartService(repo, prodClient)
s.cartSvc = cartService.NewCartService(repo, prodClient, lomsService.NewLomsService(lomsClient))
appSrv := &app.App{}
@@ -89,8 +125,11 @@ func (s *CartHandlerSuite) BeforeAll(t provider.T) {
}
func (s *CartHandlerSuite) AfterAll(t provider.T) {
_ = s.psContainer.Terminate(context.Background())
s.server.Close()
s.lomsSrv.Stop()
s.lomsConn.Close()
_ = s.psContainer.Terminate(context.Background())
}
// DELETE /user/<user_id>/cart/<sku_id>