core/tests/main_test.go
pasha1coil 6135f18ac0
Some checks failed
Deploy / DeployService (push) Failing after 36s
Deploy / CreateImage (push) Failing after 51s
added test docker file compouse and gitea workflow
2025-07-30 15:14:48 +03:00

10172 lines
308 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package tests
import (
"bytes"
"database/sql"
"encoding/json"
"fmt"
"gitea.pena/SQuiz/common/model"
"gitea.pena/SQuiz/common/repository/statistics"
"gitea.pena/SQuiz/core/internal/controllers/http_controllers/account"
"gitea.pena/SQuiz/core/internal/controllers/http_controllers/question"
"gitea.pena/SQuiz/core/internal/controllers/http_controllers/quiz"
result2 "gitea.pena/SQuiz/core/internal/controllers/http_controllers/result"
"github.com/pioz/faker"
"github.com/stretchr/testify/assert"
"net/http"
"strings"
"sync"
"testing"
"time"
)
// todo нужно определить из кликхауса на чем будем тестировать статистику
var validQuizIDForTestingClickHouse = 21211
var PublicKey = `-----BEGIN PUBLIC KEY-----MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAE=-----END PUBLIC KEY-----`
var postgresCred = "postgres://squiz:Redalert2@10.7.0.2:35432/squiz?sslmode=disable" //os.Getenv("POSTGRES_CRED")
var baseURL = "http://localhost:1488" //os.Getenv("API_BASE_URL")
var validToken = CreateJWT(validUserID) // validUserID
var expiredToken = CreateExpiredToken(validUserID)
// todo
var validAdminToken = CreateJWT(validUserID) // os.Getenv("VALID_ADMIN_JWT_TOKEN")
var existingUserIDToken = CreateJWT(existingUserID) // existingUserID
var AccountWithOutPrivilegeToken = CreateJWT(userWithoutPrivileges) // userWithoutPrivileges
var notFoundAccountToken = CreateJWT("notFound-123")
// todo
var existingUserID = "existing_user_456"
var testUserID = "test_user_123"
var userWithoutPrivileges = "no_privileges_user"
var validUserID = "multi_privileges_user"
var sqlInjectionInput = "'; DROP TABLE accounts; --"
var xssInput = "<script>alert('xss')</script>"
// отсмотрено
func TestGetAccount_Success(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var acc model.Account
err = json.NewDecoder(resp.Body).Decode(&acc)
assert.NoError(t, err)
assert.NotEmpty(t, acc.ID)
assert.NotEmpty(t, acc.UserID)
assert.IsType(t, map[string]model.ShortPrivilege{}, acc.Privileges)
}
// отсмотрено
func TestGetAccount_Auth(t *testing.T) {
t.Run("AccountNoToken", func(t *testing.T) {
resp, err := http.Get(baseURL + "/account/get")
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("AccountInvalidToken", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer invalid_token")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("AccountExpiredToken", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+expiredToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestGetAccount_NotFound(t *testing.T) {
t.Run("DeletedAccount", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+notFoundAccountToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
})
}
// отсмотрено
func TestGetAccount_Privileges(t *testing.T) {
t.Run("NoPrivileges", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+AccountWithOutPrivilegeToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var acc model.Account
err = json.NewDecoder(resp.Body).Decode(&acc)
assert.NoError(t, err)
assert.Empty(t, acc.Privileges)
})
t.Run("MultiplePrivileges", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var acc model.Account
err = json.NewDecoder(resp.Body).Decode(&acc)
assert.NoError(t, err)
assert.Greater(t, len(acc.Privileges), 1)
})
}
// отсмотрено
func TestAccount_Performance(t *testing.T) {
t.Run("AccountResponseTime", func(t *testing.T) {
start := time.Now()
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
duration := time.Since(start)
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("AccountLoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
resp, err := http.DefaultClient.Do(req)
if err == nil {
defer resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// todo
//func TestGetAccount_Security(t *testing.T) {
// t.Run("XSSProtection", func(t *testing.T) {
// req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
// assert.NoError(t, err)
// req.Header.Set("Authorization", "Bearer "+validToken)
//
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// defer resp.Body.Close()
//
// assert.Equal(t, "nosniff", resp.Header.Get("X-Content-Type-Options"))
// assert.Equal(t, "1; mode=block", resp.Header.Get("X-XSS-Protection"))
// assert.Equal(t, "DENY", resp.Header.Get("X-Frame-Options"))
// })
//
// t.Run("CSRFProtection", func(t *testing.T) {
// req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
// assert.NoError(t, err)
// req.Header.Set("Authorization", "Bearer "+validToken)
// req.Header.Set("X-CSRF-Token", "invalid_token")
//
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// defer resp.Body.Close()
//
// assert.Equal(t, http.StatusForbidden, resp.StatusCode)
// })
//}
// отсмотрено
func TestGetAccount_BoundaryCases(t *testing.T) {
t.Run("LongFieldValues", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
if userID, ok := result["user_id"].(string); ok {
assert.LessOrEqual(t, len(userID), 255)
}
})
}
// отсмотрено
func TestGetAccount_SpecialCases(t *testing.T) {
t.Run("AccountWithoutPrivileges", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
if privileges, ok := result["privileges"].(map[string]interface{}); ok {
assert.NotNil(t, privileges)
}
})
t.Run("MultiplePrivileges", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
if privileges, ok := result["privileges"].(map[string]interface{}); ok {
assert.NotNil(t, privileges)
}
})
}
// отсмотрено
func TestCreateAccount(t *testing.T) {
t.Run("Success", func(t *testing.T) {
resp := createAccountRequest(t, CreateJWT(faker.String()), map[string]interface{}{
"user_id": testUserID,
})
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
})
t.Run("MissingToken", func(t *testing.T) {
req, err := http.NewRequest("POST", baseURL+"/account/create", nil)
assert.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp := createAccountRequest(t, expiredToken, map[string]interface{}{
"user_id": "some-id",
})
defer resp.Body.Close()
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp := createAccountRequest(t, expiredToken, map[string]interface{}{
"user_id": "some-id",
})
defer resp.Body.Close()
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("Conflict_ExistingUserID", func(t *testing.T) {
resp := createAccountRequest(t, validToken, map[string]interface{}{
"user_id": existingUserID,
})
defer resp.Body.Close()
assert.Equal(t, http.StatusConflict, resp.StatusCode)
})
//t.Run("SQLInjection", func(t *testing.T) {
// resp := createAccountRequest(t, CreateJWT(fmt.Sprintf("perf_test_%d", time.Now().Unix())), map[string]interface{}{
// "user_id": sqlInjectionInput,
// })
// defer resp.Body.Close()
// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
//})
//
//t.Run("XSSInjection", func(t *testing.T) {
// resp := createAccountRequest(t, CreateJWT(fmt.Sprintf("perf_test_%d", time.Now().Unix())), map[string]interface{}{
// "user_id": xssInput,
// })
// defer resp.Body.Close()
// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
//})
t.Run("Performance_CreationTime", func(t *testing.T) {
start := time.Now()
userID := faker.String()
resp := createAccountRequest(t, CreateJWT(userID), map[string]interface{}{
"user_id": fmt.Sprintf(userID),
})
defer resp.Body.Close()
duration := time.Since(start)
assert.Less(t, duration.Milliseconds(), int64(1000)) // < 1s
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("Performance_LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
successCount := 0
var mu sync.Mutex
for i := 0; i < 50; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
resp := createAccountRequest(t, CreateJWT(fmt.Sprintf("load_test_%d_%d", time.Now().Unix(), index)), map[string]interface{}{
"user_id": fmt.Sprintf("load_test_%d_%d", time.Now().Unix(), index),
})
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
mu.Lock()
successCount++
mu.Unlock()
}
}(i)
}
wg.Wait()
assert.Greater(t, successCount, 40) // > 80% успешных
})
t.Run("BoundaryCases_LongValues", func(t *testing.T) {
longUserID := strings.Repeat("a", 1000) // Очень длинный user_id
resp := createAccountRequest(t, CreateJWT(longUserID), map[string]interface{}{
"user_id": longUserID,
})
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
t.Run("BoundaryCases_UnicodeCharacters", func(t *testing.T) {
unicodeUserID := fmt.Sprintf(ест_%d", faker.Int32()) // Unicode символы
resp := createAccountRequest(t, CreateJWT(unicodeUserID), map[string]interface{}{
"user_id": unicodeUserID,
})
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result account.CreateAccountResp
err := json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, unicodeUserID, result.CreatedAccount.UserID)
})
}
func createAccountRequest(t *testing.T, token string, payload map[string]interface{}) *http.Response {
body, err := json.Marshal(payload)
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/account/create", bytes.NewBuffer(body))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
return resp
}
// отсмотрено
func TestDeleteAccount_Success(t *testing.T) {
testDeleteUserID := faker.String()
testDeleteUserIDJWT := CreateJWT(testDeleteUserID)
createResp := createAccountRequest(t, testDeleteUserIDJWT, map[string]interface{}{
"user_id": testDeleteUserID,
})
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+testDeleteUserIDJWT)
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result account.DeleteAccountResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEmpty(t, result.DeletedAccountID)
}
// отсмотрено
func TestDeleteAccount_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer invalid_token")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+expiredToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestDeleteAccount_AlreadyDeleted(t *testing.T) {
testDeleteUserID := faker.String()
testDeleteUserIDJWT := CreateJWT(testDeleteUserID)
createResp := createAccountRequest(t, testDeleteUserIDJWT, map[string]interface{}{
"user_id": testDeleteUserID,
})
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+testDeleteUserIDJWT)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
// 404
req2, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req2.Header.Set("Authorization", "Bearer "+testDeleteUserIDJWT)
resp2, err := http.DefaultClient.Do(req2)
assert.NoError(t, err)
defer resp2.Body.Close()
// 404
assert.Equal(t, http.StatusInternalServerError, resp2.StatusCode)
}
// отсмотрено
func TestDeleteAccount_NonExistent(t *testing.T) {
nonExistentUserID := faker.String()
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+CreateJWT(nonExistentUserID))
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.True(t, resp.StatusCode == http.StatusInternalServerError)
}
// todo
func TestDeleteAccount_CascadeDeletion(t *testing.T) {
t.Run("RelatedDataDeletion", func(t *testing.T) {
testDeleteUserID := faker.String()
testDeleteUserIDJWT := CreateJWT(testDeleteUserID)
createResp := createAccountRequest(t, testDeleteUserIDJWT, map[string]interface{}{
"user_id": testDeleteUserID,
})
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+testDeleteUserIDJWT)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
// Проверяем, что связанные данные удалены или помечены как удаленные
// Здесь можно добавить дополнительные проверки, если есть API для проверки связанных данных
})
t.Run("StatisticsPreservation", func(t *testing.T) {
testDeleteUserID := faker.String()
testDeleteUserIDJWT := CreateJWT(testDeleteUserID)
createResp := createAccountRequest(t, testDeleteUserIDJWT, map[string]interface{}{
"user_id": testDeleteUserID,
})
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+testDeleteUserIDJWT)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
// Проверяем, что статистические данные сохранены
// Здесь можно добавить проверки статистики, если есть соответствующий API
})
}
// todo
//func TestDeleteAccount_Security(t *testing.T) {
// t.Run("CSRFProtection", func(t *testing.T) {
// testDeleteUserID := faker.String()
// testDeleteUserIDJWT := CreateJWT(testDeleteUserID)
// createResp := createAccountRequest(t, testDeleteUserIDJWT, map[string]interface{}{
// "user_id": testDeleteUserID,
// })
// defer createResp.Body.Close()
// assert.Equal(t, http.StatusOK, createResp.StatusCode)
//
// req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
// assert.NoError(t, err)
// req.Header.Set("Authorization", "Bearer "+testDeleteUserIDJWT)
// req.Header.Set("X-CSRF-Token", "invalid_token")
//
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// defer resp.Body.Close()
//
// assert.True(t, resp.StatusCode == http.StatusBadRequest)
// })
//}
// отсмотрено
func TestDeleteAccount_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
testDeleteUserID := faker.String()
testDeleteUserIDJWT := CreateJWT(testDeleteUserID)
createResp := createAccountRequest(t, testDeleteUserIDJWT, map[string]interface{}{
"user_id": testDeleteUserID,
})
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
req, _ := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
req.Header.Set("Authorization", "Bearer "+testDeleteUserIDJWT)
start := time.Now()
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
duration := time.Since(start)
assert.Less(t, duration.Milliseconds(), int64(500))
})
}
// отсмотрено
func TestDeleteAccount_Load(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
testDeleteUserID := fmt.Sprintf(faker.String(), "%d", index)
testDeleteUserIDJWT := CreateJWT(testDeleteUserID)
createResp := createAccountRequest(t, testDeleteUserIDJWT, map[string]interface{}{
"user_id": testDeleteUserID,
})
defer createResp.Body.Close()
if createResp.StatusCode == http.StatusOK {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+testDeleteUserIDJWT)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
if resp != nil {
defer resp.Body.Close()
}
}
}(i)
}
wg.Wait()
}
// отсмотрено
func TestDeleteAccount_BoundaryCases(t *testing.T) {
t.Run("ConcurrentOperations", func(t *testing.T) {
var wg sync.WaitGroup
successCount := 0
var mu sync.Mutex
for i := 0; i < 5; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
testDeleteUserID := fmt.Sprintf(faker.String())
testDeleteUserIDJWT := CreateJWT(testDeleteUserID)
createResp := createAccountRequest(t, testDeleteUserIDJWT, map[string]interface{}{
"user_id": testDeleteUserID,
})
defer createResp.Body.Close()
if createResp.StatusCode == http.StatusOK {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+testDeleteUserIDJWT)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
mu.Lock()
successCount++
mu.Unlock()
}
}
}(i)
}
wg.Wait()
assert.Greater(t, successCount, 0)
})
}
// отсмотрено
func TestGetAccounts_Success(t *testing.T) {
body := map[string]interface{}{
"limit": 10,
"page": 1,
}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result struct {
Count uint64 `json:"count"`
Items []model.Account `json:"items"`
}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEqual(t, len(result.Items), 0)
for _, acc := range result.Items {
assert.NotEmpty(t, acc.ID)
assert.NotEmpty(t, acc.UserID)
assert.NotEmpty(t, acc.CreatedAt)
}
}
// отсмотрено
func TestGetAccounts_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
body := map[string]interface{}{
"limit": 10,
"page": 1,
}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
body := map[string]interface{}{
"limit": 10,
"page": 1,
}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer invalid_token")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
body := map[string]interface{}{
"limit": 10,
"page": 1,
}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+expiredToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
func TestGetAccounts_Pagination(t *testing.T) {
t.Run("ValidPagination", func(t *testing.T) {
body := map[string]interface{}{"limit": 5, "page": 1}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
// todo еще обдумать
t.Run("ZeroPagination", func(t *testing.T) {
body := map[string]interface{}{"limit": 0, "page": 0}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
// todo еще обдумать
t.Run("TooHighLimit", func(t *testing.T) {
body := map[string]interface{}{"limit": 1000}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
// todo
func TestGetAccounts_Security(t *testing.T) {
t.Run("SQLInjection", func(t *testing.T) {
body := map[string]interface{}{
"limit": "10' OR '1'='1",
"page": "1' OR '1'='1",
}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
//t.Run("XSSProtection", func(t *testing.T) {
// body := map[string]interface{}{
// "limit": 10,
// "page": 1,
// }
// b, err := json.Marshal(body)
// assert.NoError(t, err)
// req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
// assert.NoError(t, err)
// req.Header.Set("Authorization", "Bearer "+validAdminToken)
// req.Header.Set("Content-Type", "application/json")
//
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// defer resp.Body.Close()
//
// assert.Equal(t, "nosniff", resp.Header.Get("X-Content-Type-Options"))
// assert.Equal(t, "1; mode=block", resp.Header.Get("X-XSS-Protection"))
// assert.Equal(t, "DENY", resp.Header.Get("X-Frame-Options"))
//})
}
// отсмотрено
func TestGetAccounts_Performance(t *testing.T) {
t.Run("ResponseTimeUnder500ms", func(t *testing.T) {
body := map[string]interface{}{"limit": 10, "page": 1}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
start := time.Now()
resp, err := http.DefaultClient.Do(req)
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("LoadTest100Requests", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
body := map[string]interface{}{"limit": 10, "page": 1}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err == nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// отсмотрено
func TestGetAccounts_BoundaryCases(t *testing.T) {
t.Run("LargeLimit", func(t *testing.T) {
body := map[string]interface{}{
"limit": 10000, // Очень большой лимит
"page": 1,
}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode)
})
t.Run("UnicodeCharacters", func(t *testing.T) {
body := map[string]interface{}{
"limit": 10,
"page": 1,
"search": ест_поиск",
}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
})
}
// отсмотрено
func TestGetAccounts_SpecialCases(t *testing.T) {
t.Run("EmptyResult", func(t *testing.T) {
body := map[string]interface{}{
"limit": 10,
"page": 999999, // Несуществующая страница
}
b, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
if accounts, ok := result["accounts"].([]interface{}); ok {
assert.Empty(t, accounts)
}
})
}
func TestGetPrivilege_Success(t *testing.T) {
body := map[string]string{"userId": existingUserID}
data, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, bytes.NewBuffer(data))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+existingUserIDToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var privileges []model.ShortPrivilege
err = json.NewDecoder(resp.Body).Decode(&privileges)
assert.NoError(t, err)
for _, p := range privileges {
assert.NotEmpty(t, p.ID)
assert.NotEmpty(t, p.PrivilegeID)
assert.NotEmpty(t, p.PrivilegeName)
}
}
// отсмотрено
func TestGetPrivilege_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, nil)
assert.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer invalid_token")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+expiredToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestGetPrivilege_InputValidation(t *testing.T) {
t.Run("MissingUserID", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/privilege/", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
})
t.Run("InvalidUserID", func(t *testing.T) {
body := map[string]int{"userId": 111}
data, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/privilege/!!!", bytes.NewBuffer(data))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NonExistentUserID", func(t *testing.T) {
nonExistentID := "non_existent_user_123"
body := map[string]string{"userId": nonExistentID}
data, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/privilege/"+nonExistentID, bytes.NewBuffer(data))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var privileges []model.ShortPrivilege
err = json.NewDecoder(resp.Body).Decode(&privileges)
assert.NoError(t, err)
assert.Empty(t, privileges)
})
}
// отсмотрено
func TestGetPrivilege_BoundaryCases(t *testing.T) {
t.Run("LongUserID", func(t *testing.T) {
longUserID := strings.Repeat("a", 1000)
body := map[string]string{"userId": longUserID}
data, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/privilege/"+longUserID, bytes.NewBuffer(data))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode)
})
t.Run("UnicodeUserID", func(t *testing.T) {
unicodeUserID := ест_пользователь_123"
body := map[string]string{"userId": unicodeUserID}
data, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/privilege/"+unicodeUserID, bytes.NewBuffer(data))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
})
}
// todo
//func TestGetPrivilege_Security(t *testing.T) {
// t.Run("SQLInjection", func(t *testing.T) {
// injection := "1' OR '1'='1"
// body := map[string]string{"userId": injection}
// data, err := json.Marshal(body)
// assert.NoError(t, err)
// req, err := http.NewRequest("GET", baseURL+"/privilege/"+injection, bytes.NewBuffer(data))
// assert.NoError(t, err)
// req.Header.Set("Authorization", "Bearer "+validToken)
// req.Header.Set("Content-Type", "application/json")
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSS", func(t *testing.T) {
// body := map[string]string{"userId": xssInput}
// data, err := json.Marshal(body)
// assert.NoError(t, err)
// req, err := http.NewRequest("GET", baseURL+"/privilege/"+xssInput, bytes.NewBuffer(data))
// assert.NoError(t, err)
// req.Header.Set("Authorization", "Bearer "+validToken)
// req.Header.Set("Content-Type", "application/json")
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
// отсмотрено
func TestGetPrivilege_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
body := map[string]string{"userId": existingUserID}
data, err := json.Marshal(body)
assert.NoError(t, err)
start := time.Now()
req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, bytes.NewBuffer(data))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+existingUserIDToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, time.Since(start).Milliseconds(), int64(300))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
body := map[string]string{"userId": existingUserID}
data, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, bytes.NewBuffer(data))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+existingUserIDToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
if resp != nil {
defer resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// отсмотрено
func TestGetPrivilege_SpecialCases(t *testing.T) {
t.Run("UserWithoutPrivileges", func(t *testing.T) {
body := map[string]string{"userId": userWithoutPrivileges}
data, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/privilege/"+userWithoutPrivileges, bytes.NewBuffer(data))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var privileges []model.ShortPrivilege
err = json.NewDecoder(resp.Body).Decode(&privileges)
assert.NoError(t, err)
assert.Empty(t, privileges)
})
t.Run("MultiplePrivileges", func(t *testing.T) {
body := map[string]string{"userId": existingUserID}
data, err := json.Marshal(body)
assert.NoError(t, err)
req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, bytes.NewBuffer(data))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+existingUserIDToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var privileges []model.ShortPrivilege
err = json.NewDecoder(resp.Body).Decode(&privileges)
assert.NoError(t, err)
for _, privilege := range privileges {
assert.NotEmpty(t, privilege.ID)
assert.NotEmpty(t, privilege.PrivilegeID)
assert.NotEmpty(t, privilege.PrivilegeName)
}
})
}
func deleteAccountByUserIDRequest(token string, body interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("DELETE", baseURL+"/account/"+body.(map[string]string)["userId"], bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestDeleteAccountByUserID_Success(t *testing.T) {
testDeleteUserID := faker.String()
testDeleteUserIDJWT := CreateJWT(testDeleteUserID)
createResp := createAccountRequest(t, testDeleteUserIDJWT, map[string]interface{}{
"user_id": testDeleteUserID,
})
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
resp, err := deleteAccountByUserIDRequest(testDeleteUserIDJWT, map[string]string{"userId": testDeleteUserID})
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result account.DeleteAccountByUserIDResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, testDeleteUserID, result.DeletedAccountUserID)
}
// отсмотрено
func TestDeleteAccountByUserID_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/"+testUserID, nil)
assert.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := deleteAccountByUserIDRequest("invalid_token", map[string]string{"userId": testUserID})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := deleteAccountByUserIDRequest(expiredToken, map[string]string{"userId": testUserID})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestDeleteAccountByUserID_Validation(t *testing.T) {
t.Run("EmptyBody", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/", bytes.NewReader([]byte(`{}`)))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
})
}
// отсмотрено
func TestDeleteAccountByUserID_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
testDeleteUserID := faker.String()
testDeleteUserIDJWT := CreateJWT(testDeleteUserID)
createResp := createAccountRequest(t, testDeleteUserIDJWT, map[string]interface{}{
"user_id": testDeleteUserID,
})
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
start := time.Now()
resp, err := deleteAccountByUserIDRequest(testDeleteUserIDJWT, map[string]string{"userId": testDeleteUserID})
assert.NoError(t, err)
defer resp.Body.Close()
duration := time.Since(start)
assert.Less(t, duration.Milliseconds(), int64(1000))
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
successCount := 0
var mu sync.Mutex
for i := 0; i < 5; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
testDeleteUserID := faker.String()
testDeleteUserIDJWT := CreateJWT(testDeleteUserID)
createResp := createAccountRequest(t, testDeleteUserIDJWT, map[string]interface{}{
"user_id": testDeleteUserID,
})
defer createResp.Body.Close()
if createResp.StatusCode == http.StatusOK {
resp, err := deleteAccountByUserIDRequest(testDeleteUserIDJWT, map[string]string{"userId": testDeleteUserID})
if err == nil && resp != nil {
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
mu.Lock()
successCount++
mu.Unlock()
}
}
}
}(i)
}
wg.Wait()
assert.Greater(t, successCount, 2)
})
}
// todo
//func TestDeleteAccountByUserID_SQLInjection_XSS(t *testing.T) {
// t.Run("SQLInjection", func(t *testing.T) {
// resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": sqlInjectionInput})
// assert.NoError(t, err)
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSS", func(t *testing.T) {
// resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": xssInput})
// assert.NoError(t, err)
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
func manualDoneRequest(token string, body map[string]string) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/account/manualdone", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestManualDone_Success(t *testing.T) {
resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": validUserID})
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
// отсмотрено
func TestManualDone_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]string{"id": testUserID})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/account/manualdone", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := manualDoneRequest("invalid_token", map[string]string{"id": testUserID})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := manualDoneRequest(expiredToken, map[string]string{"id": testUserID})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestManualDone_Validation(t *testing.T) {
t.Run("EmptyBody", func(t *testing.T) {
payload := []byte(`{}`)
req, err := http.NewRequest("POST", baseURL+"/account/manualdone", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidID", func(t *testing.T) {
resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": "invalid_id"})
assert.NoError(t, err)
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
})
t.Run("NonExistentID", func(t *testing.T) {
resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": "nonexistent_id"})
assert.NoError(t, err)
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
})
}
// отсмотрено
func TestManualDone_BoundaryCases(t *testing.T) {
t.Run("LongID", func(t *testing.T) {
longID := strings.Repeat("a", 1000) // Очень длинный ID
resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": longID})
assert.NoError(t, err)
defer resp.Body.Close()
assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode)
})
// todo
t.Run("UnicodeID", func(t *testing.T) {
unicodeID := ест_id_123" // Unicode символы
_, err := manualDoneRequest(validAdminToken, map[string]string{"id": unicodeID})
assert.NoError(t, err)
})
}
// отсмотрено
func TestManualDone_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": validUserID})
assert.NoError(t, err)
defer resp.Body.Close()
duration := time.Since(start)
assert.Less(t, duration.Milliseconds(), int64(500)) // < 500ms
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
// todo
//func TestManualDone_Security(t *testing.T) {
// t.Run("SQLInjection", func(t *testing.T) {
// resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": sqlInjectionInput})
// assert.NoError(t, err)
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSSAttack", func(t *testing.T) {
// resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": xssInput})
// assert.NoError(t, err)
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
// отсмотрено
func TestManualDone_SpecialCases(t *testing.T) {
t.Run("TransactionAtomicity", func(t *testing.T) {
resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": validUserID})
assert.NoError(t, err)
defer resp.Body.Close()
assert.True(t, resp.StatusCode == http.StatusOK)
})
t.Run("Idempotency", func(t *testing.T) {
resp1, err := manualDoneRequest(validAdminToken, map[string]string{"id": validUserID})
assert.NoError(t, err)
defer resp1.Body.Close()
resp2, err := manualDoneRequest(validAdminToken, map[string]string{"id": validUserID})
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, resp1.StatusCode, resp2.StatusCode)
})
}
func createLeadTargetRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/account/leadtarget", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestCreateLeadTarget_Success(t *testing.T) {
resp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": "example@mail.com",
"name": "Example Channel",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
// отсмотрено
func TestCreateLeadTarget_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": "example@mail.com",
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/account/leadtarget", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := createLeadTargetRequest("invalid_token", map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": "example@mail.com",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := createLeadTargetRequest(expiredToken, map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": "example@mail.com",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestCreateLeadTarget_InputValidation(t *testing.T) {
t.Run("MissingRequiredFields", func(t *testing.T) {
resp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidType", func(t *testing.T) {
resp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "invalid",
"quizID": 123,
"target": "example@mail.com",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("EmptyBody", func(t *testing.T) {
resp, err := createLeadTargetRequest(validToken, map[string]interface{}{})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
// todo
//func TestCreateLeadTarget_Security(t *testing.T) {
// t.Run("SQLInjection", func(t *testing.T) {
// resp, err := createLeadTargetRequest(validToken, map[string]interface{}{
// "type": "mail",
// "quizID": "1' OR '1'='1",
// "target": "example@mail.com",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSSAttack", func(t *testing.T) {
// resp, err := createLeadTargetRequest(validToken, map[string]interface{}{
// "type": "mail",
// "quizID": 123,
// "target": xssInput,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
// отсмотрено
func TestCreateLeadTarget_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 999,
"target": "perf@mail.com",
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(1000))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 50; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
resp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 1000 + index,
"target": fmt.Sprintf("load%d@mail.com", index),
})
if err == nil && resp != nil {
resp.Body.Close()
}
}(i)
}
wg.Wait()
})
}
// отсмотрено
func TestCreateLeadTarget_BoundaryCases(t *testing.T) {
t.Run("MaxLengthFields", func(t *testing.T) {
longEmail := strings.Repeat("a", 100) + "@domain.com"
longName := strings.Repeat("b", 200)
resp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": longEmail,
"name": longName,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("SpecialCharacters", func(t *testing.T) {
resp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": "special!@#$%^&*()@domain.com",
"name": "Special Name!",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
// отсмотрено
func TestCreateLeadTarget_SpecialCases(t *testing.T) {
t.Run("TransactionAtomicity", func(t *testing.T) {
resp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": "atomic@mail.com",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.True(t, resp.StatusCode == http.StatusOK)
})
t.Run("DataValidation", func(t *testing.T) {
resp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": "validation@mail.com",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
func updateLeadTargetRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("PUT", baseURL+"/account/leadtarget", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestUpdateLeadTarget_Success(t *testing.T) {
quizID := faker.Int32()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": quizID,
"target": "old@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
getResp, err := getLeadTargetByQuizIDRequest(validToken, fmt.Sprintf("%d", quizID))
assert.NoError(t, err)
defer getResp.Body.Close()
assert.Equal(t, http.StatusOK, getResp.StatusCode)
var getRespLead []model.LeadTarget
err = json.NewDecoder(getResp.Body).Decode(&getRespLead)
assert.NoError(t, err)
assert.True(t, len(getRespLead) > 0)
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
"id": getRespLead[0].ID,
"target": "new_target@mail.com",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, float64(getRespLead[0].ID), result["id"])
assert.Equal(t, "new_target@mail.com", result["target"])
}
// отсмотрено
func TestUpdateLeadTarget_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"id": 123,
"target": "example@mail.com",
})
assert.NoError(t, err)
req, err := http.NewRequest("PUT", baseURL+"/account/leadtarget", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := updateLeadTargetRequest("invalid_token", map[string]interface{}{
"id": 123,
"target": "example@mail.com",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := updateLeadTargetRequest(expiredToken, map[string]interface{}{
"id": 123,
"target": "example@mail.com",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestUpdateLeadTarget_InputValidation(t *testing.T) {
t.Run("MissingRequiredFields", func(t *testing.T) {
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
"id": 123,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidID", func(t *testing.T) {
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
"id": "invalid",
"target": "example@mail.com",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo такого нет у нас
//t.Run("InvalidTargetFormat", func(t *testing.T) {
// resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
// "id": 123,
// "target": "invalid_email",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
//})
t.Run("EmptyBody", func(t *testing.T) {
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
// отсмотрено
func TestUpdateLeadTarget_Existence(t *testing.T) {
t.Run("NonExistentID", func(t *testing.T) {
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
"id": 999999,
"target": "example@mail.com",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
})
}
// todo
//func TestUpdateLeadTarget_Security(t *testing.T) {
// t.Run("SQLInjection", func(t *testing.T) {
// resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
// "id": "1' OR '1'='1",
// "target": "example@mail.com",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSSAttack", func(t *testing.T) {
// resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
// "id": 123,
// "target": xssInput,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
// отсмотрено
func TestUpdateLeadTarget_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
quizID := faker.Int32()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": quizID,
"target": "perf@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
getResp, err := getLeadTargetByQuizIDRequest(validToken, fmt.Sprintf("%d", quizID))
assert.NoError(t, err)
defer getResp.Body.Close()
assert.Equal(t, http.StatusOK, getResp.StatusCode)
var getRespLead []model.LeadTarget
err = json.NewDecoder(getResp.Body).Decode(&getRespLead)
assert.NoError(t, err)
assert.True(t, len(getRespLead) > 0)
start := time.Now()
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
"id": getRespLead[0].ID,
"target": "updated_perf@mail.com",
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(1000))
})
}
// отсмотрено
func TestUpdateLeadTarget_BoundaryCases(t *testing.T) {
t.Run("MaxLengthTarget", func(t *testing.T) {
quizID := faker.Int32()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": quizID,
"target": "perf@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
getResp, err := getLeadTargetByQuizIDRequest(validToken, fmt.Sprintf("%d", quizID))
assert.NoError(t, err)
defer getResp.Body.Close()
assert.Equal(t, http.StatusOK, getResp.StatusCode)
var getRespLead []model.LeadTarget
err = json.NewDecoder(getResp.Body).Decode(&getRespLead)
assert.NoError(t, err)
assert.True(t, len(getRespLead) > 0)
longEmail := strings.Repeat("a", 100) + "@domain.com"
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
"id": getRespLead[0].ID,
"target": longEmail,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("SpecialCharacters", func(t *testing.T) {
quizID := faker.Int32()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": quizID,
"target": "perf@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
getResp, err := getLeadTargetByQuizIDRequest(validToken, fmt.Sprintf("%d", quizID))
assert.NoError(t, err)
defer getResp.Body.Close()
assert.Equal(t, http.StatusOK, getResp.StatusCode)
var getRespLead []model.LeadTarget
err = json.NewDecoder(getResp.Body).Decode(&getRespLead)
assert.NoError(t, err)
assert.True(t, len(getRespLead) > 0)
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
"id": getRespLead[0].ID,
"target": "special!@#$%^&*()@domain.com",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
// отсмотрено
func TestDeleteLeadTarget_SpecialCases(t *testing.T) {
t.Run("TransactionAtomicity", func(t *testing.T) {
quizID := faker.Int32()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": quizID,
"target": "atomic@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
getResp, err := getLeadTargetByQuizIDRequest(validToken, fmt.Sprintf("%d", quizID))
assert.NoError(t, err)
defer getResp.Body.Close()
assert.Equal(t, http.StatusOK, getResp.StatusCode)
var getRespLead []model.LeadTarget
err = json.NewDecoder(getResp.Body).Decode(&getRespLead)
assert.NoError(t, err)
assert.True(t, len(getRespLead) > 0)
resp, err := deleteLeadTargetRequest(validToken, getRespLead[0].ID)
assert.NoError(t, err)
defer resp.Body.Close()
assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError)
})
}
func deleteLeadTargetRequest(token string, targetID int64) (*http.Response, error) {
req, err := http.NewRequest("DELETE", baseURL+"/account/leadtarget/"+fmt.Sprintf("%d", targetID), nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestDeleteLeadTarget_Success(t *testing.T) {
quizID := faker.Int32()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": quizID,
"target": "delete@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
getResp, err := getLeadTargetByQuizIDRequest(validToken, fmt.Sprintf("%d", quizID))
assert.NoError(t, err)
defer getResp.Body.Close()
assert.Equal(t, http.StatusOK, getResp.StatusCode)
var getRespLead []model.LeadTarget
err = json.NewDecoder(getResp.Body).Decode(&getRespLead)
assert.NoError(t, err)
assert.True(t, len(getRespLead) > 0)
resp, err := deleteLeadTargetRequest(validToken, getRespLead[0].ID)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
// отсмотрено
func TestDeleteLeadTarget_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/leadtarget/123", nil)
assert.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := deleteLeadTargetRequest("invalid_token", 123)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := deleteLeadTargetRequest(expiredToken, 123)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestDeleteLeadTarget_InputValidation(t *testing.T) {
t.Run("InvalidID", func(t *testing.T) {
resp, err := deleteLeadTargetRequest(validToken, 999999)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("EmptyID", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/leadtarget/", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NonExistentID", func(t *testing.T) {
resp, err := deleteLeadTargetRequest(validToken, 999999)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
// отсмотрено
func TestDeleteLeadTarget_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
quizID := faker.Int32()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": quizID,
"target": "perf_delete@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
getResp, err := getLeadTargetByQuizIDRequest(validToken, fmt.Sprintf("%d", quizID))
assert.NoError(t, err)
defer getResp.Body.Close()
assert.Equal(t, http.StatusOK, getResp.StatusCode)
var getRespLead []model.LeadTarget
err = json.NewDecoder(getResp.Body).Decode(&getRespLead)
assert.NoError(t, err)
assert.True(t, len(getRespLead) > 0)
start := time.Now()
resp, err := deleteLeadTargetRequest(validToken, getRespLead[0].ID)
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(1000))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 30; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
quizID := faker.Int32()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": quizID,
"target": fmt.Sprintf("load_delete%d@mail.com", index),
})
if err != nil {
return
}
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
getResp, err := getLeadTargetByQuizIDRequest(validToken, fmt.Sprintf("%d", quizID))
if err != nil {
return
}
defer getResp.Body.Close()
assert.Equal(t, http.StatusOK, getResp.StatusCode)
var getRespLead []model.LeadTarget
err = json.NewDecoder(getResp.Body).Decode(&getRespLead)
if err != nil || len(getRespLead) == 0 {
return
}
resp, err := deleteLeadTargetRequest(validToken, getRespLead[0].ID)
if err == nil && resp != nil {
resp.Body.Close()
}
}(i)
}
wg.Wait()
})
}
// отсмотрено
func TestDeleteLeadTarget_AlreadyDeleted(t *testing.T) {
quizID := faker.Int32()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": quizID,
"target": "already_deleted@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
getResp, err := getLeadTargetByQuizIDRequest(validToken, fmt.Sprintf("%d", quizID))
assert.NoError(t, err)
defer getResp.Body.Close()
assert.Equal(t, http.StatusOK, getResp.StatusCode)
var getRespLead []model.LeadTarget
err = json.NewDecoder(getResp.Body).Decode(&getRespLead)
assert.NoError(t, err)
assert.True(t, len(getRespLead) > 0)
resp1, err := deleteLeadTargetRequest(validToken, getRespLead[0].ID)
assert.NoError(t, err)
resp1.Body.Close()
resp2, err := deleteLeadTargetRequest(validToken, getRespLead[0].ID)
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusOK, resp2.StatusCode)
}
func getLeadTargetByQuizIDRequest(token string, quizID string) (*http.Response, error) {
req, err := http.NewRequest("GET", baseURL+"/account/leadtarget/"+quizID, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestGetLeadTargetByQuizID_Success(t *testing.T) {
quizID := faker.Int32()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": quizID,
"target": "get@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
resp, err := getLeadTargetByQuizIDRequest(validToken, fmt.Sprintf("%d", quizID))
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result []model.LeadTarget
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.True(t, len(result) > 0)
assert.Equal(t, quizID, result[0].QuizID)
assert.Equal(t, model.LeadTargetType("mail"), result[0].Type)
assert.Equal(t, "get@mail.com", result[0].Target)
}
// отсмотрено
func TestGetLeadTargetByQuizID_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/account/leadtarget/123", nil)
assert.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := getLeadTargetByQuizIDRequest("invalid_token", "123")
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := getLeadTargetByQuizIDRequest(expiredToken, "123")
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestGetLeadTargetByQuizID_InputValidation(t *testing.T) {
t.Run("InvalidQuizID", func(t *testing.T) {
resp, err := getLeadTargetByQuizIDRequest(validToken, "invalid_id")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("EmptyQuizID", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/account/leadtarget/", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode)
})
}
// todo
//func TestGetLeadTargetByQuizID_Security(t *testing.T) {
// t.Run("SQLInjection", func(t *testing.T) {
// resp, err := getLeadTargetByQuizIDRequest(validToken, "1' OR '1'='1")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSSAttack", func(t *testing.T) {
// resp, err := getLeadTargetByQuizIDRequest(validToken, xssInput)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
// отсмотрено
func TestGetLeadTargetByQuizID_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 456,
"target": "perf_get@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
start := time.Now()
resp, err := getLeadTargetByQuizIDRequest(validToken, "456")
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(300))
})
t.Run("LoadTest", func(t *testing.T) {
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 789,
"target": "load_get@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
resp, err := getLeadTargetByQuizIDRequest(validToken, "789")
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// отсмотрено
func TestGetLeadTargetByQuizID_DeletedTarget(t *testing.T) {
quizID := faker.Int32()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": quizID,
"target": "deleted@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
assert.Equal(t, http.StatusOK, createResp.StatusCode)
getResp, err := getLeadTargetByQuizIDRequest(validToken, fmt.Sprintf("%d", quizID))
assert.NoError(t, err)
defer getResp.Body.Close()
assert.Equal(t, http.StatusOK, getResp.StatusCode)
var getRespLead []model.LeadTarget
err = json.NewDecoder(getResp.Body).Decode(&getRespLead)
assert.NoError(t, err)
assert.True(t, len(getRespLead) > 0)
deleteResp, err := deleteLeadTargetRequest(validToken, getRespLead[0].ID)
assert.NoError(t, err)
deleteResp.Body.Close()
resp, err := getLeadTargetByQuizIDRequest(validToken, fmt.Sprintf("%d", quizID))
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
//todo сомнительное надо бы там на len проверять ответ из бдшки
var getRespLead2 []model.LeadTarget
err = json.NewDecoder(getResp.Body).Decode(&getRespLead2)
assert.Error(t, err)
}
func createQuestionRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/question/create", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestCreateQuestion_Success(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования вопросов",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Какой основной компонент воздуха?",
"type": "variant",
"description": "Выберите один правильный ответ.",
"required": true,
"page": 1,
"content": "{}",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result model.Question
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEmpty(t, result.Id)
assert.Equal(t, quizResult.Id, result.QuizId)
assert.Equal(t, "Какой основной компонент воздуха?", result.Title)
assert.Equal(t, "variant", result.Type)
assert.Equal(t, "Выберите один правильный ответ.", result.Description)
assert.Equal(t, true, result.Required)
assert.Equal(t, 1, result.Page)
assert.Equal(t, "{}", result.Content)
assert.NotEmpty(t, result.CreatedAt)
}
// отсмотрено
func TestCreateQuestion_Auth(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования аутентификации вопросов",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question",
"type": "variant",
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/question/create", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := createQuestionRequest("invalid_token", map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question",
"type": "variant",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := createQuestionRequest(expiredToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question",
"type": "variant",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestCreateQuestion_InputValidation(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования валидации вопросов",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
t.Run("MissingRequiredFields", func(t *testing.T) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"title": "Test Question",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode)
})
t.Run("InvalidQuizID", func(t *testing.T) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": "invalid",
"title": "Test Question",
"type": "variant",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidType", func(t *testing.T) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question",
"type": "invalid_type",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode)
})
t.Run("InvalidRequired", func(t *testing.T) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question",
"type": "variant",
"required": "not_boolean",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidPage", func(t *testing.T) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question",
"type": "variant",
"page": "not_number",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("EmptyBody", func(t *testing.T) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode)
})
}
// отсмотрено
func TestCreateQuestion_DifferentTypes(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования разных типов вопросов",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
// todo "result"
questionTypes := []string{"text", "variant", "images", "select", "varimg", "emoji", "date", "number", "page", "rating", "file", "result"} // "result"
for _, questionType := range questionTypes {
t.Run(questionType, func(t *testing.T) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": fmt.Sprintf("Test %s Question", questionType),
"type": questionType,
"content": "{}",
})
assert.NoError(t, err)
defer resp.Body.Close()
fmt.Println(questionType)
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
}
//func TestCreateQuestion_Security(t *testing.T) {
// quizResp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": "Квиз для тестирования безопасности вопросов",
// "status": "draft",
// })
// assert.NoError(t, err)
// defer quizResp.Body.Close()
//
// assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
//
// var quizResult model.Quiz
// err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
// assert.NoError(t, err)
//
// t.Run("SQLInjection", func(t *testing.T) {
// resp, err := createQuestionRequest(validToken, map[string]interface{}{
// "quiz_id": "1' OR '1'='1",
// "title": "Test Question",
// "type": "variant",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
// // todo
// t.Run("XSSAttack", func(t *testing.T) {
// resp, err := createQuestionRequest(validToken, map[string]interface{}{
// "quiz_id": quizResult.Id,
// "title": xssInput,
// "type": "variant",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
// отсмотрено
func TestCreateQuestion_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования производительности",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
start := time.Now()
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.CreatedAt,
"title": "Performance Test Question",
"type": "variant",
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 50; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": fmt.Sprintf("Load Test Quiz %d", index),
"status": "draft",
})
if err != nil {
return
}
defer quizResp.Body.Close()
if quizResp.StatusCode != http.StatusCreated {
return
}
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": fmt.Sprintf("Load Test Question %d", index),
"type": "variant",
})
if err == nil && resp != nil {
resp.Body.Close()
}
}(i)
}
wg.Wait()
})
}
// отсмотрено
func TestCreateQuestion_BoundaryCases(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования граничных случаев",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
t.Run("MaxLengthTitle", func(t *testing.T) {
longTitle := strings.Repeat("a", 511)
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": longTitle,
"type": "variant",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("LongDescription", func(t *testing.T) {
longDescription := strings.Repeat("b", 1000)
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question",
"type": "variant",
"description": longDescription,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("SpecialCharacters", func(t *testing.T) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Special!@#$%^&*() Question",
"type": "variant",
"description": "Description with special chars: !@#$%^&*()",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
func getQuestionListRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/question/getList", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestGetQuestionList_Success(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования списка вопросов",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
fmt.Println(quizResult.Id)
for i := 0; i < 3; i++ {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": fmt.Sprintf("Test Question %d", i),
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
}
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"limit": 10,
"page": 0,
"quiz_id": quizResult.Id,
"type": "variant",
"deleted": false,
"required": false,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result question.GetQuestionListResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEmpty(t, result.Count)
assert.NotEmpty(t, result.Items)
assert.LessOrEqual(t, len(result.Items), 10)
if len(result.Items) > 0 {
assert.NotEmpty(t, result.Items[0].Id)
assert.Equal(t, quizResult.Id, result.Items[0].QuizId)
assert.Equal(t, "variant", result.Items[0].Type)
}
}
// отсмотрено
func TestGetQuestionList_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"limit": 10,
"page": 1,
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/question/getList", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := getQuestionListRequest("invalid_token", map[string]interface{}{
"limit": 10,
"page": 1,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := getQuestionListRequest(expiredToken, map[string]interface{}{
"limit": 10,
"page": 1,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestGetQuestionList_InputValidation(t *testing.T) {
t.Run("InvalidPagination", func(t *testing.T) {
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"limit": "invalid",
"page": "invalid",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidQuizID", func(t *testing.T) {
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"quiz_id": "invalid",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidType", func(t *testing.T) {
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"type": "invalid_type",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode)
})
t.Run("InvalidBoolean", func(t *testing.T) {
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"deleted": "not_boolean",
"required": "not_boolean",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo
//t.Run("InvalidTimeRange", func(t *testing.T) {
// resp, err := getQuestionListRequest(validToken, map[string]interface{}{
// "from": 1000,
// "to": 500,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
//})
}
// отсмотрено
func TestGetQuestionList_Pagination(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования пагинации",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
for i := 0; i < 15; i++ {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": fmt.Sprintf("Pagination Question %d", i),
"type": "text",
})
assert.NoError(t, err)
createResp.Body.Close()
}
t.Run("FirstPage", func(t *testing.T) {
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"limit": 5,
"page": 1,
"quiz_id": quizResult.Id,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result question.GetQuestionListResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.LessOrEqual(t, len(result.Items), 5)
})
t.Run("SecondPage", func(t *testing.T) {
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"limit": 5,
"page": 2,
"quiz_id": quizResult.Id,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result question.GetQuestionListResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.LessOrEqual(t, len(result.Items), 5)
})
t.Run("EmptyPage", func(t *testing.T) {
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"limit": 5,
"page": 1000,
"quiz_id": quizResult.Id,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result question.GetQuestionListResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Empty(t, result.Items)
})
}
// отсмотрено
func TestGetQuestionList_Filters(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования фильтров",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
questionTypes := []string{"text", "variant", "select"}
for _, questionType := range questionTypes {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": fmt.Sprintf("Filter Question %s", questionType),
"type": questionType,
})
assert.NoError(t, err)
createResp.Body.Close()
}
t.Run("FilterByType", func(t *testing.T) {
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"type": "text",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result question.GetQuestionListResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
for _, item := range result.Items {
assert.Equal(t, "text", item.Type)
}
})
//todo не работает
//t.Run("FilterBySearch", func(t *testing.T) {
// resp, err := getQuestionListRequest(validToken, map[string]interface{}{
// "quiz_id": quizResult.Id,
// "search": "Квиз",
// "limit": 1,
// "page": 6,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
//
// var result question.GetQuestionListResp
// err = json.NewDecoder(resp.Body).Decode(&result)
// assert.NoError(t, err)
//
// assert.NotEmpty(t, result.Items)
//})
t.Run("FilterByRequired", func(t *testing.T) {
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"required": true,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result question.GetQuestionListResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
for _, item := range result.Items {
assert.Equal(t, true, item.Required)
}
})
}
// отсмотрено
func TestGetQuestionList_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"limit": 10,
"page": 1,
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"limit": 5,
"page": 1,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
func editQuestionRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("PATCH", baseURL+"/question/edit", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestEditQuestion_Success(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования редактирования",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Original Question",
"type": "variant",
"required": true,
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"title": "Обновленный заголовок вопроса?",
"desc": "Новое описание для вопроса.",
"type": "text",
"required": false,
"content": "{\"placeholder\":\"Введите ваш ответ здесь\"}",
"page": 2,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result question.UpdateResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, createResult.Id, result.Updated)
}
// отсмотрено
func TestEditQuestion_SingleField(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования редактирования одного поля",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Single Field Question",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"title": "Только заголовок обновлен",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result question.UpdateResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, createResult.Id, result.Updated)
}
// отсмотрено
func TestEditQuestion_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"id": 123,
"title": "Test Question",
})
assert.NoError(t, err)
req, err := http.NewRequest("PATCH", baseURL+"/question/edit", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := editQuestionRequest("invalid_token", map[string]interface{}{
"id": 123,
"title": "Test Question",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := editQuestionRequest(expiredToken, map[string]interface{}{
"id": 123,
"title": "Test Question",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestEditQuestion_InputValidation(t *testing.T) {
t.Run("MissingID", func(t *testing.T) {
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"title": "Запрос без ID",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusFailedDependency, resp.StatusCode)
})
t.Run("InvalidID", func(t *testing.T) {
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": "not_an_integer",
"title": "Невалидный ID",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo если нет то надо 404
t.Run("NonExistentID", func(t *testing.T) {
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": 99999,
"title": "Несуществующий вопрос",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
t.Run("InvalidTitle", func(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования невалидного заголовка",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
longTitle := strings.Repeat("a", 513)
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"title": longTitle,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusUnprocessableEntity, resp.StatusCode)
})
t.Run("InvalidType", func(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования невалидного типа",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"type": "invalid_type",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode)
})
t.Run("InvalidRequired", func(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования невалидного required",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"required": "not_boolean",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
// todo
//func TestEditQuestion_Security(t *testing.T) {
// quizResp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": "Квиз для тестирования невалидного required",
// "status": "draft",
// })
// assert.NoError(t, err)
// defer quizResp.Body.Close()
//
// assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
//
// var quizResult model.Quiz
// err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
// assert.NoError(t, err)
//
// createResp, err := createQuestionRequest(validToken, map[string]interface{}{
// "quiz_id": quizResult.Id,
// "title": "Test Question",
// "type": "variant",
// })
// assert.NoError(t, err)
// defer createResp.Body.Close()
//
// var createResult model.Question
// err = json.NewDecoder(createResp.Body).Decode(&createResult)
// assert.NoError(t, err)
//
// t.Run("SQLInjection", func(t *testing.T) {
// resp, err := editQuestionRequest(validToken, map[string]interface{}{
// "id": createResult.Id,
// "title": "1' OR '1'='1",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSSAttack", func(t *testing.T) {
// resp, err := editQuestionRequest(validToken, map[string]interface{}{
// "id": createResult.Id,
// "title": xssInput,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
// отсмотрено
func TestEditQuestion_Performance(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования невалидного required",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Performance Test Question",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"title": "Updated Performance Test Question",
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 30; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": fmt.Sprintf("Load Test Question %d", index),
"type": "variant",
})
if err != nil {
return
}
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"title": fmt.Sprintf("Updated Load Test Question %d", index),
})
if err == nil && resp != nil {
resp.Body.Close()
}
}(i)
}
wg.Wait()
})
}
func copyQuestionRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/question/copy", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestCopyQuestion_Success(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Исходный квиз для копирования",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Какой основной компонент воздуха?",
"type": "variant",
"description": "Выберите один правильный ответ из предложенных.",
"required": true,
"page": 1,
"content": "{}",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
targetQuizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Целевой квиз для копирования",
"status": "draft",
})
assert.NoError(t, err)
defer targetQuizResp.Body.Close()
assert.Equal(t, http.StatusCreated, targetQuizResp.StatusCode)
var targetQuizResult model.Quiz
err = json.NewDecoder(targetQuizResp.Body).Decode(&targetQuizResult)
assert.NoError(t, err)
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"quiz_id": targetQuizResult.Id,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var resultCopy question.UpdateResp
err = json.NewDecoder(resp.Body).Decode(&resultCopy)
assert.NoError(t, err)
assert.NoError(t, err)
assert.NotEmpty(t, resultCopy.Updated)
assert.NotEqual(t, createResult.Id, resultCopy.Updated)
}
// отсмотрено
func TestCopyQuestion_Auth(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования аутентификации копирования",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question for Copy",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"id": createResult.Id,
"quiz_id": quizResult.Id,
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/question/copy", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := copyQuestionRequest("invalid_token", map[string]interface{}{
"id": createResult.Id,
"quiz_id": quizResult.Id,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := copyQuestionRequest(expiredToken, map[string]interface{}{
"id": createResult.Id,
"quiz_id": quizResult.Id,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено todo
func TestCopyQuestion_InputValidation(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования валидации копирования",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question for Validation",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
t.Run("MissingID", func(t *testing.T) {
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusFailedDependency, resp.StatusCode)
})
// todo как проходит? надо фиксить
//t.Run("MissingQuizID", func(t *testing.T) {
// resp, err := copyQuestionRequest(validToken, map[string]interface{}{
// "id": createResult.Id,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
//})
t.Run("InvalidID", func(t *testing.T) {
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": "invalid",
"quiz_id": quizResult.Id,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidQuizID", func(t *testing.T) {
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"quiz_id": "invalid",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo надо возвращать 404
t.Run("NonExistentID", func(t *testing.T) {
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": 99999,
"quiz_id": quizResult.Id,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
}
// todo
//func TestCopyQuestion_Security(t *testing.T) {
// quizResp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": "Квиз для тестирования безопасности копирования",
// "status": "draft",
// })
// assert.NoError(t, err)
// defer quizResp.Body.Close()
//
// assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
//
// var quizResult model.Quiz
// err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
// assert.NoError(t, err)
//
// createResp, err := createQuestionRequest(validToken, map[string]interface{}{
// "quiz_id": quizResult.Id,
// "title": "Security Test Question",
// "type": "variant",
// })
// assert.NoError(t, err)
// defer createResp.Body.Close()
//
// var createResult model.Question
// err = json.NewDecoder(createResp.Body).Decode(&createResult)
// assert.NoError(t, err)
//
// t.Run("SQLInjection", func(t *testing.T) {
// resp, err := copyQuestionRequest(validToken, map[string]interface{}{
// "id": "1' OR '1'='1",
// "quiz_id": quizResult.Id,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSSAttack", func(t *testing.T) {
// resp, err := copyQuestionRequest(validToken, map[string]interface{}{
// "id": createResult.Id,
// "quiz_id": quizResult.Id,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
// отсмотрено
func TestCopyQuestion_Performance(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования производительности копирования",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Performance Test Question",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"quiz_id": quizResult.Id,
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 30; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": fmt.Sprintf("Load Test Quiz %d", index),
"status": "draft",
})
if err != nil {
return
}
defer quizResp.Body.Close()
if quizResp.StatusCode != http.StatusCreated {
return
}
var quizResult map[string]interface{}
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
if err != nil {
return
}
quizID := quizResult["id"]
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizID,
"title": fmt.Sprintf("Load Test Question %d", index),
"type": "variant",
})
if err != nil {
return
}
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
if err != nil {
return
}
originalID := createResult["id"]
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": originalID,
"quiz_id": quizID,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}(i)
}
wg.Wait()
})
}
// отсмотрено
func TestCopyQuestion_OriginalPreserved(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Исходный квиз для проверки сохранения оригинала",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Original Question",
"type": "variant",
"required": true,
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"quiz_id": quizResult.Id,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var copyResult question.UpdateResp
err = json.NewDecoder(resp.Body).Decode(&copyResult)
assert.NoError(t, err)
assert.NotEqual(t, createResult.Id, copyResult.Updated)
}
func getQuestionHistoryRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/question/history", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestGetQuestionHistory_Success(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования истории вопросов",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Original Question",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
for i := 1; i <= 3; i++ {
editResp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"title": fmt.Sprintf("Updated Question Version %d", i),
})
assert.NoError(t, err)
editResp.Body.Close()
}
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"l": 10,
"p": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result []model.Question
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
fmt.Println(result)
if len(result) > 0 {
assert.Equal(t, createResult.Id, result[0].Id)
assert.Equal(t, quizResult.Id, result[0].QuizId)
assert.NotEmpty(t, result[0].Version)
}
}
// отсмотрено
func TestGetQuestionHistory_Auth(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования аутентификации истории",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question for History",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"id": createResult.Id,
"l": 10,
"p": 0,
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/question/history", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := getQuestionHistoryRequest("invalid_token", map[string]interface{}{
"id": createResult.Id,
"l": 10,
"p": 0,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := getQuestionHistoryRequest(expiredToken, map[string]interface{}{
"id": createResult.Id,
"l": 10,
"p": 0,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestGetQuestionHistory_InputValidation(t *testing.T) {
t.Run("MissingID", func(t *testing.T) {
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"l": 10,
"p": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusFailedDependency, resp.StatusCode)
})
t.Run("InvalidID", func(t *testing.T) {
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": "not_an_integer",
"l": 10,
"p": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidLimit", func(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования невалидного лимита",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult map[string]interface{}
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
quizID := quizResult["id"]
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizID,
"title": "Test Question for Invalid Limit",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
questionID := createResult["id"]
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": questionID,
"l": "ten",
"p": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidPage", func(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования невалидной страницы",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult map[string]interface{}
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
quizID := quizResult["id"]
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizID,
"title": "Test Question for Invalid Page",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
questionID := createResult["id"]
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": questionID,
"l": 10,
"p": "one",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NegativeLimit", func(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования отрицательного лимита",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult map[string]interface{}
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
quizID := quizResult["id"]
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizID,
"title": "Test Question for Negative Limit",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
questionID := createResult["id"]
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": questionID,
"l": -5,
"p": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NonExistentID", func(t *testing.T) {
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": 99999,
"l": 10,
"p": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
// отсмотрено
func TestGetQuestionHistory_Pagination(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования пагинации истории",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult map[string]interface{}
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
quizID := quizResult["id"]
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizID,
"title": "Pagination Test Question",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
questionID := createResult["id"]
for i := 1; i <= 15; i++ {
editResp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
"title": fmt.Sprintf("Version %d", i),
})
assert.NoError(t, err)
editResp.Body.Close()
}
t.Run("FirstPage", func(t *testing.T) {
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": questionID,
"l": 5,
"p": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result []model.Question
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.LessOrEqual(t, len(result), 5)
})
t.Run("SecondPage", func(t *testing.T) {
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": questionID,
"l": 5,
"p": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result []model.Question
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.LessOrEqual(t, len(result), 5)
})
t.Run("EmptyPage", func(t *testing.T) {
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": questionID,
"l": 5,
"p": 100,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result []model.Question
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Empty(t, len(result), 5)
})
}
// отсмотрено
func TestGetQuestionHistory_NewQuestion(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования нового вопроса",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "New Question",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": createResult.Id,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result []model.Question
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Len(t, result, 0)
}
// отсмотрено
func TestGetQuestionHistory_Performance(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования производительности истории",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Performance Test Question",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
for i := 1; i <= 5; i++ {
editResp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"title": fmt.Sprintf("Version %d", i),
})
assert.NoError(t, err)
editResp.Body.Close()
}
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"l": 10,
"p": 1,
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"l": 5,
"p": 1,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
func deleteQuestionRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("DELETE", baseURL+"/question/delete", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestDeleteQuestion_Success(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования удаления",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Question to Delete",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result question.DeactivateResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, createResult.Id, result.Deactivated)
}
// отсмотрено
func TestDeleteQuestion_Idempotency(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования идемпотентности удаления",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Question for Idempotency Test",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp1, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
})
assert.NoError(t, err)
defer resp1.Body.Close()
assert.Equal(t, http.StatusOK, resp1.StatusCode)
resp2, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
})
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusOK, resp2.StatusCode)
var result1, result2 question.DeactivateResp
err = json.NewDecoder(resp1.Body).Decode(&result1)
assert.NoError(t, err)
err = json.NewDecoder(resp2.Body).Decode(&result2)
assert.NoError(t, err)
assert.Equal(t, createResult.Id, result1.Deactivated)
assert.Equal(t, createResult.Id, result2.Deactivated)
}
// отсмотрено
func TestDeleteQuestion_Auth(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования аутентификации удаления",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Test Question for Delete Auth",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"id": createResult.Id,
})
assert.NoError(t, err)
req, err := http.NewRequest("DELETE", baseURL+"/question/delete", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := deleteQuestionRequest("invalid_token", map[string]interface{}{
"id": createResult.Id,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := deleteQuestionRequest(expiredToken, map[string]interface{}{
"id": createResult.Id,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestDeleteQuestion_InputValidation(t *testing.T) {
t.Run("MissingID", func(t *testing.T) {
resp, err := deleteQuestionRequest(validToken, map[string]interface{}{})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusFailedDependency, resp.StatusCode)
})
t.Run("InvalidID", func(t *testing.T) {
resp, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": "not_an_integer",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo 404?
t.Run("NonExistentID", func(t *testing.T) {
resp, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": 99999,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
}
// отсмотрено
func TestDeleteQuestion_AlreadyDeleted(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования повторного удаления",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": "Question to Delete Twice",
"type": "variant",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Question
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp1, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
})
assert.NoError(t, err)
defer resp1.Body.Close()
assert.Equal(t, http.StatusOK, resp1.StatusCode)
resp2, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": createResult.Id,
})
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusOK, resp2.StatusCode)
var result1, result2 question.DeactivateResp
err = json.NewDecoder(resp1.Body).Decode(&result1)
assert.NoError(t, err)
err = json.NewDecoder(resp2.Body).Decode(&result2)
assert.NoError(t, err)
assert.Equal(t, createResult.Id, result1.Deactivated)
assert.Equal(t, createResult.Id, result2.Deactivated)
}
// отсмотрено
func TestDeleteQuestion_Performance(t *testing.T) {
quizResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для тестирования производительности удаления",
"status": "draft",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
var questionIDs []uint64
for i := 0; i < 10; i++ {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": quizResult.Id,
"title": fmt.Sprintf("Performance Test Question %d", i),
"type": "variant",
})
assert.NoError(t, err)
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
createResp.Body.Close()
assert.NoError(t, err)
questionIDs = append(questionIDs, createResult.Id)
}
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": questionIDs[0],
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("BulkDelete", func(t *testing.T) {
var wg sync.WaitGroup
for i := 1; i < len(questionIDs); i++ {
wg.Add(1)
go func(id interface{}) {
defer wg.Done()
resp, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": id,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}(questionIDs[i])
}
wg.Wait()
})
}
func createQuizRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/quiz/create", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// todo если у нас квиз без статуса передается, то будет ошибка
func TestCreateQuiz_Success(t *testing.T) {
//t.Run("MinimalQuiz", func(t *testing.T) {
// resp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": "Новый квиз по истории",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
//
// assert.Equal(t, http.StatusCreated, resp.StatusCode)
// assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
//
// var result model.Quiz
// err = json.NewDecoder(resp.Body).Decode(&result)
// assert.NoError(t, err)
//
// assert.NotEmpty(t, result.Id)
// assert.NotEmpty(t, result.Qid)
// assert.NotEmpty(t, result.AccountId)
// assert.Equal(t, "Новый квиз по истории", result.Name)
// assert.Equal(t, "draft", result.Status)
// assert.Equal(t, false, result.Deleted)
// assert.Equal(t, false, result.Archived)
// assert.Equal(t, 1, result.Version)
//})
// отсмотрено
t.Run("FullQuiz", func(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Полный квиз по географии",
"description": "Детальный тест на знание столиц и стран.",
"fingerprinting": true,
"repeatable": true,
"note_prevented": false,
"mail_notifications": true,
"unique_answers": false,
"config": "{\"showCorrectAnswers\": true}",
"status": "start",
"limit": 100,
"question_cnt": 10,
"time_of_passing": 3600,
"pausable": true,
"super": false,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
var result model.Quiz
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEmpty(t, result.Id)
assert.Equal(t, "Полный квиз по географии", result.Name)
assert.Equal(t, "Детальный тест на знание столиц и стран.", result.Description)
assert.Equal(t, true, result.Fingerprinting)
assert.Equal(t, true, result.Repeatable)
assert.Equal(t, false, result.NotePrevented)
assert.Equal(t, true, result.MailNotifications)
assert.Equal(t, false, result.UniqueAnswers)
assert.Equal(t, "{\"showCorrectAnswers\": true}", result.Config)
assert.Equal(t, "start", result.Status)
assert.Equal(t, uint64(100), result.Limit)
assert.Equal(t, uint64(10), result.QuestionsCount)
assert.Equal(t, uint64(3600), result.TimeOfPassing)
assert.Equal(t, true, result.Pausable)
assert.Equal(t, false, result.Super)
})
}
// отсмотрено
func TestCreateQuiz_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"name": "Test Quiz",
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/quiz/create", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := createQuizRequest("invalid_token", map[string]interface{}{
"name": "Test Quiz",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := createQuizRequest(expiredToken, map[string]interface{}{
"name": "Test Quiz",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestCreateQuiz_InputValidation(t *testing.T) {
t.Run("NameTooLong", func(t *testing.T) {
longName := strings.Repeat("a", 701) // Больше 700 символов
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": longName,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusUnprocessableEntity, resp.StatusCode)
})
t.Run("InvalidStatus", func(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Test Quiz",
"status": "invalid_status",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode)
})
t.Run("InvalidFingerprinting", func(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Test Quiz",
"fingerprinting": "not_boolean",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NegativeLimit", func(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Test Quiz",
"limit": -5,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidDueTo", func(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Test Quiz",
"due_to": "not_timestamp",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
// отсмотрено
func TestCreateQuiz_StatusValues(t *testing.T) {
statuses := []string{"draft", "template", "stop", "start"}
for _, status := range statuses {
t.Run("Status_"+status, func(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": fmt.Sprintf("Quiz with status %s", status),
"status": status,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, status, result["status"])
})
}
}
// отсмотрено
func TestCreateQuiz_DefaultValues(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Quiz with defaults",
"status": "draft",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, "draft", result["status"])
assert.Equal(t, false, result["fingerprinting"])
assert.Equal(t, false, result["repeatable"])
assert.Equal(t, false, result["note_prevented"])
assert.Equal(t, false, result["pausable"])
assert.Equal(t, false, result["super"])
}
// отсмотрено
func TestCreateQuiz_Conflict(t *testing.T) {
resp1, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Duplicate Quiz Name",
"status": "draft",
"pausable": true,
"time_of_passing": 0,
})
assert.NoError(t, err)
defer resp1.Body.Close()
assert.Equal(t, http.StatusConflict, resp1.StatusCode)
}
// todo
//func TestCreateQuiz_Security(t *testing.T) {
// t.Run("SQLInjection", func(t *testing.T) {
// resp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": sqlInjectionInput,
// "description": sqlInjectionInput,
// "status": "draft",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
//
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSS", func(t *testing.T) {
// resp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": xssInput,
// "description": xssInput,
// "status": "draft",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
//
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
// отсмотрено
func TestCreateQuiz_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Performance Test Quiz",
"status": "draft",
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 50; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": fmt.Sprintf("Load Test Quiz %d", index),
"status": "draft",
})
if err == nil && resp != nil {
resp.Body.Close()
}
}(i)
}
wg.Wait()
})
}
// отсмотрено
func TestCreateQuiz_SuperQuiz(t *testing.T) {
t.Run("SuperQuizWithoutGroup", func(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Super Quiz",
"super": true,
"status": "draft",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
var result model.Quiz
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, true, result.Super)
assert.Equal(t, uint64(0), result.GroupId)
})
t.Run("NonSuperQuizWithGroup", func(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Group Quiz",
"super": false,
"group_id": 123,
"status": "draft",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
var result model.Quiz
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, false, result.Super)
assert.Equal(t, uint64(123), result.GroupId)
})
}
func getQuizListRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/quiz/getList", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestGetQuizList_Success(t *testing.T) {
quizNames := []string{"Квиз по географии", "Квиз по истории", "Квиз по математике"}
var quizIDs []uint64
for _, name := range quizNames {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": name,
"status": "start",
})
assert.NoError(t, err)
var result model.Quiz
err = json.NewDecoder(resp.Body).Decode(&result)
resp.Body.Close()
assert.NoError(t, err)
quizIDs = append(quizIDs, result.Id)
}
t.Run("BasicList", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"limit": 5,
"page": 0,
"status": "start",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result quiz.GetQuizListResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEmpty(t, result.Count)
assert.NotEmpty(t, result.Items)
assert.LessOrEqual(t, len(result.Items), 5)
if len(result.Items) > 0 {
assert.NotEmpty(t, result.Items[0].Id)
assert.NotEmpty(t, result.Items[0].Name)
assert.NotEmpty(t, result.Items[0].Status)
}
})
t.Run("WithFilters", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"limit": 10,
"page": 1,
"search": "географии",
"status": "start",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
items, ok := result["items"].([]interface{})
assert.True(t, ok)
for _, item := range items {
quiz, ok := item.(map[string]interface{})
assert.True(t, ok)
name, ok := quiz["name"].(string)
assert.True(t, ok)
assert.Contains(t, strings.ToLower(name), "географии")
}
})
}
// отсмотрено
func TestGetQuizList_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"limit": 10,
"page": 1,
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/quiz/getList", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := getQuizListRequest("invalid_token", map[string]interface{}{
"limit": 10,
"page": 1,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := getQuizListRequest(expiredToken, map[string]interface{}{
"limit": 10,
"page": 1,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestGetQuizList_InputValidation(t *testing.T) {
t.Run("InvalidLimit", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"limit": "not_integer",
"page": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidPage", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"limit": 10,
"page": "not_integer",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("ZeroLimit", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"limit": 0,
"page": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("ZeroPage", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"limit": 10,
"page": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("InvalidFrom", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"from": "not_timestamp",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidTo", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"to": "not_timestamp",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidStatus", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"status": "invalid_status",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode)
})
t.Run("InvalidDeleted", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"deleted": "not_boolean",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidArchived", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"archived": "not_boolean",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidSuper", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"super": "not_boolean",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidGroupID", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"group_id": "not_integer",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
// отсмотрено
func TestGetQuizList_Pagination(t *testing.T) {
for i := 0; i < 15; i++ {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": fmt.Sprintf("Pagination Test Quiz %d", i),
"status": "draft",
})
assert.NoError(t, err)
resp.Body.Close()
}
t.Run("FirstPage", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"limit": 5,
"page": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
items, ok := result["items"].([]interface{})
assert.True(t, ok)
assert.LessOrEqual(t, len(items), 5)
})
t.Run("SecondPage", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"limit": 5,
"page": 2,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
items, ok := result["items"].([]interface{})
assert.True(t, ok)
assert.LessOrEqual(t, len(items), 5)
})
}
// отсмотрено
func TestGetQuizList_Filters(t *testing.T) {
statuses := []string{"draft", "start", "stop", "template"}
for _, status := range statuses {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": fmt.Sprintf("тест по фильтру %s", status),
"status": status,
})
assert.NoError(t, err)
resp.Body.Close()
}
t.Run("StatusFilter", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"status": "draft",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result quiz.GetQuizListResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
items := result.Items
for _, item := range items {
assert.Equal(t, "draft", item.Status)
}
})
t.Run("SearchFilter", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"search": "тест",
"limit": 5,
"page": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result quiz.GetQuizListResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
items := result.Items
for _, item := range items {
assert.Contains(t, item.Name, "тест")
}
})
t.Run("TimeRangeFilter", func(t *testing.T) {
now := time.Now().Unix()
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"from": now - 86400,
"to": now,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result quiz.GetQuizListResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
items := result.Items
assert.GreaterOrEqual(t, len(items), 0)
})
}
// отсмотрено
func TestGetQuizList_DefaultValues(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result quiz.GetQuizListResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.LessOrEqual(t, len(result.Items), 10)
}
// отсмотрено
func TestGetQuizList_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"limit": 10,
"page": 1,
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(1000))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"limit": 5,
"page": 1,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
func editQuizRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("PATCH", baseURL+"/quiz/edit", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestEditQuiz_Success(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для редактирования",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"name": "Обновленное название квиза",
"desc": "Новое описание",
"status": "start",
"limit": 150,
"fp": true,
"rep": false,
"conf": "{}",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result quiz.UpdateResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, createResult.Id, result.Updated)
}
// отсмотрено
func TestEditQuiz_OneField(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для смены статуса",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"status": "stop",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result quiz.UpdateResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, createResult.Id, result.Updated)
}
// отсмотрено
func TestEditQuiz_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"id": 101,
"name": "Test Quiz",
})
assert.NoError(t, err)
req, err := http.NewRequest("PATCH", baseURL+"/quiz/edit", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := editQuizRequest("invalid_token", map[string]interface{}{
"id": 101,
"name": "Test Quiz",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := editQuizRequest(expiredToken, map[string]interface{}{
"id": 101,
"name": "Test Quiz",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestEditQuiz_InputValidation(t *testing.T) {
t.Run("MissingID", func(t *testing.T) {
resp, err := editQuizRequest(validToken, map[string]interface{}{
"name": "Без ID",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusFailedDependency, resp.StatusCode)
})
t.Run("InvalidID", func(t *testing.T) {
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": "not_an_integer",
"name": "Невалидный ID",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo нужен ответ 404
t.Run("NonExistentID", func(t *testing.T) {
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": 99999,
"name": "Несуществующий квиз",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
t.Run("NameTooLong", func(t *testing.T) {
longName := strings.Repeat("a", 701)
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": 101,
"name": longName,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusUnprocessableEntity, resp.StatusCode)
})
t.Run("InvalidStatus", func(t *testing.T) {
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": 101,
"status": "invalid_status",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode)
})
t.Run("InvalidFP", func(t *testing.T) {
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": 101,
"fp": "not_boolean",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidLimit", func(t *testing.T) {
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": 101,
"limit": -5,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
// todo
//func TestEditQuiz_Security(t *testing.T) {
// t.Run("SQLInjection", func(t *testing.T) {
// resp, err := editQuizRequest(validToken, map[string]interface{}{
// "id": 101,
// "name": sqlInjectionInput,
// "desc": sqlInjectionInput,
// "conf": "{}",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSS", func(t *testing.T) {
// resp, err := editQuizRequest(validToken, map[string]interface{}{
// "id": 101,
// "name": xssInput,
// "desc": xssInput,
// "conf": "{}",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
// отсмотрено
func TestEditQuiz_Performance(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"name": "Быстрое обновление",
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 50; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"name": fmt.Sprintf("Load Test Quiz %d", index),
})
if err == nil && resp != nil {
resp.Body.Close()
}
assert.Equal(t, http.StatusOK, resp.StatusCode)
}(i)
}
wg.Wait()
})
}
func copyQuizRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/quiz/copy", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestCopyQuiz_Success(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Оригинальный квиз для копирования",
"description": "Описание оригинала",
"status": "start",
"limit": 50,
"config": "{}",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := copyQuizRequest(validToken, map[string]interface{}{
"id": createResult.Id,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result quiz.UpdateResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEqual(t, createResult.Id, result.Updated)
}
// отсмотрено
func TestCopyQuiz_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/quiz/copy", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := copyQuizRequest("invalid_token", map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := copyQuizRequest(expiredToken, map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestCopyQuiz_InputValidation(t *testing.T) {
t.Run("MissingID", func(t *testing.T) {
resp, err := copyQuizRequest(validToken, map[string]interface{}{})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusFailedDependency, resp.StatusCode)
})
t.Run("InvalidID", func(t *testing.T) {
resp, err := copyQuizRequest(validToken, map[string]interface{}{
"id": "not_an_integer",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo 404
t.Run("NonExistentID", func(t *testing.T) {
resp, err := copyQuizRequest(validToken, map[string]interface{}{
"id": 99999,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
}
// отсмотрено
func TestCopyQuiz_Performance(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для копирования (перфоманс)",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := copyQuizRequest(validToken, map[string]interface{}{
"id": createResult.Id,
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 20; i++ {
wg.Add(1)
go func() {
defer wg.Done()
resp, err := copyQuizRequest(validToken, map[string]interface{}{
"id": createResult.Id,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
func getQuizHistoryRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/quiz/history", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestGetQuizHistory_Success(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для истории",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
for i := 1; i <= 3; i++ {
editResp, err := editQuizRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"name": fmt.Sprintf("Обновленный квиз версия %d", i),
"status": "start",
})
assert.NoError(t, err)
editResp.Body.Close()
}
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": createResult.Id,
"l": 5,
"p": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result []model.Quiz
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
fmt.Println(result)
assert.LessOrEqual(t, len(result), 5)
assert.Greater(t, len(result), 0)
if len(result) > 0 {
firstItem := result[0]
assert.Equal(t, createResult.Id, firstItem.Id)
}
}
// отсмотрено
func TestGetQuizHistory_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"id": 101,
"l": 10,
"p": 1,
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/quiz/history", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := getQuizHistoryRequest("invalid_token", map[string]interface{}{
"id": 101,
"l": 10,
"p": 1,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := getQuizHistoryRequest(expiredToken, map[string]interface{}{
"id": 101,
"l": 10,
"p": 1,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestGetQuizHistory_InputValidation(t *testing.T) {
t.Run("MissingID", func(t *testing.T) {
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"l": 10,
"p": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusFailedDependency, resp.StatusCode)
})
t.Run("InvalidID", func(t *testing.T) {
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": "not_an_integer",
"l": 10,
"p": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidLimit", func(t *testing.T) {
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": 101,
"l": "ten",
"p": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidPage", func(t *testing.T) {
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": 101,
"l": 10,
"p": "one",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NonExistentID", func(t *testing.T) {
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": 99999,
"l": 10,
"p": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
var result []model.Quiz
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Empty(t, result)
})
}
// отсмотрено
func TestGetQuizHistory_Pagination(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для пагинации истории",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult.Id
for i := 1; i <= 15; i++ {
editResp, err := editQuizRequest(validToken, map[string]interface{}{
"id": quizID,
"name": fmt.Sprintf("Версия %d", i),
})
assert.NoError(t, err)
editResp.Body.Close()
}
t.Run("FirstPage", func(t *testing.T) {
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": quizID,
"l": 5,
"p": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
var result []model.Quiz
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.LessOrEqual(t, len(result), 5)
})
t.Run("SecondPage", func(t *testing.T) {
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": quizID,
"l": 5,
"p": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
var result []model.Quiz
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.LessOrEqual(t, len(result), 5)
})
t.Run("EmptyPage", func(t *testing.T) {
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": quizID,
"l": 5,
"p": 100,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
var result []model.Quiz
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Empty(t, result)
})
}
// отсмотрено
func TestGetQuizHistory_NewQuiz(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Новый квиз для истории",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult.Id
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": quizID,
"l": 5,
"p": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
var result []model.Quiz
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Len(t, result, 1) // Должна быть только одна запись для нового квиза
if len(result) > 0 {
firstItem := result[0]
assert.Equal(t, quizID, firstItem.Id)
}
}
// отсмотрено
func TestGetQuizHistory_Performance(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности истории",
"status": "draft",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusCreated, createResp.StatusCode)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult.Id
for i := 1; i <= 5; i++ {
editResp, err := editQuizRequest(validToken, map[string]interface{}{
"id": quizID,
"name": fmt.Sprintf("Версия %d", i),
})
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, editResp.StatusCode)
editResp.Body.Close()
}
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": quizID,
"l": 10,
"p": 0,
})
duration := time.Since(start)
assert.NoError(t, err)
assert.Equal(t, http.StatusCreated, resp.StatusCode)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": quizID,
"l": 5,
"p": 0,
})
if err == nil && resp != nil {
assert.Equal(t, http.StatusCreated, resp.StatusCode)
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
func deleteQuizRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("DELETE", baseURL+"/quiz/delete", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestDeleteQuiz_Success(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для удаления",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult.Id
resp, err := deleteQuizRequest(validToken, map[string]interface{}{
"id": quizID,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result quiz.DeactivateResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, quizID, result.Deactivated)
}
// отсмотрено
func TestDeleteQuiz_Idempotency(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для идемпотентности",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult.Id
resp1, err := deleteQuizRequest(validToken, map[string]interface{}{
"id": quizID,
})
assert.NoError(t, err)
defer resp1.Body.Close()
assert.Equal(t, http.StatusOK, resp1.StatusCode)
resp2, err := deleteQuizRequest(validToken, map[string]interface{}{
"id": quizID,
})
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusOK, resp2.StatusCode)
var result1, result2 quiz.DeactivateResp
err = json.NewDecoder(resp1.Body).Decode(&result1)
assert.NoError(t, err)
err = json.NewDecoder(resp2.Body).Decode(&result2)
assert.NoError(t, err)
assert.Equal(t, quizID, result1.Deactivated)
assert.Equal(t, quizID, result2.Deactivated)
}
// отсмотрено
func TestDeleteQuiz_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
req, err := http.NewRequest("DELETE", baseURL+"/quiz/delete", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := deleteQuizRequest("invalid_token", map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := deleteQuizRequest(expiredToken, map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestDeleteQuiz_InputValidation(t *testing.T) {
t.Run("MissingID", func(t *testing.T) {
resp, err := deleteQuizRequest(validToken, map[string]interface{}{})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusFailedDependency, resp.StatusCode)
})
t.Run("InvalidID", func(t *testing.T) {
resp, err := deleteQuizRequest(validToken, map[string]interface{}{
"id": "not_an_integer",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo 404
t.Run("NonExistentID", func(t *testing.T) {
resp, err := deleteQuizRequest(validToken, map[string]interface{}{
"id": 99999,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) // Идемпотентность
})
}
// отсмотрено
func TestDeleteQuiz_Performance(t *testing.T) {
var quizIDs []uint64
for i := 0; i < 10; i++ {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": fmt.Sprintf("Квиз для удаления %d", i),
"status": "draft",
})
assert.NoError(t, err)
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
createResp.Body.Close()
assert.NoError(t, err)
quizIDs = append(quizIDs, createResult.Id)
}
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := deleteQuizRequest(validToken, map[string]interface{}{
"id": quizIDs[0],
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("BulkDelete", func(t *testing.T) {
var wg sync.WaitGroup
for i := 1; i < len(quizIDs); i++ {
wg.Add(1)
go func(id interface{}) {
defer wg.Done()
resp, err := deleteQuizRequest(validToken, map[string]interface{}{
"id": id,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}(quizIDs[i])
}
wg.Wait()
})
}
func archiveQuizRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("PATCH", baseURL+"/quiz/archive", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestArchiveQuiz_Success(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для архивации",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult.Id
resp, err := archiveQuizRequest(validToken, map[string]interface{}{
"id": quizID,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result quiz.DeactivateResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, quizID, result.Deactivated)
}
// отсмотрено
func TestArchiveQuiz_Idempotency(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для архивации (идемпотентность)",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult.Id
resp1, err := archiveQuizRequest(validToken, map[string]interface{}{
"id": quizID,
})
assert.NoError(t, err)
defer resp1.Body.Close()
assert.Equal(t, http.StatusOK, resp1.StatusCode)
resp2, err := archiveQuizRequest(validToken, map[string]interface{}{
"id": quizID,
})
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusOK, resp2.StatusCode)
var result1, result2 quiz.DeactivateResp
err = json.NewDecoder(resp1.Body).Decode(&result1)
assert.NoError(t, err)
err = json.NewDecoder(resp2.Body).Decode(&result2)
assert.NoError(t, err)
assert.Equal(t, quizID, result1.Deactivated)
assert.Equal(t, quizID, result2.Deactivated)
}
// отсмотрено
func TestArchiveQuiz_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
req, err := http.NewRequest("PATCH", baseURL+"/quiz/archive", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := archiveQuizRequest("invalid_token", map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ExpiredToken", func(t *testing.T) {
resp, err := archiveQuizRequest(expiredToken, map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestArchiveQuiz_InputValidation(t *testing.T) {
t.Run("MissingID", func(t *testing.T) {
resp, err := archiveQuizRequest(validToken, map[string]interface{}{})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusFailedDependency, resp.StatusCode)
})
t.Run("InvalidID", func(t *testing.T) {
resp, err := archiveQuizRequest(validToken, map[string]interface{}{
"id": "not_an_integer",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo 404
t.Run("NonExistentID", func(t *testing.T) {
resp, err := archiveQuizRequest(validToken, map[string]interface{}{
"id": 99999,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
}
// отсмотрено
func TestArchiveQuiz_Performance(t *testing.T) {
var quizIDs []uint64
for i := 0; i < 10; i++ {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": fmt.Sprintf("Квиз для архивации %d", i),
"status": "draft",
})
assert.NoError(t, err)
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
createResp.Body.Close()
assert.NoError(t, err)
quizIDs = append(quizIDs, createResult.Id)
}
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := archiveQuizRequest(validToken, map[string]interface{}{
"id": quizIDs[0],
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("BulkArchive", func(t *testing.T) {
var wg sync.WaitGroup
for i := 1; i < len(quizIDs); i++ {
wg.Add(1)
go func(id interface{}) {
defer wg.Done()
resp, err := archiveQuizRequest(validToken, map[string]interface{}{
"id": id,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}(quizIDs[i])
}
wg.Wait()
})
}
// отсмотрено
func TestMoveQuiz_BoundaryCases(t *testing.T) {
t.Run("EmptyQIDAndAccountID", func(t *testing.T) {
resp, err := moveQuizRequest(validToken, map[string]interface{}{
"qid": "",
"accountID": "",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("LongQIDAndAccountID", func(t *testing.T) {
longStr := make([]byte, 1025)
for i := range longStr {
longStr[i] = 'a'
}
resp, err := moveQuizRequest(validToken, map[string]interface{}{
"qid": string(longStr),
"accountID": string(longStr),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
}
// отсмотрено
func TestMoveQuiz_ErrorHandling(t *testing.T) {
t.Run("MalformedJSON", func(t *testing.T) {
req, err := http.NewRequest("POST", baseURL+"/quiz/move", bytes.NewReader([]byte("{invalid_json}")))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
func moveQuizRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/quiz/move", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestMoveQuiz_Success(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для переноса",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizQID := createResult.Qid
resp, err := moveQuizRequest(validToken, map[string]interface{}{
"qid": quizQID,
"accountID": "new-account-id",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
}
// отсмотрено
func TestMoveQuiz_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"qid": "test-qid",
"accountID": "new-account",
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/quiz/move", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := moveQuizRequest("invalid_token", map[string]interface{}{
"qid": "test-qid",
"accountID": "new-account",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestMoveQuiz_InputValidation(t *testing.T) {
t.Run("MissingQID", func(t *testing.T) {
resp, err := moveQuizRequest(validToken, map[string]interface{}{
"accountID": "new-account",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("MissingAccountID", func(t *testing.T) {
resp, err := moveQuizRequest(validToken, map[string]interface{}{
"qid": "test-qid",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo 404
t.Run("NonExistentQID", func(t *testing.T) {
resp, err := moveQuizRequest(validToken, map[string]interface{}{
"qid": "non-existent-qid",
"accountID": "new-account",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
// todo 404
t.Run("NonExistentAccountID", func(t *testing.T) {
resp, err := moveQuizRequest(validToken, map[string]interface{}{
"qid": "test-qid",
"accountID": "non-existent-account",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
}
// отсмотрено
func TestMoveQuiz_Performance(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности переноса",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizQID := createResult.Qid
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := moveQuizRequest(validToken, map[string]interface{}{
"qid": quizQID,
"accountID": "performance-test-account",
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
}
// отсмотрено
func TestCreateQuizTemplate_BoundaryCases(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста граничных случаев",
"description": "Детальный тест для проверки граничных случаев создания шаблонов",
"fingerprinting": true,
"repeatable": true,
"note_prevented": false,
"mail_notifications": true,
"unique_answers": false,
"config": "{\"showCorrectAnswers\": true, \"allowReview\": true}",
"status": "template",
"limit": 100,
"question_cnt": 15,
"time_of_passing": 3600,
"pausable": true,
"super": false,
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizQID := createResult.Qid
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": createResult.Id,
"title": "Какой основной компонент воздуха?",
"type": "variant",
"description": "Выберите один правильный ответ.",
"required": true,
"page": 1,
"content": "{}",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
t.Run("ManyRequests", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 20; i++ {
wg.Add(1)
go func() {
defer wg.Done()
resp, err := createQuizTemplateRequest(validToken, quizQID)
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
func createQuizTemplateRequest(token string, qid string) (*http.Response, error) {
payload := map[string]interface{}{
"qid": qid,
}
jsonData, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/quiz/template", bytes.NewReader(jsonData))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestCreateQuizTemplate_Success(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности переноса",
"description": "Комплексный тест для проверки создания шаблонов",
"fingerprinting": true,
"repeatable": true,
"note_prevented": false,
"mail_notifications": true,
"unique_answers": false,
"config": "{\"showCorrectAnswers\": true, \"allowReview\": true, \"showProgress\": true}",
"status": "template",
"limit": 200,
"question_cnt": 20,
"time_of_passing": 7200,
"pausable": true,
"super": false,
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizQID := createResult.Qid
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": createResult.Id,
"title": "Какой основной компонент воздуха?",
"type": "variant",
"description": "Выберите один правильный ответ.",
"required": true,
"page": 1,
"content": "{}",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
resp, err = createQuizTemplateRequest(validToken, quizQID)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEmpty(t, result["id"])
assert.IsType(t, float64(0), result["id"])
}
// отсмотрено
func TestCreateQuizTemplate_Auth(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста авторизации",
"description": "Тест для проверки авторизации при создании шаблонов",
"fingerprinting": false,
"repeatable": false,
"note_prevented": true,
"mail_notifications": false,
"unique_answers": true,
"config": "{\"showCorrectAnswers\": false, \"strictMode\": true}",
"status": "template",
"limit": 50,
"question_cnt": 5,
"time_of_passing": 1800,
"pausable": false,
"super": false,
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizQID := createResult.Qid
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": createResult.Id,
"title": "Какой основной компонент воздуха?",
"type": "variant",
"description": "Выберите один правильный ответ.",
"required": true,
"page": 1,
"content": "{}",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
t.Run("NoToken", func(t *testing.T) {
payload := map[string]interface{}{
"qid": quizQID,
}
jsonData, err := json.Marshal(payload)
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/quiz/template", bytes.NewReader(jsonData))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := createQuizTemplateRequest("invalid_token", quizQID)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestCreateQuizTemplate_Performance(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности",
"description": "Тест производительности создания шаблонов",
"fingerprinting": true,
"repeatable": true,
"note_prevented": false,
"mail_notifications": true,
"unique_answers": false,
"config": "{\"showCorrectAnswers\": true, \"performanceMode\": true}",
"status": "template",
"limit": 150,
"question_cnt": 12,
"time_of_passing": 5400,
"pausable": true,
"super": false,
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizQID := createResult.Qid
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": createResult.Id,
"title": "Какой основной компонент воздуха?",
"type": "variant",
"description": "Выберите один правильный ответ.",
"required": true,
"page": 1,
"content": "{}",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
resp, err = createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": createResult.Id,
"title": "Какой основной компонент воздуха?",
"type": "variant",
"description": "Выберите один правильный ответ.",
"required": true,
"page": 1,
"content": "{}",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := createQuizTemplateRequest(validToken, quizQID)
duration := time.Since(start)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("MultipleTemplates", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
resp, err := createQuizTemplateRequest(validToken, quizQID)
if err == nil && resp != nil {
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// отсмотрено
func TestCreateQuizTemplate_InputValidation(t *testing.T) {
t.Run("EmptyQid", func(t *testing.T) {
payload := map[string]interface{}{
"qid": "",
}
jsonData, err := json.Marshal(payload)
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/quiz/template", bytes.NewReader(jsonData))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("MissingQid", func(t *testing.T) {
payload := map[string]interface{}{}
jsonData, err := json.Marshal(payload)
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/quiz/template", bytes.NewReader(jsonData))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidQid", func(t *testing.T) {
resp, err := createQuizTemplateRequest(validToken, "invalid-qid-format")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
t.Run("NonExistentQid", func(t *testing.T) {
resp, err := createQuizTemplateRequest(validToken, "non-existent-qid-12345")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
t.Run("QuizWithDraftStatus", func(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз со статусом draft",
"description": "Квиз для тестирования статуса draft",
"fingerprinting": false,
"repeatable": true,
"note_prevented": false,
"mail_notifications": false,
"unique_answers": false,
"config": "{\"showCorrectAnswers\": true}",
"status": "draft",
"limit": 10,
"question_cnt": 2,
"time_of_passing": 600,
"pausable": false,
"super": false,
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := createQuizTemplateRequest(validToken, createResult.Qid)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
}
// todo
//func TestCreateQuizTemplate_Security(t *testing.T) {
// createResp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": "Квиз для теста безопасности",
// "description": "Тест безопасности при создании шаблонов",
// "fingerprinting": true,
// "repeatable": false,
// "note_prevented": true,
// "mail_notifications": false,
// "unique_answers": true,
// "config": "{\"showCorrectAnswers\": false, \"securityMode\": true}",
// "status": "template",
// "limit": 75,
// "question_cnt": 8,
// "time_of_passing": 2700,
// "pausable": false,
// "super": false,
// })
// assert.NoError(t, err)
// defer createResp.Body.Close()
// var createResult model.Quiz
// err = json.NewDecoder(createResp.Body).Decode(&createResult)
// assert.NoError(t, err)
//
// t.Run("SQLInjection", func(t *testing.T) {
// resp, err := createQuizTemplateRequest(validToken, sqlInjectionInput)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSSAttempt", func(t *testing.T) {
// resp, err := createQuizTemplateRequest(validToken, xssInput)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("LargeQid", func(t *testing.T) {
// largeQid := strings.Repeat("a", 1000)
// resp, err := createQuizTemplateRequest(validToken, largeQid)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
// отсмотрено
func TestCreateQuizTemplate_SpecialCases(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для специальных случаев",
"description": "Тест специальных случаев создания шаблонов",
"fingerprinting": false,
"repeatable": true,
"note_prevented": false,
"mail_notifications": true,
"unique_answers": false,
"config": "{\"showCorrectAnswers\": true, \"flexibleMode\": true}",
"status": "template",
"limit": 300,
"question_cnt": 25,
"time_of_passing": 9000,
"pausable": true,
"super": false,
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult model.Quiz
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizQID := createResult.Qid
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": createResult.Id,
"title": "Какой основной компонент воздуха?",
"type": "variant",
"description": "Выберите один правильный ответ.",
"required": true,
"page": 1,
"content": "{}",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
t.Run("DuplicateTemplateCreation", func(t *testing.T) {
resp1, err := createQuizTemplateRequest(validToken, quizQID)
assert.NoError(t, err)
defer resp1.Body.Close()
assert.Equal(t, http.StatusOK, resp1.StatusCode)
resp2, err := createQuizTemplateRequest(validToken, quizQID)
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusOK, resp2.StatusCode)
var result1, result2 map[string]interface{}
err = json.NewDecoder(resp1.Body).Decode(&result1)
assert.NoError(t, err)
err = json.NewDecoder(resp2.Body).Decode(&result2)
assert.NoError(t, err)
assert.NotEqual(t, result1["id"], result2["id"])
})
// todo need 403
t.Run("TemplateFromDifferentUser", func(t *testing.T) {
createResp2, err := createQuizRequest(existingUserIDToken, map[string]interface{}{
"name": "Квиз другого пользователя",
"description": "Квиз созданный другим пользователем для тестирования доступа",
"fingerprinting": true,
"repeatable": false,
"note_prevented": true,
"mail_notifications": false,
"unique_answers": true,
"config": "{\"showCorrectAnswers\": false, \"privateMode\": true}",
"status": "template",
"limit": 60,
"question_cnt": 6,
"time_of_passing": 2400,
"pausable": false,
"super": false,
})
assert.NoError(t, err)
defer createResp2.Body.Close()
var createResult2 model.Quiz
err = json.NewDecoder(createResp2.Body).Decode(&createResult2)
assert.NoError(t, err)
resp, err := createQuizTemplateRequest(validToken, createResult2.Qid)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
}
// todo перевроверить через дамп
type TestData struct {
Quiz model.Quiz
Questions []model.Question
Answers []model.Answer
}
func createTestDataForResults(t *testing.T, token string, quizName string) *TestData {
quizResp, err := createQuizRequest(token, map[string]interface{}{
"name": quizName,
"status": "start",
})
assert.NoError(t, err)
defer quizResp.Body.Close()
assert.Equal(t, http.StatusCreated, quizResp.StatusCode)
var quizResult model.Quiz
err = json.NewDecoder(quizResp.Body).Decode(&quizResult)
assert.NoError(t, err)
questions := []model.Question{}
questionData := []map[string]interface{}{
{
"title": "Какой основной компонент воздуха?",
"type": "variant",
"description": "Выберите один правильный ответ.",
"required": true,
"page": 1,
"content": `{"variants": ["Кислород", "Азот", "Углекислый газ", "Водород"], "correct": 1}`,
},
{
"title": "Столица России?",
"type": "text",
"description": "Введите название столицы.",
"required": true,
"page": 1,
"content": `{"maxLength": 50}`,
},
{
"title": "Ваш возраст",
"type": "number",
"description": "Укажите ваш возраст.",
"required": false,
"page": 2,
"content": `{"min": 0, "max": 120}`,
},
{
"title": "Форма контактов",
"type": "result",
"description": "Оставьте свои контактные данные.",
"required": true,
"page": 2,
"content": `{"fields": ["name", "email", "phone"]}`,
},
}
for _, qData := range questionData {
qData["quiz_id"] = quizResult.Id
questionResp, err := createQuestionRequest(token, qData)
assert.NoError(t, err)
defer questionResp.Body.Close()
assert.Equal(t, http.StatusOK, questionResp.StatusCode)
var questionResult model.Question
err = json.NewDecoder(questionResp.Body).Decode(&questionResult)
assert.NoError(t, err)
questions = append(questions, questionResult)
}
answers := createAnswersInDB(t, quizResult.Id, questions)
return &TestData{
Quiz: quizResult,
Questions: questions,
Answers: answers,
}
}
func createAnswersInDB(t *testing.T, quizID uint64, questions []model.Question) []model.Answer {
db, err := sql.Open("postgres", postgresCred)
assert.NoError(t, err)
defer db.Close()
answers := []model.Answer{}
sessions := []string{faker.String(), faker.String(), faker.String()}
for i, session := range sessions {
for j, question := range questions {
if question.Type == "result" {
continue
}
answerContent := ""
switch question.Type {
case "variant":
answerContent = `{"selected": 1, "answer": "Азот"}`
case "text":
answerContent = `{"answer": "Москва"}`
case "number":
answerContent = `{"answer": 25}`
default:
answerContent = `{"answer": "тестовый ответ"}`
}
query := `
INSERT INTO answer (content, quiz_id, question_id, fingerprint, session, result, new, deleted, email, device_type, device, browser, ip, os, start, version)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
RETURNING id, content, quiz_id, question_id, fingerprint, session, result, new, deleted, email, device_type, device, browser, ip, os, start, version, created_at
`
var answer model.Answer
err := db.QueryRow(
query,
answerContent,
quizID,
question.Id,
fmt.Sprintf("fingerprint_%d_%d", i, j),
session,
false,
true,
false,
faker.Email(),
"desktop",
"Chrome",
"Chrome 120.0",
"192.168.1.1",
"Windows 10",
j == 0,
1,
).Scan(
&answer.Id,
&answer.Content,
&answer.QuizId,
&answer.QuestionId,
&answer.Fingerprint,
&answer.Session,
&answer.Result,
&answer.New,
&answer.Deleted,
&answer.Email,
&answer.DeviceType,
&answer.Device,
&answer.Browser,
&answer.IP,
&answer.OS,
&answer.Start,
&answer.Version,
&answer.CreatedAt,
)
assert.NoError(t, err)
answers = append(answers, answer)
}
for _, question := range questions {
if question.Type == "result" {
answerContent := `{"name": "Иван Иванов", "email": "ivan@test.com", "phone": "+7-999-123-45-67"}`
query := `
INSERT INTO answer (content, quiz_id, question_id, fingerprint, session, result, new, deleted, email, device_type, device, browser, ip, os, start, version)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
RETURNING id, content, quiz_id, question_id, fingerprint, session, result, new, deleted, email, device_type, device, browser, ip, os, start, version, created_at
`
var answer model.Answer
err := db.QueryRow(
query,
answerContent,
quizID,
question.Id,
fmt.Sprintf("fingerprint_result_%d", i),
session,
true,
true,
false,
faker.Email(),
"desktop",
"Chrome",
"Chrome 120.0",
"192.168.1.1",
"Windows 10",
false,
1,
).Scan(
&answer.Id,
&answer.Content,
&answer.QuizId,
&answer.QuestionId,
&answer.Fingerprint,
&answer.Session,
&answer.Result,
&answer.New,
&answer.Deleted,
&answer.Email,
&answer.DeviceType,
&answer.Device,
&answer.Browser,
&answer.IP,
&answer.OS,
&answer.Start,
&answer.Version,
&answer.CreatedAt,
)
assert.NoError(t, err)
answers = append(answers, answer)
break
}
}
}
return answers
}
func getResultsRequest(token string, quizId string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/results/getResults/"+quizId, bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestGetResults_Success(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для тестирования результатов")
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotNil(t, result.TotalCount)
assert.NotNil(t, result.Results)
assert.IsType(t, uint64(0), result.TotalCount)
assert.IsType(t, []model.AnswerExport{}, result.Results)
assert.Greater(t, result.TotalCount, uint64(0))
assert.Greater(t, len(result.Results), 0)
for _, answer := range result.Results {
assert.NotEmpty(t, answer.Id)
}
}
// отсмотрено
func TestGetResults_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/results/getResults/12345", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := getResultsRequest("invalid_token", "12345", map[string]interface{}{
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestGetResults_InputValidation(t *testing.T) {
t.Run("InvalidQuizID", func(t *testing.T) {
resp, err := getResultsRequest(validToken, "invalid-quiz-id", map[string]interface{}{
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NonExistentQuizID", func(t *testing.T) {
resp, err := getResultsRequest(validToken, "99999", map[string]interface{}{
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, float64(0), result["total_count"])
assert.Empty(t, result["results"])
})
}
// отсмотрено
func TestGetResults_Pagination(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для тестирования пагинации результатов")
t.Run("FirstPage", func(t *testing.T) {
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 5,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.LessOrEqual(t, uint64(len(result.Results)), uint64(5))
assert.Equal(t, result.TotalCount, uint64(len(result.Results)))
})
t.Run("SecondPage", func(t *testing.T) {
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 1,
"Limit": 5,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.IsType(t, uint64(0), result.TotalCount)
assert.IsType(t, []model.AnswerExport{}, result.Results)
})
t.Run("EmptyPage", func(t *testing.T) {
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 100,
"Limit": 5,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Empty(t, result.Results)
})
t.Run("ZeroLimit", func(t *testing.T) {
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 0,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Empty(t, result.Results)
})
}
// отсмотрено
func TestGetResults_Filtering(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для тестирования фильтрации результатов")
t.Run("WithDateRange", func(t *testing.T) {
fromDate := time.Now().AddDate(0, -1, 0).Format("2006-01-02T15:04:05Z")
toDate := time.Now().AddDate(0, 1, 0).Format("2006-01-02T15:04:05Z")
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"From": fromDate,
"To": toDate,
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.IsType(t, uint64(0), result.TotalCount)
assert.IsType(t, []model.AnswerExport{}, result.Results)
})
t.Run("NewResultsOnly", func(t *testing.T) {
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"New": true,
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
for _, answer := range result.Results {
assert.True(t, answer.New)
}
})
t.Run("CombinedFilters", func(t *testing.T) {
fromDate := time.Now().AddDate(0, -1, 0).Format("2006-01-02T15:04:05Z")
toDate := time.Now().AddDate(0, 1, 0).Format("2006-01-02T15:04:05Z")
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"From": fromDate,
"To": toDate,
"New": true,
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.IsType(t, uint64(0), result.TotalCount)
assert.IsType(t, []model.AnswerExport{}, result.Results)
})
t.Run("InvalidDateRange", func(t *testing.T) {
fromDate := time.Now().AddDate(0, 1, 0).Format("2006-01-02T15:04:05Z")
toDate := time.Now().AddDate(0, -1, 0).Format("2006-01-02T15:04:05Z")
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"From": fromDate,
"To": toDate,
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Empty(t, result.Results)
assert.Equal(t, uint64(0), result.TotalCount)
})
}
// отсмотрено
func TestGetResults_Performance(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для тестирования производительности результатов")
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 10,
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
fmt.Println(result)
assert.IsType(t, uint64(0), result.TotalCount)
assert.IsType(t, []model.AnswerExport{}, result.Results)
})
t.Run("ConcurrentRequests", func(t *testing.T) {
var wg sync.WaitGroup
errors := make(chan error, 10)
for i := 0; i < 10; i++ {
wg.Add(1)
go func(requestID int) {
defer wg.Done()
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 5,
})
if err != nil {
errors <- err
return
}
if resp != nil {
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
var result result2.ReqExportResponse
if decodeErr := json.NewDecoder(resp.Body).Decode(&result); decodeErr != nil {
errors <- decodeErr
}
}
}
}(i)
}
wg.Wait()
close(errors)
for err := range errors {
assert.NoError(t, err)
}
})
t.Run("LargeLimit", func(t *testing.T) {
start := time.Now()
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 1000,
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(1000))
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
fmt.Println(result)
assert.IsType(t, uint64(0), result.TotalCount)
assert.IsType(t, []model.AnswerExport{}, result.Results)
})
}
// отсмотрено
func TestGetResults_ErrorHandling(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для тестирования обработки ошибок")
t.Run("MalformedJSON", func(t *testing.T) {
req, err := http.NewRequest("POST", baseURL+fmt.Sprintf("/results/getResults/%v", testData.Quiz.Id), bytes.NewReader([]byte("{invalid_json}")))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidQuizID", func(t *testing.T) {
resp, err := getResultsRequest(validToken, "invalid-quiz-id", map[string]interface{}{
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NonExistentQuizID", func(t *testing.T) {
resp, err := getResultsRequest(validToken, "999999", map[string]interface{}{
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, uint64(0), result.TotalCount)
assert.Empty(t, result.Results)
})
t.Run("InvalidPagination", func(t *testing.T) {
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": "invalid-page",
"Limit": "invalid-limit",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidDateFormat", func(t *testing.T) {
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"From": "invalid-date",
"To": "invalid-date",
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("EmptyBody", func(t *testing.T) {
req, err := http.NewRequest("POST", baseURL+fmt.Sprintf("/results/getResults/%v", testData.Quiz.Id), bytes.NewReader([]byte("{}")))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result result2.ReqExportResponse
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.IsType(t, uint64(0), result.TotalCount)
assert.IsType(t, []model.AnswerExport{}, result.Results)
})
}
func deleteResultRequest(token string, resultId string) (*http.Response, error) {
req, err := http.NewRequest("DELETE", baseURL+"/results/delete/"+resultId, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestDeleteResult_Success(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для тестирования удаления результатов")
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData result2.ReqExportResponse
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
firstResult := resultsData.Results[0]
resultID := fmt.Sprintf("%v", firstResult.Id)
resp, err := deleteResultRequest(validToken, resultID)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
getResultsAfterDeleteResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer getResultsAfterDeleteResp.Body.Close()
var resultsAfterDelete result2.ReqExportResponse
err = json.NewDecoder(getResultsAfterDeleteResp.Body).Decode(&resultsAfterDelete)
assert.NoError(t, err)
assert.LessOrEqual(t, resultsAfterDelete.TotalCount, resultsData.TotalCount)
}
func TestDeleteResult_Idempotency(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для идемпотентности удаления результатов")
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 5,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData result2.ReqExportResponse
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
if len(resultsData.Results) > 0 {
firstResult := resultsData.Results[0]
resultID := fmt.Sprintf("%v", firstResult.Id)
resp1, err := deleteResultRequest(validToken, resultID)
assert.NoError(t, err)
defer resp1.Body.Close()
assert.Equal(t, http.StatusOK, resp1.StatusCode)
resp2, err := deleteResultRequest(validToken, resultID)
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp2.StatusCode)
}
}
// отсмотрено
func TestDeleteResult_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/results/123456", nil)
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := deleteResultRequest("invalid_token", "123456")
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestDeleteResult_InputValidation(t *testing.T) {
t.Run("InvalidResultID", func(t *testing.T) {
resp, err := deleteResultRequest(validToken, "not_a_number")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo need 404
t.Run("NonExistentResultID", func(t *testing.T) {
resp, err := deleteResultRequest(validToken, "99999999")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
}
// отсмотрено
func TestDeleteResult_BoundaryCases(t *testing.T) {
t.Run("VeryLongResultID", func(t *testing.T) {
longID := make([]byte, 1025)
for i := range longID {
longID[i] = '1'
}
resp, err := deleteResultRequest(validToken, string(longID))
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NegativeResultID", func(t *testing.T) {
resp, err := deleteResultRequest(validToken, "-123")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
func updateResultsStatusRequest(token string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("PATCH", baseURL+"/result/seen", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestUpdateResultsStatus_Success(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для тестирования обновления статуса результатов")
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData result2.ReqExportResponse
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
if len(resultsData.Results) >= 2 {
var answerIDs []int64
for i := 0; i < 2 && i < len(resultsData.Results); i++ {
answerIDs = append(answerIDs, int64(resultsData.Results[i].Id))
}
resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{
"Answers": answerIDs,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
getResultsAfterUpdateResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer getResultsAfterUpdateResp.Body.Close()
var resultsAfterUpdate result2.ReqExportResponse
err = json.NewDecoder(getResultsAfterUpdateResp.Body).Decode(&resultsAfterUpdate)
assert.NoError(t, err)
for _, answerID := range answerIDs {
for _, result := range resultsAfterUpdate.Results {
if result.Id == uint64(answerID) {
assert.False(t, result.New, "Результат должен быть помечен как просмотренный")
}
}
}
}
}
// отсмотрено
func TestUpdateResultsStatus_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"Answers": []int64{101, 102, 103},
})
assert.NoError(t, err)
req, err := http.NewRequest("PATCH", baseURL+"/result/seen", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := updateResultsStatusRequest("invalid_token", map[string]interface{}{
"Answers": []int64{101, 102, 103},
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestUpdateResultsStatus_InputValidation(t *testing.T) {
// todo check len
//t.Run("MissingAnswers", func(t *testing.T) {
// resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{})
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
//})
// todo check len
//t.Run("EmptyAnswers", func(t *testing.T) {
// resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{
// "Answers": []int64{},
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
//})
t.Run("InvalidAnswersType", func(t *testing.T) {
resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{
"Answers": "not_an_array",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NonExistentAnswers", func(t *testing.T) {
resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{
"Answers": []int64{999999, 999998, 999997},
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode)
})
}
// отсмотрено
func TestUpdateResultsStatus_Idempotency(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для идемпотентности обновления статуса")
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 5,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData result2.ReqExportResponse
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
results := resultsData.Results
var answerIDs []uint64
for i := 0; i < 2; i++ {
result := results[i]
answerIDs = append(answerIDs, result.Id)
}
resp1, err := updateResultsStatusRequest(validToken, map[string]interface{}{
"Answers": answerIDs,
})
assert.NoError(t, err)
defer resp1.Body.Close()
assert.Equal(t, http.StatusOK, resp1.StatusCode)
resp2, err := updateResultsStatusRequest(validToken, map[string]interface{}{
"Answers": answerIDs,
})
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusOK, resp2.StatusCode)
}
// отсмотрено
func TestUpdateResultsStatus_Performance(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для теста производительности обновления статуса")
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 20,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData result2.ReqExportResponse
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
var answerIDs []uint64
for i := 0; i < len(resultsData.Results) && i < 10; i++ {
result := resultsData.Results[i]
answerIDs = append(answerIDs, result.Id)
}
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{
"Answers": answerIDs,
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
}
// отсмотрено
func TestUpdateResultsStatus_ErrorHandling(t *testing.T) {
t.Run("MalformedJSON", func(t *testing.T) {
req, err := http.NewRequest("PATCH", baseURL+"/result/seen", bytes.NewReader([]byte("{invalid_json}")))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("ServerError", func(t *testing.T) {
resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{
"Answers": "not_an_array",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
func exportResultsRequest(token string, quizID string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/results/"+quizID+"/export", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestExportResults_Success(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для экспорта результатов")
resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"From": "2023-01-01T00:00:00Z",
"To": "2023-12-31T23:59:59Z",
"New": false,
"Page": 1,
"Limit": 100,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", resp.Header.Get("Content-Type"))
}
// отсмотрено
func TestExportResults_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/results/123/export", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := exportResultsRequest("invalid_token", "123", map[string]interface{}{
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestExportResults_InputValidation(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для валидации экспорта")
t.Run("InvalidDateFormat", func(t *testing.T) {
resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"From": "not-a-date",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidQuizID", func(t *testing.T) {
resp, err := exportResultsRequest(validToken, "invalid-quiz-id", map[string]interface{}{
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo 404
t.Run("NonExistentQuizID", func(t *testing.T) {
resp, err := exportResultsRequest(validToken, "99999", map[string]interface{}{
"Page": 0,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
}
func getResultRequest(token string, resultID string) (*http.Response, error) {
req, err := http.NewRequest("GET", baseURL+"/result/"+resultID, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// отсмотрено
func TestGetResult_Success(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для получения результата")
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 5,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData result2.ReqExportResponse
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
firstResult := resultsData.Results[0]
resultID := fmt.Sprintf("%v", firstResult.Id)
resp, err := getResultRequest(validToken, resultID)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var answers []model.Answer
err = json.NewDecoder(resp.Body).Decode(&answers)
assert.NoError(t, err)
if len(answers) > 0 {
answer := answers[0]
assert.NotEmpty(t, answer.Id)
assert.NotEmpty(t, answer.Content)
assert.NotEmpty(t, answer.QuestionId)
assert.NotEmpty(t, answer.QuizId)
}
}
// отсмотрено
func TestGetResult_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/result/abc123xyz", nil)
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := getResultRequest("invalid_token", "abc123xyz")
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestGetResult_InputValidation(t *testing.T) {
t.Run("EmptyResultID", func(t *testing.T) {
resp, err := getResultRequest(validToken, "")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
})
t.Run("NonExistentResultID", func(t *testing.T) {
resp, err := getResultRequest(validToken, "nonexistent123")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidResultID", func(t *testing.T) {
resp, err := getResultRequest(validToken, "invalid-result-id!")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
// отсмотрено
func TestGetResult_Performance(t *testing.T) {
testData := createTestDataForResults(t, validToken, "Квиз для получения результата")
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", testData.Quiz.Id), map[string]interface{}{
"Page": 0,
"Limit": 1,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData result2.ReqExportResponse
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
results := resultsData.Results
firstResult := results[0]
resultID := fmt.Sprintf("%v", firstResult.Id)
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getResultRequest(validToken, resultID)
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
}
// отсмотрено
func TestGetResult_BoundaryCases(t *testing.T) {
t.Run("VeryLongResultID", func(t *testing.T) {
longID := make([]byte, 1025)
for i := range longID {
longID[i] = 'a'
}
resp, err := getResultRequest(validToken, string(longID))
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NegativeResultID", func(t *testing.T) {
resp, err := getResultRequest(validToken, "-123")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo
//t.Run("SpecialCharactersInResultID", func(t *testing.T) {
// resp, err := getResultRequest(validToken, "result@#$%^&*()")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
//})
}
func getDeviceStatsRequest(token string, quizID string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/statistic/"+quizID+"/devices", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// todo нужно заранее в кликхаусе выбрать на чем честить будем
func TestGetDeviceStats_Success(t *testing.T) {
resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", 111), map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotNil(t, result["Device"])
assert.NotNil(t, result["OS"])
assert.NotNil(t, result["Browser"])
deviceStats := result["Device"].(map[string]interface{})
osStats := result["OS"].(map[string]interface{})
browserStats := result["Browser"].(map[string]interface{})
for _, value := range deviceStats {
assert.IsType(t, float64(0), value)
}
for _, value := range osStats {
assert.IsType(t, float64(0), value)
}
for _, value := range browserStats {
assert.IsType(t, float64(0), value)
}
}
// todo нужно заранее в кликхаусе выбрать на чем честить будем
func TestGetDeviceStats_WithoutDateRange(t *testing.T) {
resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result statistics.DeviceStatResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotNil(t, result.Device)
assert.NotNil(t, result.OS)
assert.NotNil(t, result.Browser)
}
// отсмотрено
func TestGetDeviceStats_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/statistic/12345/devices", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := getDeviceStatsRequest("invalid_token", "12345", map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// отсмотрено
func TestGetDeviceStats_InputValidation(t *testing.T) {
t.Run("InvalidDateFormat", func(t *testing.T) {
resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": "not_a_timestamp",
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidQuizID", func(t *testing.T) {
resp, err := getDeviceStatsRequest(validToken, "invalid-quiz-id", map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NonExistentQuizID", func(t *testing.T) {
resp, err := getDeviceStatsRequest(validToken, "99999", map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result statistics.DeviceStatResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Empty(t, result.Device)
assert.Empty(t, result.OS)
assert.Empty(t, result.Browser)
})
}
// отсмотрено
func TestGetDeviceStats_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("ConcurrentRequests", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
func getGeneralStatsRequest(token string, quizID string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/statistic/"+quizID+"/general", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// todo нужно заранее в кликхаусе выбрать на чем честить будем
func TestGetGeneralStats_Success(t *testing.T) {
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result statistics.GeneralStatsResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotNil(t, result.Open)
assert.NotNil(t, result.Result)
assert.NotNil(t, result.AvTime)
assert.NotNil(t, result.Conversion)
}
// todo нужно заранее в кликхаусе выбрать на чем честить будем
func TestGetGeneralStats_WithoutDateRange(t *testing.T) {
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result statistics.GeneralStatsResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotNil(t, result.Open)
assert.NotNil(t, result.Result)
assert.NotNil(t, result.AvTime)
assert.NotNil(t, result.Conversion)
}
// отсмотрено
func TestGetGeneralStats_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/statistic/12345/general", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := getGeneralStatsRequest("invalid_token", "12345", map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// todo нужно заранее в кликхаусе выбрать на чем честить будем
func TestGetGeneralStats_InputValidation(t *testing.T) {
t.Run("InvalidDateFormat", func(t *testing.T) {
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": "not_a_timestamp",
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidQuizID", func(t *testing.T) {
resp, err := getGeneralStatsRequest(validToken, "invalid-quiz-id", map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo
//t.Run("NonExistentQuizID", func(t *testing.T) {
// resp, err := getGeneralStatsRequest(validToken, "99999", map[string]interface{}{
// "From": time.Now().Unix() - 100,
// "To": time.Now().Unix(),
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
//
// var result statistics.GeneralStatsResp
// err = json.NewDecoder(resp.Body).Decode(&result)
// assert.NoError(t, err)
// openStats := result.Open
// resultStats := result.Result
// avTimeStats := result.AvTime
// conversionStats := result.Conversion
// assert.Empty(t, openStats)
// assert.Empty(t, resultStats)
// assert.Empty(t, avTimeStats)
// assert.Empty(t, conversionStats)
//})
}
// todo нужно заранее в кликхаусе выбрать на чем честить будем
func TestGetGeneralStats_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("ConcurrentRequests", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// todo нужно заранее в кликхаусе выбрать на чем честить будем
func TestGetGeneralStats_BoundaryCases(t *testing.T) {
t.Run("VeryLargeDateRange", func(t *testing.T) {
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": 0,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("NegativeTimestamps", func(t *testing.T) {
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": -1000000,
"To": -500000,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
func getQuestionStatsRequest(token string, quizID string, body map[string]interface{}) (*http.Response, error) {
payload, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/statistic/"+quizID+"/questions", bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// todo нужно заранее в кликхаусе выбрать на чем честить будем
func TestGetQuestionStats_Success(t *testing.T) {
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result statistics.QuestionsStatsResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEmpty(t, result)
assert.NotNil(t, result.Funnel)
assert.NotNil(t, result.FunnelData)
assert.NotNil(t, result.Results)
assert.NotNil(t, result.Questions)
assert.LessOrEqual(t, len(result.Funnel), 3)
assert.LessOrEqual(t, len(result.FunnelData), 4)
}
// todo нужно заранее в кликхаусе выбрать на чем честить будем
func TestGetQuestionStats_WithoutDateRange(t *testing.T) {
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result statistics.QuestionsStatsResp
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEmpty(t, result)
}
// отсмотрено
func TestGetQuestionStats_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/statistic/12345/questions", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("InvalidToken", func(t *testing.T) {
resp, err := getQuestionStatsRequest("invalid_token", "12345", map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// todo нужно заранее в кликхаусе выбрать на чем честить будем
func TestGetQuestionStats_InputValidation(t *testing.T) {
t.Run("InvalidDateFormat", func(t *testing.T) {
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": "not_a_timestamp",
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidQuizID", func(t *testing.T) {
resp, err := getQuestionStatsRequest(validToken, "invalid-quiz-id", map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
// todo
//t.Run("NonExistentQuizID", func(t *testing.T) {
// resp, err := getQuestionStatsRequest(validToken, "99999", map[string]interface{}{
// "From": time.Now().Unix() - 100,
// "To": time.Now().Unix(),
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
//
// var result statistics.QuestionsStatsResp
// err = json.NewDecoder(resp.Body).Decode(&result)
// assert.NoError(t, err)
// assert.NotEmpty(t, result)
//
// funnel := result.Funnel
// funnelData := result.FunnelData
// results := result.Results
// questions := result.Questions
// assert.Empty(t, funnel)
// assert.Empty(t, funnelData)
// assert.Empty(t, results)
// assert.Empty(t, questions)
//})
}
// todo нужно заранее в кликхаусе выбрать на чем честить будем
func TestGetQuestionStats_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
duration := time.Since(start)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Less(t, duration.Milliseconds(), int64(500))
})
t.Run("ConcurrentRequests", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// todo нужно заранее в кликхаусе выбрать на чем честить будем
func TestGetQuestionStats_BoundaryCases(t *testing.T) {
t.Run("VeryLargeDateRange", func(t *testing.T) {
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": 0,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("NegativeTimestamps", func(t *testing.T) {
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", validQuizIDForTestingClickHouse), map[string]interface{}{
"From": -1000000,
"To": -500000,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
// todo ПРОПУСК 35 36 тест кесов
// todo ждет когда будет применятся
//func getTelegramPoolRequest(token string) (*http.Response, error) {
// req, err := http.NewRequest("GET", baseURL+"/telegram/pool", nil)
// if err != nil {
// return nil, err
// }
// req.Header.Set("Authorization", "Bearer "+token)
// req.Header.Set("Content-Type", "application/json")
// return http.DefaultClient.Do(req)
//}
//
//func TestGetTelegramPool_Success(t *testing.T) {
// resp, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer resp.Body.Close()
//
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
//
// var accounts []map[string]interface{}
// err = json.NewDecoder(resp.Body).Decode(&accounts)
// assert.NoError(t, err)
//
// for _, account := range accounts {
// assert.NotEmpty(t, account["ID"])
// assert.NotEmpty(t, account["ApiID"])
// assert.NotEmpty(t, account["ApiHash"])
// assert.NotEmpty(t, account["PhoneNumber"])
// assert.NotEmpty(t, account["Status"])
// assert.NotEmpty(t, account["CreatedAt"])
//
// assert.IsType(t, float64(0), account["ID"])
// assert.IsType(t, float64(0), account["ApiID"])
// assert.IsType(t, "", account["ApiHash"])
// assert.IsType(t, "", account["PhoneNumber"])
// assert.IsType(t, "", account["Status"])
// assert.IsType(t, "", account["CreatedAt"])
// assert.IsType(t, false, account["Deleted"])
//
// assert.Equal(t, false, account["Deleted"])
//
// status := account["Status"].(string)
// assert.Contains(t, []string{"active", "inactive", "ban"}, status)
//
// createdAt := account["CreatedAt"].(string)
// _, err := time.Parse(time.RFC3339, createdAt)
// assert.NoError(t, err)
// }
//}
//
//func TestGetTelegramPool_Auth(t *testing.T) {
// t.Run("NoToken", func(t *testing.T) {
// req, err := http.NewRequest("GET", baseURL+"/telegram/pool", nil)
// assert.NoError(t, err)
// req.Header.Set("Content-Type", "application/json")
//
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//
// t.Run("InvalidToken", func(t *testing.T) {
// resp, err := getTelegramPoolRequest("invalid_token")
// assert.NoError(t, err)
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//
// t.Run("ExpiredToken", func(t *testing.T) {
// resp, err := getTelegramPoolRequest(expiredToken)
// assert.NoError(t, err)
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//}
//
//func TestGetTelegramPool_Security(t *testing.T) {
// t.Run("SQLInjectionAttempt", func(t *testing.T) {
// resp, err := getTelegramPoolRequest("' OR 1=1 --")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//
// t.Run("XSSAttempt", func(t *testing.T) {
// resp, err := getTelegramPoolRequest("<script>alert('xss')</script>")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//}
//
//func TestGetTelegramPool_Performance(t *testing.T) {
// t.Run("ResponseTime", func(t *testing.T) {
// start := time.Now()
// resp, err := getTelegramPoolRequest(validToken)
// duration := time.Since(start)
//
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Less(t, duration.Milliseconds(), int64(1000)) // Менее 1 секунды
// })
//
// t.Run("ConcurrentRequests", func(t *testing.T) {
// var wg sync.WaitGroup
// for i := 0; i < 10; i++ {
// wg.Add(1)
// go func() {
// defer wg.Done()
// resp, err := getTelegramPoolRequest(validToken)
// if err == nil && resp != nil {
// resp.Body.Close()
// }
// }()
// }
// wg.Wait()
// })
//}
//
//func TestGetTelegramPool_BoundaryCases(t *testing.T) {
// t.Run("EmptyPool", func(t *testing.T) {
// resp, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
//
// var accounts []map[string]interface{}
// err = json.NewDecoder(resp.Body).Decode(&accounts)
// assert.NoError(t, err)
// assert.NotNil(t, accounts)
// })
//
// t.Run("LargePool", func(t *testing.T) {
// resp, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
//
// var accounts []map[string]interface{}
// err = json.NewDecoder(resp.Body).Decode(&accounts)
// assert.NoError(t, err)
// // Проверяем, что система может обработать большое количество аккаунтов
// assert.NotNil(t, accounts)
// })
//}
//
//func TestGetTelegramPool_ErrorHandling(t *testing.T) {
// t.Run("MalformedRequest", func(t *testing.T) {
// // Тестируем с некорректным запросом
// req, err := http.NewRequest("GET", baseURL+"/telegram/pool", bytes.NewReader([]byte("invalid_body")))
// assert.NoError(t, err)
// req.Header.Set("Authorization", "Bearer "+validToken)
// req.Header.Set("Content-Type", "application/json")
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.NotEqual(t, http.StatusOK, resp.StatusCode)
// })
//
// t.Run("ServerError", func(t *testing.T) {
// // Тестируем с некорректным токеном, который может вызвать серверную ошибку
// resp, err := getTelegramPoolRequest("invalid-token-with-special-chars@#$%")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.NotEqual(t, http.StatusOK, resp.StatusCode)
// })
//}
//
//func TestGetTelegramPool_AuditMetricsConsistency(t *testing.T) {
// t.Run("AuditLog", func(t *testing.T) {
// resp, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// // Здесь можно добавить проверку на появление записи аудита, если есть доступ к логам/БД
// })
//
// t.Run("Consistency", func(t *testing.T) {
// // Проверяем консистентность данных при повторных запросах
// // Первый запрос
// resp1, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer resp1.Body.Close()
// assert.Equal(t, http.StatusOK, resp1.StatusCode)
//
// // Второй запрос с теми же параметрами
// resp2, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer resp2.Body.Close()
// assert.Equal(t, http.StatusOK, resp2.StatusCode)
//
// // Проверяем, что результаты консистентны
// var accounts1, accounts2 []map[string]interface{}
// err = json.NewDecoder(resp1.Body).Decode(&accounts1)
// assert.NoError(t, err)
// err = json.NewDecoder(resp2.Body).Decode(&accounts2)
// assert.NoError(t, err)
// assert.Equal(t, len(accounts1), len(accounts2))
// })
//}
//
//func TestGetTelegramPool_IdempotencyAndTransactions(t *testing.T) {
// t.Run("Idempotency", func(t *testing.T) {
// // Первый запрос
// resp1, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer resp1.Body.Close()
// assert.Equal(t, http.StatusOK, resp1.StatusCode)
//
// // Повторный запрос с теми же параметрами
// resp2, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer resp2.Body.Close()
// assert.Equal(t, http.StatusOK, resp2.StatusCode)
// assert.Equal(t, resp1.StatusCode, resp2.StatusCode)
// })
//
// t.Run("TransactionRollback", func(t *testing.T) {
// // Пытаемся получить пул с некорректным токеном
// resp, err := getTelegramPoolRequest("invalid-token")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
//
// // Проверяем, что система корректно обрабатывает ошибки авторизации
// assert.NotEqual(t, http.StatusOK, resp.StatusCode)
// })
//}
//
//func createTelegramRequest(token string, body map[string]interface{}) (*http.Response, error) {
// payload, err := json.Marshal(body)
// if err != nil {
// return nil, err
// }
// req, err := http.NewRequest("POST", baseURL+"/telegram/create", bytes.NewReader(payload))
// if err != nil {
// return nil, err
// }
// req.Header.Set("Authorization", "Bearer "+token)
// req.Header.Set("Content-Type", "application/json")
// return http.DefaultClient.Do(req)
//}
//
//func TestCreateTelegram_Success(t *testing.T) {
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678901",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
//
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
//
// var result map[string]interface{}
// err = json.NewDecoder(resp.Body).Decode(&result)
// assert.NoError(t, err)
//
// // Проверяем, что возвращается signature
// assert.NotEmpty(t, result["signature"])
// assert.IsType(t, "", result["signature"])
//}
//
//func TestCreateTelegram_Auth(t *testing.T) {
// t.Run("NoToken", func(t *testing.T) {
// payload, err := json.Marshal(map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678901",
// "password": "secure_password",
// })
// assert.NoError(t, err)
//
// req, err := http.NewRequest("POST", baseURL+"/telegram/create", bytes.NewReader(payload))
// assert.NoError(t, err)
// req.Header.Set("Content-Type", "application/json")
//
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//
// t.Run("InvalidToken", func(t *testing.T) {
// resp, err := createTelegramRequest("invalid_token", map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678901",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//}
//
//func TestCreateTelegram_InputValidation(t *testing.T) {
// t.Run("MissingApiID", func(t *testing.T) {
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678901",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("MissingApiHash", func(t *testing.T) {
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "phone_number": "+12345678901",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("MissingPhoneNumber", func(t *testing.T) {
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("InvalidApiID", func(t *testing.T) {
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": "not_a_number",
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678901",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("InvalidPhoneNumber", func(t *testing.T) {
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "invalid_phone",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
//
//func TestCreateTelegram_Performance(t *testing.T) {
// t.Run("ResponseTime", func(t *testing.T) {
// start := time.Now()
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678901",
// "password": "secure_password",
// })
// duration := time.Since(start)
//
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Less(t, duration.Milliseconds(), int64(500))
// })
//}
//
//func TestCreateTelegram_ErrorHandling(t *testing.T) {
// t.Run("MalformedJSON", func(t *testing.T) {
// req, err := http.NewRequest("POST", baseURL+"/telegram/create", bytes.NewReader([]byte("{invalid_json}")))
// assert.NoError(t, err)
// req.Header.Set("Authorization", "Bearer "+validToken)
// req.Header.Set("Content-Type", "application/json")
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("ServerError", func(t *testing.T) {
// // Тестируем с некорректными данными, которые могут вызвать серверную ошибку
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": "invalid_api_id",
// "api_hash": "invalid_hash",
// "phone_number": "invalid_phone",
// "password": "invalid_password",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.NotEqual(t, http.StatusOK, resp.StatusCode)
// })
//}
//
//func TestCreateTelegram_BoundaryCases(t *testing.T) {
// t.Run("VeryLongApiHash", func(t *testing.T) {
// longHash := make([]byte, 1025)
// for i := range longHash {
// longHash[i] = 'a'
// }
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": string(longHash),
// "phone_number": "+12345678901",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("VeryLongPhoneNumber", func(t *testing.T) {
// longPhone := make([]byte, 1025)
// for i := range longPhone {
// longPhone[i] = '1'
// }
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": string(longPhone),
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("NegativeApiID", func(t *testing.T) {
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": -123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678901",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
//
//func TestCreateTelegram_SpecialCases(t *testing.T) {
// t.Run("DuplicatePhoneNumber", func(t *testing.T) {
// // Создаем первый аккаунт
// resp1, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678901",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp1.Body.Close()
// assert.Equal(t, http.StatusOK, resp1.StatusCode)
//
// // Пытаемся создать второй аккаунт с тем же номером телефона
// resp2, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678901",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp2.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp2.StatusCode)
// })
//
// t.Run("SpecialCharactersInPassword", func(t *testing.T) {
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678901",
// "password": "pass@#$%^&*()word",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// })
//}
//
//func TestCreateTelegram_AuditMetricsConsistency(t *testing.T) {
// t.Run("AuditLog", func(t *testing.T) {
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678901",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// // Здесь можно добавить проверку на появление записи аудита, если есть доступ к логам/БД
// })
//
// t.Run("Consistency", func(t *testing.T) {
// // Проверяем консистентность данных при создании аккаунтов
// resp1, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678902",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp1.Body.Close()
// assert.Equal(t, http.StatusOK, resp1.StatusCode)
//
// var result1 map[string]interface{}
// err = json.NewDecoder(resp1.Body).Decode(&result1)
// assert.NoError(t, err)
// assert.NotEmpty(t, result1["signature"])
//
// // Проверяем, что signature уникален
// resp2, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678903",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp2.Body.Close()
// assert.Equal(t, http.StatusOK, resp2.StatusCode)
//
// var result2 map[string]interface{}
// err = json.NewDecoder(resp2.Body).Decode(&result2)
// assert.NoError(t, err)
// assert.NotEmpty(t, result2["signature"])
// assert.NotEqual(t, result1["signature"], result2["signature"])
// })
//}
//
//func TestCreateTelegram_IdempotencyAndTransactions(t *testing.T) {
// t.Run("Idempotency", func(t *testing.T) {
// // Создаем аккаунт
// resp1, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678904",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp1.Body.Close()
// assert.Equal(t, http.StatusOK, resp1.StatusCode)
//
// // Повторно создаем аккаунт с теми же данными
// resp2, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": 123456,
// "api_hash": "abcdef1234567890abcdef1234567890",
// "phone_number": "+12345678904",
// "password": "secure_password",
// })
// assert.NoError(t, err)
// defer resp2.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp2.StatusCode) // Дубликат не должен создаваться
// })
//
// t.Run("TransactionRollback", func(t *testing.T) {
// // Пытаемся создать аккаунт с некорректными данными
// resp, err := createTelegramRequest(validToken, map[string]interface{}{
// "api_id": "invalid",
// "api_hash": "invalid",
// "phone_number": "invalid",
// "password": "invalid",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
//
// // Проверяем, что транзакция откатилась и аккаунт не создался
// poolResp, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer poolResp.Body.Close()
// assert.Equal(t, http.StatusOK, poolResp.StatusCode)
// })
//}
//
//func deleteTelegramRequest(token string, id string) (*http.Response, error) {
// req, err := http.NewRequest("DELETE", baseURL+"/telegram/"+id, nil)
// if err != nil {
// return nil, err
// }
// req.Header.Set("Authorization", "Bearer "+token)
// req.Header.Set("Content-Type", "application/json")
// return http.DefaultClient.Do(req)
//}
//
//func TestDeleteTelegram_Success(t *testing.T) {
// // Сначала получаем список аккаунтов, чтобы найти существующий ID
// poolResp, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer poolResp.Body.Close()
//
// var accounts []map[string]interface{}
// err = json.NewDecoder(poolResp.Body).Decode(&accounts)
// assert.NoError(t, err)
//
// // Если есть аккаунты, удаляем первый
// if len(accounts) > 0 {
// firstAccount := accounts[0]
// accountID := fmt.Sprintf("%v", firstAccount["ID"])
//
// // Удаляем аккаунт
// resp, err := deleteTelegramRequest(validToken, accountID)
// assert.NoError(t, err)
// defer resp.Body.Close()
//
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// }
//}
//
//func TestDeleteTelegram_Auth(t *testing.T) {
// t.Run("NoToken", func(t *testing.T) {
// req, err := http.NewRequest("DELETE", baseURL+"/telegram/123", nil)
// assert.NoError(t, err)
// req.Header.Set("Content-Type", "application/json")
//
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//
// t.Run("InvalidToken", func(t *testing.T) {
// resp, err := deleteTelegramRequest("invalid_token", "123")
// assert.NoError(t, err)
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//
// t.Run("ExpiredToken", func(t *testing.T) {
// resp, err := deleteTelegramRequest(expiredToken, "123")
// assert.NoError(t, err)
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//}
//
//func TestDeleteTelegram_InputValidation(t *testing.T) {
// t.Run("NonExistentID", func(t *testing.T) {
// resp, err := deleteTelegramRequest(validToken, "99999")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("InvalidID", func(t *testing.T) {
// resp, err := deleteTelegramRequest(validToken, "!@#")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("EmptyID", func(t *testing.T) {
// resp, err := deleteTelegramRequest(validToken, "")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
//
//func TestDeleteTelegram_Security(t *testing.T) {
// t.Run("SQLInjectionAttempt", func(t *testing.T) {
// resp, err := deleteTelegramRequest(validToken, "' OR 1=1 --")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSSAttempt", func(t *testing.T) {
// resp, err := deleteTelegramRequest(validToken, "<script>alert(1)</script>")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
//
//func TestDeleteTelegram_Performance(t *testing.T) {
// t.Run("ResponseTime", func(t *testing.T) {
// start := time.Now()
// resp, err := deleteTelegramRequest(validToken, "123")
// duration := time.Since(start)
//
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Less(t, duration.Milliseconds(), int64(500))
// })
//}
//
//func TestDeleteTelegram_BoundaryCases(t *testing.T) {
// t.Run("VeryLongID", func(t *testing.T) {
// longID := make([]byte, 1025)
// for i := range longID {
// longID[i] = '1'
// }
// resp, err := deleteTelegramRequest(validToken, string(longID))
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("NegativeID", func(t *testing.T) {
// resp, err := deleteTelegramRequest(validToken, "-123")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("ZeroID", func(t *testing.T) {
// resp, err := deleteTelegramRequest(validToken, "0")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
//
//func TestDeleteTelegram_ErrorHandling(t *testing.T) {
// t.Run("MalformedRequest", func(t *testing.T) {
// // Тестируем с некорректным запросом
// req, err := http.NewRequest("DELETE", baseURL+"/telegram/123", bytes.NewReader([]byte("invalid_body")))
// assert.NoError(t, err)
// req.Header.Set("Authorization", "Bearer "+validToken)
// req.Header.Set("Content-Type", "application/json")
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.NotEqual(t, http.StatusOK, resp.StatusCode)
// })
//
// t.Run("ServerError", func(t *testing.T) {
// // Тестируем с некорректным ID, который может вызвать серверную ошибку
// resp, err := deleteTelegramRequest(validToken, "invalid-id-with-special-chars@#$%")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.NotEqual(t, http.StatusOK, resp.StatusCode)
// })
//}
//
//func TestDeleteTelegram_AuditMetricsConsistency(t *testing.T) {
// t.Run("AuditLog", func(t *testing.T) {
// // Сначала получаем список аккаунтов, чтобы найти существующий ID
// poolResp, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer poolResp.Body.Close()
//
// var accounts []map[string]interface{}
// err = json.NewDecoder(poolResp.Body).Decode(&accounts)
// assert.NoError(t, err)
//
// // Если есть аккаунты, удаляем первый для тестирования аудита
// if len(accounts) > 0 {
// firstAccount := accounts[0]
// accountID := fmt.Sprintf("%v", firstAccount["ID"])
//
// resp, err := deleteTelegramRequest(validToken, accountID)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// // Здесь можно добавить проверку на появление записи аудита, если есть доступ к логам/БД
// }
// })
//
// t.Run("Consistency", func(t *testing.T) {
// // Проверяем консистентность данных при удалении
// // Сначала получаем список аккаунтов
// poolResp1, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer poolResp1.Body.Close()
//
// var accounts1 []map[string]interface{}
// err = json.NewDecoder(poolResp1.Body).Decode(&accounts1)
// assert.NoError(t, err)
//
// // Если есть аккаунты, удаляем один
// if len(accounts1) > 0 {
// firstAccount := accounts1[0]
// accountID := fmt.Sprintf("%v", firstAccount["ID"])
//
// resp, err := deleteTelegramRequest(validToken, accountID)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
//
// // Проверяем, что аккаунт действительно удален
// poolResp2, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer poolResp2.Body.Close()
//
// var accounts2 []map[string]interface{}
// err = json.NewDecoder(poolResp2.Body).Decode(&accounts2)
// assert.NoError(t, err)
//
// // Проверяем, что количество аккаунтов уменьшилось
// assert.LessOrEqual(t, len(accounts2), len(accounts1))
// }
// })
//}
//
//func TestDeleteTelegram_IdempotencyAndTransactions(t *testing.T) {
// t.Run("Idempotency", func(t *testing.T) {
// // Сначала получаем список аккаунтов, чтобы найти существующий ID
// poolResp, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer poolResp.Body.Close()
//
// var accounts []map[string]interface{}
// err = json.NewDecoder(poolResp.Body).Decode(&accounts)
// assert.NoError(t, err)
//
// // Если есть аккаунты, удаляем первый
// if len(accounts) > 0 {
// firstAccount := accounts[0]
// accountID := fmt.Sprintf("%v", firstAccount["ID"])
//
// // Первое удаление
// resp1, err := deleteTelegramRequest(validToken, accountID)
// assert.NoError(t, err)
// defer resp1.Body.Close()
// assert.Equal(t, http.StatusOK, resp1.StatusCode)
//
// // Повторное удаление того же аккаунта
// resp2, err := deleteTelegramRequest(validToken, accountID)
// assert.NoError(t, err)
// defer resp2.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp2.StatusCode) // Уже удален
// }
// })
//
// t.Run("TransactionRollback", func(t *testing.T) {
// // Пытаемся удалить несуществующий аккаунт
// resp, err := deleteTelegramRequest(validToken, "999999")
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
//
// // Проверяем, что система корректно обрабатывает ошибки
// assert.NotEqual(t, http.StatusOK, resp.StatusCode)
// })
//}
//
//func setTelegramCodeRequest(token string, body map[string]interface{}) (*http.Response, error) {
// payload, err := json.Marshal(body)
// if err != nil {
// return nil, err
// }
// req, err := http.NewRequest("POST", baseURL+"/telegram/setCode", bytes.NewReader(payload))
// if err != nil {
// return nil, err
// }
// req.Header.Set("Authorization", "Bearer "+token)
// req.Header.Set("Content-Type", "application/json")
// return http.DefaultClient.Do(req)
//}
//
//func TestSetTelegramCode_Success(t *testing.T) {
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "123456",
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
//
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
//
// var result map[string]interface{}
// err = json.NewDecoder(resp.Body).Decode(&result)
// assert.NoError(t, err)
//
// // Проверяем, что возвращается ID
// assert.NotEmpty(t, result["id"])
// assert.IsType(t, float64(0), result["id"])
//}
//
//func TestSetTelegramCode_Auth(t *testing.T) {
// t.Run("NoToken", func(t *testing.T) {
// payload, err := json.Marshal(map[string]interface{}{
// "code": "123456",
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
//
// req, err := http.NewRequest("POST", baseURL+"/telegram/setCode", bytes.NewReader(payload))
// assert.NoError(t, err)
// req.Header.Set("Content-Type", "application/json")
//
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//
// t.Run("InvalidToken", func(t *testing.T) {
// resp, err := setTelegramCodeRequest("invalid_token", map[string]interface{}{
// "code": "123456",
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//
// t.Run("ExpiredToken", func(t *testing.T) {
// resp, err := setTelegramCodeRequest(expiredToken, map[string]interface{}{
// "code": "123456",
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
// })
//}
//
//func TestSetTelegramCode_InputValidation(t *testing.T) {
// t.Run("MissingCode", func(t *testing.T) {
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("MissingSignature", func(t *testing.T) {
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "123456",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("InvalidCodeType", func(t *testing.T) {
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": 123456,
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("InvalidSignatureType", func(t *testing.T) {
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "123456",
// "signature": 12345,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
//
//func TestSetTelegramCode_Security(t *testing.T) {
// t.Run("XSSInCode", func(t *testing.T) {
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "<script>alert(1)</script>",
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("SQLInjectionInCode", func(t *testing.T) {
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "' OR 1=1 --",
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("XSSInSignature", func(t *testing.T) {
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "123456",
// "signature": "<script>alert(1)</script>",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
//
//func TestSetTelegramCode_Performance(t *testing.T) {
// t.Run("ResponseTime", func(t *testing.T) {
// start := time.Now()
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "123456",
// "signature": "abc123signature",
// })
// duration := time.Since(start)
//
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Less(t, duration.Milliseconds(), int64(500))
// })
//
// t.Run("ConcurrentRequests", func(t *testing.T) {
// var wg sync.WaitGroup
// for i := 0; i < 10; i++ {
// wg.Add(1)
// go func(index int) {
// defer wg.Done()
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": fmt.Sprintf("12345%d", index),
// "signature": fmt.Sprintf("signature%d", index),
// })
// if err == nil && resp != nil {
// resp.Body.Close()
// }
// }(i)
// }
// wg.Wait()
// })
//}
//
//func TestSetTelegramCode_BoundaryCases(t *testing.T) {
// t.Run("VeryLongCode", func(t *testing.T) {
// longCode := make([]byte, 1025)
// for i := range longCode {
// longCode[i] = '1'
// }
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": string(longCode),
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("VeryLongSignature", func(t *testing.T) {
// longSignature := make([]byte, 1025)
// for i := range longSignature {
// longSignature[i] = 'a'
// }
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "123456",
// "signature": string(longSignature),
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("EmptyCode", func(t *testing.T) {
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "",
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("EmptySignature", func(t *testing.T) {
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "123456",
// "signature": "",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//}
//
//func TestSetTelegramCode_ErrorHandling(t *testing.T) {
// t.Run("MalformedJSON", func(t *testing.T) {
// req, err := http.NewRequest("POST", baseURL+"/telegram/setCode", bytes.NewReader([]byte("{invalid_json}")))
// assert.NoError(t, err)
// req.Header.Set("Authorization", "Bearer "+validToken)
// req.Header.Set("Content-Type", "application/json")
// resp, err := http.DefaultClient.Do(req)
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// })
//
// t.Run("ServerError", func(t *testing.T) {
// // Тестируем с некорректными данными, которые могут вызвать серверную ошибку
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "invalid_code",
// "signature": "invalid_signature",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.NotEqual(t, http.StatusOK, resp.StatusCode)
// })
//}
//
//func TestSetTelegramCode_AuditMetricsConsistency(t *testing.T) {
// t.Run("AuditLog", func(t *testing.T) {
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "123456",
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// // Здесь можно добавить проверку на появление записи аудита, если есть доступ к логам/БД
// })
//
// t.Run("Consistency", func(t *testing.T) {
// // Проверяем консистентность данных при установке кода
// resp1, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "123456",
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// defer resp1.Body.Close()
// assert.Equal(t, http.StatusOK, resp1.StatusCode)
//
// var result1 map[string]interface{}
// err = json.NewDecoder(resp1.Body).Decode(&result1)
// assert.NoError(t, err)
// assert.NotEmpty(t, result1["id"])
//
// // Проверяем, что ID уникален при разных кодах
// resp2, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "654321",
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// defer resp2.Body.Close()
// assert.Equal(t, http.StatusOK, resp2.StatusCode)
//
// var result2 map[string]interface{}
// err = json.NewDecoder(resp2.Body).Decode(&result2)
// assert.NoError(t, err)
// assert.NotEmpty(t, result2["id"])
// assert.NotEqual(t, result1["id"], result2["id"])
// })
//}
//
//func TestSetTelegramCode_IdempotencyAndTransactions(t *testing.T) {
// t.Run("Idempotency", func(t *testing.T) {
// // Первая установка кода
// resp1, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "123456",
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// defer resp1.Body.Close()
// assert.Equal(t, http.StatusOK, resp1.StatusCode)
//
// // Повторная установка того же кода
// resp2, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "123456",
// "signature": "abc123signature",
// })
// assert.NoError(t, err)
// defer resp2.Body.Close()
// assert.Equal(t, http.StatusOK, resp2.StatusCode)
// assert.Equal(t, resp1.StatusCode, resp2.StatusCode)
// })
//
// t.Run("TransactionRollback", func(t *testing.T) {
// // Пытаемся установить код с некорректными данными
// resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{
// "code": "invalid",
// "signature": "invalid",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
//
// // Проверяем, что транзакция откатилась и код не установился
// poolResp, err := getTelegramPoolRequest(validToken)
// assert.NoError(t, err)
// defer poolResp.Body.Close()
// assert.Equal(t, http.StatusOK, poolResp.StatusCode)
// })
//}