//go:build integration // +build integration package integration import ( "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" "time" "github.com/ozontech/allure-go/pkg/framework/provider" "github.com/ozontech/allure-go/pkg/framework/suite" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" "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" ) const ( productServiceImage = "gitlab-registry.ozon.dev/go/classroom-18/students/base/products:latest" testToken = "testToken" testSku = 1076963 testName = "Теория нравственных чувств | Смит Адам" testUID = entity.UID(1337) ) type CartHandlerSuite struct { suite.Suite psContainer testcontainers.Container server *httptest.Server cartSvc *cartService.CartService } func TestCartHandlerSuite(t *testing.T) { suite.RunSuite(t, new(CartHandlerSuite)) } func (s *CartHandlerSuite) BeforeAll(t provider.T) { ctx := context.Background() t.WithNewStep("start product-service container", func(sCtx provider.StepCtx) { req := testcontainers.ContainerRequest{ Image: productServiceImage, ExposedPorts: []string{"8082/tcp"}, WaitingFor: wait.ForHTTP("/docs").WithStartupTimeout(10 * time.Second), } container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) sCtx.Require().NoError(err, "create container") s.psContainer = container }) var productURL string t.WithNewStep("discover product-service URL", func(sCtx provider.StepCtx) { endpoint, err := s.psContainer.Endpoint(ctx, "") sCtx.Require().NoError(err) productURL = endpoint }) t.WithNewStep("init cart-service", func(sCtx provider.StepCtx) { prodClient := productsService.NewProductService( *http.DefaultClient, testToken, productURL, ) repo := repository.NewInMemoryRepository(10) s.cartSvc = cartService.NewCartService(repo, prodClient) appSrv := &app.App{} s.server = httptest.NewServer(appSrv.BootstrapHandlers(s.cartSvc)) }) } func (s *CartHandlerSuite) AfterAll(t provider.T) { _ = s.psContainer.Terminate(context.Background()) s.server.Close() } // DELETE /user//cart/ func (s *CartHandlerSuite) TestDeleteItemPositive(t provider.T) { ctx := context.Background() item := &model.Item{ Product: &model.Product{ Sku: testSku, }, Count: 1, } t.WithNewStep("fill cart in usecase", func(sCtx provider.StepCtx) { err := s.cartSvc.AddItem(context.Background(), testUID, item) sCtx.Require().NoError(err) cart, err := s.cartSvc.GetItemsByUserID(ctx, testUID) sCtx.Require().NoError(err) sCtx.Require().Len(cart.Items, 1, "check cart contains product") sCtx.Require().Equal(testUID, cart.UserID, "check user ID equals") sCtx.Require().Equal(item.Product.Sku, cart.Items[0].Product.Sku, "check product sku equals") }) t.WithNewStep("call DELETE handler", func(sCtx provider.StepCtx) { url := fmt.Sprintf("%s/user/%d/cart/%d", s.server.URL, testUID, testSku) req, err := http.NewRequest(http.MethodDelete, url, nil) sCtx.Require().NoError(err) resp, err := s.server.Client().Do(req) sCtx.Require().NoError(err) defer resp.Body.Close() sCtx.Require().Equal(http.StatusNoContent, resp.StatusCode, "delete handler unexpected status code") }) t.WithNewStep("check cart empty in usecase", func(sCtx provider.StepCtx) { _, err := s.cartSvc.GetItemsByUserID(ctx, testUID) sCtx.Require().Error(err) }) } // GET /user//cart func (s *CartHandlerSuite) TestGetCartPositive(t provider.T) { ctx := context.Background() item := &model.Item{ Product: &model.Product{ Sku: testSku, }, Count: 2, } t.WithNewStep("fill cart in usecase", func(sCtx provider.StepCtx) { err := s.cartSvc.AddItem(context.Background(), testUID, item) sCtx.Require().NoError(err) cart, err := s.cartSvc.GetItemsByUserID(ctx, testUID) sCtx.Require().NoError(err) sCtx.Require().Len(cart.Items, 1, "check cart contains product") sCtx.Require().Equal(testUID, cart.UserID, "check user ID equals") sCtx.Require().Equal(item.Product.Sku, cart.Items[0].Product.Sku, "check product sku equals") sCtx.Require().Equal(item.Count, cart.Items[0].Count, "check product count") }) t.WithNewStep("call GET handler", func(sCtx provider.StepCtx) { url := fmt.Sprintf("%s/user/%d/cart", s.server.URL, testUID) req, err := http.NewRequest(http.MethodGet, url, nil) sCtx.Require().NoError(err) resp, err := s.server.Client().Do(req) sCtx.Require().NoError(err) defer resp.Body.Close() sCtx.Require().Equal(http.StatusOK, resp.StatusCode, "check GET status code") sCtx.Require().Equal("application/json", resp.Header.Get("Content-Type"), "check GET content type") var body struct { Items []struct { Sku int64 `json:"sku"` Name string `json:"name"` Count uint32 `json:"count"` Price uint32 `json:"price"` } `json:"items"` TotalPrice uint32 `json:"total_price"` } sCtx.Require().NoError(json.NewDecoder(resp.Body).Decode(&body)) sCtx.Require().Len(body.Items, 1, "check items len") sCtx.Require().Equal(int64(testSku), body.Items[0].Sku, "check sku") sCtx.Require().Equal(uint32(2), body.Items[0].Count, "check item count") sCtx.Require().Greater(body.TotalPrice, uint32(0), "check price") }) }