core/tests/main_test.go

10324 lines
313 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"
"encoding/json"
"fmt"
"gitea.pena/SQuiz/common/model"
"github.com/stretchr/testify/assert"
"net/http"
"os"
"strings"
"sync"
"testing"
"time"
)
// todo нужно прочекать все запросы на удаление, придумать с токенами что то
var baseURL = os.Getenv("API_BASE_URL")
var validToken = os.Getenv("VALID_JWT_TOKEN")
var expiredToken = os.Getenv("EXPIRED_JWT_TOKEN")
var validTokenForDelete = os.Getenv("VALID_JWT_TOKEN_FOR_DELETE")
var validAdminToken = os.Getenv("VALID_ADMIN_JWT_TOKEN")
var existingUserIDToken = os.Getenv("EXISTING_USER_ID_JWT_TOKEN")
var userIDForDelete = os.Getenv("USER_ID_FOR_DELETE")
var existingUserID = os.Getenv("EXISTING_USER_ID")
var testUserID = os.Getenv("TEST_USER_ID")
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]interface{}{}, 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 "+os.Getenv("DELETED_ACCOUNT_TOKEN"))
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 "+os.Getenv("NO_PRIVILEGES_TOKEN"))
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 "+os.Getenv("MULTI_PRIVILEGES_TOKEN"))
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)
}
})
t.Run("UnicodeCharacters", 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, "application/json", resp.Header.Get("Content-Type"))
assert.Equal(t, "utf-8", resp.Header.Get("Content-Type"))
})
}
// todo error_simulation_token
func TestGetAccount_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+"error_simulation_token")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
req, err := http.NewRequest("GET", baseURL+"/account/get", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
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, validToken, 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("EmptyJSON", func(t *testing.T) {
resp := createAccountRequest(t, validToken, map[string]interface{}{})
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidFormat", func(t *testing.T) {
resp := createAccountRequest(t, validToken, map[string]interface{}{
"user_id": 123,
})
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("SQLInjection", func(t *testing.T) {
resp := createAccountRequest(t, validToken, map[string]interface{}{
"user_id": sqlInjectionInput,
})
defer resp.Body.Close()
assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode)
})
t.Run("XSSInjection", func(t *testing.T) {
resp := createAccountRequest(t, validToken, map[string]interface{}{
"user_id": xssInput,
})
defer resp.Body.Close()
assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode)
})
t.Run("Performance_CreationTime", func(t *testing.T) {
start := time.Now()
resp := createAccountRequest(t, validToken, map[string]interface{}{
"user_id": fmt.Sprintf("perf_test_%d", time.Now().Unix()),
})
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, validToken, 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, validToken, map[string]interface{}{
"user_id": longUserID,
})
defer resp.Body.Close()
assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode)
})
t.Run("BoundaryCases_UnicodeCharacters", func(t *testing.T) {
unicodeUserID := ест_пользователь_123" // Unicode символы
resp := createAccountRequest(t, validToken, map[string]interface{}{
"user_id": unicodeUserID,
})
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, unicodeUserID, result["user_id"])
})
// todo error_simulation_token
t.Run("ErrorHandling_InternalServerError", func(t *testing.T) {
resp := createAccountRequest(t, "error_simulation_token", map[string]interface{}{
"user_id": "test_user",
})
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err := json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("ErrorHandling_Timeout", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
body, err := json.Marshal(map[string]interface{}{
"user_id": "test_user",
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/account/create", bytes.NewBuffer(body))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
// todo need check
t.Run("SpecialCases_TransactionRollback", func(t *testing.T) {
resp := createAccountRequest(t, validToken, map[string]interface{}{
"user_id": "transaction_test_user",
})
defer resp.Body.Close()
assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusConflict)
})
}
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) {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validTokenForDelete)
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 map[string]string
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEmpty(t, result["accountId"])
}
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) {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validTokenForDelete)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
func TestDeleteAccount_NonExistent(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validTokenForDelete)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
// todo check we have it?
func TestDeleteAccount_CascadeDeletion(t *testing.T) {
t.Run("RelatedDataDeletion", func(t *testing.T) {
createResp := createAccountRequest(t, validToken, map[string]interface{}{
"user_id": fmt.Sprintf("cascade_test_%d", time.Now().Unix()),
})
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 "+validTokenForDelete)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
// Проверяем, что связанные данные удалены или помечены как удаленные
})
t.Run("StatisticsPreservation", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validTokenForDelete)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
// Проверяем, что статистические данные сохранены
})
}
func TestDeleteAccount_Security(t *testing.T) {
t.Run("CSRFProtection", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validTokenForDelete)
req.Header.Set("X-CSRF-Token", "invalid_token")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
// Проверяем CSRF защиту
assert.Equal(t, http.StatusForbidden, resp.StatusCode)
})
}
func TestDeleteAccount_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
req, _ := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
req.Header.Set("Authorization", "Bearer "+validTokenForDelete)
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 < 50; i++ {
wg.Add(1)
go func() {
defer wg.Done()
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validTokenForDelete)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
if resp != nil {
defer resp.Body.Close()
}
}()
}
wg.Wait()
}
func TestDeleteAccount_BoundaryCases(t *testing.T) {
t.Run("LargeDataDeletion", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validTokenForDelete)
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)
assert.Contains(t, result, "accountId")
})
// todo тут бы массив токенов на удаление передавать
t.Run("ConcurrentOperations", func(t *testing.T) {
var wg sync.WaitGroup
successCount := 0
var mu sync.Mutex
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validTokenForDelete)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
mu.Lock()
successCount++
mu.Unlock()
}
}()
}
wg.Wait()
assert.Greater(t, successCount, 0)
})
}
// todo error_simulation_token
func TestDeleteAccount_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+"error_simulation_token")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validTokenForDelete)
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
func TestDeleteAccount_SpecialCases(t *testing.T) {
t.Run("TransactionAtomicity", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validTokenForDelete)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
// Проверяем, что операция либо полностью выполнена, либо полностью откачена
assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError)
})
}
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.LessOrEqual(t, len(result.Items), 2)
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.StatusBadRequest, 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.StatusBadRequest, resp.StatusCode)
//})
}
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"))
})
}
// todo error_simulation_token
func TestGetAccounts_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", 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 "+"error_simulation_token")
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.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
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")
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
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)
}
})
// todo не имеем этого
t.Run("Caching", func(t *testing.T) {
body := map[string]interface{}{
"limit": 10,
"page": 1,
}
b, err := json.Marshal(body)
assert.NoError(t, err)
// Первый запрос
req1, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req1.Header.Set("Authorization", "Bearer "+validAdminToken)
req1.Header.Set("Content-Type", "application/json")
resp1, err := http.DefaultClient.Do(req1)
assert.NoError(t, err)
defer resp1.Body.Close()
// Второй запрос (должен быть быстрее из-за кэша)
req2, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b))
assert.NoError(t, err)
req2.Header.Set("Authorization", "Bearer "+validAdminToken)
req2.Header.Set("Content-Type", "application/json")
resp2, err := http.DefaultClient.Do(req2)
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusOK, resp1.StatusCode)
assert.Equal(t, http.StatusOK, resp2.StatusCode)
})
}
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"))
})
}
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()
})
}
// todo error_simulation_token
func TestGetPrivilege_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", 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 "+"error_simulation_token")
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.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
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")
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
// todo user_without_privileges
func TestGetPrivilege_SpecialCases(t *testing.T) {
t.Run("UserWithoutPrivileges", func(t *testing.T) {
userWithoutPrivileges := "user_without_privileges"
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) {
resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": userIDForDelete})
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
var result map[string]string
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, testUserID, result["userId"])
}
func TestDeleteAccountByUserID_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/account/"+userIDForDelete, 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": userIDForDelete})
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": userIDForDelete})
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.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidUserID", func(t *testing.T) {
resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": "invalid_id"})
assert.NoError(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
func TestDeleteAccountByUserID_BoundaryCases(t *testing.T) {
t.Run("LongUserID", func(t *testing.T) {
longUserID := strings.Repeat("a", 1000) // Очень длинный user_id
resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": longUserID})
assert.NoError(t, err)
defer resp.Body.Close()
assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode)
})
t.Run("UnicodeUserID", func(t *testing.T) {
unicodeUserID := ест_пользователь_123" // Unicode символы
resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": unicodeUserID})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
})
}
func TestDeleteAccountByUserID_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": userIDForDelete})
assert.NoError(t, err)
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("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
successCount := 0
var mu sync.Mutex
for i := 0; i < 10; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
// todo
testUserID := fmt.Sprintf("load_test_user_%d", index)
resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": testUserID})
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, 5) // > 50% успешных
})
}
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)
})
}
// todo error_simulation_token
func TestDeleteAccountByUserID_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := deleteAccountByUserIDRequest("error_simulation_token", map[string]string{"userId": userIDForDelete})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
body, err := json.Marshal(map[string]string{"userId": userIDForDelete})
assert.NoError(t, err)
req, err := http.NewRequest("DELETE", baseURL+"/account/"+userIDForDelete, bytes.NewBuffer(body))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
func TestDeleteAccountByUserID_SpecialCases(t *testing.T) {
t.Run("TransactionAtomicity", func(t *testing.T) {
resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": userIDForDelete})
assert.NoError(t, err)
defer resp.Body.Close()
assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError)
})
t.Run("CascadeDeletion", func(t *testing.T) {
resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": testUserID})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, 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": testUserID})
assert.NoError(t, err)
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, testUserID, result["id"])
}
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.StatusBadRequest, 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)
})
t.Run("UnicodeID", func(t *testing.T) {
unicodeID := ест_id_123" // Unicode символы
resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": unicodeID})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
})
}
func TestManualDone_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": testUserID})
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)
})
t.Run("LoadTest", func(t *testing.T) {
var wg sync.WaitGroup
successCount := 0
var mu sync.Mutex
for i := 0; i < 20; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
testID := fmt.Sprintf("load_test_id_%d", index)
resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": testID})
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, 10) // > 50% успешных
})
}
func TestManualDone_Security(t *testing.T) {
t.Run("SQLInjection", func(t *testing.T) {
resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": "1' OR '1'='1"})
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": "<script>alert('xss')</script>"})
assert.NoError(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
// todo error_simulation_token
func TestManualDone_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := manualDoneRequest("error_simulation_token", map[string]string{"id": testUserID})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
body, err := json.Marshal(map[string]string{"id": testUserID})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/account/manualdone", bytes.NewBuffer(body))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validAdminToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
func TestManualDone_SpecialCases(t *testing.T) {
t.Run("TransactionAtomicity", func(t *testing.T) {
resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": testUserID})
assert.NoError(t, err)
defer resp.Body.Close()
assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError)
})
t.Run("Idempotency", func(t *testing.T) {
resp1, err := manualDoneRequest(validAdminToken, map[string]string{"id": testUserID})
assert.NoError(t, err)
defer resp1.Body.Close()
resp2, err := manualDoneRequest(validAdminToken, map[string]string{"id": testUserID})
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, resp1.StatusCode, resp2.StatusCode)
})
}
// TODO ВСЕ ЧТО НИЖЕ ДЕЛАЛ КУРСОР НАДО ВСЕ ТЕСТЫ ПЕРЕПРОВЕРИТЬ ПОКА ЧТО ПРОВЕРЕННО ТОЛЬКО НАЛИЧИЕ ДЛЯ КАЖДОГО ТЕСТ КЕЙСА
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)
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"])
}
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)
})
}
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": "<script>alert('xss')</script>",
})
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)
})
}
// todo error_simulation_token
func TestCreateLeadTarget_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := createLeadTargetRequest("error_simulation_token", map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": "example@mail.com",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
body, 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.NewBuffer(body))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
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 || resp.StatusCode == http.StatusInternalServerError)
})
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)
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEmpty(t, result["id"])
assert.Equal(t, "mail", result["type"])
assert.Equal(t, float64(123), result["quizID"])
assert.Equal(t, "validation@mail.com", result["target"])
})
}
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) {
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": "old@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
targetID := createResult["id"]
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
"id": targetID,
"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, targetID, 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)
})
}
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": "<script>alert('xss')</script>",
})
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) {
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 456,
"target": "perf@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
targetID := createResult["id"]
start := time.Now()
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
"id": targetID,
"target": "updated_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 < 30; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 1000,
"target": fmt.Sprintf("load%d@mail.com", index),
})
if err != nil {
return
}
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
if err != nil {
return
}
targetID := createResult["id"]
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
"id": targetID,
"target": fmt.Sprintf("updated_load%d@mail.com", index),
})
if err == nil && resp != nil {
resp.Body.Close()
}
}(i)
}
wg.Wait()
})
}
func TestUpdateLeadTarget_BoundaryCases(t *testing.T) {
t.Run("MaxLengthTarget", func(t *testing.T) {
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 1000,
"target": "old@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
targetID := createResult["id"]
longEmail := strings.Repeat("a", 100) + "@domain.com"
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
"id": targetID,
"target": longEmail,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("SpecialCharacters", func(t *testing.T) {
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 999,
"target": "old@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
targetID := createResult["id"]
resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{
"id": targetID,
"target": "special!@#$%^&*()@domain.com",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
// todo error_simulation_token
func TestDeleteLeadTarget_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := deleteLeadTargetRequest("error_simulation_token", "123")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
req, err := http.NewRequest("DELETE", baseURL+"/account/leadtarget/123", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
func TestDeleteLeadTarget_SpecialCases(t *testing.T) {
t.Run("TransactionAtomicity", func(t *testing.T) {
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": "atomic@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
targetID := fmt.Sprintf("%v", createResult["id"])
resp, err := deleteLeadTargetRequest(validToken, targetID)
assert.NoError(t, err)
defer resp.Body.Close()
assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError)
})
}
func deleteLeadTargetRequest(token string, targetID string) (*http.Response, error) {
req, err := http.NewRequest("DELETE", baseURL+"/account/leadtarget/"+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) {
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": "delete@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
targetID := fmt.Sprintf("%v", createResult["id"])
resp, err := deleteLeadTargetRequest(validToken, targetID)
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, targetID, fmt.Sprintf("%v", result["id"]))
}
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, "invalid_id")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, 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_Security(t *testing.T) {
t.Run("SQLInjection", func(t *testing.T) {
resp, err := deleteLeadTargetRequest(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 := deleteLeadTargetRequest(validToken, "<script>alert('xss')</script>")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
func TestDeleteLeadTarget_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 456,
"target": "perf_delete@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
targetID := fmt.Sprintf("%v", createResult["id"])
start := time.Now()
resp, err := deleteLeadTargetRequest(validToken, targetID)
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()
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 2000 + index,
"target": fmt.Sprintf("load_delete%d@mail.com", index),
})
if err != nil {
return
}
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
if err != nil {
return
}
targetID := fmt.Sprintf("%v", createResult["id"])
resp, err := deleteLeadTargetRequest(validToken, targetID)
if err == nil && resp != nil {
resp.Body.Close()
}
}(i)
}
wg.Wait()
})
}
func TestDeleteLeadTarget_AlreadyDeleted(t *testing.T) {
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 789,
"target": "already_deleted@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
targetID := fmt.Sprintf("%v", createResult["id"])
resp1, err := deleteLeadTargetRequest(validToken, targetID)
assert.NoError(t, err)
resp1.Body.Close()
resp2, err := deleteLeadTargetRequest(validToken, targetID)
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusOK, resp2.StatusCode)
}
// todo error_simulation_token
func TestGetLeadTargetByQuizID_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := getLeadTargetByQuizIDRequest("error_simulation_token", "123")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
req, err := http.NewRequest("GET", baseURL+"/account/leadtarget/123", nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
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) {
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 123,
"target": "get@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
resp, err := getLeadTargetByQuizIDRequest(validToken, "123")
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.Equal(t, float64(123), result["quizID"])
assert.Equal(t, "mail", result["type"])
assert.Equal(t, "get@mail.com", result["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.StatusBadRequest, resp.StatusCode)
})
t.Run("NonExistentQuizID", func(t *testing.T) {
resp, err := getLeadTargetByQuizIDRequest(validToken, "999999")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
})
}
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, "<script>alert('xss')</script>")
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) {
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 999,
"target": "deleted@mail.com",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
targetID := fmt.Sprintf("%v", createResult["id"])
deleteResp, err := deleteLeadTargetRequest(validToken, targetID)
assert.NoError(t, err)
deleteResp.Body.Close()
resp, err := getLeadTargetByQuizIDRequest(validToken, "999")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
}
// todo error_simulation_token
func TestCreateQuestion_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := createQuestionRequest("error_simulation_token", map[string]interface{}{
"quiz_id": 12345,
"title": "Test Question",
"type": "variant",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
payload, err := json.Marshal(map[string]interface{}{
"quiz_id": 12345,
"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("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
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) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"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 map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEmpty(t, result["id"])
assert.Equal(t, float64(12345), result["quiz_id"])
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, float64(1), result["page"])
assert.Equal(t, "{}", result["content"])
assert.NotEmpty(t, result["created_at"])
}
func TestCreateQuestion_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"quiz_id": 12345,
"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": 12345,
"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": 12345,
"title": "Test Question",
"type": "variant",
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
func TestCreateQuestion_InputValidation(t *testing.T) {
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.StatusBadRequest, 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": 12345,
"title": "Test Question",
"type": "invalid_type",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidRequired", func(t *testing.T) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"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": 12345,
"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.StatusBadRequest, resp.StatusCode)
})
}
func TestCreateQuestion_DifferentTypes(t *testing.T) {
questionTypes := []string{"text", "variant", "images", "select", "varimg", "emoji", "date", "number", "page", "rating", "result", "file"}
for _, questionType := range questionTypes {
t.Run(questionType, func(t *testing.T) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": fmt.Sprintf("Test %s Question", questionType),
"type": questionType,
"content": "{}",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
}
func TestCreateQuestion_Security(t *testing.T) {
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)
})
t.Run("XSSAttack", func(t *testing.T) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "<script>alert('xss')</script>",
"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) {
start := time.Now()
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"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()
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345 + index,
"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) {
t.Run("MaxLengthTitle", func(t *testing.T) {
longTitle := strings.Repeat("a", 512)
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"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": 12345,
"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": 12345,
"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)
})
}
// todo error_simulation_token
func TestGetQuestionList_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := getQuestionListRequest("error_simulation_token", map[string]interface{}{
"limit": 10,
"page": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
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("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
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) {
for i := 0; i < 3; i++ {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": fmt.Sprintf("Test Question %d", i),
"type": "variant",
})
assert.NoError(t, err)
createResp.Body.Close()
}
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"limit": 10,
"page": 1,
"quiz_id": 12345,
"type": "variant",
"deleted": false,
"required": true,
})
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["count"])
assert.NotEmpty(t, result["items"])
items, ok := result["items"].([]interface{})
assert.True(t, ok)
assert.LessOrEqual(t, len(items), 10)
if len(items) > 0 {
firstItem, ok := items[0].(map[string]interface{})
assert.True(t, ok)
assert.NotEmpty(t, firstItem["id"])
assert.Equal(t, float64(12345), firstItem["quiz_id"])
assert.Equal(t, "variant", firstItem["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.StatusBadRequest, 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 check
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) {
for i := 0; i < 15; i++ {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12346,
"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": 12346,
})
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 := getQuestionListRequest(validToken, map[string]interface{}{
"limit": 5,
"page": 2,
"quiz_id": 12346,
})
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("EmptyPage", func(t *testing.T) {
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"limit": 5,
"page": 1000,
"quiz_id": 12346,
})
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.Empty(t, items)
})
}
func TestGetQuestionList_Filters(t *testing.T) {
questionTypes := []string{"text", "variant", "select"}
for _, questionType := range questionTypes {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12347,
"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": 12347,
"type": "text",
})
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 {
question, ok := item.(map[string]interface{})
assert.True(t, ok)
assert.Equal(t, "text", question["type"])
}
})
// todo check
t.Run("FilterBySearch", func(t *testing.T) {
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"quiz_id": 12347,
"search": "Filter",
})
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.NotEmpty(t, items)
})
// todo check
t.Run("FilterByRequired", func(t *testing.T) {
resp, err := getQuestionListRequest(validToken, map[string]interface{}{
"quiz_id": 12347,
"required": true,
})
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 {
question, ok := item.(map[string]interface{})
assert.True(t, ok)
assert.Equal(t, true, question["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()
})
}
// todo error_simulation_token
func TestEditQuestion_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := editQuestionRequest("error_simulation_token", map[string]interface{}{
"id": 123,
"title": "Test Question",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
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("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
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) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Original Question",
"type": "variant",
"required": true,
})
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 := editQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
"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 map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, questionID, result["updated"])
}
func TestEditQuestion_SingleField(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Single Field 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"]
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
"title": "Только заголовок обновлен",
})
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, questionID, 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.StatusBadRequest, 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)
})
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.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidTitle", func(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "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"]
longTitle := strings.Repeat("a", 513)
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
"title": longTitle,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidType", func(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "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"]
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
"type": "invalid_type",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidRequired", func(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "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"]
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
"required": "not_boolean",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
func TestEditQuestion_Security(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Security 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"]
t.Run("SQLInjection", func(t *testing.T) {
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
"title": "1' OR '1'='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)
assert.Equal(t, questionID, result["updated"])
})
t.Run("XSSAttack", func(t *testing.T) {
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
"title": "<script>alert('xss')</script>",
})
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, questionID, result["updated"])
})
}
func TestEditQuestion_Performance(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Performance 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"]
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
"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": 12345 + index,
"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
}
questionID := createResult["id"]
resp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
"title": fmt.Sprintf("Updated Load Test Question %d", index),
})
if err == nil && resp != nil {
resp.Body.Close()
}
}(i)
}
wg.Wait()
})
}
// todo error_simulation_token
func TestCopyQuestion_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := copyQuestionRequest("error_simulation_token", map[string]interface{}{
"id": 101,
"quiz_id": 202,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
payload, err := json.Marshal(map[string]interface{}{
"id": 101,
"quiz_id": 202,
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/question/copy", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
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) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Какой основной компонент воздуха?",
"type": "variant",
"description": "Выберите один правильный ответ из предложенных.",
"required": true,
"page": 1,
"content": "{}",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
originalID := createResult["id"]
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": originalID,
"quiz_id": 202,
})
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.NotEqual(t, originalID, result["id"]) // Новый ID должен отличаться
assert.Equal(t, float64(202), result["quiz_id"])
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, float64(1), result["page"])
assert.Equal(t, "{}", result["content"])
assert.Equal(t, false, result["deleted"])
assert.NotEmpty(t, result["created_at"])
assert.NotEmpty(t, result["updated_at"])
}
func TestCopyQuestion_Auth(t *testing.T) {
t.Run("NoToken", func(t *testing.T) {
payload, err := json.Marshal(map[string]interface{}{
"id": 101,
"quiz_id": 202,
})
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": 101,
"quiz_id": 202,
})
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": 101,
"quiz_id": 202,
})
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
func TestCopyQuestion_InputValidation(t *testing.T) {
t.Run("MissingID", func(t *testing.T) {
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 202,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("MissingQuizID", func(t *testing.T) {
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": 101,
})
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": 202,
})
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": 101,
"quiz_id": "invalid",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("NonExistentID", func(t *testing.T) {
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": 99999,
"quiz_id": 202,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
func TestCopyQuestion_Security(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Security 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)
originalID := createResult["id"]
t.Run("SQLInjection", func(t *testing.T) {
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": "1' OR '1'='1",
"quiz_id": 202,
})
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": originalID,
"quiz_id": 202,
})
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.NotEmpty(t, result["id"])
})
}
func TestCopyQuestion_Performance(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Performance 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)
originalID := createResult["id"]
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": originalID,
"quiz_id": 202,
})
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": 12345 + index,
"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": 202 + index,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}(i)
}
wg.Wait()
})
}
func TestCopyQuestion_OriginalPreserved(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Original Question",
"type": "variant",
"required": true,
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
originalID := createResult["id"]
originalTitle := createResult["title"]
resp, err := copyQuestionRequest(validToken, map[string]interface{}{
"id": originalID,
"quiz_id": 202,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
var copyResult map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&copyResult)
assert.NoError(t, err)
copyID := copyResult["id"]
// Проверяем, что оригинальный вопрос остался без изменений
assert.NotEqual(t, originalID, copyID)
assert.Equal(t, originalTitle, copyResult["title"])
}
// todo error_simulation_token
func TestGetQuestionHistory_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := getQuestionHistoryRequest("error_simulation_token", map[string]interface{}{
"id": 101,
"l": 10,
"p": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
payload, err := json.Marshal(map[string]interface{}{
"id": 101,
"l": 10,
"p": 1,
})
assert.NoError(t, err)
req, err := http.NewRequest("POST", baseURL+"/question/history", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
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) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Original 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 <= 3; i++ {
editResp, err := editQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
"title": fmt.Sprintf("Updated Question Version %d", i),
})
assert.NoError(t, err)
editResp.Body.Close()
}
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": questionID,
"l": 10,
"p": 1,
})
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["items"])
items, ok := result["items"].([]interface{})
assert.True(t, ok)
assert.LessOrEqual(t, len(items), 10)
assert.Greater(t, len(items), 0)
if len(items) > 0 {
firstItem, ok := items[0].(map[string]interface{})
assert.True(t, ok)
assert.Equal(t, questionID, firstItem["id"])
assert.Equal(t, float64(12345), firstItem["quiz_id"])
assert.NotEmpty(t, firstItem["version"])
assert.NotEmpty(t, firstItem["created_at"])
assert.NotEmpty(t, firstItem["updated_at"])
}
}
func TestGetQuestionHistory_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+"/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": 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 := getQuestionHistoryRequest(expiredToken, map[string]interface{}{
"id": 101,
"l": 10,
"p": 1,
})
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": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("InvalidID", func(t *testing.T) {
resp, err := getQuestionHistoryRequest(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 := getQuestionHistoryRequest(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 := getQuestionHistoryRequest(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("NegativeLimit", func(t *testing.T) {
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": 101,
"l": -5,
"p": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("ZeroPage", func(t *testing.T) {
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": 101,
"l": 10,
"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": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
func TestGetQuestionHistory_Pagination(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"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": 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 := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": questionID,
"l": 5,
"p": 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)
})
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 map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
items, ok := result["items"].([]interface{})
assert.True(t, ok)
assert.Empty(t, items)
})
}
func TestGetQuestionHistory_NewQuestion(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "New 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"]
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": questionID,
})
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.Len(t, items, 1)
if len(items) > 0 {
firstItem, ok := items[0].(map[string]interface{})
assert.True(t, ok)
assert.Equal(t, questionID, firstItem["id"])
assert.Equal(t, float64(1), firstItem["version"])
}
}
func TestGetQuestionHistory_Performance(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Performance 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 <= 5; 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("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{
"id": questionID,
"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": questionID,
"l": 5,
"p": 1,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// todo error_simulation_token
func TestDeleteQuestion_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := deleteQuestionRequest("error_simulation_token", map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
payload, err := json.Marshal(map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
req, err := http.NewRequest("DELETE", baseURL+"/question/delete", bytes.NewReader(payload))
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
})
}
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) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Question to Delete",
"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 := deleteQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
})
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, questionID, result["deactivated"])
}
func TestDeleteQuestion_Idempotency(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Question for Idempotency Test",
"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"]
resp1, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
})
assert.NoError(t, err)
defer resp1.Body.Close()
assert.Equal(t, http.StatusOK, resp1.StatusCode)
resp2, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
})
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.Equal(t, questionID, result1["deactivated"])
assert.Equal(t, questionID, result2["deactivated"])
}
func TestDeleteQuestion_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+"/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": 101,
})
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": 101,
})
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.StatusBadRequest, 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)
})
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.StatusBadRequest, resp.StatusCode)
})
}
func TestDeleteQuestion_AlreadyDeleted(t *testing.T) {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Question to Delete Twice",
"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"]
resp1, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
})
assert.NoError(t, err)
defer resp1.Body.Close()
assert.Equal(t, http.StatusOK, resp1.StatusCode)
resp2, err := deleteQuestionRequest(validToken, map[string]interface{}{
"id": questionID,
})
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.Equal(t, questionID, result1["deactivated"])
assert.Equal(t, questionID, result2["deactivated"])
}
func TestDeleteQuestion_Performance(t *testing.T) {
var questionIDs []interface{}
for i := 0; i < 10; i++ {
createResp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": fmt.Sprintf("Performance Test Question %d", i),
"type": "variant",
})
assert.NoError(t, err)
var createResult map[string]interface{}
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()
})
}
// todo error_simulation_token
func TestCreateQuiz_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := createQuizRequest("error_simulation_token", map[string]interface{}{
"name": "Test Quiz",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
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("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err) // Ожидаем ошибку таймаута
})
}
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)
}
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 map[string]interface{}
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, float64(1), result["version"])
assert.NotEmpty(t, result["created_at"])
assert.NotEmpty(t, result["updated_at"])
})
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,
"due_to": 1700000000,
"question_cnt": 10,
"time_of_passing": 3600,
"pausable": true,
"super": false,
"group_id": nil,
})
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.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["note_prevented"])
assert.Equal(t, true, result["mail_notifications"])
assert.Equal(t, false, result["unique_answers"])
assert.Equal(t, "{\"showCorrectAnswers\": true}", result["config"])
assert.Equal(t, "start", result["status"])
assert.Equal(t, float64(100), result["limit"])
assert.Equal(t, float64(1700000000), result["due_to"])
assert.Equal(t, float64(10), result["question_cnt"])
assert.Equal(t, float64(3600), result["time_of_passing"])
assert.Equal(t, true, result["pausable"])
assert.Equal(t, false, result["super"])
assert.Nil(t, result["group_id"])
})
}
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("MissingName", func(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"description": "Test description",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
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.StatusBadRequest, 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.StatusBadRequest, 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",
})
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, true, 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",
})
assert.NoError(t, err)
defer resp1.Body.Close()
assert.Equal(t, http.StatusCreated, resp1.StatusCode)
resp2, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Duplicate Quiz Name",
})
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusConflict, resp2.StatusCode)
}
func TestCreateQuiz_Security(t *testing.T) {
t.Run("SQLInjection", func(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "'; DROP TABLE quizzes; --",
"description": "'; DELETE FROM users; --",
})
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": "<script>alert('xss')</script>",
"description": "<img src=x onerror=alert('xss')>",
})
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",
})
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),
})
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,
})
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, true, result["super"])
assert.Nil(t, result["group_id"])
})
t.Run("NonSuperQuizWithGroup", func(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Group Quiz",
"super": false,
"group_id": 123,
})
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, false, result["super"])
assert.Equal(t, float64(123), result["group_id"])
})
}
// todo error_simulation_token
func TestGetQuizList_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := getQuizListRequest("error_simulation_token", map[string]interface{}{
"limit": 10,
"page": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
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("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err)
})
t.Run("DatabaseConnectionError", func(t *testing.T) {
resp, err := getQuizListRequest("db_error_token", map[string]interface{}{
"limit": 10,
"page": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
t.Run("MemoryError", func(t *testing.T) {
resp, err := getQuizListRequest("memory_error_token", map[string]interface{}{
"limit": 10,
"page": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
})
}
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 []interface{}
for _, name := range quizNames {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": name,
"status": "start",
})
assert.NoError(t, err)
var result map[string]interface{}
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": 1,
})
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["count"])
assert.NotEmpty(t, result["items"])
items, ok := result["items"].([]interface{})
assert.True(t, ok)
assert.LessOrEqual(t, len(items), 5)
if len(items) > 0 {
firstItem, ok := items[0].(map[string]interface{})
assert.True(t, ok)
assert.NotEmpty(t, firstItem["id"])
assert.NotEmpty(t, firstItem["name"])
assert.NotEmpty(t, firstItem["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.StatusBadRequest, 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.StatusBadRequest, 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.StatusBadRequest, 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)
})
// todo
t.Run("EmptyPage", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"limit": 5,
"page": 100,
})
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.Empty(t, items)
})
}
// todo check
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("Filter Test Quiz %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 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)
assert.Equal(t, "draft", quiz["status"])
}
})
t.Run("SearchFilter", func(t *testing.T) {
resp, err := getQuizListRequest(validToken, map[string]interface{}{
"search": "Filter Test",
})
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, name, "Filter Test")
}
})
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 map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
items, ok := result["items"].([]interface{})
assert.True(t, ok)
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 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), 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()
})
}
// todo error_simulation_token
func TestEditQuiz_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := editQuizRequest("error_simulation_token", map[string]interface{}{
"id": 101,
"name": "Test Quiz",
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
}
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 map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": quizID,
"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 map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, quizID, 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 map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": quizID,
"status": "stop",
})
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, quizID, 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.Contains(t, []int{http.StatusBadRequest, 422}, 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.Contains(t, []int{http.StatusBadRequest, 422}, resp.StatusCode)
})
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.Contains(t, []int{http.StatusBadRequest, 422}, 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.Contains(t, []int{http.StatusBadRequest, 422}, 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.Contains(t, []int{http.StatusBadRequest, 422}, 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.Contains(t, []int{http.StatusBadRequest, 422}, 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.Contains(t, []int{http.StatusBadRequest, 422}, resp.StatusCode)
})
}
func TestEditQuiz_Security(t *testing.T) {
t.Run("SQLInjection", func(t *testing.T) {
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": 101,
"name": "'; DROP TABLE quizzes; --",
"desc": "'; DELETE FROM users; --",
"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": "<script>alert('xss')</script>",
"desc": "<img src=x onerror=alert('xss')>",
"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 map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": quizID,
"name": "Быстрое обновление",
})
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 := editQuizRequest(validToken, map[string]interface{}{
"id": quizID,
"name": fmt.Sprintf("Load Test Quiz %d", index),
})
if err == nil && resp != nil {
resp.Body.Close()
}
}(i)
}
wg.Wait()
})
}
// todo error_simulation_token
func TestCopyQuiz_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := copyQuizRequest("error_simulation_token", map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
}
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 map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
origID := createResult["id"]
origName := createResult["name"]
resp, err := copyQuizRequest(validToken, map[string]interface{}{
"id": origID,
})
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.NotEqual(t, origID, result["id"])
assert.NotEmpty(t, result["qid"])
assert.Equal(t, origName, result["name"])
assert.Equal(t, false, result["deleted"])
assert.Equal(t, false, result["archived"])
assert.Equal(t, "draft", result["status"])
assert.Equal(t, float64(1), result["version"])
assert.Equal(t, nil, result["version_comment"])
assert.Equal(t, float64(0), result["session_count"])
assert.Equal(t, float64(0), result["passed_count"])
assert.Equal(t, float64(0), result["average_time"])
assert.NotEmpty(t, result["created_at"])
assert.NotEmpty(t, result["updated_at"])
}
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.StatusBadRequest, 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)
})
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.StatusBadRequest, 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 map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
origID := createResult["id"]
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := copyQuizRequest(validToken, map[string]interface{}{
"id": origID,
})
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": origID,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// todo error_simulation_token
func TestGetQuizHistory_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := getQuizHistoryRequest("error_simulation_token", map[string]interface{}{
"id": 101,
"l": 10,
"p": 1,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
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("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err) // Ожидаем ошибку таймаута
})
}
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 map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
for i := 1; i <= 3; i++ {
editResp, err := editQuizRequest(validToken, map[string]interface{}{
"id": quizID,
"name": fmt.Sprintf("Обновленный квиз версия %d", i),
"status": "start",
})
assert.NoError(t, err)
editResp.Body.Close()
}
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.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.LessOrEqual(t, len(result), 5)
assert.Greater(t, len(result), 0)
if len(result) > 0 {
firstItem := result[0]
assert.Equal(t, quizID, firstItem["id"])
assert.NotEmpty(t, firstItem["version"])
assert.NotEmpty(t, firstItem["created_at"])
assert.NotEmpty(t, firstItem["updated_at"])
}
}
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.StatusBadRequest, 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.StatusOK, resp.StatusCode)
var result []map[string]interface{}
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 map[string]interface{}
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": 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)
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": 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)
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.StatusOK, resp.StatusCode)
var result []map[string]interface{}
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 map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": quizID,
})
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.Len(t, result, 1) // Должна быть только одна запись для нового квиза
if len(result) > 0 {
firstItem := result[0]
assert.Equal(t, quizID, firstItem["id"])
assert.Equal(t, float64(1), firstItem["version"])
}
}
func TestGetQuizHistory_Performance(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности истории",
"status": "draft",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
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)
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": 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 := getQuizHistoryRequest(validToken, map[string]interface{}{
"id": quizID,
"l": 5,
"p": 1,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// todo error_simulation_token
func TestDeleteQuiz_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := deleteQuizRequest("error_simulation_token", map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
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("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err) // Ожидаем ошибку таймаута
})
}
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 map[string]interface{}
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 map[string]interface{}
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 map[string]interface{}
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 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.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.StatusBadRequest, 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)
})
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.StatusOK, resp.StatusCode) // Идемпотентность
})
}
func TestDeleteQuiz_Performance(t *testing.T) {
var quizIDs []interface{}
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 map[string]interface{}
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()
})
}
// todo error_simulation_token
func TestArchiveQuiz_ErrorHandling(t *testing.T) {
t.Run("InternalServerError", func(t *testing.T) {
resp, err := archiveQuizRequest("error_simulation_token", map[string]interface{}{
"id": 101,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
var errorResponse map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
})
t.Run("TimeoutHandling", func(t *testing.T) {
client := &http.Client{Timeout: 1 * time.Millisecond}
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("Authorization", "Bearer "+validToken)
req.Header.Set("Content-Type", "application/json")
_, err = client.Do(req)
assert.Error(t, err) // Ожидаем ошибку таймаута
})
}
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 map[string]interface{}
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 map[string]interface{}
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 map[string]interface{}
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 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.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.StatusBadRequest, 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)
})
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.StatusOK, resp.StatusCode) // Идемпотентность
})
}
func TestArchiveQuiz_Performance(t *testing.T) {
var quizIDs []interface{}
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 map[string]interface{}
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.StatusBadRequest, 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 map[string]interface{}
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)
})
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.StatusBadRequest, resp.StatusCode)
})
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.StatusBadRequest, 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 map[string]interface{}
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) {
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)
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
func createQuizTemplateRequest(token string) (*http.Response, error) {
req, err := http.NewRequest("POST", baseURL+"/quiz/template", 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 TestCreateQuizTemplate_Success(t *testing.T) {
resp, err := createQuizTemplateRequest(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 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) {
t.Run("NoToken", func(t *testing.T) {
req, err := http.NewRequest("POST", baseURL+"/quiz/template", 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 := createQuizTemplateRequest("invalid_token")
assert.NoError(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
func TestCreateQuizTemplate_Performance(t *testing.T) {
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := createQuizTemplateRequest(validToken)
duration := time.Since(start)
assert.NoError(t, err)
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)
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
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) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для результатов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"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 map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotNil(t, result["total_count"])
assert.NotNil(t, result["results"])
assert.IsType(t, []interface{}{}, result["results"])
}
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) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для пагинации результатов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
t.Run("FirstPage", func(t *testing.T) {
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"Limit": 5,
})
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)
results := result["results"].([]interface{})
assert.LessOrEqual(t, len(results), 5)
})
t.Run("SecondPage", func(t *testing.T) {
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 2,
"Limit": 5,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("EmptyPage", func(t *testing.T) {
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 100,
"Limit": 5,
})
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)
results := result["results"].([]interface{})
assert.Empty(t, results)
})
}
func TestGetResults_Filtering(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для фильтрации результатов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
t.Run("WithDateRange", func(t *testing.T) {
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"From": "2023-01-01",
"To": "2023-12-31",
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("NewResultsOnly", func(t *testing.T) {
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"New": true,
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
func TestGetResults_Performance(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности результатов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"Limit": 10,
})
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 := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"Limit": 5,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
func TestGetResults_ErrorHandling(t *testing.T) {
t.Run("MalformedJSON", func(t *testing.T) {
req, err := http.NewRequest("POST", baseURL+"/results/getResults/12345", 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) {
// Тестируем с некорректным quizId, который может вызвать серверную ошибку
resp, err := getResultsRequest(validToken, "invalid-id", map[string]interface{}{
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.NotEqual(t, http.StatusOK, resp.StatusCode)
})
}
func deleteResultRequest(token string, resultId string) (*http.Response, error) {
req, err := http.NewRequest("DELETE", baseURL+"/results/"+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) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для удаления результатов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData map[string]interface{}
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
results := resultsData["results"].([]interface{})
if len(results) > 0 {
firstResult := results[0].(map[string]interface{})
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)
}
}
func TestDeleteResult_Idempotency(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для идемпотентности удаления результатов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"Limit": 5,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData map[string]interface{}
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
results := resultsData["results"].([]interface{})
if len(results) > 0 {
firstResult := results[0].(map[string]interface{})
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.StatusOK, 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)
})
t.Run("NonExistentResultID", func(t *testing.T) {
resp, err := deleteResultRequest(validToken, "99999999")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
func TestDeleteResult_Performance(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности удаления результатов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData map[string]interface{}
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
results := resultsData["results"].([]interface{})
if len(results) > 0 {
firstResult := results[0].(map[string]interface{})
resultID := fmt.Sprintf("%v", firstResult["id"])
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := deleteResultRequest(validToken, resultID)
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) {
getMoreResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"Limit": 20,
})
assert.NoError(t, err)
defer getMoreResultsResp.Body.Close()
var moreResultsData map[string]interface{}
err = json.NewDecoder(getMoreResultsResp.Body).Decode(&moreResultsData)
assert.NoError(t, err)
moreResults := moreResultsData["results"].([]interface{})
var wg sync.WaitGroup
for i := 0; i < len(moreResults) && i < 5; i++ {
wg.Add(1)
go func(result interface{}) {
defer wg.Done()
resultMap := result.(map[string]interface{})
resultID := fmt.Sprintf("%v", resultMap["id"])
resp, err := deleteResultRequest(validToken, resultID)
if err == nil && resp != nil {
resp.Body.Close()
}
}(moreResults[i])
}
wg.Wait()
})
}
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 TestDeleteResult_ErrorHandling(t *testing.T) {
t.Run("MalformedRequest", func(t *testing.T) {
req, err := http.NewRequest("DELETE", baseURL+"/results/invalid-id", 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 := deleteResultRequest(validToken, "invalid-id")
assert.NoError(t, err)
defer resp.Body.Close()
assert.NotEqual(t, http.StatusOK, 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) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для обновления статуса результатов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData map[string]interface{}
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
results := resultsData["results"].([]interface{})
if len(results) >= 3 {
var answerIDs []int64
for i := 0; i < 3; i++ {
result := results[i].(map[string]interface{})
answerIDs = append(answerIDs, int64(result["id"].(float64)))
}
resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{
"Answers": answerIDs,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
}
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) {
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)
})
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.StatusOK, resp.StatusCode) // Идемпотентность
})
}
func TestUpdateResultsStatus_Idempotency(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для идемпотентности обновления статуса",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"Limit": 5,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData map[string]interface{}
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
results := resultsData["results"].([]interface{})
if len(results) >= 2 {
var answerIDs []int64
for i := 0; i < 2; i++ {
result := results[i].(map[string]interface{})
answerIDs = append(answerIDs, int64(result["id"].(float64)))
}
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) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности обновления статуса",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"Limit": 20,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData map[string]interface{}
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
results := resultsData["results"].([]interface{})
if len(results) > 0 {
var answerIDs []int64
for i := 0; i < len(results) && i < 10; i++ {
result := results[i].(map[string]interface{})
answerIDs = append(answerIDs, int64(result["id"].(float64)))
}
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))
})
t.Run("LargeBatch", func(t *testing.T) {
largeBatch := make([]int64, 100)
for i := 0; i < 100; i++ {
largeBatch[i] = int64(1000000 + i) // Используем несуществующие ID
}
resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{
"Answers": largeBatch,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
}
func TestUpdateResultsStatus_BoundaryCases(t *testing.T) {
t.Run("VeryLargeAnswersArray", func(t *testing.T) {
largeAnswers := make([]int64, 10000)
for i := 0; i < 10000; i++ {
largeAnswers[i] = int64(1000000 + i)
}
resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{
"Answers": largeAnswers,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("NegativeAnswerIDs", func(t *testing.T) {
resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{
"Answers": []int64{-1, -2, -3},
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
}
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 TestUpdateResultsStatus_SpecialCases(t *testing.T) {
t.Run("DuplicateAnswerIDs", func(t *testing.T) {
resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{
"Answers": []int64{101, 101, 102, 102, 103},
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, 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) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для экспорта результатов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), 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) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для валидации экспорта",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
t.Run("InvalidDateFormat", func(t *testing.T) {
resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), 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)
})
t.Run("NonExistentQuizID", func(t *testing.T) {
resp, err := exportResultsRequest(validToken, "99999", map[string]interface{}{
"Page": 1,
"Limit": 10,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode) // Возвращает пустой файл
})
}
// todo nned check
//func TestExportResults_Performance(t *testing.T) {
// // Создаем квиз для тестирования производительности
// createResp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": "Квиз для теста производительности экспорта",
// "status": "start",
// })
// assert.NoError(t, err)
// defer createResp.Body.Close()
// var createResult map[string]interface{}
// err = json.NewDecoder(createResp.Body).Decode(&createResult)
// assert.NoError(t, err)
// quizID := createResult["id"]
//
// t.Run("ResponseTime", func(t *testing.T) {
// start := time.Now()
// resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
// "Page": 1,
// "Limit": 100,
// })
// duration := time.Since(start)
//
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Less(t, duration.Milliseconds(), int64(2000)) // Экспорт может занимать больше времени
// })
//
// t.Run("LargeExport", func(t *testing.T) {
// resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
// "Page": 1,
// "Limit": 1000,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// })
//}
//
//func TestExportResults_BoundaryCases(t *testing.T) {
// t.Run("VeryLargeLimit", func(t *testing.T) {
// // Создаем квиз для тестирования
// createResp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": "Квиз для граничных случаев экспорта",
// "status": "start",
// })
// assert.NoError(t, err)
// defer createResp.Body.Close()
// var createResult map[string]interface{}
// err = json.NewDecoder(createResp.Body).Decode(&createResult)
// assert.NoError(t, err)
// quizID := createResult["id"]
//
// resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
// "Page": 1,
// "Limit": 100000, // Очень большой лимит
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// })
//
// t.Run("VeryOldDateRange", func(t *testing.T) {
// // Создаем квиз для тестирования
// createResp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": "Квиз для старых дат экспорта",
// "status": "start",
// })
// assert.NoError(t, err)
// defer createResp.Body.Close()
// var createResult map[string]interface{}
// err = json.NewDecoder(createResp.Body).Decode(&createResult)
// assert.NoError(t, err)
// quizID := createResult["id"]
//
// resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
// "From": "1900-01-01T00:00:00Z",
// "To": "1950-12-31T23:59:59Z",
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// })
//}
//
//func TestExportResults_ErrorHandling(t *testing.T) {
// t.Run("MalformedJSON", func(t *testing.T) {
// req, err := http.NewRequest("POST", baseURL+"/results/123/export", 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) {
// // Тестируем с некорректным quizId, который может вызвать серверную ошибку
// resp, err := exportResultsRequest(validToken, "invalid-id", map[string]interface{}{
// "Page": 1,
// "Limit": 10,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.NotEqual(t, http.StatusOK, resp.StatusCode)
// })
//}
//
//func TestExportResults_AuditMetricsConsistency(t *testing.T) {
// t.Run("AuditLog", func(t *testing.T) {
// // Создаем квиз для тестирования аудита
// createResp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": "Квиз для аудита экспорта",
// "status": "start",
// })
// assert.NoError(t, err)
// defer createResp.Body.Close()
// var createResult map[string]interface{}
// err = json.NewDecoder(createResp.Body).Decode(&createResult)
// assert.NoError(t, err)
// quizID := createResult["id"]
//
// resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
// "Page": 1,
// "Limit": 50,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
// // Здесь можно добавить проверку на появление записи аудита, если есть доступ к логам/БД
// })
//
// t.Run("Consistency", func(t *testing.T) {
// // Проверяем консистентность данных при повторных экспортах
// createResp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": "Квиз для консистентности экспорта",
// "status": "start",
// })
// assert.NoError(t, err)
// defer createResp.Body.Close()
// var createResult map[string]interface{}
// err = json.NewDecoder(createResp.Body).Decode(&createResult)
// assert.NoError(t, err)
// quizID := createResult["id"]
//
// // Первый экспорт
// resp1, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
// "Page": 1,
// "Limit": 10,
// })
// assert.NoError(t, err)
// defer resp1.Body.Close()
// assert.Equal(t, http.StatusOK, resp1.StatusCode)
//
// // Второй экспорт с теми же параметрами
// resp2, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
// "Page": 1,
// "Limit": 10,
// })
// assert.NoError(t, err)
// defer resp2.Body.Close()
// assert.Equal(t, http.StatusOK, resp2.StatusCode)
//
// // Проверяем, что размеры файлов одинаковые (консистентность)
// body1, err := io.ReadAll(resp1.Body)
// assert.NoError(t, err)
// body2, err := io.ReadAll(resp2.Body)
// assert.NoError(t, err)
// assert.Equal(t, len(body1), len(body2))
// })
//}
//
//func TestExportResults_IdempotencyAndTransactions(t *testing.T) {
// t.Run("Idempotency", func(t *testing.T) {
// // Создаем квиз для тестирования идемпотентности
// createResp, err := createQuizRequest(validToken, map[string]interface{}{
// "name": "Квиз для идемпотентности экспорта",
// "status": "start",
// })
// assert.NoError(t, err)
// defer createResp.Body.Close()
// var createResult map[string]interface{}
// err = json.NewDecoder(createResp.Body).Decode(&createResult)
// assert.NoError(t, err)
// quizID := createResult["id"]
//
// // Первый экспорт
// resp1, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
// "Page": 1,
// "Limit": 5,
// })
// assert.NoError(t, err)
// defer resp1.Body.Close()
// assert.Equal(t, http.StatusOK, resp1.StatusCode)
//
// // Повторный экспорт с теми же параметрами
// resp2, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
// "Page": 1,
// "Limit": 5,
// })
// 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 := exportResultsRequest(validToken, "999999", map[string]interface{}{
// "Page": 1,
// "Limit": 10,
// })
// assert.NoError(t, err)
// defer resp.Body.Close()
// assert.Equal(t, http.StatusOK, resp.StatusCode)
//
// // Проверяем, что файл пустой или содержит заголовки
// body, err := io.ReadAll(resp.Body)
// assert.NoError(t, err)
// // Файл может быть пустым или содержать только заголовки
// assert.NotNil(t, body)
// })
//}
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) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для получения результата",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"Limit": 5,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData map[string]interface{}
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
results := resultsData["results"].([]interface{})
if len(results) > 0 {
firstResult := results[0].(map[string]interface{})
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 []map[string]interface{}
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["question_id"])
assert.NotEmpty(t, answer["QuizId"])
assert.NotEmpty(t, answer["CreatedAt"])
assert.IsType(t, false, answer["Result"])
assert.IsType(t, false, answer["new"])
assert.IsType(t, false, answer["Deleted"])
assert.IsType(t, false, answer["Start"])
}
}
}
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.StatusBadRequest, 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.StatusOK, resp.StatusCode)
var answers []map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&answers)
assert.NoError(t, err)
assert.Empty(t, answers)
})
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) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности результата",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 1,
"Limit": 1,
})
assert.NoError(t, err)
defer getResultsResp.Body.Close()
var resultsData map[string]interface{}
err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData)
assert.NoError(t, err)
results := resultsData["results"].([]interface{})
if len(results) > 0 {
firstResult := results[0].(map[string]interface{})
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)
})
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 TestGetResult_ErrorHandling(t *testing.T) {
t.Run("MalformedRequest", func(t *testing.T) {
req, err := http.NewRequest("GET", baseURL+"/result/invalid-id", 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 := getResultRequest(validToken, "invalid-id")
assert.NoError(t, err)
defer resp.Body.Close()
assert.NotEqual(t, http.StatusOK, 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)
}
func TestGetDeviceStats_Success(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для статистики устройств",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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)
}
}
func TestGetDeviceStats_WithoutDateRange(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для статистики без диапазона дат",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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 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"])
}
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) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для валидации статистики устройств",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
t.Run("InvalidDateFormat", func(t *testing.T) {
resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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 map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
deviceStats := result["Device"].(map[string]interface{})
osStats := result["OS"].(map[string]interface{})
browserStats := result["Browser"].(map[string]interface{})
assert.Empty(t, deviceStats)
assert.Empty(t, osStats)
assert.Empty(t, browserStats)
})
}
func TestGetDeviceStats_Performance(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности статистики устройств",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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", quizID), map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
func TestGetDeviceStats_ErrorHandling(t *testing.T) {
t.Run("MalformedJSON", func(t *testing.T) {
req, err := http.NewRequest("POST", baseURL+"/statistic/12345/devices", 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 := getDeviceStatsRequest(validToken, "invalid-id", map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.NotEqual(t, http.StatusOK, resp.StatusCode)
})
}
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)
}
func TestGetGeneralStats_Success(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для общей статистики",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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["Open"])
assert.NotNil(t, result["Result"])
assert.NotNil(t, result["AvTime"])
assert.NotNil(t, result["Conversion"])
openStats := result["Open"].(map[string]interface{})
resultStats := result["Result"].(map[string]interface{})
avTimeStats := result["AvTime"].(map[string]interface{})
conversionStats := result["Conversion"].(map[string]interface{})
for _, value := range openStats {
assert.IsType(t, float64(0), value)
assert.GreaterOrEqual(t, value.(float64), float64(0))
}
for _, value := range resultStats {
assert.IsType(t, float64(0), value)
assert.GreaterOrEqual(t, value.(float64), float64(0))
}
for _, value := range avTimeStats {
assert.IsType(t, float64(0), value)
assert.GreaterOrEqual(t, value.(float64), float64(0))
}
for _, value := range conversionStats {
assert.IsType(t, float64(0), value)
assert.GreaterOrEqual(t, value.(float64), float64(0))
assert.LessOrEqual(t, value.(float64), float64(100))
}
}
func TestGetGeneralStats_WithoutDateRange(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для общей статистики без диапазона",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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 map[string]interface{}
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)
})
}
func TestGetGeneralStats_InputValidation(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для валидации общей статистики",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
t.Run("InvalidDateFormat", func(t *testing.T) {
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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)
})
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 map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
openStats := result["Open"].(map[string]interface{})
resultStats := result["Result"].(map[string]interface{})
avTimeStats := result["AvTime"].(map[string]interface{})
conversionStats := result["Conversion"].(map[string]interface{})
assert.Empty(t, openStats)
assert.Empty(t, resultStats)
assert.Empty(t, avTimeStats)
assert.Empty(t, conversionStats)
})
}
func TestGetGeneralStats_Performance(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности общей статистики",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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", quizID), map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
func TestGetGeneralStats_BoundaryCases(t *testing.T) {
t.Run("VeryLargeDateRange", func(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для граничных случаев общей статистики",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для отрицательных временных меток",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"From": -1000000,
"To": -500000,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, 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)
}
func TestGetQuestionStats_Success(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для статистики вопросов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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.NotEmpty(t, result)
if len(result) > 0 {
questionStat := result[0]
assert.NotNil(t, questionStat["Funnel"])
assert.NotNil(t, questionStat["FunnelData"])
assert.NotNil(t, questionStat["Results"])
assert.NotNil(t, questionStat["Questions"])
funnel := questionStat["Funnel"].([]interface{})
funnelData := questionStat["FunnelData"].([]interface{})
results := questionStat["Results"].(map[string]interface{})
questions := questionStat["Questions"].(map[string]interface{})
assert.LessOrEqual(t, len(funnel), 3)
assert.LessOrEqual(t, len(funnelData), 4)
for _, value := range funnel {
assert.IsType(t, float64(0), value)
assert.GreaterOrEqual(t, value.(float64), float64(0))
assert.LessOrEqual(t, value.(float64), float64(100))
}
for _, value := range funnelData {
assert.IsType(t, float64(0), value)
assert.GreaterOrEqual(t, value.(float64), float64(0))
}
for _, value := range results {
assert.IsType(t, float64(0), value)
assert.GreaterOrEqual(t, value.(float64), float64(0))
assert.LessOrEqual(t, value.(float64), float64(100))
}
for _, questionData := range questions {
questionMap := questionData.(map[string]interface{})
for _, percentage := range questionMap {
assert.IsType(t, float64(0), percentage)
assert.GreaterOrEqual(t, percentage.(float64), float64(0))
assert.LessOrEqual(t, percentage.(float64), float64(100))
}
}
}
}
func TestGetQuestionStats_WithoutDateRange(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для статистики вопросов без диапазона",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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 []map[string]interface{}
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)
})
}
func TestGetQuestionStats_InputValidation(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для валидации статистики вопросов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
t.Run("InvalidDateFormat", func(t *testing.T) {
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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)
})
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 []map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.NotEmpty(t, result)
if len(result) > 0 {
questionStat := result[0]
funnel := questionStat["Funnel"].([]interface{})
funnelData := questionStat["FunnelData"].([]interface{})
results := questionStat["Results"].(map[string]interface{})
questions := questionStat["Questions"].(map[string]interface{})
assert.Empty(t, funnel)
assert.Empty(t, funnelData)
assert.Empty(t, results)
assert.Empty(t, questions)
}
})
}
func TestGetQuestionStats_Performance(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для теста производительности статистики вопросов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
t.Run("ResponseTime", func(t *testing.T) {
start := time.Now()
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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", quizID), map[string]interface{}{
"From": time.Now().Unix() - 100,
"To": time.Now().Unix(),
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
func TestGetQuestionStats_BoundaryCases(t *testing.T) {
t.Run("VeryLargeDateRange", func(t *testing.T) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для граничных случаев статистики вопросов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), 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) {
createResp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Квиз для отрицательных временных меток вопросов",
"status": "start",
})
assert.NoError(t, err)
defer createResp.Body.Close()
var createResult map[string]interface{}
err = json.NewDecoder(createResp.Body).Decode(&createResult)
assert.NoError(t, err)
quizID := createResult["id"]
resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"From": -1000000,
"To": -500000,
})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, 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)
// })
//}