This commit is contained in:
@@ -81,7 +81,7 @@ func main() {
|
||||
|
||||
boot.Bootstrap(ctx)
|
||||
|
||||
log.Println("gRPC server started via rk-boot on port 9091")
|
||||
log.Println("gRPC server started via rk-boot")
|
||||
|
||||
boot.WaitForShutdownSig(ctx)
|
||||
|
||||
|
||||
@@ -16,9 +16,5 @@ security:
|
||||
jwt_secret: ${JWT_SECRET:xM8KhJVkk28cIJeBo0306O2e6Ifni6tNVlcCMxDFAEc=}
|
||||
crypto_secret: ${CRYPTO_SECRET:xM8KhJVkk28cIJeBo0306O2e6Ifni6tNVlcCMxDFAEc=}
|
||||
|
||||
grpc:
|
||||
port: ${GRPC_PORT:9091}
|
||||
max_connections: ${GRPC_MAX_CONNS:100}
|
||||
|
||||
logging:
|
||||
level: ${LOG_LEVEL:info}
|
||||
|
||||
@@ -12,7 +12,6 @@ type Config struct {
|
||||
Database DatabaseConfig `yaml:"database"`
|
||||
AI AIConfig `yaml:"ai"`
|
||||
Security SecurityConfig `yaml:"security"`
|
||||
GRPC GRPCConfig `yaml:"grpc"`
|
||||
Logging LoggingConfig `yaml:"logging"`
|
||||
}
|
||||
|
||||
@@ -37,11 +36,6 @@ type SecurityConfig struct {
|
||||
CryptoSecret string `yaml:"crypto_secret"`
|
||||
}
|
||||
|
||||
type GRPCConfig struct {
|
||||
Port int `yaml:"port"`
|
||||
MaxConnections int `yaml:"max_connections"`
|
||||
}
|
||||
|
||||
type LoggingConfig struct {
|
||||
Level string `yaml:"level"`
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (h *RequestHandler) GetMailingListByID(ctx context.Context, req *pb.GetMail
|
||||
return nil, errors.ToGRPCError(err, h.logger, "RequestService.GetMailingListByID")
|
||||
}
|
||||
|
||||
detail, err := h.requestService.GetMailingListByID(ctx, requestID)
|
||||
detail, err := h.requestService.GetMailingListByID(ctx, requestID, int(req.UserId))
|
||||
if err != nil {
|
||||
return nil, errors.ToGRPCError(err, h.logger, "RequestService.GetMailingListByID")
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func NewHandlers(pool *pgxpool.Pool, jwtSecret, cryptoSecret, openAIKey, perplex
|
||||
userService := service.NewUserService(userRepo, requestRepo, cryptoSecret)
|
||||
inviteService := service.NewInviteService(inviteRepo, userRepo, txManager)
|
||||
requestService := service.NewRequestService(requestRepo, supplierRepo, tokenUsageRepo, userRepo, openAIClient, perplexityClient, txManager)
|
||||
supplierService := service.NewSupplierService(supplierRepo)
|
||||
supplierService := service.NewSupplierService(supplierRepo, requestRepo)
|
||||
|
||||
return &AuthHandler{authService: authService, logger: logger},
|
||||
&UserHandler{userService: userService, logger: logger},
|
||||
|
||||
@@ -14,7 +14,7 @@ func (h *SupplierHandler) ExportExcel(ctx context.Context, req *pb.ExportExcelRe
|
||||
return nil, errors.ToGRPCError(err, h.logger, "SupplierService.ExportExcel")
|
||||
}
|
||||
|
||||
fileData, err := h.supplierService.ExportExcel(ctx, requestID)
|
||||
fileData, err := h.supplierService.ExportExcel(ctx, requestID, int(req.UserId))
|
||||
if err != nil {
|
||||
return nil, errors.ToGRPCError(err, h.logger, "SupplierService.ExportExcel")
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ type RequestRepository interface {
|
||||
GetByID(ctx context.Context, id uuid.UUID) (*model.Request, error)
|
||||
GetDetailByID(ctx context.Context, id uuid.UUID) (*model.RequestDetail, error)
|
||||
GetUserStatistics(ctx context.Context, userID int) (requestsCount, suppliersCount, createdTZ int, err error)
|
||||
CheckOwnership(ctx context.Context, requestID uuid.UUID, userID int) (bool, error)
|
||||
}
|
||||
|
||||
type SupplierRepository interface {
|
||||
|
||||
@@ -214,3 +214,24 @@ func (r *requestRepository) GetUserStatistics(ctx context.Context, userID int) (
|
||||
|
||||
return requestsCount, suppliersCount, createdTZ, nil
|
||||
}
|
||||
|
||||
func (r *requestRepository) CheckOwnership(ctx context.Context, requestID uuid.UUID, userID int) (bool, error) {
|
||||
query := r.qb.Select("1").From("requests_for_suppliers").
|
||||
Where(sq.Eq{"id": requestID, "user_id": userID})
|
||||
|
||||
sqlQuery, args, err := query.ToSql()
|
||||
if err != nil {
|
||||
return false, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
|
||||
}
|
||||
|
||||
var exists int
|
||||
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(&exists)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, errs.NewInternalError(errs.DatabaseError, "failed to check ownership", err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ type RequestService interface {
|
||||
CreateTZ(ctx context.Context, userID int, requestTxt string) (uuid.UUID, string, error)
|
||||
ApproveTZ(ctx context.Context, requestID uuid.UUID, tzText string, userID int) ([]*model.Supplier, error)
|
||||
GetMailingList(ctx context.Context, userID int) ([]*model.Request, error)
|
||||
GetMailingListByID(ctx context.Context, requestID uuid.UUID) (*model.RequestDetail, error)
|
||||
GetMailingListByID(ctx context.Context, requestID uuid.UUID, userID int) (*model.RequestDetail, error)
|
||||
}
|
||||
|
||||
type SupplierService interface {
|
||||
ExportExcel(ctx context.Context, requestID uuid.UUID) ([]byte, error)
|
||||
ExportExcel(ctx context.Context, requestID uuid.UUID, userID int) ([]byte, error)
|
||||
}
|
||||
|
||||
@@ -107,13 +107,20 @@ func (s *requestService) CreateTZ(ctx context.Context, userID int, requestTxt st
|
||||
}
|
||||
|
||||
func (s *requestService) ApproveTZ(ctx context.Context, requestID uuid.UUID, tzText string, userID int) ([]*model.Supplier, error) {
|
||||
if err := s.requestRepo.UpdateFinalTZ(ctx, requestID, tzText); err != nil {
|
||||
isOwner, err := s.requestRepo.CheckOwnership(ctx, requestID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isOwner {
|
||||
return nil, errors.NewBusinessError(errors.PermissionDenied, "access denied to this request")
|
||||
}
|
||||
|
||||
if err = s.requestRepo.UpdateFinalTZ(ctx, requestID, tzText); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var suppliers []*model.Supplier
|
||||
var promptTokens, responseTokens int
|
||||
var err error
|
||||
|
||||
for attempt := 0; attempt < 3; attempt++ {
|
||||
suppliers, promptTokens, responseTokens, err = s.perplexity.FindSuppliers(tzText)
|
||||
@@ -169,6 +176,14 @@ func (s *requestService) GetMailingList(ctx context.Context, userID int) ([]*mod
|
||||
return s.requestRepo.GetByUserID(ctx, userID)
|
||||
}
|
||||
|
||||
func (s *requestService) GetMailingListByID(ctx context.Context, requestID uuid.UUID) (*model.RequestDetail, error) {
|
||||
func (s *requestService) GetMailingListByID(ctx context.Context, requestID uuid.UUID, userID int) (*model.RequestDetail, error) {
|
||||
isOwner, err := s.requestRepo.CheckOwnership(ctx, requestID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isOwner {
|
||||
return nil, errors.NewBusinessError(errors.PermissionDenied, "access denied to this request")
|
||||
}
|
||||
|
||||
return s.requestRepo.GetDetailByID(ctx, requestID)
|
||||
}
|
||||
|
||||
@@ -5,21 +5,32 @@ import (
|
||||
"fmt"
|
||||
|
||||
"git.techease.ru/Smart-search/smart-search-back/internal/repository"
|
||||
"git.techease.ru/Smart-search/smart-search-back/pkg/errors"
|
||||
"github.com/google/uuid"
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
type supplierService struct {
|
||||
supplierRepo repository.SupplierRepository
|
||||
requestRepo repository.RequestRepository
|
||||
}
|
||||
|
||||
func NewSupplierService(supplierRepo repository.SupplierRepository) SupplierService {
|
||||
func NewSupplierService(supplierRepo repository.SupplierRepository, requestRepo repository.RequestRepository) SupplierService {
|
||||
return &supplierService{
|
||||
supplierRepo: supplierRepo,
|
||||
requestRepo: requestRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *supplierService) ExportExcel(ctx context.Context, requestID uuid.UUID) ([]byte, error) {
|
||||
func (s *supplierService) ExportExcel(ctx context.Context, requestID uuid.UUID, userID int) ([]byte, error) {
|
||||
isOwner, err := s.requestRepo.CheckOwnership(ctx, requestID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isOwner {
|
||||
return nil, errors.NewBusinessError(errors.PermissionDenied, "access denied to this request")
|
||||
}
|
||||
|
||||
suppliers, err := s.supplierRepo.GetByRequestID(ctx, requestID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -11,6 +11,7 @@ const (
|
||||
InsufficientBalance = "INSUFFICIENT_BALANCE"
|
||||
UserNotFound = "USER_NOT_FOUND"
|
||||
RequestNotFound = "REQUEST_NOT_FOUND"
|
||||
PermissionDenied = "PERMISSION_DENIED"
|
||||
|
||||
DatabaseError = "DATABASE_ERROR"
|
||||
EncryptionError = "ENCRYPTION_ERROR"
|
||||
|
||||
@@ -91,6 +91,8 @@ func ToGRPCError(err error, zapLogger *zap.Logger, method string) error {
|
||||
switch appErr.Code {
|
||||
case AuthInvalidCredentials, AuthMissing, AuthInvalidToken, RefreshInvalid:
|
||||
return status.Error(codes.Unauthenticated, appErr.Message)
|
||||
case PermissionDenied:
|
||||
return status.Error(codes.PermissionDenied, appErr.Message)
|
||||
case InviteLimitReached:
|
||||
return status.Error(codes.ResourceExhausted, appErr.Message)
|
||||
case InsufficientBalance, InviteInvalidOrExpired:
|
||||
|
||||
@@ -283,3 +283,140 @@ func (s *IntegrationSuite) TestEdgeCase_LoginWithWrongPassword() {
|
||||
s.True(ok)
|
||||
s.Equal(codes.Unauthenticated, st.Code())
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) TestEdgeCase_ApproveTZTwice() {
|
||||
ctx := context.Background()
|
||||
|
||||
loginReq := &authpb.LoginRequest{
|
||||
Email: "test@example.com",
|
||||
Password: "testpassword",
|
||||
Ip: "127.0.0.1",
|
||||
UserAgent: "integration-test",
|
||||
}
|
||||
|
||||
loginResp, err := s.authClient.Login(ctx, loginReq)
|
||||
s.NoError(err)
|
||||
|
||||
validateReq := &authpb.ValidateRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
validateResp, err := s.authClient.Validate(ctx, validateReq)
|
||||
s.NoError(err)
|
||||
userID := validateResp.UserId
|
||||
|
||||
createReq := &requestpb.CreateTZRequest{
|
||||
UserId: userID,
|
||||
RequestTxt: "Тест двойного approve",
|
||||
}
|
||||
|
||||
createResp, err := s.requestClient.CreateTZ(ctx, createReq)
|
||||
s.NoError(err)
|
||||
requestID := createResp.RequestId
|
||||
|
||||
approveReq1 := &requestpb.ApproveTZRequest{
|
||||
RequestId: requestID,
|
||||
FinalTz: "Первое утверждение",
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
approveResp1, err := s.requestClient.ApproveTZ(ctx, approveReq1)
|
||||
s.NoError(err)
|
||||
s.True(approveResp1.Success)
|
||||
|
||||
approveReq2 := &requestpb.ApproveTZRequest{
|
||||
RequestId: requestID,
|
||||
FinalTz: "Второе утверждение",
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
approveResp2, err := s.requestClient.ApproveTZ(ctx, approveReq2)
|
||||
s.NoError(err)
|
||||
s.True(approveResp2.Success)
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) TestEdgeCase_CreateTZWithVeryLongText() {
|
||||
ctx := context.Background()
|
||||
|
||||
loginReq := &authpb.LoginRequest{
|
||||
Email: "test@example.com",
|
||||
Password: "testpassword",
|
||||
Ip: "127.0.0.1",
|
||||
UserAgent: "integration-test",
|
||||
}
|
||||
|
||||
loginResp, err := s.authClient.Login(ctx, loginReq)
|
||||
s.NoError(err)
|
||||
|
||||
validateReq := &authpb.ValidateRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
validateResp, err := s.authClient.Validate(ctx, validateReq)
|
||||
s.NoError(err)
|
||||
|
||||
_, err = s.pool.Exec(ctx, "UPDATE users SET balance = 10000 WHERE id = $1", validateResp.UserId)
|
||||
s.NoError(err)
|
||||
|
||||
longText := "Нужны поставщики. "
|
||||
for i := 0; i < 500; i++ {
|
||||
longText += "Дополнительные требования к качеству и срокам поставки материалов. "
|
||||
}
|
||||
|
||||
req := &requestpb.CreateTZRequest{
|
||||
UserId: validateResp.UserId,
|
||||
RequestTxt: longText,
|
||||
}
|
||||
|
||||
resp, err := s.requestClient.CreateTZ(ctx, req)
|
||||
s.NoError(err)
|
||||
s.NotNil(resp)
|
||||
s.NotEmpty(resp.RequestId)
|
||||
s.NotEmpty(resp.TzText)
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) TestEdgeCase_ApproveTZWithVeryLongFinalTZ() {
|
||||
ctx := context.Background()
|
||||
|
||||
loginReq := &authpb.LoginRequest{
|
||||
Email: "test@example.com",
|
||||
Password: "testpassword",
|
||||
Ip: "127.0.0.1",
|
||||
UserAgent: "integration-test",
|
||||
}
|
||||
|
||||
loginResp, err := s.authClient.Login(ctx, loginReq)
|
||||
s.NoError(err)
|
||||
|
||||
validateReq := &authpb.ValidateRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
validateResp, err := s.authClient.Validate(ctx, validateReq)
|
||||
s.NoError(err)
|
||||
userID := validateResp.UserId
|
||||
|
||||
createReq := &requestpb.CreateTZRequest{
|
||||
UserId: userID,
|
||||
RequestTxt: "Тест длинного ТЗ",
|
||||
}
|
||||
|
||||
createResp, err := s.requestClient.CreateTZ(ctx, createReq)
|
||||
s.NoError(err)
|
||||
requestID := createResp.RequestId
|
||||
|
||||
longFinalTZ := "ТЕХНИЧЕСКОЕ ЗАДАНИЕ\n\n"
|
||||
for i := 0; i < 500; i++ {
|
||||
longFinalTZ += "Пункт требований с детальным описанием спецификации и условий поставки. "
|
||||
}
|
||||
|
||||
approveReq := &requestpb.ApproveTZRequest{
|
||||
RequestId: requestID,
|
||||
FinalTz: longFinalTZ,
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
approveResp, err := s.requestClient.ApproveTZ(ctx, approveReq)
|
||||
s.NoError(err)
|
||||
s.True(approveResp.Success)
|
||||
}
|
||||
|
||||
@@ -184,6 +184,83 @@ func (s *IntegrationSuite) TestFullFlow_InviteCodeLifecycle() {
|
||||
s.True(logoutResp.Success)
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) TestFullFlow_CreateTZ_ApproveTZ_GetMailingListByID_ExportExcel() {
|
||||
ctx := context.Background()
|
||||
|
||||
loginReq := &authpb.LoginRequest{
|
||||
Email: "test@example.com",
|
||||
Password: "testpassword",
|
||||
Ip: "127.0.0.1",
|
||||
UserAgent: "integration-test",
|
||||
}
|
||||
|
||||
loginResp, err := s.authClient.Login(ctx, loginReq)
|
||||
s.NoError(err)
|
||||
s.NotEmpty(loginResp.AccessToken)
|
||||
|
||||
validateReq := &authpb.ValidateRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
validateResp, err := s.authClient.Validate(ctx, validateReq)
|
||||
s.NoError(err)
|
||||
s.True(validateResp.Valid)
|
||||
userID := validateResp.UserId
|
||||
|
||||
createTZReq := &requestpb.CreateTZRequest{
|
||||
UserId: userID,
|
||||
RequestTxt: "Нужны поставщики офисной мебели: столы 20 шт, стулья 50 шт",
|
||||
}
|
||||
|
||||
createTZResp, err := s.requestClient.CreateTZ(ctx, createTZReq)
|
||||
s.NoError(err)
|
||||
s.NotEmpty(createTZResp.RequestId)
|
||||
s.NotEmpty(createTZResp.TzText)
|
||||
requestID := createTZResp.RequestId
|
||||
|
||||
approveTZReq := &requestpb.ApproveTZRequest{
|
||||
RequestId: requestID,
|
||||
FinalTz: createTZResp.TzText,
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
approveTZResp, err := s.requestClient.ApproveTZ(ctx, approveTZReq)
|
||||
s.NoError(err)
|
||||
s.True(approveTZResp.Success)
|
||||
|
||||
getMailingListByIDReq := &requestpb.GetMailingListByIDRequest{
|
||||
RequestId: requestID,
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
mailingListByIDResp, err := s.requestClient.GetMailingListByID(ctx, getMailingListByIDReq)
|
||||
s.NoError(err)
|
||||
s.NotNil(mailingListByIDResp.Item)
|
||||
s.Equal(requestID, mailingListByIDResp.Item.RequestId)
|
||||
s.Greater(mailingListByIDResp.Item.SuppliersFound, int32(0))
|
||||
|
||||
exportExcelReq := &supplierpb.ExportExcelRequest{
|
||||
RequestId: requestID,
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
exportExcelResp, err := s.supplierClient.ExportExcel(ctx, exportExcelReq)
|
||||
s.NoError(err)
|
||||
s.NotNil(exportExcelResp)
|
||||
s.NotEmpty(exportExcelResp.FileName)
|
||||
s.NotEmpty(exportExcelResp.FileData)
|
||||
s.Equal("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", exportExcelResp.MimeType)
|
||||
s.Greater(len(exportExcelResp.FileData), 0)
|
||||
|
||||
logoutReq := &authpb.LogoutRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
logoutResp, err := s.authClient.Logout(ctx, logoutReq)
|
||||
s.NoError(err)
|
||||
s.True(logoutResp.Success)
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) TestFullFlow_MultipleRefresh() {
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
@@ -233,3 +233,45 @@ func (s *IntegrationSuite) TearDownTest() {
|
||||
_, _ = s.pool.Exec(s.ctx, "DELETE FROM suppliers")
|
||||
_, _ = s.pool.Exec(s.ctx, "DELETE FROM requests_for_suppliers")
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) createSecondTestUser() (email string, password string, userID int64) {
|
||||
email = "second_user@example.com"
|
||||
password = "secondpassword"
|
||||
|
||||
cryptoHelper := crypto.NewCrypto(testCryptoSecret)
|
||||
|
||||
encryptedEmail, err := cryptoHelper.Encrypt(email)
|
||||
s.Require().NoError(err)
|
||||
|
||||
encryptedPhone, err := cryptoHelper.Encrypt("+9876543210")
|
||||
s.Require().NoError(err)
|
||||
|
||||
encryptedUserName, err := cryptoHelper.Encrypt("Second User")
|
||||
s.Require().NoError(err)
|
||||
|
||||
emailHash := cryptoHelper.EmailHash(email)
|
||||
passwordHash := crypto.PasswordHash(password)
|
||||
|
||||
query := `
|
||||
INSERT INTO users (email, email_hash, password_hash, phone, user_name, company_name, balance, payment_status, invites_issued, invites_limit)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
ON CONFLICT (email_hash) DO UPDATE SET balance = $7
|
||||
RETURNING id
|
||||
`
|
||||
|
||||
err = s.pool.QueryRow(s.ctx, query,
|
||||
encryptedEmail,
|
||||
emailHash,
|
||||
passwordHash,
|
||||
encryptedPhone,
|
||||
encryptedUserName,
|
||||
"Second Company",
|
||||
1000.0,
|
||||
"active",
|
||||
0,
|
||||
10,
|
||||
).Scan(&userID)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return email, password, userID
|
||||
}
|
||||
|
||||
214
tests/ownership_test.go
Normal file
214
tests/ownership_test.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
authpb "git.techease.ru/Smart-search/smart-search-back/pkg/pb/auth"
|
||||
requestpb "git.techease.ru/Smart-search/smart-search-back/pkg/pb/request"
|
||||
supplierpb "git.techease.ru/Smart-search/smart-search-back/pkg/pb/supplier"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func (s *IntegrationSuite) TestOwnership_GetMailingListByID_AnotherUsersRequest() {
|
||||
ctx := context.Background()
|
||||
|
||||
loginReq := &authpb.LoginRequest{
|
||||
Email: "test@example.com",
|
||||
Password: "testpassword",
|
||||
Ip: "127.0.0.1",
|
||||
UserAgent: "integration-test",
|
||||
}
|
||||
|
||||
loginResp, err := s.authClient.Login(ctx, loginReq)
|
||||
s.NoError(err)
|
||||
|
||||
validateReq := &authpb.ValidateRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
validateResp, err := s.authClient.Validate(ctx, validateReq)
|
||||
s.NoError(err)
|
||||
user1ID := validateResp.UserId
|
||||
|
||||
createTZReq := &requestpb.CreateTZRequest{
|
||||
UserId: user1ID,
|
||||
RequestTxt: "Нужны поставщики для теста ownership",
|
||||
}
|
||||
|
||||
createTZResp, err := s.requestClient.CreateTZ(ctx, createTZReq)
|
||||
s.NoError(err)
|
||||
s.NotEmpty(createTZResp.RequestId)
|
||||
requestID := createTZResp.RequestId
|
||||
|
||||
_, _, user2ID := s.createSecondTestUser()
|
||||
|
||||
getMailingByIDReq := &requestpb.GetMailingListByIDRequest{
|
||||
RequestId: requestID,
|
||||
UserId: user2ID,
|
||||
}
|
||||
|
||||
resp, err := s.requestClient.GetMailingListByID(ctx, getMailingByIDReq)
|
||||
s.Error(err)
|
||||
s.Nil(resp)
|
||||
|
||||
st, ok := status.FromError(err)
|
||||
s.True(ok)
|
||||
s.Equal(codes.PermissionDenied, st.Code())
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) TestOwnership_ApproveTZ_AnotherUsersRequest() {
|
||||
ctx := context.Background()
|
||||
|
||||
loginReq := &authpb.LoginRequest{
|
||||
Email: "test@example.com",
|
||||
Password: "testpassword",
|
||||
Ip: "127.0.0.1",
|
||||
UserAgent: "integration-test",
|
||||
}
|
||||
|
||||
loginResp, err := s.authClient.Login(ctx, loginReq)
|
||||
s.NoError(err)
|
||||
|
||||
validateReq := &authpb.ValidateRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
validateResp, err := s.authClient.Validate(ctx, validateReq)
|
||||
s.NoError(err)
|
||||
user1ID := validateResp.UserId
|
||||
|
||||
createTZReq := &requestpb.CreateTZRequest{
|
||||
UserId: user1ID,
|
||||
RequestTxt: "Нужны поставщики для теста ownership approve",
|
||||
}
|
||||
|
||||
createTZResp, err := s.requestClient.CreateTZ(ctx, createTZReq)
|
||||
s.NoError(err)
|
||||
s.NotEmpty(createTZResp.RequestId)
|
||||
requestID := createTZResp.RequestId
|
||||
|
||||
_, _, user2ID := s.createSecondTestUser()
|
||||
|
||||
approveTZReq := &requestpb.ApproveTZRequest{
|
||||
RequestId: requestID,
|
||||
FinalTz: "Утвержденное ТЗ от чужого пользователя",
|
||||
UserId: user2ID,
|
||||
}
|
||||
|
||||
resp, err := s.requestClient.ApproveTZ(ctx, approveTZReq)
|
||||
s.Error(err)
|
||||
s.Nil(resp)
|
||||
|
||||
st, ok := status.FromError(err)
|
||||
s.True(ok)
|
||||
s.Equal(codes.PermissionDenied, st.Code())
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) TestOwnership_ExportExcel_AnotherUsersRequest() {
|
||||
ctx := context.Background()
|
||||
|
||||
loginReq := &authpb.LoginRequest{
|
||||
Email: "test@example.com",
|
||||
Password: "testpassword",
|
||||
Ip: "127.0.0.1",
|
||||
UserAgent: "integration-test",
|
||||
}
|
||||
|
||||
loginResp, err := s.authClient.Login(ctx, loginReq)
|
||||
s.NoError(err)
|
||||
|
||||
validateReq := &authpb.ValidateRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
validateResp, err := s.authClient.Validate(ctx, validateReq)
|
||||
s.NoError(err)
|
||||
user1ID := validateResp.UserId
|
||||
|
||||
createTZReq := &requestpb.CreateTZRequest{
|
||||
UserId: user1ID,
|
||||
RequestTxt: "Нужны поставщики для теста ownership export",
|
||||
}
|
||||
|
||||
createTZResp, err := s.requestClient.CreateTZ(ctx, createTZReq)
|
||||
s.NoError(err)
|
||||
s.NotEmpty(createTZResp.RequestId)
|
||||
requestID := createTZResp.RequestId
|
||||
|
||||
approveTZReq := &requestpb.ApproveTZRequest{
|
||||
RequestId: requestID,
|
||||
FinalTz: "Утвержденное ТЗ для экспорта",
|
||||
UserId: user1ID,
|
||||
}
|
||||
|
||||
_, err = s.requestClient.ApproveTZ(ctx, approveTZReq)
|
||||
s.NoError(err)
|
||||
|
||||
_, _, user2ID := s.createSecondTestUser()
|
||||
|
||||
exportReq := &supplierpb.ExportExcelRequest{
|
||||
RequestId: requestID,
|
||||
UserId: user2ID,
|
||||
}
|
||||
|
||||
resp, err := s.supplierClient.ExportExcel(ctx, exportReq)
|
||||
s.Error(err)
|
||||
s.Nil(resp)
|
||||
|
||||
st, ok := status.FromError(err)
|
||||
s.True(ok)
|
||||
s.Equal(codes.PermissionDenied, st.Code())
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) TestOwnership_GetMailingListByID_OwnRequest_Success() {
|
||||
ctx := context.Background()
|
||||
|
||||
loginReq := &authpb.LoginRequest{
|
||||
Email: "test@example.com",
|
||||
Password: "testpassword",
|
||||
Ip: "127.0.0.1",
|
||||
UserAgent: "integration-test",
|
||||
}
|
||||
|
||||
loginResp, err := s.authClient.Login(ctx, loginReq)
|
||||
s.NoError(err)
|
||||
|
||||
validateReq := &authpb.ValidateRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
validateResp, err := s.authClient.Validate(ctx, validateReq)
|
||||
s.NoError(err)
|
||||
userID := validateResp.UserId
|
||||
|
||||
createTZReq := &requestpb.CreateTZRequest{
|
||||
UserId: userID,
|
||||
RequestTxt: "Нужны поставщики для теста ownership success",
|
||||
}
|
||||
|
||||
createTZResp, err := s.requestClient.CreateTZ(ctx, createTZReq)
|
||||
s.NoError(err)
|
||||
s.NotEmpty(createTZResp.RequestId)
|
||||
requestID := createTZResp.RequestId
|
||||
|
||||
approveTZReq := &requestpb.ApproveTZRequest{
|
||||
RequestId: requestID,
|
||||
FinalTz: "Утвержденное ТЗ",
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
_, err = s.requestClient.ApproveTZ(ctx, approveTZReq)
|
||||
s.NoError(err)
|
||||
|
||||
getMailingByIDReq := &requestpb.GetMailingListByIDRequest{
|
||||
RequestId: requestID,
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
resp, err := s.requestClient.GetMailingListByID(ctx, getMailingByIDReq)
|
||||
s.NoError(err)
|
||||
s.NotNil(resp)
|
||||
s.NotNil(resp.Item)
|
||||
s.Equal(requestID, resp.Item.RequestId)
|
||||
}
|
||||
@@ -136,3 +136,133 @@ func (s *IntegrationSuite) TestRequestHandler_GetMailingListWithValidUser() {
|
||||
s.NoError(err)
|
||||
s.NotNil(resp)
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) TestRequestHandler_CreateTZWithFile() {
|
||||
ctx := context.Background()
|
||||
|
||||
loginReq := &authpb.LoginRequest{
|
||||
Email: "test@example.com",
|
||||
Password: "testpassword",
|
||||
Ip: "127.0.0.1",
|
||||
UserAgent: "integration-test",
|
||||
}
|
||||
|
||||
loginResp, err := s.authClient.Login(ctx, loginReq)
|
||||
s.NoError(err)
|
||||
|
||||
validateReq := &authpb.ValidateRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
validateResp, err := s.authClient.Validate(ctx, validateReq)
|
||||
s.NoError(err)
|
||||
|
||||
req := &requestpb.CreateTZRequest{
|
||||
UserId: validateResp.UserId,
|
||||
RequestTxt: "Нужны поставщики металлоконструкций",
|
||||
FileData: []byte("Содержимое файла с дополнительными требованиями"),
|
||||
FileName: "requirements.txt",
|
||||
}
|
||||
|
||||
resp, err := s.requestClient.CreateTZ(ctx, req)
|
||||
s.NoError(err)
|
||||
s.NotNil(resp)
|
||||
s.NotEmpty(resp.RequestId)
|
||||
s.NotEmpty(resp.TzText)
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) TestRequestHandler_ApproveTZSuccess() {
|
||||
ctx := context.Background()
|
||||
|
||||
loginReq := &authpb.LoginRequest{
|
||||
Email: "test@example.com",
|
||||
Password: "testpassword",
|
||||
Ip: "127.0.0.1",
|
||||
UserAgent: "integration-test",
|
||||
}
|
||||
|
||||
loginResp, err := s.authClient.Login(ctx, loginReq)
|
||||
s.NoError(err)
|
||||
|
||||
validateReq := &authpb.ValidateRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
validateResp, err := s.authClient.Validate(ctx, validateReq)
|
||||
s.NoError(err)
|
||||
userID := validateResp.UserId
|
||||
|
||||
createReq := &requestpb.CreateTZRequest{
|
||||
UserId: userID,
|
||||
RequestTxt: "Нужны поставщики кирпича для строительства",
|
||||
}
|
||||
|
||||
createResp, err := s.requestClient.CreateTZ(ctx, createReq)
|
||||
s.NoError(err)
|
||||
s.NotEmpty(createResp.RequestId)
|
||||
|
||||
approveReq := &requestpb.ApproveTZRequest{
|
||||
RequestId: createResp.RequestId,
|
||||
FinalTz: "Утвержденное техническое задание на поставку кирпича",
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
approveResp, err := s.requestClient.ApproveTZ(ctx, approveReq)
|
||||
s.NoError(err)
|
||||
s.NotNil(approveResp)
|
||||
s.True(approveResp.Success)
|
||||
s.Equal("sent", approveResp.MailingStatus)
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) TestRequestHandler_GetMailingListByIDSuccess() {
|
||||
ctx := context.Background()
|
||||
|
||||
loginReq := &authpb.LoginRequest{
|
||||
Email: "test@example.com",
|
||||
Password: "testpassword",
|
||||
Ip: "127.0.0.1",
|
||||
UserAgent: "integration-test",
|
||||
}
|
||||
|
||||
loginResp, err := s.authClient.Login(ctx, loginReq)
|
||||
s.NoError(err)
|
||||
|
||||
validateReq := &authpb.ValidateRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
validateResp, err := s.authClient.Validate(ctx, validateReq)
|
||||
s.NoError(err)
|
||||
userID := validateResp.UserId
|
||||
|
||||
createReq := &requestpb.CreateTZRequest{
|
||||
UserId: userID,
|
||||
RequestTxt: "Нужны поставщики бетона",
|
||||
}
|
||||
|
||||
createResp, err := s.requestClient.CreateTZ(ctx, createReq)
|
||||
s.NoError(err)
|
||||
s.NotEmpty(createResp.RequestId)
|
||||
requestID := createResp.RequestId
|
||||
|
||||
approveReq := &requestpb.ApproveTZRequest{
|
||||
RequestId: requestID,
|
||||
FinalTz: "Утвержденное ТЗ на поставку бетона",
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
_, err = s.requestClient.ApproveTZ(ctx, approveReq)
|
||||
s.NoError(err)
|
||||
|
||||
getByIDReq := &requestpb.GetMailingListByIDRequest{
|
||||
RequestId: requestID,
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
getByIDResp, err := s.requestClient.GetMailingListByID(ctx, getByIDReq)
|
||||
s.NoError(err)
|
||||
s.NotNil(getByIDResp)
|
||||
s.NotNil(getByIDResp.Item)
|
||||
s.Equal(requestID, getByIDResp.Item.RequestId)
|
||||
s.Greater(getByIDResp.Item.SuppliersFound, int32(0))
|
||||
}
|
||||
|
||||
@@ -93,3 +93,58 @@ func (s *IntegrationSuite) TestSupplierHandler_ExportExcelWithValidRequest() {
|
||||
s.NotEmpty(exportResp.FileName)
|
||||
s.Equal("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", exportResp.MimeType)
|
||||
}
|
||||
|
||||
func (s *IntegrationSuite) TestSupplierHandler_ExportExcelWithSuppliers() {
|
||||
ctx := context.Background()
|
||||
|
||||
loginReq := &authpb.LoginRequest{
|
||||
Email: "test@example.com",
|
||||
Password: "testpassword",
|
||||
Ip: "127.0.0.1",
|
||||
UserAgent: "integration-test",
|
||||
}
|
||||
|
||||
loginResp, err := s.authClient.Login(ctx, loginReq)
|
||||
s.NoError(err)
|
||||
|
||||
validateReq := &authpb.ValidateRequest{
|
||||
AccessToken: loginResp.AccessToken,
|
||||
}
|
||||
|
||||
validateResp, err := s.authClient.Validate(ctx, validateReq)
|
||||
s.NoError(err)
|
||||
userID := validateResp.UserId
|
||||
|
||||
createReq := &requestpb.CreateTZRequest{
|
||||
UserId: userID,
|
||||
RequestTxt: "Нужны поставщики офисной мебели для большого офиса",
|
||||
}
|
||||
|
||||
createResp, err := s.requestClient.CreateTZ(ctx, createReq)
|
||||
s.NoError(err)
|
||||
s.NotEmpty(createResp.RequestId)
|
||||
requestID := createResp.RequestId
|
||||
|
||||
approveReq := &requestpb.ApproveTZRequest{
|
||||
RequestId: requestID,
|
||||
FinalTz: "Техническое задание на поставку офисной мебели",
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
approveResp, err := s.requestClient.ApproveTZ(ctx, approveReq)
|
||||
s.NoError(err)
|
||||
s.True(approveResp.Success)
|
||||
|
||||
exportReq := &supplierpb.ExportExcelRequest{
|
||||
RequestId: requestID,
|
||||
UserId: userID,
|
||||
}
|
||||
|
||||
exportResp, err := s.supplierClient.ExportExcel(ctx, exportReq)
|
||||
s.NoError(err)
|
||||
s.NotNil(exportResp)
|
||||
s.NotEmpty(exportResp.FileName)
|
||||
s.NotEmpty(exportResp.FileData)
|
||||
s.Equal("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", exportResp.MimeType)
|
||||
s.Greater(len(exportResp.FileData), 1000)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user