core/tests/main_test.go

8110 lines
242 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"
"io"
"net/http"
"os"
"strings"
"sync"
"testing"
"time"
)
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 1.3.6 1.3.7 1.3.8. 1.4 пока не знаю как делать надо подумать
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)
})
// todo 2.3.6 2.3.7 2.3.8 2.4
}
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 3.3.4 3.3.5
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 "+validToken)
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 "+validToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
if resp != nil {
defer resp.Body.Close()
}
}()
}
wg.Wait()
}
// todo 3.3.7 3.3.8 3.4
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)
//})
}
// todo 4.3.4 4.3.5
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()
})
}
// todo 4.3.7 4.3.8 4.4
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)
})
}
// todo 5.3.4
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 5.3.7 5.3.8 5.4
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)
})
}
// todo 6.3.4 6.3.5
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 6.3.7 6.3.8 6.3.9 6.4
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)
})
}
// todo 7.3.4 7.3.5
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 7.3.7 7.3.8 7.3.9 7.4
// 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("InvalidTargetFormat", func(t *testing.T) {
resp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 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 := createLeadTargetRequest(validToken, map[string]interface{}{})
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
func TestCreateLeadTarget_Duplication(t *testing.T) {
t.Run("ExistingTarget", func(t *testing.T) {
// Сначала создаем target
resp1, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 456,
"target": "duplicate@mail.com",
})
assert.NoError(t, err)
resp1.Body.Close()
// Пытаемся создать тот же target снова
resp2, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 456,
"target": "duplicate@mail.com",
})
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusAlreadyReported, resp2.StatusCode)
})
t.Run("DifferentCase", func(t *testing.T) {
// Сначала создаем target
resp1, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 789,
"target": "case@mail.com",
})
assert.NoError(t, err)
resp1.Body.Close()
// Пытаемся создать тот же target с другим регистром
resp2, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 789,
"target": "CASE@mail.com",
})
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusAlreadyReported, resp2.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 8.3.8 8.4 8.5
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) {
// Сначала создаем target для обновления
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"]
// Обновляем target
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)
})
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) {
// Создаем target для обновления
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()
// Создаем target для обновления
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 1000 + index,
"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) {
// Создаем target для обновления
createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{
"type": "mail",
"quizID": 789,
"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) {
// Создаем target для обновления
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 9.3.8 9.4 9.5
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) {
// Сначала создаем target для удаления
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"])
// Удаляем target
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) {
// Создаем target для удаления
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()
// Создаем target для удаления
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) {
// Сначала создаем target
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"])
// Удаляем target первый раз
resp1, err := deleteLeadTargetRequest(validToken, targetID)
assert.NoError(t, err)
resp1.Body.Close()
// Пытаемся удалить тот же target снова
resp2, err := deleteLeadTargetRequest(validToken, targetID)
assert.NoError(t, err)
defer resp2.Body.Close()
assert.Equal(t, http.StatusOK, resp2.StatusCode)
}
// todo 10.3.8 10.3.9 10.4 10.5
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) {
// Сначала создаем target для получения
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)
// Получаем target по quizID
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) {
// Создаем target для получения
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) {
// Создаем target для тестирования
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) {
// Сначала создаем target
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"])
// Удаляем target
deleteResp, err := deleteLeadTargetRequest(validToken, targetID)
assert.NoError(t, err)
deleteResp.Body.Close()
// Пытаемся получить удаленный target
resp, err := getLeadTargetByQuizIDRequest(validToken, "999")
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
}
// todo 11.3.8 11.3.9 11.4 11.5
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("InvalidContent", func(t *testing.T) {
resp, err := createQuestionRequest(validToken, map[string]interface{}{
"quiz_id": 12345,
"title": "Test Question",
"type": "variant",
"content": "invalid_json",
})
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.StatusOK, resp.StatusCode)
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, "<script>alert('xss')</script>", result["title"])
})
}
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 12.3.4 12.3.5 12.3.6 12.3.7 12.3.8 12.4 12.5
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)
})
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": 100,
"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"])
}
})
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)
})
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 13.3.4 13.3.5 13.3.6 13.4 13.5
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)
})
t.Run("InvalidContent", 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,
"content": "invalid_json",
})
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 14.3.5 14.3.6 14.3.7 14.4 14.5
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 15.3.4 15.3.5 15.3.6 15.4 15.5
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"]) // Версия должна быть 1
}
}
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 16.3.4 16.3.5 16.4 16.5
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)
// Проверяем, что оба ответа содержат правильный ID
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 17.3.5 17.3.6 17.4 17.5
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)
})
t.Run("InvalidConfig", func(t *testing.T) {
resp, err := createQuizRequest(validToken, map[string]interface{}{
"name": "Test Quiz",
"config": "invalid json",
})
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()
// Должен либо вернуть ошибку валидации, либо успешно создать квиз с экранированными данными
if resp.StatusCode == http.StatusCreated {
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, "'; DROP TABLE quizzes; --", result["name"])
} else {
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()
if resp.StatusCode == http.StatusCreated {
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
assert.Equal(t, "<script>alert('xss')</script>", result["name"])
} else {
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 18.3.4 18.3.5 18.3.6 18.3.7 18.3.8 18.4 18.5
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)
})
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)
})
}
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)
// Проверяем, что все квизы имеют статус "draft"
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)
// Проверяем, что все квизы содержат "Filter Test" в названии
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, // 24 часа назад
"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) // Значение по умолчанию для limit
}
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 19.3.4 19.3.5 19.3.6 19.4 19.5
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)
})
t.Run("InvalidConf", func(t *testing.T) {
resp, err := editQuizRequest(validToken, map[string]interface{}{
"id": 101,
"conf": "invalid json",
})
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.Contains(t, []int{http.StatusOK, http.StatusBadRequest, 422}, 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.Contains(t, []int{http.StatusOK, http.StatusBadRequest, 422}, 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 20.3.5 20.4 20.5
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 21.3.4 21.3.5 21.4 21.5
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 22.3.4 22.3.5 22.4 22.5
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)
// Проверяем, что оба ответа содержат правильный ID
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 23.3.4 23.3.5 23.4 23.5
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)
// Проверяем, что оба ответа содержат правильный ID
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()
})
}
// todo 24.3.4 24.3.5 24.4 24.5
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))
})
}
// todo 25.3.4 25.4 25.5
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"]) // 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()
})
}
// todo 26.3.3 26.4 26.5
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()
})
}
// todo 27.3.4 27.4 27.5
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()
})
}
// todo 28.3.4 28.3.5 28.3.6 28.4 28.5
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)
})
}
}
// todo 29.3.2 29.3.3 29.4 29.5
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"))
// Проверяем, что ответ содержит данные
body, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
assert.NotEmpty(t, body)
}
func TestExportResults_WithFilters(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("NewResultsOnly", func(t *testing.T) {
resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"New": true,
})
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"))
})
t.Run("WithDateRange", func(t *testing.T) {
resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"From": "2023-02-01T00:00:00Z",
"To": "2023-02-28T23:59:59Z",
})
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"))
})
t.Run("WithPagination", func(t *testing.T) {
resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{
"Page": 2,
"Limit": 50,
})
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) // Возвращает пустой файл
})
}
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)
})
}
// todo 30.3.6 30.3.7 30.4 30.5
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))
})
}
}
// todo 31.3.3 31.4 31.5
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// todo 32.3.2 32.3.3 32.4 32.5
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// todo 33.3.2 33.3.3 33.4 33.5
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": 1700000000,
"To": 1709999999,
})
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)
// Проверяем, что это массив объектов QuestionStat
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)
// Проверяем типы значений в Funnel
for _, value := range funnel {
assert.IsType(t, float64(0), value)
assert.GreaterOrEqual(t, value.(float64), float64(0))
assert.LessOrEqual(t, value.(float64), float64(100))
}
// Проверяем типы значений в FunnelData
for _, value := range funnelData {
assert.IsType(t, float64(0), value)
assert.GreaterOrEqual(t, value.(float64), float64(0))
}
// Проверяем типы значений в Results
for _, value := range results {
assert.IsType(t, float64(0), value)
assert.GreaterOrEqual(t, value.(float64), float64(0))
assert.LessOrEqual(t, value.(float64), float64(100))
}
// Проверяем типы значений в Questions
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
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": 1700000000,
"To": 1709999999,
})
if err == nil && resp != nil {
resp.Body.Close()
}
}()
}
wg.Wait()
})
}
// todo 34.4.2 34.4.3 34.4.4 34.5
// todo ПРОПУСК 35 36 тест кесов
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)
// Проверяем формат даты (ISO 8601)
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()
})
}
// todo 37.3.3 37.3.4 37.3.5 37.4 37.5
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))
})
}
// todo 38.3.2 38.3.3 38.4 38.5
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))
})
}
// todo 39.3.4 39.3.5 39.4 39.5
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()
})
}
// todo 40.4.4 40.4.5 40.4.6 40.5 40.6