Files
3ybactuk-marketplace-go-ser…/loms/tests/integration/loms_integration_test.go
Никита Шубин 4396bebe80 [hw-7] add metrics, tracing
2025-07-26 14:15:40 +00:00

337 lines
8.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//go:build integration
// +build integration
package integration
import (
"context"
"database/sql"
"fmt"
"net"
"sync"
"testing"
"time"
"github.com/jackc/pgx/v5/pgxpool"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
"github.com/pressly/goose/v3"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/status"
"route256/loms/internal/app/server"
"route256/loms/internal/domain/entity"
ordersRepository "route256/loms/internal/domain/repository/orders/sqlc"
stocksRepository "route256/loms/internal/domain/repository/stocks/sqlc"
lomsService "route256/loms/internal/domain/service"
"route256/loms/internal/infra/db/postgres"
pb "route256/pkg/api/loms/v1"
)
const (
// "total_count": 100,
// "reserved": 35
testSKU = entity.Sku(1076963)
testUID = entity.ID(1337)
testCount = uint32(2)
migrationsDir = "../../db/migrations"
)
// TODO: drop, use actual kafka for tests.
type mockKafkaProducer struct{}
func (kp mockKafkaProducer) Send(_ context.Context, id entity.ID, status string) error {
return nil
}
func startPostgres(ctx context.Context, migrationsDir string) (*pgxpool.Pool, func(), error) {
req := testcontainers.ContainerRequest{
Image: "gitlab-registry.ozon.dev/go/classroom-18/students/base/postgres:16",
Env: map[string]string{
"POSTGRESQL_USERNAME": "user",
"POSTGRESQL_PASSWORD": "postgres",
"POSTGRESQL_DATABASE": "loms_test",
},
ExposedPorts: []string{"5432/tcp"},
WaitingFor: wait.ForListeningPort("5432/tcp").WithStartupTimeout(30 * time.Second),
}
container, err := testcontainers.GenericContainer(ctx,
testcontainers.GenericContainerRequest{ContainerRequest: req, Started: true})
if err != nil {
return nil, nil, err
}
endpoint, err := container.PortEndpoint(ctx, "5432", "")
if err != nil {
container.Terminate(ctx)
return nil, nil, err
}
dsn := fmt.Sprintf("postgresql://user:postgres@%s/loms_test?sslmode=disable", endpoint)
var pool *pgxpool.Pool
for i := 0; i < 30; i++ {
pool, err = pgxpool.New(ctx, dsn)
if err == nil && pool.Ping(ctx) == nil {
break
}
time.Sleep(1 * time.Second)
}
if err != nil {
container.Terminate(ctx)
return nil, nil, err
}
std, err := sql.Open("pgx", dsn)
if err != nil {
container.Terminate(ctx)
return nil, nil, err
}
if err := goose.Up(std, migrationsDir); err != nil {
container.Terminate(ctx)
return nil, nil, err
}
std.Close()
cleanup := func() {
pool.Close()
_ = container.Terminate(context.Background())
}
return pool, cleanup, nil
}
type LomsIntegrationSuite struct {
suite.Suite
grpcSrv *grpc.Server
grpcConn *grpc.ClientConn
lomsClient pb.LOMSClient
cleanup func()
}
func TestLomsIntegrationSuite(t *testing.T) {
suite.RunSuite(t, new(LomsIntegrationSuite))
}
func (s *LomsIntegrationSuite) BeforeAll(t provider.T) {
ctx := context.Background()
t.WithNewStep("init cart-service", func(sCtx provider.StepCtx) {
pool, cleanup, err := startPostgres(ctx, migrationsDir)
sCtx.Require().NoError(err, "failed postgres setup")
s.cleanup = cleanup
orderRepo := ordersRepository.NewOrderRepository(pool)
stockRepo := stocksRepository.NewStockRepository(pool, pool)
txManager := postgres.NewTxManager(pool, pool)
svc := lomsService.NewLomsService(orderRepo, stockRepo, txManager, &mockKafkaProducer{})
lomsServer := server.NewServer(svc)
lis, err := net.Listen("tcp", "127.0.0.1:0")
sCtx.Require().NoError(err)
s.grpcSrv = grpc.NewServer()
pb.RegisterLOMSServer(s.grpcSrv, lomsServer)
go func() { _ = s.grpcSrv.Serve(lis) }()
time.Sleep(50 * time.Millisecond)
conn, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
sCtx.Require().NoError(err)
s.grpcConn = conn
s.lomsClient = pb.NewLOMSClient(conn)
})
}
func (s *LomsIntegrationSuite) AfterAll(t provider.T) {
s.grpcSrv.Stop()
_ = s.grpcConn.Close()
s.cleanup()
}
func (s *LomsIntegrationSuite) TestOrderProcessPositive(t provider.T) {
ctx := context.Background()
var orderID int64
t.WithNewStep("create order", func(sCtx provider.StepCtx) {
req := &pb.OrderCreateRequest{
UserId: int64(testUID),
Items: []*pb.OrderItem{{
Sku: int64(testSKU),
Count: testCount,
}},
}
resp, err := s.lomsClient.OrderCreate(ctx, req)
sCtx.Require().NoError(err)
sCtx.Require().Greater(resp.OrderId, int64(0))
orderID = resp.OrderId
})
t.WithNewStep("verify order info (NEW)", func(sCtx provider.StepCtx) {
resp, err := s.lomsClient.OrderInfo(ctx, &pb.OrderInfoRequest{OrderId: orderID})
sCtx.Require().NoError(err)
sCtx.Require().Equal("awaiting payment", resp.Status)
sCtx.Require().Equal(int64(testUID), resp.UserId)
sCtx.Require().Len(resp.Items, 1)
sCtx.Require().Equal(int64(testSKU), resp.Items[0].Sku)
sCtx.Require().Equal(testCount, resp.Items[0].Count)
})
t.WithNewStep("pay order", func(sCtx provider.StepCtx) {
_, err := s.lomsClient.OrderPay(ctx, &pb.OrderPayRequest{OrderId: orderID})
sCtx.Require().NoError(err)
})
t.WithNewStep("verify order info (PAYED)", func(sCtx provider.StepCtx) {
resp, err := s.lomsClient.OrderInfo(ctx, &pb.OrderInfoRequest{OrderId: orderID})
sCtx.Require().NoError(err)
sCtx.Require().Equal("payed", resp.Status)
})
}
func (s *LomsIntegrationSuite) TestStocksInfoPositive(t provider.T) {
ctx := context.Background()
t.WithNewStep("call StocksInfo", func(sCtx provider.StepCtx) {
resp, err := s.lomsClient.StocksInfo(ctx, &pb.StocksInfoRequest{Sku: int64(testSKU)})
sCtx.Require().NoError(err)
sCtx.Require().Greater(resp.Count, uint32(0))
})
}
func (s *LomsIntegrationSuite) TestOrderCreate_SuccessAsync(t provider.T) {
t.Title("Успешное создание заказов (async)")
const (
sku = 1625903
count = 1
ordersCount = 100
)
var (
userIDs = []int64{42, 43, 44}
orders = make([]struct {
userID int64
orderID int64
}, ordersCount)
initStocksCount uint64
ctx = context.Background()
)
t.WithNewStep("Получение изначальных стоков", func(sCtx provider.StepCtx) {
stockCount, err := s.lomsClient.StocksInfo(ctx, &pb.StocksInfoRequest{Sku: sku})
sCtx.Require().NoError(err)
initStocksCount = uint64(stockCount.GetCount())
})
t.WithNewStep("Создание заказов", func(sCtx provider.StepCtx) {
var wg sync.WaitGroup
for i := range ordersCount {
wg.Add(1)
go func() {
defer wg.Done()
userID := userIDs[i%len(userIDs)]
req := &pb.OrderCreateRequest{
UserId: userID,
Items: []*pb.OrderItem{
{
Sku: sku,
Count: count,
},
},
}
orderCreateResp, err := s.lomsClient.OrderCreate(ctx, req)
sCtx.Require().NoError(err)
sCtx.Require().Greater(orderCreateResp.OrderId, int64(0))
orders[i].userID = userID
orders[i].orderID = orderCreateResp.OrderId
}()
}
wg.Wait()
})
t.WithNewStep("Проверка заказов", func(sCtx provider.StepCtx) {
for _, order := range orders {
res, err := s.lomsClient.OrderInfo(ctx, &pb.OrderInfoRequest{
OrderId: order.orderID,
})
sCtx.Require().NoError(err)
expected := entity.Order{
Status: "awaiting payment",
UserID: entity.ID(order.userID),
Items: []entity.OrderItem{
{
ID: sku,
Count: count,
},
},
}
sCtx.Require().Equal(expected.Status, res.Status, "Не совпадает статус заказа")
sCtx.Require().Equal(expected.UserID, entity.ID(res.UserId), "Не совпадает пользователь заказа")
sCtx.Require().Equal(len(expected.Items), len(res.Items), "Не совпадает количество товаров в заказе")
}
})
t.WithNewStep("Проверка стоков", func(sCtx provider.StepCtx) {
stocksCount, err := s.lomsClient.StocksInfo(ctx, &pb.StocksInfoRequest{
Sku: sku,
})
sCtx.Require().NoError(err)
sCtx.Require().Equal(uint32(initStocksCount-ordersCount*count), stocksCount.Count)
})
}
func (s *LomsIntegrationSuite) TestOrderCreate_NoStockInfo(t provider.T) {
t.Title("Неуспешное создание заказ из-за отсутствия информации о стоках товара")
const (
userID = 42
sku = 404
)
ctx := context.Background()
t.WithNewStep("Создание заказа", func(sCtx provider.StepCtx) {
req := &pb.OrderCreateRequest{
UserId: userID,
Items: []*pb.OrderItem{
{
Sku: sku,
Count: 1,
},
},
}
_, err := s.lomsClient.OrderCreate(ctx, req)
e, ok := status.FromError(err)
sCtx.Require().True(ok)
sCtx.Require().Equal(codes.FailedPrecondition, e.Code(), "expect 400 (failed precondition) status code, got: %s", err.Error())
})
}