package testutils import ( "bytes" "encoding/json" "fmt" "io" "net/http" "net/http/cookiejar" "testing" "time" ) // TestConfig хранит конфигурацию для тестов // Содержит базовый URL API и HTTP клиент с поддержкой cookies type TestConfig struct { BaseURL string // Базовый URL API сервера Client *http.Client // HTTP клиент с поддержкой cookies } // TestUser хранит данные тестового пользователя type TestUser struct { Email string // Email пользователя Password string // Пароль пользователя FirstName string // Имя пользователя LastName string // Фамилия пользователя Token string // JWT токен доступа UserID uint // ID пользователя в системе } // NewTestConfig создает новую конфигурацию для тестов // Настраивает HTTP клиент с поддержкой cookies и таймаутом 30 секунд // Возвращает указатель на TestConfig func NewTestConfig() *TestConfig { jar, _ := cookiejar.New(nil) return &TestConfig{ BaseURL: "http://localhost:8088/api/v1", Client: &http.Client{ Jar: jar, Timeout: 30 * time.Second, }, } } // Request выполняет HTTP запрос к API // Параметры: // - method: HTTP метод (GET, POST, PUT, DELETE) // - path: путь эндпоинта (относительно BaseURL) // - body: тело запроса (будет сериализовано в JSON) // - token: JWT токен для авторизации (может быть пустым) // Возвращает: HTTP ответ и ошибку func (c *TestConfig) Request(method, path string, body interface{}, token string) (*http.Response, error) { var reqBody io.Reader if body != nil { jsonBody, err := json.Marshal(body) if err != nil { return nil, err } reqBody = bytes.NewBuffer(jsonBody) } req, err := http.NewRequest(method, c.BaseURL+path, reqBody) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") if token != "" { req.Header.Set("Authorization", "Bearer "+token) } return c.Client.Do(req) } // ParseResponse парсит JSON ответ HTTP запроса в указанную структуру // Параметры: // - resp: HTTP ответ для парсинга // - target: указатель на структуру, в которую нужно распарсить JSON // Возвращает: ошибку парсинга func (c *TestConfig) ParseResponse(resp *http.Response, target interface{}) error { defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return err } return json.Unmarshal(body, target) } // CreateTestUser создает тестового пользователя через API регистрации // Автоматически генерирует уникальный email на основе timestamp // Параметры: // - t: указатель на тест для логирования ошибок // Возвращает: указатель на созданного TestUser с заполненными полями (включая токен) func (c *TestConfig) CreateTestUser(t *testing.T) *TestUser { user := &TestUser{ Email: fmt.Sprintf("test_%d@example.com", time.Now().UnixNano()), Password: "test123456", FirstName: "Test", LastName: "User", } resp, err := c.Request("POST", "/auth/register", map[string]interface{}{ "email": user.Email, "password": user.Password, "first_name": user.FirstName, "last_name": user.LastName, }, "") if err != nil { t.Fatalf("Failed to create test user: %v", err) } defer resp.Body.Close() var result map[string]interface{} if err := c.ParseResponse(resp, &result); err != nil { t.Fatalf("Failed to parse response: %v", err) } // Извлекаем токен из ответа if token, ok := result["token"].(string); ok { user.Token = token } // Извлекаем ID пользователя из ответа if userData, ok := result["user"].(map[string]interface{}); ok { if id, ok := userData["id"].(float64); ok { user.UserID = uint(id) } } return user } // CleanupTestUser удаляет тестового пользователя через API // Вызывается через defer после создания пользователя для очистки // Параметры: // - t: указатель на тест для логирования предупреждений // - user: тестовый пользователь для удаления func (c *TestConfig) CleanupTestUser(t *testing.T, user *TestUser) { if user.Token != "" { _, err := c.Request("DELETE", "/account", nil, user.Token) if err != nil { t.Logf("Warning: Failed to cleanup test user: %v", err) } } } // GetAuthToken выполняет вход пользователя и возвращает JWT токен // Параметры: // - email: email пользователя // - password: пароль пользователя // Возвращает: JWT токен и ошибку func (c *TestConfig) GetAuthToken(email, password string) (string, error) { resp, err := c.Request("POST", "/auth/login", map[string]interface{}{ "email": email, "password": password, }, "") if err != nil { return "", err } defer resp.Body.Close() var result map[string]interface{} if err := c.ParseResponse(resp, &result); err != nil { return "", err } if token, ok := result["token"].(string); ok { return token, nil } return "", fmt.Errorf("token not found in response") }