On branch main
modified: main_dc/yalarba/api_yal/cmd/testrunner/main.go modified: main_dc/yalarba/api_yal/cmd/testrunner/runner.go modified: main_dc/yalarba/api_yal/tests/integration/account_test.go modified: main_dc/yalarba/api_yal/tests/integration/appeal_test.go modified: main_dc/yalarba/api_yal/tests/integration/auth_test.go modified: main_dc/yalarba/api_yal/tests/integration/comment_test.go modified: main_dc/yalarba/api_yal/tests/integration/feedback_test.go modified: main_dc/yalarba/api_yal/tests/integration/object_test.go modified: main_dc/yalarba/api_yal/tests/integration/rating_test.go deleted: main_dc/yalarba/api_yal/tests/testutils/client.go modified: main_dc/yalarba/api_yal/tests/testutils/fixtures.go modified: main_dc/yalarba/api_yal/tests/testutils/setup.go write comments for and into test's functions
This commit is contained in:
@@ -15,29 +15,32 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Конфигурация
|
// Config хранит конфигурацию тестового раннера
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ServerURL string
|
ServerURL string // URL API сервера
|
||||||
ServerPort string
|
ServerPort string // Порт API сервера
|
||||||
TestTimeout time.Duration
|
TestTimeout time.Duration // Таймаут выполнения тестов
|
||||||
Verbose bool
|
Verbose bool // Флаг подробного вывода
|
||||||
Parallel bool
|
Parallel bool // Флаг параллельного выполнения
|
||||||
Coverage bool
|
Coverage bool // Флаг генерации отчета о покрытии
|
||||||
TestSuite string
|
TestSuite string // Выбранный набор тестов
|
||||||
}
|
}
|
||||||
|
|
||||||
// Цвета для вывода
|
// Colors хранит ANSI коды цветов для вывода в консоль
|
||||||
type Colors struct {
|
type Colors struct {
|
||||||
Reset string
|
Reset string // Сброс цвета
|
||||||
Red string
|
Red string // Красный цвет (ошибки)
|
||||||
Green string
|
Green string // Зеленый цвет (успех)
|
||||||
Yellow string
|
Yellow string // Желтый цвет (предупреждения)
|
||||||
Blue string
|
Blue string // Синий цвет (информация)
|
||||||
Cyan string
|
Cyan string // Бирюзовый цвет (заголовки)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Глобальная переменная с цветами
|
||||||
var colors Colors
|
var colors Colors
|
||||||
|
|
||||||
|
// init инициализирует цвета для консольного вывода
|
||||||
|
// Определяет поддержку цветов для разных ОС
|
||||||
func init() {
|
func init() {
|
||||||
// Определяем поддержку цветов
|
// Определяем поддержку цветов
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
@@ -62,23 +65,25 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Тестовый набор
|
// TestSuite представляет набор тестов
|
||||||
type TestSuite struct {
|
type TestSuite struct {
|
||||||
Name string
|
Name string // Название набора
|
||||||
Pattern string
|
Pattern string // Регулярное выражение для фильтрации тестов
|
||||||
Timeout time.Duration
|
Timeout time.Duration // Таймаут для этого набора
|
||||||
}
|
}
|
||||||
|
|
||||||
// Результат теста
|
// TestResult хранит результат выполнения тестового набора
|
||||||
type TestResult struct {
|
type TestResult struct {
|
||||||
Suite string
|
Suite string // Название набора
|
||||||
Passed bool
|
Passed bool // Флаг успешности
|
||||||
Duration time.Duration
|
Duration time.Duration // Длительность выполнения
|
||||||
Output string
|
Output string // Вывод тестов
|
||||||
Error error
|
Error error // Ошибка выполнения
|
||||||
Coverage float64
|
Coverage float64 // Процент покрытия кода
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// main - точка входа в программу
|
||||||
|
// Обрабатывает аргументы командной строки и запускает соответствующий режим
|
||||||
func main() {
|
func main() {
|
||||||
config := parseFlags()
|
config := parseFlags()
|
||||||
|
|
||||||
@@ -105,6 +110,8 @@ func main() {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseFlags парсит аргументы командной строки
|
||||||
|
// Возвращает указатель на Config с заполненными значениями
|
||||||
func parseFlags() *Config {
|
func parseFlags() *Config {
|
||||||
config := &Config{}
|
config := &Config{}
|
||||||
|
|
||||||
@@ -121,14 +128,18 @@ func parseFlags() *Config {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// printBanner выводит графический баннер программы
|
||||||
func printBanner() {
|
func printBanner() {
|
||||||
fmt.Printf("%s╔════════════════════════════════════════════════════════════════╗%s\n", colors.Cyan, colors.Reset)
|
fmt.Printf("%s╔══════════════════════════════════════════════════════════════════╗%s\n", colors.Cyan, colors.Reset)
|
||||||
fmt.Printf("%s║ Go Test Runner for API ║%s\n", colors.Cyan, colors.Reset)
|
fmt.Printf("%s║ Go Test Runner for API ║%s\n", colors.Cyan, colors.Reset)
|
||||||
fmt.Printf("%s║ Version 1.0.0 ║%s\n", colors.Cyan, colors.Reset)
|
fmt.Printf("%s║ Version 1.0.0 ║%s\n", colors.Cyan, colors.Reset)
|
||||||
fmt.Printf("%s╚════════════════════════════════════════════════════════════════╝%s\n", colors.Cyan, colors.Reset)
|
fmt.Printf("%s╚══════════════════════════════════════════════════════════════════╝%s\n", colors.Cyan, colors.Reset)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkServer проверяет доступность API сервера
|
||||||
|
// Отправляет GET запрос на /health эндпоинт
|
||||||
|
// Возвращает true если сервер доступен
|
||||||
func checkServer(config *Config) bool {
|
func checkServer(config *Config) bool {
|
||||||
fmt.Printf("%s🔍 Checking server status...%s\n", colors.Yellow, colors.Reset)
|
fmt.Printf("%s🔍 Checking server status...%s\n", colors.Yellow, colors.Reset)
|
||||||
|
|
||||||
@@ -151,6 +162,8 @@ func checkServer(config *Config) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cleanTestCache очищает кэш тестов Go
|
||||||
|
// Выполняет команду 'go clean -testcache'
|
||||||
func cleanTestCache() {
|
func cleanTestCache() {
|
||||||
fmt.Printf("%s🧹 Cleaning test cache...%s\n", colors.Yellow, colors.Reset)
|
fmt.Printf("%s🧹 Cleaning test cache...%s\n", colors.Yellow, colors.Reset)
|
||||||
cmd := exec.Command("go", "clean", "-testcache")
|
cmd := exec.Command("go", "clean", "-testcache")
|
||||||
@@ -160,6 +173,8 @@ func cleanTestCache() {
|
|||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getTestSuites возвращает список доступных наборов тестов
|
||||||
|
// Возвращает массив TestSuite с предопределенными настройками
|
||||||
func getTestSuites() []TestSuite {
|
func getTestSuites() []TestSuite {
|
||||||
return []TestSuite{
|
return []TestSuite{
|
||||||
{Name: "All", Pattern: "", Timeout: 30 * time.Minute},
|
{Name: "All", Pattern: "", Timeout: 30 * time.Minute},
|
||||||
@@ -173,6 +188,10 @@ func getTestSuites() []TestSuite {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runTests запускает выбранные наборы тестов
|
||||||
|
// Параметры:
|
||||||
|
// - config: конфигурация запуска
|
||||||
|
// Возвращает: массив результатов тестов
|
||||||
func runTests(config *Config) []TestResult {
|
func runTests(config *Config) []TestResult {
|
||||||
suites := getTestSuites()
|
suites := getTestSuites()
|
||||||
var selectedSuites []TestSuite
|
var selectedSuites []TestSuite
|
||||||
@@ -212,6 +231,12 @@ func runTests(config *Config) []TestResult {
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runTestsParallel запускает тесты параллельно
|
||||||
|
// Использует горутины и WaitGroup для синхронизации
|
||||||
|
// Параметры:
|
||||||
|
// - suites: список наборов тестов
|
||||||
|
// - config: конфигурация запуска
|
||||||
|
// Возвращает: массив результатов тестов
|
||||||
func runTestsParallel(suites []TestSuite, config *Config) []TestResult {
|
func runTestsParallel(suites []TestSuite, config *Config) []TestResult {
|
||||||
fmt.Printf("%s🚀 Running tests in parallel mode...%s\n\n", colors.Cyan, colors.Reset)
|
fmt.Printf("%s🚀 Running tests in parallel mode...%s\n\n", colors.Cyan, colors.Reset)
|
||||||
|
|
||||||
@@ -238,6 +263,12 @@ func runTestsParallel(suites []TestSuite, config *Config) []TestResult {
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runTestSuite запускает один набор тестов
|
||||||
|
// Выполняет команду 'go test' с соответствующими параметрами
|
||||||
|
// Параметры:
|
||||||
|
// - suite: набор тестов для запуска
|
||||||
|
// - config: конфигурация запуска
|
||||||
|
// Возвращает: результат выполнения тестов
|
||||||
func runTestSuite(suite TestSuite, config *Config) TestResult {
|
func runTestSuite(suite TestSuite, config *Config) TestResult {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
@@ -307,6 +338,9 @@ func runTestSuite(suite TestSuite, config *Config) TestResult {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractCoverage извлекает процент покрытия кода из файла coverage.out
|
||||||
|
// Выполняет команду 'go tool cover -func=coverage.out'
|
||||||
|
// Возвращает: процент покрытия (0 если не удалось извлечь)
|
||||||
func extractCoverage() float64 {
|
func extractCoverage() float64 {
|
||||||
// Проверяем наличие файла покрытия
|
// Проверяем наличие файла покрытия
|
||||||
if _, err := os.Stat("coverage.out"); os.IsNotExist(err) {
|
if _, err := os.Stat("coverage.out"); os.IsNotExist(err) {
|
||||||
@@ -337,11 +371,14 @@ func extractCoverage() float64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// printResults выводит сводную таблицу результатов тестов
|
||||||
|
// Параметры:
|
||||||
|
// - results: массив результатов тестов
|
||||||
func printResults(results []TestResult) {
|
func printResults(results []TestResult) {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("%s╔════════════════════════════════════════════════════════════════╗%s\n", colors.Cyan, colors.Reset)
|
fmt.Printf("%s╔══════════════════════════════════════════════════════════════════╗%s\n", colors.Cyan, colors.Reset)
|
||||||
fmt.Printf("%s║ Test Results ║%s\n", colors.Cyan, colors.Reset)
|
fmt.Printf("%s║ Test Results ║%s\n", colors.Cyan, colors.Reset)
|
||||||
fmt.Printf("%s╚════════════════════════════════════════════════════════════════╝%s\n", colors.Cyan, colors.Reset)
|
fmt.Printf("%s╚══════════════════════════════════════════════════════════════════╝%s\n", colors.Cyan, colors.Reset)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
// Таблица результатов
|
// Таблица результатов
|
||||||
@@ -388,6 +425,8 @@ func printResults(results []TestResult) {
|
|||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generateHTMLReport генерирует HTML отчет о покрытии кода
|
||||||
|
// Создает файл coverage.html и открывает его в браузере
|
||||||
func generateHTMLReport() {
|
func generateHTMLReport() {
|
||||||
fmt.Printf("%s📊 Generating HTML coverage report...%s\n", colors.Yellow, colors.Reset)
|
fmt.Printf("%s📊 Generating HTML coverage report...%s\n", colors.Yellow, colors.Reset)
|
||||||
|
|
||||||
@@ -415,6 +454,10 @@ func generateHTMLReport() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasFailures проверяет, есть ли неудачные тесты
|
||||||
|
// Параметры:
|
||||||
|
// - results: массив результатов тестов
|
||||||
|
// Возвращает: true если есть хотя бы один проваленный тест
|
||||||
func hasFailures(results []TestResult) bool {
|
func hasFailures(results []TestResult) bool {
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
if !result.Passed {
|
if !result.Passed {
|
||||||
@@ -424,7 +467,8 @@ func hasFailures(results []TestResult) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Интерактивный режим
|
// interactiveMode запускает интерактивный режим с меню выбора
|
||||||
|
// Позволяет пользователю выбирать тесты через консольное меню
|
||||||
func interactiveMode() {
|
func interactiveMode() {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
|
||||||
@@ -468,10 +512,11 @@ func interactiveMode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// printMenu выводит интерактивное меню выбора тестов
|
||||||
func printMenu() {
|
func printMenu() {
|
||||||
fmt.Printf("\n%s╔════════════════════════════════════════════════════════════════╗%s\n", colors.Cyan, colors.Reset)
|
fmt.Printf("\n%s╔══════════════════════════════════════════════════════════════════╗%s\n", colors.Cyan, colors.Reset)
|
||||||
fmt.Printf("%s║ Interactive Menu ║%s\n", colors.Cyan, colors.Reset)
|
fmt.Printf("%s║ Interactive Menu ║%s\n", colors.Cyan, colors.Reset)
|
||||||
fmt.Printf("%s╚════════════════════════════════════════════════════════════════╝%s\n", colors.Cyan, colors.Reset)
|
fmt.Printf("%s╚══════════════════════════════════════════════════════════════════╝%s\n", colors.Cyan, colors.Reset)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("%s1.%s Run all tests\n", colors.Green, colors.Reset)
|
fmt.Printf("%s1.%s Run all tests\n", colors.Green, colors.Reset)
|
||||||
fmt.Printf("%s2.%s Run auth tests only\n", colors.Green, colors.Reset)
|
fmt.Printf("%s2.%s Run auth tests only\n", colors.Green, colors.Reset)
|
||||||
@@ -487,6 +532,10 @@ func printMenu() {
|
|||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runWithConfig запускает тесты с заданной конфигурацией
|
||||||
|
// Используется в интерактивном режиме
|
||||||
|
// Параметры:
|
||||||
|
// - config: конфигурация запуска тестов
|
||||||
func runWithConfig(config *Config) {
|
func runWithConfig(config *Config) {
|
||||||
config.ServerURL = "http://localhost:8088"
|
config.ServerURL = "http://localhost:8088"
|
||||||
config.TestTimeout = 30 * time.Minute
|
config.TestTimeout = 30 * time.Minute
|
||||||
@@ -502,9 +551,12 @@ func runWithConfig(config *Config) {
|
|||||||
printResults(results)
|
printResults(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция для мониторинга сервера
|
// watchServer запускает мониторинг состояния сервера
|
||||||
|
// Периодически проверяет доступность сервера через health check
|
||||||
|
// Параметры:
|
||||||
|
// - config: конфигурация с адресом сервера
|
||||||
func watchServer(config *Config) {
|
func watchServer(config *Config) {
|
||||||
fmt.Printf("%s👁️ Starting server health monitor...%s\n", colors.Yellow, colors.Reset)
|
fmt.Printf("%s👋️ Starting server health monitor...%s\n", colors.Yellow, colors.Reset)
|
||||||
|
|
||||||
ticker := time.NewTicker(10 * time.Second)
|
ticker := time.NewTicker(10 * time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
@@ -516,15 +568,17 @@ func watchServer(config *Config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Структура для бенчмарков
|
// BenchmarkResult хранит результат бенчмарк теста
|
||||||
type BenchmarkResult struct {
|
type BenchmarkResult struct {
|
||||||
Name string
|
Name string // Название бенчмарка
|
||||||
Ops int64
|
Ops int64 // Количество операций
|
||||||
NsPerOp time.Duration
|
NsPerOp time.Duration // Наносекунд на операцию
|
||||||
Allocs int64
|
Allocs int64 // Количество аллокаций
|
||||||
Bytes int64
|
Bytes int64 // Количество байт
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runBenchmarks запускает бенчмарк тесты
|
||||||
|
// Выполняет команду 'go test -bench=. -benchmem'
|
||||||
func runBenchmarks() {
|
func runBenchmarks() {
|
||||||
fmt.Printf("%s🏃 Running benchmarks...%s\n\n", colors.Yellow, colors.Reset)
|
fmt.Printf("%s🏃 Running benchmarks...%s\n\n", colors.Yellow, colors.Reset)
|
||||||
|
|
||||||
@@ -538,7 +592,8 @@ func runBenchmarks() {
|
|||||||
fmt.Println(string(output))
|
fmt.Println(string(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Точка входа с поддержкой аргументов командной строки
|
// mainWithArgs - альтернативная точка входа с поддержкой режимов
|
||||||
|
// Обрабатывает специальные команды: interactive, bench, watch
|
||||||
func mainWithArgs() {
|
func mainWithArgs() {
|
||||||
if len(os.Args) > 1 && os.Args[1] == "interactive" {
|
if len(os.Args) > 1 && os.Args[1] == "interactive" {
|
||||||
interactiveMode()
|
interactiveMode()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// cmd/testrunner/runner.go - Дополнительные утилиты
|
// cmd/testrunner/runner.go - Дополнительные утилиты для тестового раннера
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -12,16 +12,20 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Сохранение результатов в JSON
|
// saveResultsToJSON сохраняет результаты тестов в JSON файл
|
||||||
|
// Параметры:
|
||||||
|
// - results: массив результатов тестов
|
||||||
|
// - filename: имя файла для сохранения
|
||||||
|
// Возвращает: ошибку при сохранении
|
||||||
func saveResultsToJSON(results []TestResult, filename string) error {
|
func saveResultsToJSON(results []TestResult, filename string) error {
|
||||||
type JSONResult struct {
|
type JSONResult struct {
|
||||||
Timestamp string `json:"timestamp"`
|
Timestamp string `json:"timestamp"` // Время сохранения отчета
|
||||||
Results []TestResult `json:"results"`
|
Results []TestResult `json:"results"` // Результаты тестов
|
||||||
Summary struct {
|
Summary struct {
|
||||||
Total int `json:"total"`
|
Total int `json:"total"` // Общее количество тестов
|
||||||
Passed int `json:"passed"`
|
Passed int `json:"passed"` // Количество пройденных
|
||||||
Failed int `json:"failed"`
|
Failed int `json:"failed"` // Количество проваленных
|
||||||
PassRate float64 `json:"pass_rate"`
|
PassRate float64 `json:"pass_rate"` // Процент прохождения
|
||||||
} `json:"summary"`
|
} `json:"summary"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,6 +33,7 @@ func saveResultsToJSON(results []TestResult, filename string) error {
|
|||||||
jsonResult.Timestamp = time.Now().Format(time.RFC3339)
|
jsonResult.Timestamp = time.Now().Format(time.RFC3339)
|
||||||
jsonResult.Results = results
|
jsonResult.Results = results
|
||||||
|
|
||||||
|
// Подсчет статистики
|
||||||
for _, r := range results {
|
for _, r := range results {
|
||||||
jsonResult.Summary.Total++
|
jsonResult.Summary.Total++
|
||||||
if r.Passed {
|
if r.Passed {
|
||||||
@@ -38,6 +43,7 @@ func saveResultsToJSON(results []TestResult, filename string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Расчет процента прохождения
|
||||||
if jsonResult.Summary.Total > 0 {
|
if jsonResult.Summary.Total > 0 {
|
||||||
jsonResult.Summary.PassRate = float64(jsonResult.Summary.Passed) / float64(jsonResult.Summary.Total) * 100
|
jsonResult.Summary.PassRate = float64(jsonResult.Summary.Passed) / float64(jsonResult.Summary.Total) * 100
|
||||||
}
|
}
|
||||||
@@ -50,30 +56,38 @@ func saveResultsToJSON(results []TestResult, filename string) error {
|
|||||||
return os.WriteFile(filename, data, 0644)
|
return os.WriteFile(filename, data, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создание отчета в формате JUnit XML (для CI/CD)
|
// generateJUnitReport создает отчет в формате JUnit XML для CI/CD систем
|
||||||
|
// Параметры:
|
||||||
|
// - results: массив результатов тестов
|
||||||
|
// - filename: имя файла для сохранения
|
||||||
|
// Возвращает: ошибку при сохранении
|
||||||
func generateJUnitReport(results []TestResult, filename string) error {
|
func generateJUnitReport(results []TestResult, filename string) error {
|
||||||
|
// TestCase представляет один тестовый случай в JUnit формате
|
||||||
type TestCase struct {
|
type TestCase struct {
|
||||||
Name string `xml:"name,attr"`
|
Name string `xml:"name,attr"` // Название теста
|
||||||
Classname string `xml:"classname,attr"`
|
Classname string `xml:"classname,attr"` // Класс теста
|
||||||
Time float64 `xml:"time,attr"`
|
Time float64 `xml:"time,attr"` // Время выполнения
|
||||||
Failure *string `xml:"failure,omitempty"`
|
Failure *string `xml:"failure,omitempty"` // Информация об ошибке (опционально)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestSuite представляет набор тестов в JUnit формате
|
||||||
type TestSuite struct {
|
type TestSuite struct {
|
||||||
Name string `xml:"name,attr"`
|
Name string `xml:"name,attr"` // Название набора
|
||||||
Tests int `xml:"tests,attr"`
|
Tests int `xml:"tests,attr"` // Количество тестов
|
||||||
Failures int `xml:"failures,attr"`
|
Failures int `xml:"failures,attr"` // Количество провалов
|
||||||
Time float64 `xml:"time,attr"`
|
Time float64 `xml:"time,attr"` // Общее время
|
||||||
TestCases []TestCase `xml:"testcase"`
|
TestCases []TestCase `xml:"testcase"` // Список тестов
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestSuites корневой элемент JUnit отчета
|
||||||
type TestSuites struct {
|
type TestSuites struct {
|
||||||
XMLName string `xml:"testsuites"`
|
XMLName string `xml:"testsuites"` // Имя корневого элемента
|
||||||
TestSuites []TestSuite `xml:"testsuite"`
|
TestSuites []TestSuite `xml:"testsuite"` // Список наборов тестов
|
||||||
}
|
}
|
||||||
|
|
||||||
var suites TestSuites
|
var suites TestSuites
|
||||||
|
|
||||||
|
// Конвертируем результаты в JUnit формат
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
suite := TestSuite{
|
suite := TestSuite{
|
||||||
Name: result.Suite,
|
Name: result.Suite,
|
||||||
@@ -87,6 +101,7 @@ func generateJUnitReport(results []TestResult, filename string) error {
|
|||||||
Time: result.Duration.Seconds(),
|
Time: result.Duration.Seconds(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Добавляем информацию об ошибке если тест провален
|
||||||
if !result.Passed {
|
if !result.Passed {
|
||||||
suite.Failures = 1
|
suite.Failures = 1
|
||||||
failureMsg := result.Error.Error()
|
failureMsg := result.Error.Error()
|
||||||
@@ -97,6 +112,7 @@ func generateJUnitReport(results []TestResult, filename string) error {
|
|||||||
suites.TestSuites = append(suites.TestSuites, suite)
|
suites.TestSuites = append(suites.TestSuites, suite)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Маршалинг в XML и сохранение
|
||||||
xmlData, err := xml.MarshalIndent(suites, "", " ")
|
xmlData, err := xml.MarshalIndent(suites, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -105,7 +121,12 @@ func generateJUnitReport(results []TestResult, filename string) error {
|
|||||||
return os.WriteFile(filename, append([]byte(xml.Header), xmlData...), 0644)
|
return os.WriteFile(filename, append([]byte(xml.Header), xmlData...), 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция для запуска определенных тестов по тегам
|
// runTestsByTag запускает тесты с определенным тегом (build tag)
|
||||||
|
// Позволяет фильтровать тесты по тегам
|
||||||
|
// Параметры:
|
||||||
|
// - tag: тег для фильтрации тестов
|
||||||
|
// - config: конфигурация запуска
|
||||||
|
// Возвращает: массив результатов тестов
|
||||||
func runTestsByTag(tag string, config *Config) []TestResult {
|
func runTestsByTag(tag string, config *Config) []TestResult {
|
||||||
fmt.Printf("%s🏷️ Running tests with tag: %s%s\n", colors.Yellow, tag, colors.Reset)
|
fmt.Printf("%s🏷️ Running tests with tag: %s%s\n", colors.Yellow, tag, colors.Reset)
|
||||||
|
|
||||||
@@ -128,7 +149,8 @@ func runTestsByTag(tag string, config *Config) []TestResult {
|
|||||||
return []TestResult{result}
|
return []TestResult{result}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создание скрипта для CI/CD
|
// generateCIScript создает bash скрипт для запуска тестов в CI/CD
|
||||||
|
// Скрипт автоматически запускает сервер, выполняет тесты и останавливает сервер
|
||||||
func generateCIScript() {
|
func generateCIScript() {
|
||||||
ciScript := `#!/bin/bash
|
ciScript := `#!/bin/bash
|
||||||
# CI/CD script for API tests
|
# CI/CD script for API tests
|
||||||
@@ -153,16 +175,28 @@ kill $SERVER_PID
|
|||||||
echo "Tests completed"
|
echo "Tests completed"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
// Создаем директорию scripts если ее нет
|
||||||
|
if err := os.MkdirAll("scripts", 0755); err != nil {
|
||||||
|
fmt.Printf("Failed to create scripts directory: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сохраняем скрипт и делаем его исполняемым
|
||||||
if err := os.WriteFile("scripts/ci_test.sh", []byte(ciScript), 0755); err != nil {
|
if err := os.WriteFile("scripts/ci_test.sh", []byte(ciScript), 0755); err != nil {
|
||||||
fmt.Printf("Failed to generate CI script: %v\n", err)
|
fmt.Printf("Failed to generate CI script: %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция для сравнения результатов тестов
|
// compareResults сравнивает результаты текущего запуска с предыдущим
|
||||||
|
// Показывает изменения в прохождении тестов и времени выполнения
|
||||||
|
// Параметры:
|
||||||
|
// - current: текущие результаты тестов
|
||||||
|
// - previous: предыдущие результаты для сравнения
|
||||||
func compareResults(current, previous []TestResult) {
|
func compareResults(current, previous []TestResult) {
|
||||||
fmt.Printf("%s📊 Test Result Comparison%s\n", colors.Cyan, colors.Reset)
|
fmt.Printf("%s📊 Test Result Comparison%s\n", colors.Cyan, colors.Reset)
|
||||||
fmt.Println(strings.Repeat("-", 50))
|
fmt.Println(strings.Repeat("-", 50))
|
||||||
|
|
||||||
|
// Создаем карты для быстрого доступа к результатам
|
||||||
currentMap := make(map[string]TestResult)
|
currentMap := make(map[string]TestResult)
|
||||||
for _, r := range current {
|
for _, r := range current {
|
||||||
currentMap[r.Suite] = r
|
currentMap[r.Suite] = r
|
||||||
@@ -173,13 +207,16 @@ func compareResults(current, previous []TestResult) {
|
|||||||
prevMap[r.Suite] = r
|
prevMap[r.Suite] = r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Сравниваем каждый набор тестов
|
||||||
for name, currentResult := range currentMap {
|
for name, currentResult := range currentMap {
|
||||||
prevResult, exists := prevMap[name]
|
prevResult, exists := prevMap[name]
|
||||||
if !exists {
|
if !exists {
|
||||||
|
// Новый набор тестов
|
||||||
fmt.Printf("%s➕ New test suite: %s%s\n", colors.Green, name, colors.Reset)
|
fmt.Printf("%s➕ New test suite: %s%s\n", colors.Green, name, colors.Reset)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Сравниваем статус прохождения
|
||||||
if currentResult.Passed != prevResult.Passed {
|
if currentResult.Passed != prevResult.Passed {
|
||||||
if currentResult.Passed {
|
if currentResult.Passed {
|
||||||
fmt.Printf("%s✅ Fixed: %s (was failing)%s\n", colors.Green, name, colors.Reset)
|
fmt.Printf("%s✅ Fixed: %s (was failing)%s\n", colors.Green, name, colors.Reset)
|
||||||
@@ -187,7 +224,7 @@ func compareResults(current, previous []TestResult) {
|
|||||||
fmt.Printf("%s❌ Regression: %s (was passing)%s\n", colors.Red, name, colors.Reset)
|
fmt.Printf("%s❌ Regression: %s (was passing)%s\n", colors.Red, name, colors.Reset)
|
||||||
}
|
}
|
||||||
} else if currentResult.Passed {
|
} else if currentResult.Passed {
|
||||||
// Сравнение времени выполнения
|
// Сравнение времени выполнения для успешных тестов
|
||||||
diff := currentResult.Duration - prevResult.Duration
|
diff := currentResult.Duration - prevResult.Duration
|
||||||
if diff > 0 {
|
if diff > 0 {
|
||||||
fmt.Printf("%s⚠️ Slower: %s (+%v)%s\n", colors.Yellow, name, diff.Round(time.Millisecond), colors.Reset)
|
fmt.Printf("%s⚠️ Slower: %s (+%v)%s\n", colors.Yellow, name, diff.Round(time.Millisecond), colors.Reset)
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestAccountEndpoints тестирует все эндпоинты управления аккаунтом пользователя
|
||||||
|
// Включает получение профиля, обновление данных, смену пароля и удаление аккаунта
|
||||||
func TestAccountEndpoints(t *testing.T) {
|
func TestAccountEndpoints(t *testing.T) {
|
||||||
config := testutils.NewTestConfig()
|
config := testutils.NewTestConfig()
|
||||||
|
|
||||||
|
// GetProfile тестирует получение профиля авторизованного пользователя
|
||||||
t.Run("GetProfile", func(t *testing.T) {
|
t.Run("GetProfile", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -27,6 +30,7 @@ func TestAccountEndpoints(t *testing.T) {
|
|||||||
t.Fatalf("Failed to parse response: %v", err)
|
t.Fatalf("Failed to parse response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем наличие всех обязательных полей в ответе
|
||||||
expectedFields := []string{"id", "email", "full_name", "first_name", "last_name", "role", "stats"}
|
expectedFields := []string{"id", "email", "full_name", "first_name", "last_name", "role", "stats"}
|
||||||
for _, field := range expectedFields {
|
for _, field := range expectedFields {
|
||||||
if _, ok := profile[field]; !ok {
|
if _, ok := profile[field]; !ok {
|
||||||
@@ -35,6 +39,7 @@ func TestAccountEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// GetOwnAccount тестирует получение данных собственного аккаунта
|
||||||
t.Run("GetOwnAccount", func(t *testing.T) {
|
t.Run("GetOwnAccount", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -50,6 +55,7 @@ func TestAccountEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// UpdateAccount тестирует обновление данных аккаунта (телефон, город)
|
||||||
t.Run("UpdateAccount", func(t *testing.T) {
|
t.Run("UpdateAccount", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -74,11 +80,13 @@ func TestAccountEndpoints(t *testing.T) {
|
|||||||
t.Fatalf("Failed to parse response: %v", err)
|
t.Fatalf("Failed to parse response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем, что номер телефона обновился корректно
|
||||||
if phone, ok := updatedAccount["phone"].(string); !ok || phone != "+79998887766" {
|
if phone, ok := updatedAccount["phone"].(string); !ok || phone != "+79998887766" {
|
||||||
t.Error("Phone number not updated correctly")
|
t.Error("Phone number not updated correctly")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ChangePassword тестирует успешную смену пароля
|
||||||
t.Run("ChangePassword", func(t *testing.T) {
|
t.Run("ChangePassword", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -107,6 +115,7 @@ func TestAccountEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ChangePasswordWrongCurrent тестирует смену пароля с неверным текущим паролем
|
||||||
t.Run("ChangePasswordWrongCurrent", func(t *testing.T) {
|
t.Run("ChangePasswordWrongCurrent", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -120,11 +129,13 @@ func TestAccountEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Ожидаем ошибку 400 или 401 при неверном текущем пароле
|
||||||
if resp.StatusCode != 400 && resp.StatusCode != 401 {
|
if resp.StatusCode != 400 && resp.StatusCode != 401 {
|
||||||
t.Errorf("Expected status 400 or 401, got %d", resp.StatusCode)
|
t.Errorf("Expected status 400 or 401, got %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// DeleteAccount тестирует удаление аккаунта
|
||||||
t.Run("DeleteAccount", func(t *testing.T) {
|
t.Run("DeleteAccount", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
|
|
||||||
@@ -138,13 +149,14 @@ func TestAccountEndpoints(t *testing.T) {
|
|||||||
t.Errorf("Expected status 200, got %d", resp.StatusCode)
|
t.Errorf("Expected status 200, got %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем, что больше нельзя войти
|
// Проверяем, что больше нельзя войти после удаления аккаунта
|
||||||
_, err = config.GetAuthToken(user.Email, user.Password)
|
_, err = config.GetAuthToken(user.Email, user.Password)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Should not be able to login after account deletion")
|
t.Error("Should not be able to login after account deletion")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// UnauthorizedAccess тестирует доступ к защищенным эндпоинтам без токена
|
||||||
t.Run("UnauthorizedAccess", func(t *testing.T) {
|
t.Run("UnauthorizedAccess", func(t *testing.T) {
|
||||||
resp, err := config.Request("GET", "/account/profile", nil, "")
|
resp, err := config.Request("GET", "/account/profile", nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,19 +5,22 @@ import (
|
|||||||
"api_yal/tests/testutils"
|
"api_yal/tests/testutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestAppealEndpoints тестирует все эндпоинты для работы с обращениями пользователей
|
||||||
|
// Включает создание, получение, обновление, удаление обращений и получение списка своих обращений
|
||||||
func TestAppealEndpoints(t *testing.T) {
|
func TestAppealEndpoints(t *testing.T) {
|
||||||
config := testutils.NewTestConfig()
|
config := testutils.NewTestConfig()
|
||||||
|
|
||||||
|
// CreateAppeal тестирует создание нового обращения
|
||||||
t.Run("CreateAppeal", func(t *testing.T) {
|
t.Run("CreateAppeal", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
|
|
||||||
appealData := map[string]interface{}{
|
appealData := map[string]interface{}{
|
||||||
"type": "complaint",
|
"type": "complaint",
|
||||||
"title": "Test Appeal",
|
"title": "Test Appeal",
|
||||||
"message": "This is a test appeal message for testing purposes",
|
"message": "This is a test appeal message for testing purposes",
|
||||||
"priority": "high",
|
"priority": "high",
|
||||||
"contact_name": "Test User",
|
"contact_name": "Test User",
|
||||||
"contact_email": user.Email,
|
"contact_email": user.Email,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +30,7 @@ func TestAppealEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Ожидаем статус 201 Created
|
||||||
if resp.StatusCode != 201 {
|
if resp.StatusCode != 201 {
|
||||||
t.Errorf("Expected status 201, got %d", resp.StatusCode)
|
t.Errorf("Expected status 201, got %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
@@ -36,20 +40,22 @@ func TestAppealEndpoints(t *testing.T) {
|
|||||||
t.Fatalf("Failed to parse response: %v", err)
|
t.Fatalf("Failed to parse response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем, что ID обращения присутствует в ответе
|
||||||
if _, ok := createdAppeal["id"]; !ok {
|
if _, ok := createdAppeal["id"]; !ok {
|
||||||
t.Error("Appeal ID not found")
|
t.Error("Appeal ID not found")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// GetAppealByID тестирует получение обращения по ID
|
||||||
t.Run("GetAppealByID", func(t *testing.T) {
|
t.Run("GetAppealByID", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
|
|
||||||
// Создаем обращение
|
// Создаем обращение
|
||||||
appealData := map[string]interface{}{
|
appealData := map[string]interface{}{
|
||||||
"type": "question",
|
"type": "question",
|
||||||
"title": "Test Question",
|
"title": "Test Question",
|
||||||
"message": "This is a test question",
|
"message": "This is a test question",
|
||||||
"contact_email": user.Email,
|
"contact_email": user.Email,
|
||||||
}
|
}
|
||||||
resp, err := config.Request("POST", "/appeals", appealData, user.Token)
|
resp, err := config.Request("POST", "/appeals", appealData, user.Token)
|
||||||
@@ -76,15 +82,16 @@ func TestAppealEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// UpdateAppeal тестирует обновление существующего обращения
|
||||||
t.Run("UpdateAppeal", func(t *testing.T) {
|
t.Run("UpdateAppeal", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
|
|
||||||
// Создаем обращение
|
// Создаем обращение
|
||||||
appealData := map[string]interface{}{
|
appealData := map[string]interface{}{
|
||||||
"type": "suggestion",
|
"type": "suggestion",
|
||||||
"title": "Original Title",
|
"title": "Original Title",
|
||||||
"message": "Original message for testing",
|
"message": "Original message for testing",
|
||||||
"contact_email": user.Email,
|
"contact_email": user.Email,
|
||||||
}
|
}
|
||||||
resp, err := config.Request("POST", "/appeals", appealData, user.Token)
|
resp, err := config.Request("POST", "/appeals", appealData, user.Token)
|
||||||
@@ -117,6 +124,7 @@ func TestAppealEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// MyAppeals тестирует получение списка обращений текущего пользователя
|
||||||
t.Run("MyAppeals", func(t *testing.T) {
|
t.Run("MyAppeals", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -145,6 +153,7 @@ func TestAppealEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// DeleteAppeal тестирует удаление обращения
|
||||||
t.Run("DeleteAppeal", func(t *testing.T) {
|
t.Run("DeleteAppeal", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -174,11 +183,12 @@ func TestAppealEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer deleteResp.Body.Close()
|
defer deleteResp.Body.Close()
|
||||||
|
|
||||||
|
// Ожидаем статус 204 No Content при успешном удалении
|
||||||
if deleteResp.StatusCode != 204 {
|
if deleteResp.StatusCode != 204 {
|
||||||
t.Errorf("Expected status 204, got %d", deleteResp.StatusCode)
|
t.Errorf("Expected status 204, got %d", deleteResp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем, что обращение удалено
|
// Проверяем, что обращение удалено - должно вернуть 404
|
||||||
getResp, err := config.Request("GET", "/appeals/"+string(rune(appealID)), nil, user.Token)
|
getResp, err := config.Request("GET", "/appeals/"+string(rune(appealID)), nil, user.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to get appeal: %v", err)
|
t.Fatalf("Failed to get appeal: %v", err)
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ import (
|
|||||||
"api_yal/tests/testutils"
|
"api_yal/tests/testutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestAuthFlow тестирует полный поток аутентификации пользователя
|
||||||
|
// Включает регистрацию, логин, обновление токена, выход, сброс пароля и мобильную авторизацию
|
||||||
func TestAuthFlow(t *testing.T) {
|
func TestAuthFlow(t *testing.T) {
|
||||||
config := testutils.NewTestConfig()
|
config := testutils.NewTestConfig()
|
||||||
|
|
||||||
|
// Register тестирует регистрацию нового пользователя
|
||||||
t.Run("Register", func(t *testing.T) {
|
t.Run("Register", func(t *testing.T) {
|
||||||
testData := map[string]interface{}{
|
testData := map[string]interface{}{
|
||||||
"email": "testflow@example.com",
|
"email": "testflow@example.com",
|
||||||
@@ -22,6 +25,7 @@ func TestAuthFlow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Ожидаем статус 201 Created при успешной регистрации
|
||||||
if resp.StatusCode != 201 {
|
if resp.StatusCode != 201 {
|
||||||
t.Errorf("Expected status 201, got %d", resp.StatusCode)
|
t.Errorf("Expected status 201, got %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
@@ -31,6 +35,7 @@ func TestAuthFlow(t *testing.T) {
|
|||||||
t.Fatalf("Failed to parse response: %v", err)
|
t.Fatalf("Failed to parse response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем наличие токена и данных пользователя в ответе
|
||||||
if _, ok := result["token"]; !ok {
|
if _, ok := result["token"]; !ok {
|
||||||
t.Error("Token not found in response")
|
t.Error("Token not found in response")
|
||||||
}
|
}
|
||||||
@@ -40,6 +45,7 @@ func TestAuthFlow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Login тестирует вход существующего пользователя
|
||||||
t.Run("Login", func(t *testing.T) {
|
t.Run("Login", func(t *testing.T) {
|
||||||
// Сначала создаем пользователя
|
// Сначала создаем пользователя
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
@@ -82,6 +88,7 @@ func TestAuthFlow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// RefreshToken тестирует обновление access токена через refresh token
|
||||||
t.Run("RefreshToken", func(t *testing.T) {
|
t.Run("RefreshToken", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -107,6 +114,7 @@ func TestAuthFlow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Logout тестирует выход пользователя из системы
|
||||||
t.Run("Logout", func(t *testing.T) {
|
t.Run("Logout", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -126,11 +134,13 @@ func TestAuthFlow(t *testing.T) {
|
|||||||
t.Fatalf("Failed to parse response: %v", err)
|
t.Fatalf("Failed to parse response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем сообщение о успешном выходе
|
||||||
if msg, ok := result["message"]; !ok || msg != "Successfully logged out" {
|
if msg, ok := result["message"]; !ok || msg != "Successfully logged out" {
|
||||||
t.Error("Logout message not as expected")
|
t.Error("Logout message not as expected")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// PasswordReset тестирует полный поток сброса пароля
|
||||||
t.Run("PasswordReset", func(t *testing.T) {
|
t.Run("PasswordReset", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -153,12 +163,13 @@ func TestAuthFlow(t *testing.T) {
|
|||||||
t.Fatalf("Failed to parse response: %v", err)
|
t.Fatalf("Failed to parse response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Получаем токен сброса из ответа (в реальном API он приходит на email)
|
||||||
resetToken, ok := result["token"].(string)
|
resetToken, ok := result["token"].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("Reset token not found in response")
|
t.Fatal("Reset token not found in response")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Подтверждение сброса пароля
|
// Подтверждение сброса пароля с новым паролем
|
||||||
newPassword := "newpassword789"
|
newPassword := "newpassword789"
|
||||||
resp, err = config.Request("POST", "/auth/password-reset/confirm", map[string]interface{}{
|
resp, err = config.Request("POST", "/auth/password-reset/confirm", map[string]interface{}{
|
||||||
"token": resetToken,
|
"token": resetToken,
|
||||||
@@ -183,6 +194,7 @@ func TestAuthFlow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// MobileLogin тестирует мобильную авторизацию (возвращает access и refresh токены)
|
||||||
t.Run("MobileLogin", func(t *testing.T) {
|
t.Run("MobileLogin", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -205,6 +217,7 @@ func TestAuthFlow(t *testing.T) {
|
|||||||
t.Fatalf("Failed to parse response: %v", err)
|
t.Fatalf("Failed to parse response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Для мобильной авторизации ожидаем специальные поля
|
||||||
requiredFields := []string{"access_token", "refresh_token", "expires_at", "user"}
|
requiredFields := []string{"access_token", "refresh_token", "expires_at", "user"}
|
||||||
for _, field := range requiredFields {
|
for _, field := range requiredFields {
|
||||||
if _, ok := result[field]; !ok {
|
if _, ok := result[field]; !ok {
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ import (
|
|||||||
"api_yal/tests/testutils"
|
"api_yal/tests/testutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestCommentEndpoints тестирует все эндпоинты для работы с комментариями к отзывам
|
||||||
|
// Включает создание, получение, обновление, удаление комментариев и получение статистики
|
||||||
func TestCommentEndpoints(t *testing.T) {
|
func TestCommentEndpoints(t *testing.T) {
|
||||||
config := testutils.NewTestConfig()
|
config := testutils.NewTestConfig()
|
||||||
|
|
||||||
|
// CreateComment тестирует создание нового комментария к отзыву
|
||||||
t.Run("CreateComment", func(t *testing.T) {
|
t.Run("CreateComment", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -40,6 +43,7 @@ func TestCommentEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// GetCommentByID тестирует получение комментария по ID
|
||||||
t.Run("GetCommentByID", func(t *testing.T) {
|
t.Run("GetCommentByID", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -65,6 +69,7 @@ func TestCommentEndpoints(t *testing.T) {
|
|||||||
|
|
||||||
commentID := createdComment["id"].(float64)
|
commentID := createdComment["id"].(float64)
|
||||||
|
|
||||||
|
// Получение комментария доступно без авторизации
|
||||||
getResp, err := config.Request("GET", "/comments/"+string(rune(commentID)), nil, "")
|
getResp, err := config.Request("GET", "/comments/"+string(rune(commentID)), nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to get comment: %v", err)
|
t.Fatalf("Failed to get comment: %v", err)
|
||||||
@@ -76,6 +81,7 @@ func TestCommentEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// UpdateComment тестирует обновление текста комментария автором
|
||||||
t.Run("UpdateComment", func(t *testing.T) {
|
t.Run("UpdateComment", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -116,6 +122,7 @@ func TestCommentEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ListComments тестирует получение списка комментариев с пагинацией
|
||||||
t.Run("ListComments", func(t *testing.T) {
|
t.Run("ListComments", func(t *testing.T) {
|
||||||
resp, err := config.Request("GET", "/comments?page=1&page_size=20", nil, "")
|
resp, err := config.Request("GET", "/comments?page=1&page_size=20", nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -128,6 +135,7 @@ func TestCommentEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// MyComments тестирует получение комментариев текущего пользователя
|
||||||
t.Run("MyComments", func(t *testing.T) {
|
t.Run("MyComments", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -143,6 +151,7 @@ func TestCommentEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// CommentsByFeedback тестирует получение комментариев для конкретного отзыва
|
||||||
t.Run("CommentsByFeedback", func(t *testing.T) {
|
t.Run("CommentsByFeedback", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -161,6 +170,7 @@ func TestCommentEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// CommentStats тестирует получение статистики по комментариям
|
||||||
t.Run("CommentStats", func(t *testing.T) {
|
t.Run("CommentStats", func(t *testing.T) {
|
||||||
resp, err := config.Request("GET", "/comments/stats", nil, "")
|
resp, err := config.Request("GET", "/comments/stats", nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -177,6 +187,7 @@ func TestCommentEndpoints(t *testing.T) {
|
|||||||
t.Fatalf("Failed to parse response: %v", err)
|
t.Fatalf("Failed to parse response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем наличие полей статистики
|
||||||
expectedFields := []string{"total_comments", "verified_comments", "unverified_comments"}
|
expectedFields := []string{"total_comments", "verified_comments", "unverified_comments"}
|
||||||
for _, field := range expectedFields {
|
for _, field := range expectedFields {
|
||||||
if _, ok := stats[field]; !ok {
|
if _, ok := stats[field]; !ok {
|
||||||
@@ -185,6 +196,7 @@ func TestCommentEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// DeleteComment тестирует удаление комментария автором
|
||||||
t.Run("DeleteComment", func(t *testing.T) {
|
t.Run("DeleteComment", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ import (
|
|||||||
"api_yal/tests/testutils"
|
"api_yal/tests/testutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestFeedbackEndpoints тестирует все эндпоинты для работы с отзывами
|
||||||
|
// Включает создание, получение, обновление, удаление отзывов, поиск и статистику
|
||||||
func TestFeedbackEndpoints(t *testing.T) {
|
func TestFeedbackEndpoints(t *testing.T) {
|
||||||
config := testutils.NewTestConfig()
|
config := testutils.NewTestConfig()
|
||||||
|
|
||||||
|
// CreateFeedback тестирует создание нового отзыва
|
||||||
t.Run("CreateFeedback", func(t *testing.T) {
|
t.Run("CreateFeedback", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -41,6 +44,7 @@ func TestFeedbackEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// GetFeedbackByID тестирует получение отзыва по ID
|
||||||
t.Run("GetFeedbackByID", func(t *testing.T) {
|
t.Run("GetFeedbackByID", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -59,6 +63,7 @@ func TestFeedbackEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// UpdateFeedback тестирует обновление отзыва автором
|
||||||
t.Run("UpdateFeedback", func(t *testing.T) {
|
t.Run("UpdateFeedback", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -91,6 +96,7 @@ func TestFeedbackEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ListFeedbacks тестирует получение списка отзывов с пагинацией
|
||||||
t.Run("ListFeedbacks", func(t *testing.T) {
|
t.Run("ListFeedbacks", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -106,6 +112,7 @@ func TestFeedbackEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// MyFeedbacks тестирует получение отзывов текущего пользователя
|
||||||
t.Run("MyFeedbacks", func(t *testing.T) {
|
t.Run("MyFeedbacks", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -121,6 +128,7 @@ func TestFeedbackEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// FeedbacksByObject тестирует получение отзывов для конкретного объекта
|
||||||
t.Run("FeedbacksByObject", func(t *testing.T) {
|
t.Run("FeedbacksByObject", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -138,6 +146,7 @@ func TestFeedbackEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// FeedbacksByPlatform тестирует получение отзывов по платформе
|
||||||
t.Run("FeedbacksByPlatform", func(t *testing.T) {
|
t.Run("FeedbacksByPlatform", func(t *testing.T) {
|
||||||
resp, err := config.Request("GET", "/feedbacks/platform/tourist", nil, "")
|
resp, err := config.Request("GET", "/feedbacks/platform/tourist", nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -150,6 +159,7 @@ func TestFeedbackEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// SearchFeedbacks тестирует поиск отзывов по тексту
|
||||||
t.Run("SearchFeedbacks", func(t *testing.T) {
|
t.Run("SearchFeedbacks", func(t *testing.T) {
|
||||||
resp, err := config.Request("GET", "/feedbacks/search?q=excellent", nil, "")
|
resp, err := config.Request("GET", "/feedbacks/search?q=excellent", nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -162,6 +172,7 @@ func TestFeedbackEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// FeedbackStats тестирует получение статистики по отзывам
|
||||||
t.Run("FeedbackStats", func(t *testing.T) {
|
t.Run("FeedbackStats", func(t *testing.T) {
|
||||||
resp, err := config.Request("GET", "/feedbacks/stats", nil, "")
|
resp, err := config.Request("GET", "/feedbacks/stats", nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -178,6 +189,7 @@ func TestFeedbackEndpoints(t *testing.T) {
|
|||||||
t.Fatalf("Failed to parse response: %v", err)
|
t.Fatalf("Failed to parse response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем наличие полей статистики
|
||||||
expectedFields := []string{"total_feedbacks", "average_rating", "rating_distribution", "platform_stats"}
|
expectedFields := []string{"total_feedbacks", "average_rating", "rating_distribution", "platform_stats"}
|
||||||
for _, field := range expectedFields {
|
for _, field := range expectedFields {
|
||||||
if _, ok := stats[field]; !ok {
|
if _, ok := stats[field]; !ok {
|
||||||
@@ -186,6 +198,7 @@ func TestFeedbackEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// DeleteFeedback тестирует удаление отзыва
|
||||||
t.Run("DeleteFeedback", func(t *testing.T) {
|
t.Run("DeleteFeedback", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -199,11 +212,12 @@ func TestFeedbackEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Ожидаем статус 204 No Content при успешном удалении
|
||||||
if resp.StatusCode != 204 {
|
if resp.StatusCode != 204 {
|
||||||
t.Errorf("Expected status 204, got %d", resp.StatusCode)
|
t.Errorf("Expected status 204, got %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем, что отзыв удален
|
// Проверяем, что отзыв удален - должен вернуть 404
|
||||||
getResp, err := config.Request("GET", "/feedbacks/"+string(rune(feedbackID)), nil, "")
|
getResp, err := config.Request("GET", "/feedbacks/"+string(rune(feedbackID)), nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to get feedback: %v", err)
|
t.Fatalf("Failed to get feedback: %v", err)
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestObjectEndpoints тестирует все эндпоинты для работы с объектами (местами, заведениями)
|
||||||
|
// Включает создание, получение, обновление, удаление объектов, поиск и геопоиск
|
||||||
func TestObjectEndpoints(t *testing.T) {
|
func TestObjectEndpoints(t *testing.T) {
|
||||||
config := testutils.NewTestConfig()
|
config := testutils.NewTestConfig()
|
||||||
|
|
||||||
|
// CreateObject тестирует создание нового объекта пользователем
|
||||||
t.Run("CreateObject", func(t *testing.T) {
|
t.Run("CreateObject", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -52,6 +55,7 @@ func TestObjectEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// GetObjectByID тестирует получение объекта по ID
|
||||||
t.Run("GetObjectByID", func(t *testing.T) {
|
t.Run("GetObjectByID", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -78,6 +82,7 @@ func TestObjectEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// GetNonExistentObject тестирует получение несуществующего объекта
|
||||||
t.Run("GetNonExistentObject", func(t *testing.T) {
|
t.Run("GetNonExistentObject", func(t *testing.T) {
|
||||||
resp, err := config.Request("GET", "/objects/999999", nil, "")
|
resp, err := config.Request("GET", "/objects/999999", nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -90,6 +95,7 @@ func TestObjectEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// UpdateObject тестирует обновление объекта его владельцем
|
||||||
t.Run("UpdateObject", func(t *testing.T) {
|
t.Run("UpdateObject", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -122,6 +128,7 @@ func TestObjectEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// UpdateObjectUnauthorized тестирует попытку обновления чужого объекта
|
||||||
t.Run("UpdateObjectUnauthorized", func(t *testing.T) {
|
t.Run("UpdateObjectUnauthorized", func(t *testing.T) {
|
||||||
user1 := config.CreateTestUser(t)
|
user1 := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user1)
|
defer config.CleanupTestUser(t, user1)
|
||||||
@@ -131,6 +138,7 @@ func TestObjectEndpoints(t *testing.T) {
|
|||||||
|
|
||||||
objectID := config.CreateTestObject(t, user1.Token)
|
objectID := config.CreateTestObject(t, user1.Token)
|
||||||
|
|
||||||
|
// Пользователь user2 пытается обновить объект user1
|
||||||
resp, err := config.Request("PUT", "/objects/"+string(rune(objectID)), map[string]interface{}{
|
resp, err := config.Request("PUT", "/objects/"+string(rune(objectID)), map[string]interface{}{
|
||||||
"short_name": "Hacked Name",
|
"short_name": "Hacked Name",
|
||||||
}, user2.Token)
|
}, user2.Token)
|
||||||
@@ -139,11 +147,13 @@ func TestObjectEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Ожидаем ошибку 403 Forbidden
|
||||||
if resp.StatusCode != 403 {
|
if resp.StatusCode != 403 {
|
||||||
t.Errorf("Expected status 403, got %d", resp.StatusCode)
|
t.Errorf("Expected status 403, got %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ListObjects тестирует получение списка объектов с пагинацией
|
||||||
t.Run("ListObjects", func(t *testing.T) {
|
t.Run("ListObjects", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -173,6 +183,7 @@ func TestObjectEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// SearchObjects тестирует поиск объектов по тексту
|
||||||
t.Run("SearchObjects", func(t *testing.T) {
|
t.Run("SearchObjects", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -195,9 +206,10 @@ func TestObjectEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Search results: %+v", searchResults)
|
t.Logf("Search results: %+v", searchResults)
|
||||||
_ = objectID
|
_ = objectID // Используем переменную для избежания предупреждения
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// NearbyObjects тестирует поиск объектов рядом с заданными координатами
|
||||||
t.Run("NearbyObjects", func(t *testing.T) {
|
t.Run("NearbyObjects", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -213,6 +225,7 @@ func TestObjectEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// DeleteObject тестирует удаление объекта
|
||||||
t.Run("DeleteObject", func(t *testing.T) {
|
t.Run("DeleteObject", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -229,7 +242,7 @@ func TestObjectEndpoints(t *testing.T) {
|
|||||||
t.Errorf("Expected status 204, got %d", resp.StatusCode)
|
t.Errorf("Expected status 204, got %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем, что объект удален
|
// Проверяем, что объект удален - должен вернуть 404
|
||||||
getResp, err := config.Request("GET", "/objects/"+string(rune(objectID)), nil, "")
|
getResp, err := config.Request("GET", "/objects/"+string(rune(objectID)), nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to get object: %v", err)
|
t.Fatalf("Failed to get object: %v", err)
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ import (
|
|||||||
"api_yal/tests/testutils"
|
"api_yal/tests/testutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestRatingEndpoints тестирует все эндпоинты для работы с рейтингами и голосованием
|
||||||
|
// Включает создание рейтинга, голосование, изменение голоса и получение статистики
|
||||||
func TestRatingEndpoints(t *testing.T) {
|
func TestRatingEndpoints(t *testing.T) {
|
||||||
config := testutils.NewTestConfig()
|
config := testutils.NewTestConfig()
|
||||||
|
|
||||||
|
// CreateRating тестирует создание нового рейтинга для объекта
|
||||||
t.Run("CreateRating", func(t *testing.T) {
|
t.Run("CreateRating", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -30,6 +33,7 @@ func TestRatingEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// CastVote тестирует голосование в рейтинге
|
||||||
t.Run("CastVote", func(t *testing.T) {
|
t.Run("CastVote", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -54,7 +58,7 @@ func TestRatingEndpoints(t *testing.T) {
|
|||||||
|
|
||||||
ratingID := createdRating["id"].(float64)
|
ratingID := createdRating["id"].(float64)
|
||||||
|
|
||||||
// Голосуем
|
// Голосуем с оценкой 5
|
||||||
voteData := map[string]interface{}{
|
voteData := map[string]interface{}{
|
||||||
"score": 5,
|
"score": 5,
|
||||||
}
|
}
|
||||||
@@ -70,6 +74,7 @@ func TestRatingEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// GetMyVote тестирует получение голоса текущего пользователя
|
||||||
t.Run("GetMyVote", func(t *testing.T) {
|
t.Run("GetMyVote", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -118,11 +123,13 @@ func TestRatingEndpoints(t *testing.T) {
|
|||||||
t.Fatalf("Failed to parse response: %v", err)
|
t.Fatalf("Failed to parse response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем, что пользователь уже голосовал
|
||||||
if hasVoted, ok := voteInfo["has_voted"].(bool); !ok || !hasVoted {
|
if hasVoted, ok := voteInfo["has_voted"].(bool); !ok || !hasVoted {
|
||||||
t.Error("has_voted should be true")
|
t.Error("has_voted should be true")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// UpdateMyVote тестирует обновление голоса пользователя
|
||||||
t.Run("UpdateMyVote", func(t *testing.T) {
|
t.Run("UpdateMyVote", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -155,7 +162,7 @@ func TestRatingEndpoints(t *testing.T) {
|
|||||||
t.Fatalf("Failed to cast vote: %v", err)
|
t.Fatalf("Failed to cast vote: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновляем голос
|
// Обновляем голос с оценки 5 на 4
|
||||||
updateData := map[string]interface{}{
|
updateData := map[string]interface{}{
|
||||||
"score": 4,
|
"score": 4,
|
||||||
}
|
}
|
||||||
@@ -170,6 +177,7 @@ func TestRatingEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// GetRatingStats тестирует получение статистики по рейтингам
|
||||||
t.Run("GetRatingStats", func(t *testing.T) {
|
t.Run("GetRatingStats", func(t *testing.T) {
|
||||||
resp, err := config.Request("GET", "/ratings/stats", nil, "")
|
resp, err := config.Request("GET", "/ratings/stats", nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -186,6 +194,7 @@ func TestRatingEndpoints(t *testing.T) {
|
|||||||
t.Fatalf("Failed to parse response: %v", err)
|
t.Fatalf("Failed to parse response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем наличие полей статистики
|
||||||
expectedFields := []string{"total_ratings", "total_votes", "platform_distribution"}
|
expectedFields := []string{"total_ratings", "total_votes", "platform_distribution"}
|
||||||
for _, field := range expectedFields {
|
for _, field := range expectedFields {
|
||||||
if _, ok := stats[field]; !ok {
|
if _, ok := stats[field]; !ok {
|
||||||
@@ -194,6 +203,7 @@ func TestRatingEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// DeleteMyVote тестирует удаление голоса пользователя
|
||||||
t.Run("DeleteMyVote", func(t *testing.T) {
|
t.Run("DeleteMyVote", func(t *testing.T) {
|
||||||
user := config.CreateTestUser(t)
|
user := config.CreateTestUser(t)
|
||||||
defer config.CleanupTestUser(t, user)
|
defer config.CleanupTestUser(t, user)
|
||||||
@@ -233,6 +243,7 @@ func TestRatingEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer deleteResp.Body.Close()
|
defer deleteResp.Body.Close()
|
||||||
|
|
||||||
|
// Ожидаем статус 204 No Content при успешном удалении
|
||||||
if deleteResp.StatusCode != 204 {
|
if deleteResp.StatusCode != 204 {
|
||||||
t.Errorf("Expected status 204, got %d", deleteResp.StatusCode)
|
t.Errorf("Expected status 204, got %d", deleteResp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
package testutils
|
|
||||||
@@ -2,36 +2,46 @@ package testutils
|
|||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
|
// CreateObjectRequest представляет структуру запроса для создания объекта
|
||||||
|
// Используется в тестах для передачи данных при создании нового объекта
|
||||||
type CreateObjectRequest struct {
|
type CreateObjectRequest struct {
|
||||||
ShortName string `json:"short_name"`
|
ShortName string `json:"short_name"` // Короткое название объекта
|
||||||
LongName string `json:"long_name"`
|
LongName string `json:"long_name"` // Полное название объекта
|
||||||
Type string `json:"type"`
|
Type string `json:"type"` // Тип объекта (cafe, restaurant, museum и т.д.)
|
||||||
Phone string `json:"phone"`
|
Phone string `json:"phone"` // Контактный телефон
|
||||||
Email string `json:"email"`
|
Email string `json:"email"` // Email для связи
|
||||||
Site string `json:"site"`
|
Site string `json:"site"` // Веб-сайт объекта
|
||||||
ShortDescription string `json:"short_description"`
|
ShortDescription string `json:"short_description"` // Краткое описание
|
||||||
Description string `json:"description"`
|
Description string `json:"description"` // Полное описание
|
||||||
Address string `json:"address"`
|
Address string `json:"address"` // Физический адрес
|
||||||
Latitude float64 `json:"latitude"`
|
Latitude float64 `json:"latitude"` // Географическая широта
|
||||||
Longitude float64 `json:"longitude"`
|
Longitude float64 `json:"longitude"` // Географическая долгота
|
||||||
IsActive bool `json:"is_active"`
|
IsActive bool `json:"is_active"` // Активен ли объект
|
||||||
IsVerified bool `json:"is_verified"`
|
IsVerified bool `json:"is_verified"` // Подтвержден ли объект
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateFeedbackRequest представляет структуру запроса для создания отзыва
|
||||||
type CreateFeedbackRequest struct {
|
type CreateFeedbackRequest struct {
|
||||||
ObjectID uint `json:"object_id"`
|
ObjectID uint `json:"object_id"` // ID объекта, на который оставляется отзыв
|
||||||
Rating int `json:"rating"`
|
Rating int `json:"rating"` // Оценка от 1 до 5
|
||||||
Text string `json:"text"`
|
Text string `json:"text"` // Текст отзыва
|
||||||
Platform string `json:"platform"`
|
Platform string `json:"platform"` // Платформа, с которой оставлен отзыв (tourist, expert, local)
|
||||||
MediaURLs []string `json:"media_urls"`
|
MediaURLs []string `json:"media_urls"` // Ссылки на медиафайлы (фото, видео)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateCommentRequest представляет структуру запроса для создания комментария
|
||||||
type CreateCommentRequest struct {
|
type CreateCommentRequest struct {
|
||||||
FeedbackID uint `json:"feedback_id"`
|
FeedbackID uint `json:"feedback_id"` // ID отзыва, к которому оставляется комментарий
|
||||||
Text string `json:"text"`
|
Text string `json:"text"` // Текст комментария
|
||||||
ParentID *uint `json:"parent_id"`
|
ParentID *uint `json:"parent_id"` // ID родительского комментария (для вложенных комментариев)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateTestObject создает тестовый объект и возвращает его ID
|
||||||
|
// Используется в тестах для подготовки тестовых данных
|
||||||
|
// Параметры:
|
||||||
|
// - t: указатель на тест для логирования ошибок
|
||||||
|
// - token: JWT токен авторизованного пользователя
|
||||||
|
// Возвращает: ID созданного объекта
|
||||||
func (c *TestConfig) CreateTestObject(t *testing.T, token string) uint {
|
func (c *TestConfig) CreateTestObject(t *testing.T, token string) uint {
|
||||||
req := CreateObjectRequest{
|
req := CreateObjectRequest{
|
||||||
ShortName: "Test Object",
|
ShortName: "Test Object",
|
||||||
@@ -67,6 +77,13 @@ func (c *TestConfig) CreateTestObject(t *testing.T, token string) uint {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateTestFeedback создает тестовый отзыв и возвращает его ID
|
||||||
|
// Используется в тестах для подготовки тестовых данных
|
||||||
|
// Параметры:
|
||||||
|
// - t: указатель на тест для логирования ошибок
|
||||||
|
// - token: JWT токен авторизованного пользователя
|
||||||
|
// - objectID: ID объекта, к которому создается отзыв
|
||||||
|
// Возвращает: ID созданного отзыва
|
||||||
func (c *TestConfig) CreateTestFeedback(t *testing.T, token string, objectID uint) uint {
|
func (c *TestConfig) CreateTestFeedback(t *testing.T, token string, objectID uint) uint {
|
||||||
req := CreateFeedbackRequest{
|
req := CreateFeedbackRequest{
|
||||||
ObjectID: objectID,
|
ObjectID: objectID,
|
||||||
|
|||||||
@@ -11,20 +11,26 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestConfig хранит конфигурацию для тестов
|
||||||
|
// Содержит базовый URL API и HTTP клиент с поддержкой cookies
|
||||||
type TestConfig struct {
|
type TestConfig struct {
|
||||||
BaseURL string
|
BaseURL string // Базовый URL API сервера
|
||||||
Client *http.Client
|
Client *http.Client // HTTP клиент с поддержкой cookies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestUser хранит данные тестового пользователя
|
||||||
type TestUser struct {
|
type TestUser struct {
|
||||||
Email string
|
Email string // Email пользователя
|
||||||
Password string
|
Password string // Пароль пользователя
|
||||||
FirstName string
|
FirstName string // Имя пользователя
|
||||||
LastName string
|
LastName string // Фамилия пользователя
|
||||||
Token string
|
Token string // JWT токен доступа
|
||||||
UserID uint
|
UserID uint // ID пользователя в системе
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTestConfig создает новую конфигурацию для тестов
|
||||||
|
// Настраивает HTTP клиент с поддержкой cookies и таймаутом 30 секунд
|
||||||
|
// Возвращает указатель на TestConfig
|
||||||
func NewTestConfig() *TestConfig {
|
func NewTestConfig() *TestConfig {
|
||||||
jar, _ := cookiejar.New(nil)
|
jar, _ := cookiejar.New(nil)
|
||||||
return &TestConfig{
|
return &TestConfig{
|
||||||
@@ -36,6 +42,13 @@ func NewTestConfig() *TestConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
func (c *TestConfig) Request(method, path string, body interface{}, token string) (*http.Response, error) {
|
||||||
var reqBody io.Reader
|
var reqBody io.Reader
|
||||||
if body != nil {
|
if body != nil {
|
||||||
@@ -59,6 +72,11 @@ func (c *TestConfig) Request(method, path string, body interface{}, token string
|
|||||||
return c.Client.Do(req)
|
return c.Client.Do(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseResponse парсит JSON ответ HTTP запроса в указанную структуру
|
||||||
|
// Параметры:
|
||||||
|
// - resp: HTTP ответ для парсинга
|
||||||
|
// - target: указатель на структуру, в которую нужно распарсить JSON
|
||||||
|
// Возвращает: ошибку парсинга
|
||||||
func (c *TestConfig) ParseResponse(resp *http.Response, target interface{}) error {
|
func (c *TestConfig) ParseResponse(resp *http.Response, target interface{}) error {
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
@@ -68,6 +86,11 @@ func (c *TestConfig) ParseResponse(resp *http.Response, target interface{}) erro
|
|||||||
return json.Unmarshal(body, target)
|
return json.Unmarshal(body, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateTestUser создает тестового пользователя через API регистрации
|
||||||
|
// Автоматически генерирует уникальный email на основе timestamp
|
||||||
|
// Параметры:
|
||||||
|
// - t: указатель на тест для логирования ошибок
|
||||||
|
// Возвращает: указатель на созданного TestUser с заполненными полями (включая токен)
|
||||||
func (c *TestConfig) CreateTestUser(t *testing.T) *TestUser {
|
func (c *TestConfig) CreateTestUser(t *testing.T) *TestUser {
|
||||||
user := &TestUser{
|
user := &TestUser{
|
||||||
Email: fmt.Sprintf("test_%d@example.com", time.Now().UnixNano()),
|
Email: fmt.Sprintf("test_%d@example.com", time.Now().UnixNano()),
|
||||||
@@ -92,9 +115,11 @@ func (c *TestConfig) CreateTestUser(t *testing.T) *TestUser {
|
|||||||
t.Fatalf("Failed to parse response: %v", err)
|
t.Fatalf("Failed to parse response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Извлекаем токен из ответа
|
||||||
if token, ok := result["token"].(string); ok {
|
if token, ok := result["token"].(string); ok {
|
||||||
user.Token = token
|
user.Token = token
|
||||||
}
|
}
|
||||||
|
// Извлекаем ID пользователя из ответа
|
||||||
if userData, ok := result["user"].(map[string]interface{}); ok {
|
if userData, ok := result["user"].(map[string]interface{}); ok {
|
||||||
if id, ok := userData["id"].(float64); ok {
|
if id, ok := userData["id"].(float64); ok {
|
||||||
user.UserID = uint(id)
|
user.UserID = uint(id)
|
||||||
@@ -104,6 +129,11 @@ func (c *TestConfig) CreateTestUser(t *testing.T) *TestUser {
|
|||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CleanupTestUser удаляет тестового пользователя через API
|
||||||
|
// Вызывается через defer после создания пользователя для очистки
|
||||||
|
// Параметры:
|
||||||
|
// - t: указатель на тест для логирования предупреждений
|
||||||
|
// - user: тестовый пользователь для удаления
|
||||||
func (c *TestConfig) CleanupTestUser(t *testing.T, user *TestUser) {
|
func (c *TestConfig) CleanupTestUser(t *testing.T, user *TestUser) {
|
||||||
if user.Token != "" {
|
if user.Token != "" {
|
||||||
_, err := c.Request("DELETE", "/account", nil, user.Token)
|
_, err := c.Request("DELETE", "/account", nil, user.Token)
|
||||||
@@ -113,6 +143,11 @@ func (c *TestConfig) CleanupTestUser(t *testing.T, user *TestUser) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAuthToken выполняет вход пользователя и возвращает JWT токен
|
||||||
|
// Параметры:
|
||||||
|
// - email: email пользователя
|
||||||
|
// - password: пароль пользователя
|
||||||
|
// Возвращает: JWT токен и ошибку
|
||||||
func (c *TestConfig) GetAuthToken(email, password string) (string, error) {
|
func (c *TestConfig) GetAuthToken(email, password string) (string, error) {
|
||||||
resp, err := c.Request("POST", "/auth/login", map[string]interface{}{
|
resp, err := c.Request("POST", "/auth/login", map[string]interface{}{
|
||||||
"email": email,
|
"email": email,
|
||||||
|
|||||||
Reference in New Issue
Block a user