Files
3ybactuk-marketplace-go-ser…/loms/tests/integration/loms_integration_test.go
2025-07-06 20:52:27 +00:00

211 lines
5.5 KiB
Go

//go:build integration
// +build integration
package integration
import (
"context"
"database/sql"
"fmt"
"net"
"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"
"go.uber.org/goleak"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"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/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"
)
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) {
defer goleak.VerifyNone(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)
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))
})
}