[hw-8] add: integration tests

This commit is contained in:
3ybacTuK
2025-07-29 01:10:16 +03:00
parent 61757d1a52
commit 815089ea3f
3 changed files with 242 additions and 5 deletions

View File

@@ -24,8 +24,9 @@ run-all:
.PHONY: .integration-test
integration-test:
ALLURE_OUTPUT_PATH=$(shell pwd)/allure-results ALLURE_OUTPUT_FOLDER=cart go test -v -tags=$(INTEGRATION_TAG) ./cart/tests/integration
CGO_ENABLED=0 ALLURE_OUTPUT_PATH=$(shell pwd)/allure-results ALLURE_OUTPUT_FOLDER=loms go test -v -tags=$(INTEGRATION_TAG) ./loms/tests/integration
# ALLURE_OUTPUT_PATH=$(shell pwd)/allure-results ALLURE_OUTPUT_FOLDER=cart go test -v -tags=$(INTEGRATION_TAG) ./cart/tests/integration
# CGO_ENABLED=0 ALLURE_OUTPUT_PATH=$(shell pwd)/allure-results ALLURE_OUTPUT_FOLDER=loms go test -v -tags=$(INTEGRATION_TAG) ./loms/tests/integration
CGO_ENABLED=0 ALLURE_OUTPUT_PATH=$(shell pwd)/allure-results ALLURE_OUTPUT_FOLDER=comments go test -v -tags=$(INTEGRATION_TAG) ./comments/tests/integration
bench:
go test -bench=BenchmarkInMemoryRepository -benchmem ./cart/internal/domain/cart/repository -benchtime 3s

View File

@@ -77,8 +77,6 @@ func (r *commentsRepo) InsertComment(ctx context.Context, comment *entity.Commen
r.idMx.Lock()
id := r.curID*1000 + shardID
r.curID++
r.idMx.Unlock()
req := &InsertCommentParams{
UserID: comment.UserID,
@@ -92,6 +90,9 @@ func (r *commentsRepo) InsertComment(ctx context.Context, comment *entity.Commen
return nil, err
}
r.curID++
r.idMx.Unlock()
return mapComment(c), nil
}
@@ -125,12 +126,17 @@ func (r *commentsRepo) ListCommentsByUser(ctx context.Context, userID int64) ([]
return nil, err2
}
alreadyMerged := make(map[int64]struct{}, len(l1)+len(l2))
merged := make([]*entity.Comment, 0, len(l1)+len(l2))
for _, com := range l1 {
merged = append(merged, mapComment(com))
alreadyMerged[com.Sku] = struct{}{}
}
for _, com := range l2 {
merged = append(merged, mapComment(com))
if _, ok := alreadyMerged[com.Sku]; !ok {
merged = append(merged, mapComment(com))
alreadyMerged[com.Sku] = struct{}{}
}
}
return merged, nil

View File

@@ -0,0 +1,230 @@
//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"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
repo "route256/comments/infra/repository/sqlc"
"route256/comments/internal/app/server"
"route256/comments/internal/domain/service"
pb "route256/pkg/api/comments/v1"
)
const (
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": "comments_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/comments_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 Suite struct {
suite.Suite
grpcSrv *grpc.Server
grpcConn *grpc.ClientConn
client pb.CommentsClient
cleanup func()
}
func TestLomsIntegrationSuite(t *testing.T) {
suite.RunSuite(t, new(Suite))
}
func (s *Suite) 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
repo := repo.NewCommentsRepository(pool, pool)
svc := service.NewCommentsService(repo, 1)
commentsServer := server.NewServer(svc)
lis, err := net.Listen("tcp", "127.0.0.1:0")
sCtx.Require().NoError(err)
s.grpcSrv = grpc.NewServer()
pb.RegisterCommentsServer(s.grpcSrv, commentsServer)
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.client = pb.NewCommentsClient(conn)
})
}
func (s *Suite) AfterAll(t provider.T) {
s.grpcSrv.Stop()
_ = s.grpcConn.Close()
s.cleanup()
}
func (s *Suite) TestCommentListByOneUser_Success(t provider.T) {
t.Title("Успешное получение списка комментариев, которые оставил один юзер по SKU")
ctx := context.Background()
const (
userID = int64(40)
sku = int64(40)
commentText1 = "тестовый комментарий 1"
commentText2 = "тестовый комментарий 2"
)
var (
commentIDs []int64
commentID int64
)
t.WithNewParameters("userID", userID, "sku", sku)
t.WithNewStep("Создание комментария 1", func(sCtx provider.StepCtx) {
req := &pb.CreateCommentRequest{
UserId: userID,
Sku: sku,
Comment: commentText1,
}
resp, err := s.client.CommentAdd(ctx, req)
sCtx.Require().NoError(err)
commentID = resp.Id
commentIDs = append(commentIDs, commentID)
})
t.WithNewParameters("commentID1", commentID)
t.WithNewStep("Получение первого созданного комментария", func(sCtx provider.StepCtx) {
req := &pb.ListByUserRequest{
UserId: userID,
}
resp, err := s.client.CommentListByUser(ctx, req)
sCtx.Require().NoError(err)
sCtx.Require().Len(resp.Comments, 1)
sCtx.Require().NotNil(resp.Comments[0])
resultComment := resp.Comments[0]
sCtx.Require().Equal(commentID, resultComment.Id)
sCtx.Require().Equal(sku, resultComment.Sku)
sCtx.Require().Equal(commentText1, resultComment.Text)
})
t.WithNewStep("Создание комментария 2", func(sCtx provider.StepCtx) {
req := &pb.CreateCommentRequest{
UserId: userID,
Sku: sku,
Comment: commentText2,
}
resp, err := s.client.CommentAdd(ctx, req)
sCtx.Require().NoError(err)
commentID = resp.Id
commentIDs = append(commentIDs, commentID)
})
t.WithNewParameters("commentID2", commentID)
t.WithNewStep("Получение созданных комментариев в обратном хронологическом порядке", func(sCtx provider.StepCtx) {
req := &pb.ListByUserRequest{
UserId: userID,
}
resp, err := s.client.CommentListByUser(ctx, req)
sCtx.Require().NoError(err)
sCtx.Require().Len(resp.Comments, 2)
sCtx.Require().NotNil(resp.Comments[0])
sCtx.Require().NotNil(resp.Comments[1])
// commentIDs содержит в себе id комментариев в прямом хронологическом порядке
sCtx.Require().Equal(commentIDs[1], resp.Comments[0].Id)
sCtx.Require().Equal(sku, resp.Comments[0].Sku)
sCtx.Require().Equal(commentText2, resp.Comments[0].Text)
sCtx.Require().Equal(commentIDs[0], resp.Comments[1].Id)
sCtx.Require().Equal(sku, resp.Comments[1].Sku)
sCtx.Require().Equal(commentText1, resp.Comments[1].Text)
})
}