refactor: реорганизация структуры тестов

- Перемещены unit тесты рядом с тестируемым кодом:
  * auth_test.go -> internal/auth/auth_test.go
  * repository_test.go -> internal/repository/repository_test.go
- Перемещены integration тесты в отдельную директорию:
  * api_test.go -> tests/api_integration_test.go
- Обновлены пакеты тестов:
  * auth_test.go: package auth_test
  * repository_test.go: package repository_test
  * api_integration_test.go: package tests
- Удалена директория examples/
- Обновлен pre-commit хук для новой структуры
- Все тесты проходят успешно
This commit is contained in:
2025-08-27 16:17:12 +04:00
parent 282613edb9
commit 225635ed4b
4 changed files with 4 additions and 3 deletions

View File

@@ -0,0 +1,183 @@
package auth_test
import (
"testing"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"erp-mvp/core-service/internal/auth"
)
// TestJWTService_GenerateToken тестирует генерацию JWT токенов
func TestJWTService_GenerateToken(t *testing.T) {
// Arrange
secret := "test_secret_key"
ttl := 24 * time.Hour
jwtService := auth.NewJWTService(secret, ttl)
userID := uuid.New()
orgID := uuid.New()
email := "test@example.com"
role := "admin"
// Act
token, err := jwtService.GenerateToken(userID, orgID, email, role)
// Assert
require.NoError(t, err)
assert.NotEmpty(t, token)
// Проверяем, что токен можно декодировать
parsedToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
require.NoError(t, err)
assert.True(t, parsedToken.Valid)
// Проверяем claims
claims, ok := parsedToken.Claims.(jwt.MapClaims)
require.True(t, ok)
assert.Equal(t, userID.String(), claims["user_id"])
assert.Equal(t, orgID.String(), claims["organization_id"])
assert.Equal(t, email, claims["email"])
assert.Equal(t, role, claims["role"])
}
// TestJWTService_ValidateToken тестирует валидацию JWT токенов
func TestJWTService_ValidateToken(t *testing.T) {
// Arrange
secret := "test_secret_key"
ttl := 24 * time.Hour
jwtService := auth.NewJWTService(secret, ttl)
// Создаем валидный токен для тестирования
userID := uuid.New()
orgID := uuid.New()
validToken, err := jwtService.GenerateToken(userID, orgID, "test@example.com", "admin")
require.NoError(t, err)
tests := []struct {
name string
secret string
token string
wantErr bool
}{
{
name: "valid token",
secret: secret,
token: validToken,
wantErr: false,
},
{
name: "invalid signature",
secret: "wrong_secret",
token: validToken,
wantErr: true,
},
{
name: "invalid token format",
secret: secret,
token: "invalid_token_format",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Arrange
testJWTService := auth.NewJWTService(tt.secret, ttl)
// Act
claims, err := testJWTService.ValidateToken(tt.token)
// Assert
if tt.wantErr {
assert.Error(t, err)
assert.Nil(t, claims)
} else {
assert.NoError(t, err)
assert.NotNil(t, claims)
assert.Equal(t, userID, claims.UserID)
assert.Equal(t, orgID, claims.OrganizationID)
}
})
}
}
// TestPasswordHashing тестирует хеширование и проверку паролей
func TestPasswordHashing(t *testing.T) {
// Arrange
password := "mySecurePassword123"
// Act - хешируем пароль
hashedPassword, err := auth.HashPassword(password)
// Assert
require.NoError(t, err)
assert.NotEmpty(t, hashedPassword)
assert.NotEqual(t, password, hashedPassword)
// Act - проверяем правильный пароль
isValid := auth.CheckPassword(password, hashedPassword)
// Assert
assert.True(t, isValid)
// Act - проверяем неправильный пароль
isValid = auth.CheckPassword("wrongPassword", hashedPassword)
// Assert
assert.False(t, isValid)
}
// TestPasswordHashing_EmptyPassword тестирует обработку пустого пароля
func TestPasswordHashing_EmptyPassword(t *testing.T) {
// Arrange
password := ""
// Act
hashedPassword, err := auth.HashPassword(password)
// Assert
assert.NoError(t, err)
assert.NotEmpty(t, hashedPassword)
// Проверяем, что пустой пароль работает
isValid := auth.CheckPassword(password, hashedPassword)
assert.True(t, isValid)
}
// BenchmarkPasswordHashing тестирует производительность хеширования
func BenchmarkPasswordHashing(b *testing.B) {
password := "benchmarkPassword123"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := auth.HashPassword(password)
if err != nil {
b.Fatal(err)
}
}
}
// BenchmarkJWTGeneration тестирует производительность генерации JWT
func BenchmarkJWTGeneration(b *testing.B) {
secret := "benchmark_secret_key"
ttl := 24 * time.Hour
jwtService := auth.NewJWTService(secret, ttl)
userID := uuid.New()
orgID := uuid.New()
email := "benchmark@example.com"
role := "admin"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := jwtService.GenerateToken(userID, orgID, email, role)
if err != nil {
b.Fatal(err)
}
}
}

View File

@@ -0,0 +1,373 @@
package repository_test
import (
"context"
"database/sql"
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"erp-mvp/core-service/internal/models"
"erp-mvp/core-service/internal/repository"
)
// TestOrganizationRepository_Create тестирует создание организации
func TestOrganizationRepository_Create(t *testing.T) {
// Arrange
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
repo := repository.NewOrganizationRepository(db)
orgID := uuid.New()
org := &models.Organization{
ID: orgID,
Name: "Test Organization",
Type: "workshop",
Settings: models.JSON{
"created_at": 1234567890,
},
CreatedAt: time.Now(),
}
// Ожидаем SQL запрос
mock.ExpectExec("INSERT INTO organizations").
WithArgs(orgID, org.Name, org.Type, sqlmock.AnyArg(), sqlmock.AnyArg()).
WillReturnResult(sqlmock.NewResult(1, 1))
// Act
err = repo.Create(context.Background(), org)
// Assert
assert.NoError(t, err)
assert.NoError(t, mock.ExpectationsWereMet())
}
// TestOrganizationRepository_GetByID тестирует получение организации по ID
func TestOrganizationRepository_GetByID(t *testing.T) {
// Arrange
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
repo := repository.NewOrganizationRepository(db)
orgID := uuid.New()
expectedOrg := &models.Organization{
ID: orgID,
Name: "Test Organization",
Type: "workshop",
Settings: models.JSON{
"created_at": 1234567890,
},
}
// Ожидаем SQL запрос
rows := sqlmock.NewRows([]string{"id", "name", "type", "settings", "created_at"}).
AddRow(orgID, expectedOrg.Name, expectedOrg.Type, `{"created_at":1234567890}`, time.Now())
mock.ExpectQuery("SELECT (.+) FROM organizations").
WithArgs(orgID).
WillReturnRows(rows)
// Act
org, err := repo.GetByID(context.Background(), orgID)
// Assert
assert.NoError(t, err)
assert.NotNil(t, org)
assert.Equal(t, expectedOrg.ID, org.ID)
assert.Equal(t, expectedOrg.Name, org.Name)
assert.Equal(t, expectedOrg.Type, org.Type)
assert.NoError(t, mock.ExpectationsWereMet())
}
// TestOrganizationRepository_GetByID_NotFound тестирует случай, когда организация не найдена
func TestOrganizationRepository_GetByID_NotFound(t *testing.T) {
// Arrange
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
repo := repository.NewOrganizationRepository(db)
orgID := uuid.New()
// Ожидаем SQL запрос с пустым результатом
mock.ExpectQuery("SELECT (.+) FROM organizations").
WithArgs(orgID).
WillReturnError(sql.ErrNoRows)
// Act
org, err := repo.GetByID(context.Background(), orgID)
// Assert
assert.Error(t, err)
assert.Nil(t, org)
assert.Equal(t, "organization not found", err.Error())
assert.NoError(t, mock.ExpectationsWereMet())
}
// TestUserRepository_Create тестирует создание пользователя
func TestUserRepository_Create(t *testing.T) {
// Arrange
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
repo := repository.NewUserRepository(db)
userID := uuid.New()
orgID := uuid.New()
user := &models.User{
ID: userID,
OrganizationID: orgID,
Email: "test@example.com",
PasswordHash: "hashed_password",
Role: "admin",
CreatedAt: time.Now(),
}
password := "hashed_password"
// Ожидаем SQL запрос
mock.ExpectExec("INSERT INTO users").
WithArgs(userID, orgID, user.Email, password, user.Role, sqlmock.AnyArg()).
WillReturnResult(sqlmock.NewResult(1, 1))
// Act
err = repo.Create(context.Background(), user, password)
// Assert
assert.NoError(t, err)
assert.NoError(t, mock.ExpectationsWereMet())
}
// TestUserRepository_GetByEmail тестирует получение пользователя по email
func TestUserRepository_GetByEmail(t *testing.T) {
// Arrange
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
repo := repository.NewUserRepository(db)
userID := uuid.New()
orgID := uuid.New()
email := "test@example.com"
expectedUser := &models.User{
ID: userID,
OrganizationID: orgID,
Email: email,
PasswordHash: "hashed_password",
Role: "admin",
}
// Ожидаем SQL запрос
rows := sqlmock.NewRows([]string{"id", "organization_id", "email", "password_hash", "role", "created_at"}).
AddRow(userID, orgID, email, "hashed_password", "admin", time.Now())
mock.ExpectQuery("SELECT (.+) FROM users").
WithArgs(email).
WillReturnRows(rows)
// Act
user, err := repo.GetByEmail(context.Background(), email)
// Assert
assert.NoError(t, err)
assert.NotNil(t, user)
assert.Equal(t, expectedUser.ID, user.ID)
assert.Equal(t, expectedUser.Email, user.Email)
assert.Equal(t, expectedUser.Role, user.Role)
assert.NoError(t, mock.ExpectationsWereMet())
}
// TestLocationRepository_Create тестирует создание места хранения
func TestLocationRepository_Create(t *testing.T) {
// Arrange
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
repo := repository.NewLocationRepository(db)
locationID := uuid.New()
orgID := uuid.New()
location := &models.StorageLocation{
ID: locationID,
OrganizationID: orgID,
Name: "Test Location",
Address: "Test Address",
Type: "warehouse",
Coordinates: models.JSON{
"lat": 55.7558,
"lng": 37.6176,
},
CreatedAt: time.Now(),
}
// Ожидаем SQL запрос
mock.ExpectExec("INSERT INTO storage_locations").
WithArgs(locationID, orgID, sqlmock.AnyArg(), location.Name, location.Address, location.Type, sqlmock.AnyArg(), sqlmock.AnyArg()).
WillReturnResult(sqlmock.NewResult(1, 1))
// Act
err = repo.Create(context.Background(), location)
// Assert
assert.NoError(t, err)
assert.NoError(t, mock.ExpectationsWereMet())
}
// TestLocationRepository_GetByID тестирует получение места хранения по ID
func TestLocationRepository_GetByID(t *testing.T) {
// Arrange
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
repo := repository.NewLocationRepository(db)
locationID := uuid.New()
orgID := uuid.New()
expectedLocation := &models.StorageLocation{
ID: locationID,
OrganizationID: orgID,
Name: "Test Location",
Address: "Test Address",
Type: "warehouse",
Coordinates: models.JSON{
"lat": 55.7558,
"lng": 37.6176,
},
}
// Ожидаем SQL запрос
rows := sqlmock.NewRows([]string{"id", "organization_id", "parent_id", "name", "address", "type", "coordinates", "created_at"}).
AddRow(locationID, orgID, nil, expectedLocation.Name, expectedLocation.Address, expectedLocation.Type, `{"lat":55.7558,"lng":37.6176}`, time.Now())
mock.ExpectQuery("SELECT (.+) FROM storage_locations").
WithArgs(locationID, orgID).
WillReturnRows(rows)
// Act
location, err := repo.GetByID(context.Background(), locationID, orgID)
// Assert
assert.NoError(t, err)
assert.NotNil(t, location)
assert.Equal(t, expectedLocation.ID, location.ID)
assert.Equal(t, expectedLocation.Name, location.Name)
assert.Equal(t, expectedLocation.Type, location.Type)
assert.NoError(t, mock.ExpectationsWereMet())
}
// TestItemRepository_Create тестирует создание товара
func TestItemRepository_Create(t *testing.T) {
// Arrange
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
repo := repository.NewItemRepository(db)
itemID := uuid.New()
orgID := uuid.New()
item := &models.Item{
ID: itemID,
OrganizationID: orgID,
Name: "Test Item",
Description: "Test Description",
Category: "Test Category",
CreatedAt: time.Now(),
}
// Ожидаем SQL запрос
mock.ExpectExec("INSERT INTO items").
WithArgs(itemID, orgID, item.Name, item.Description, item.Category, sqlmock.AnyArg()).
WillReturnResult(sqlmock.NewResult(1, 1))
// Act
err = repo.Create(context.Background(), item)
// Assert
assert.NoError(t, err)
assert.NoError(t, mock.ExpectationsWereMet())
}
// TestOperationsRepository_PlaceItem тестирует размещение товара
func TestOperationsRepository_PlaceItem(t *testing.T) {
// Arrange
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
repo := repository.NewOperationsRepository(db)
placementID := uuid.New()
orgID := uuid.New()
itemID := uuid.New()
locationID := uuid.New()
placement := &models.ItemPlacement{
ID: placementID,
OrganizationID: orgID,
ItemID: itemID,
LocationID: locationID,
Quantity: 5,
CreatedAt: time.Now(),
}
// Ожидаем SQL запрос
mock.ExpectExec("INSERT INTO item_placements").
WithArgs(placementID, orgID, itemID, locationID, placement.Quantity, sqlmock.AnyArg()).
WillReturnResult(sqlmock.NewResult(1, 1))
// Act
err = repo.PlaceItem(context.Background(), placement)
// Assert
assert.NoError(t, err)
assert.NoError(t, mock.ExpectationsWereMet())
}
// TestOperationsRepository_Search тестирует поиск товаров с местами размещения
func TestOperationsRepository_Search(t *testing.T) {
// Arrange
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
repo := repository.NewOperationsRepository(db)
orgID := uuid.New()
itemID := uuid.New()
locationID := uuid.New()
// Ожидаем SQL запрос с JOIN
rows := sqlmock.NewRows([]string{
"i.id", "i.organization_id", "i.name", "i.description", "i.category", "i.created_at",
"sl.id", "sl.organization_id", "sl.parent_id", "sl.name", "sl.address", "sl.type", "sl.coordinates", "sl.created_at",
"ip.quantity",
}).AddRow(
itemID, orgID, "Test Item", "Test Description", "Test Category", time.Now(),
locationID, orgID, nil, "Test Location", "Test Address", "warehouse", `{"lat":55.7558,"lng":37.6176}`, time.Now(),
5,
)
mock.ExpectQuery("SELECT (.+) FROM items i").
WithArgs(orgID, "%test%").
WillReturnRows(rows)
// Act
results, err := repo.Search(context.Background(), orgID, "test", "", "")
// Assert
assert.NoError(t, err)
assert.NotNil(t, results)
assert.Len(t, results, 1)
assert.NoError(t, mock.ExpectationsWereMet())
}