package tests import ( "bytes" "encoding/json" "fmt" "gitea.pena/SQuiz/common/model" "github.com/pioz/faker" "github.com/stretchr/testify/assert" "net/http" "strings" "sync" "testing" "time" ) var PublicKey = `-----BEGIN PUBLIC KEY-----MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAE=-----END PUBLIC KEY-----` var baseURL = "http://127.0.0.1:1488" //os.Getenv("API_BASE_URL") var validToken = CreateJWT(validUserID) // validUserID var expiredToken = CreateExpiredToken(validUserID) // todo var validTokenForDelete = CreateJWT(userIDForDelete) // userIDForDelete var validAdminToken = CreateJWT(validUserID) // os.Getenv("VALID_ADMIN_JWT_TOKEN") var existingUserIDToken = CreateJWT(existingUserID) // existingUserID // Токены для операций удаления // todo var deleteAccountToken = CreateJWT(userIDForDelete) // userIDForDelete var deleteQuestionToken = CreateJWT(userIDForDelete) // userIDForDelete var deleteQuizToken = CreateJWT(userIDForDelete) // userIDForDelete var deleteResultToken = CreateJWT(userIDForDelete) // userIDForDelete var deleteLeadTargetToken = CreateJWT(userIDForDelete) // userIDForDelete var deletedAccountToken = CreateJWT(userIDForDelete) // userIDForDelete var AccountWithOutPrivilegeToken = CreateJWT(userWithoutPrivileges) // userWithoutPrivileges var notFoundAccountToken = CreateJWT("notFound-123") // todo var userIDForDelete = "user_for_delete_789" var existingUserID = "existing_user_456" var testUserID = "test_user_123" var userWithoutPrivileges = "no_privileges_user" var validUserID = "multi_privileges_user" var sqlInjectionInput = "'; DROP TABLE accounts; --" var xssInput = "" func TestGetAccount_Success(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/get", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var acc model.Account err = json.NewDecoder(resp.Body).Decode(&acc) assert.NoError(t, err) assert.NotEmpty(t, acc.ID) assert.NotEmpty(t, acc.UserID) assert.IsType(t, map[string]model.ShortPrivilege{}, acc.Privileges) } func TestGetAccount_Auth(t *testing.T) { t.Run("AccountNoToken", func(t *testing.T) { resp, err := http.Get(baseURL + "/account/get") assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("AccountInvalidToken", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/get", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer invalid_token") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("AccountExpiredToken", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/get", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+expiredToken) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetAccount_NotFound(t *testing.T) { t.Run("DeletedAccount", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/get", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+notFoundAccountToken) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusNotFound, resp.StatusCode) }) } func TestGetAccount_Privileges(t *testing.T) { t.Run("NoPrivileges", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/get", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+AccountWithOutPrivilegeToken) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var acc model.Account err = json.NewDecoder(resp.Body).Decode(&acc) assert.NoError(t, err) assert.Empty(t, acc.Privileges) }) t.Run("MultiplePrivileges", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/get", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var acc model.Account err = json.NewDecoder(resp.Body).Decode(&acc) assert.NoError(t, err) assert.Greater(t, len(acc.Privileges), 1) }) } func TestAccount_Performance(t *testing.T) { t.Run("AccountResponseTime", func(t *testing.T) { start := time.Now() req, err := http.NewRequest("GET", baseURL+"/account/get", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() duration := time.Since(start) assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("AccountLoadTest", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done() req, err := http.NewRequest("GET", baseURL+"/account/get", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) resp, err := http.DefaultClient.Do(req) if err == nil { defer resp.Body.Close() } }() } wg.Wait() }) } // todo нужны ли? //func TestGetAccount_Security(t *testing.T) { // t.Run("XSSProtection", func(t *testing.T) { // req, err := http.NewRequest("GET", baseURL+"/account/get", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validToken) // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, "nosniff", resp.Header.Get("X-Content-Type-Options")) // assert.Equal(t, "1; mode=block", resp.Header.Get("X-XSS-Protection")) // assert.Equal(t, "DENY", resp.Header.Get("X-Frame-Options")) // }) // // t.Run("CSRFProtection", func(t *testing.T) { // req, err := http.NewRequest("GET", baseURL+"/account/get", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validToken) // req.Header.Set("X-CSRF-Token", "invalid_token") // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, http.StatusForbidden, resp.StatusCode) // }) //} func TestGetAccount_BoundaryCases(t *testing.T) { t.Run("LongFieldValues", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/get", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) if userID, ok := result["user_id"].(string); ok { assert.LessOrEqual(t, len(userID), 255) } }) // todo //t.Run("UnicodeCharacters", func(t *testing.T) { // req, err := http.NewRequest("GET", baseURL+"/account/get", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validToken) // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) // assert.Equal(t, "utf-8", resp.Header.Get("Content-Type")) //}) } func TestGetAccount_SpecialCases(t *testing.T) { t.Run("AccountWithoutPrivileges", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/get", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) if privileges, ok := result["privileges"].(map[string]interface{}); ok { assert.NotNil(t, privileges) } }) t.Run("MultiplePrivileges", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/get", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) if privileges, ok := result["privileges"].(map[string]interface{}); ok { assert.NotNil(t, privileges) } }) } func TestCreateAccount(t *testing.T) { t.Run("Success", func(t *testing.T) { resp := createAccountRequest(t, CreateJWT(faker.String()), map[string]interface{}{ "user_id": testUserID, }) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) }) t.Run("MissingToken", func(t *testing.T) { req, err := http.NewRequest("POST", baseURL+"/account/create", nil) assert.NoError(t, err) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp := createAccountRequest(t, expiredToken, map[string]interface{}{ "user_id": "some-id", }) defer resp.Body.Close() assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp := createAccountRequest(t, expiredToken, map[string]interface{}{ "user_id": "some-id", }) defer resp.Body.Close() assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("Conflict_ExistingUserID", func(t *testing.T) { resp := createAccountRequest(t, validToken, map[string]interface{}{ "user_id": existingUserID, }) defer resp.Body.Close() assert.Equal(t, http.StatusConflict, resp.StatusCode) }) t.Run("SQLInjection", func(t *testing.T) { resp := createAccountRequest(t, validToken, map[string]interface{}{ "user_id": sqlInjectionInput, }) defer resp.Body.Close() assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode) }) t.Run("XSSInjection", func(t *testing.T) { resp := createAccountRequest(t, validToken, map[string]interface{}{ "user_id": xssInput, }) defer resp.Body.Close() assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode) }) t.Run("Performance_CreationTime", func(t *testing.T) { start := time.Now() resp := createAccountRequest(t, validToken, map[string]interface{}{ "user_id": fmt.Sprintf("perf_test_%d", time.Now().Unix()), }) defer resp.Body.Close() duration := time.Since(start) assert.Less(t, duration.Milliseconds(), int64(1000)) // < 1s assert.Equal(t, http.StatusOK, resp.StatusCode) }) t.Run("Performance_LoadTest", func(t *testing.T) { var wg sync.WaitGroup successCount := 0 var mu sync.Mutex for i := 0; i < 50; i++ { wg.Add(1) go func(index int) { defer wg.Done() resp := createAccountRequest(t, validToken, map[string]interface{}{ "user_id": fmt.Sprintf("load_test_%d_%d", time.Now().Unix(), index), }) defer resp.Body.Close() if resp.StatusCode == http.StatusOK { mu.Lock() successCount++ mu.Unlock() } }(i) } wg.Wait() assert.Greater(t, successCount, 40) // > 80% успешных }) t.Run("BoundaryCases_LongValues", func(t *testing.T) { longUserID := strings.Repeat("a", 1000) // Очень длинный user_id resp := createAccountRequest(t, validToken, map[string]interface{}{ "user_id": longUserID, }) defer resp.Body.Close() assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode) }) t.Run("BoundaryCases_UnicodeCharacters", func(t *testing.T) { unicodeUserID := "тест_пользователь_123" // Unicode символы resp := createAccountRequest(t, validToken, map[string]interface{}{ "user_id": unicodeUserID, }) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err := json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, unicodeUserID, result["user_id"]) }) t.Run("ErrorHandling_Timeout", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} body, err := json.Marshal(map[string]interface{}{ "user_id": "test_user", }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/account/create", bytes.NewBuffer(body)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "timeout") }) } 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 } // todo //func TestDeleteAccount_Success(t *testing.T) { // req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validTokenForDelete) // // client := &http.Client{Timeout: 5 * time.Second} // resp, err := client.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, http.StatusOK, resp.StatusCode) // assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) // // var result map[string]string // err = json.NewDecoder(resp.Body).Decode(&result) // assert.NoError(t, err) // assert.NotEmpty(t, result["accountId"]) //} func TestDeleteAccount_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) assert.NoError(t, err) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer invalid_token") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+expiredToken) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } //func TestDeleteAccount_AlreadyDeleted(t *testing.T) { // req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validTokenForDelete) // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // assert.Equal(t, http.StatusOK, resp.StatusCode) //} // //func TestDeleteAccount_NonExistent(t *testing.T) { // req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validTokenForDelete) // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // assert.Equal(t, http.StatusOK, resp.StatusCode) //} // todo check we have it? //func TestDeleteAccount_CascadeDeletion(t *testing.T) { // t.Run("RelatedDataDeletion", func(t *testing.T) { // createResp := createAccountRequest(t, validToken, map[string]interface{}{ // "user_id": fmt.Sprintf("cascade_test_%d", time.Now().Unix()), // }) // defer createResp.Body.Close() // assert.Equal(t, http.StatusOK, createResp.StatusCode) // // req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validTokenForDelete) // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, http.StatusOK, resp.StatusCode) // // // Проверяем, что связанные данные удалены или помечены как удаленные // }) // // t.Run("StatisticsPreservation", func(t *testing.T) { // req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validTokenForDelete) // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, http.StatusOK, resp.StatusCode) // // // Проверяем, что статистические данные сохранены // }) //} // //func TestDeleteAccount_Security(t *testing.T) { // t.Run("CSRFProtection", func(t *testing.T) { // req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validTokenForDelete) // req.Header.Set("X-CSRF-Token", "invalid_token") // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // // // Проверяем CSRF защиту // assert.Equal(t, http.StatusForbidden, resp.StatusCode) // }) //} // //func TestDeleteAccount_Performance(t *testing.T) { // t.Run("ResponseTime", func(t *testing.T) { // req, _ := http.NewRequest("DELETE", baseURL+"/account/delete", nil) // req.Header.Set("Authorization", "Bearer "+validTokenForDelete) // // start := time.Now() // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // // duration := time.Since(start) // assert.Less(t, duration.Milliseconds(), int64(500)) // }) //} // //func TestDeleteAccount_Load(t *testing.T) { // var wg sync.WaitGroup // for i := 0; i < 50; i++ { // wg.Add(1) // go func() { // defer wg.Done() // req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validTokenForDelete) // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // if resp != nil { // defer resp.Body.Close() // } // }() // } // wg.Wait() //} // //func TestDeleteAccount_BoundaryCases(t *testing.T) { // t.Run("LargeDataDeletion", func(t *testing.T) { // req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validTokenForDelete) // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, http.StatusOK, resp.StatusCode) // // var result map[string]interface{} // err = json.NewDecoder(resp.Body).Decode(&result) // assert.NoError(t, err) // assert.Contains(t, result, "accountId") // }) // t.Run("ConcurrentOperations", func(t *testing.T) { // var wg sync.WaitGroup // successCount := 0 // var mu sync.Mutex // // for i := 0; i < 10; i++ { // wg.Add(1) // go func() { // defer wg.Done() // req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validTokenForDelete) // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // // if resp.StatusCode == http.StatusOK { // mu.Lock() // successCount++ // mu.Unlock() // } // }() // } // wg.Wait() // // assert.Greater(t, successCount, 0) // }) //} // //func TestDeleteAccount_ErrorHandling(t *testing.T) { // t.Run("TimeoutHandling", func(t *testing.T) { // client := &http.Client{Timeout: 1 * time.Millisecond} // // req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validTokenForDelete) // // _, err = client.Do(req) // assert.Error(t, err) // assert.Contains(t, err.Error(), "timeout") // }) //} // //func TestDeleteAccount_SpecialCases(t *testing.T) { // t.Run("TransactionAtomicity", func(t *testing.T) { // req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validTokenForDelete) // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // // // Проверяем, что операция либо полностью выполнена, либо полностью откачена // assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError) // }) //} func TestGetAccounts_Success(t *testing.T) { body := map[string]interface{}{ "limit": 10, "page": 1, } b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result struct { Count uint64 `json:"count"` Items []model.Account `json:"items"` } err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEqual(t, len(result.Items), 0) for _, acc := range result.Items { assert.NotEmpty(t, acc.ID) assert.NotEmpty(t, acc.UserID) assert.NotEmpty(t, acc.CreatedAt) } } func TestGetAccounts_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { body := map[string]interface{}{ "limit": 10, "page": 1, } b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { body := map[string]interface{}{ "limit": 10, "page": 1, } b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer invalid_token") req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { body := map[string]interface{}{ "limit": 10, "page": 1, } b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+expiredToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetAccounts_Pagination(t *testing.T) { t.Run("ValidPagination", func(t *testing.T) { body := map[string]interface{}{"limit": 5, "page": 1} b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) // todo //t.Run("ZeroPagination", func(t *testing.T) { // body := map[string]interface{}{"limit": 0, "page": 0} // b, err := json.Marshal(body) // assert.NoError(t, err) // req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validAdminToken) // req.Header.Set("Content-Type", "application/json") // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) //}) // todo //t.Run("TooHighLimit", func(t *testing.T) { // body := map[string]interface{}{"limit": 1000} // b, err := json.Marshal(body) // assert.NoError(t, err) // req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validAdminToken) // req.Header.Set("Content-Type", "application/json") // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) //}) } func TestGetAccounts_Security(t *testing.T) { t.Run("SQLInjection", func(t *testing.T) { body := map[string]interface{}{ "limit": "10' OR '1'='1", "page": "1' OR '1'='1", } b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("XSSProtection", func(t *testing.T) { body := map[string]interface{}{ "limit": 10, "page": 1, } b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, "nosniff", resp.Header.Get("X-Content-Type-Options")) assert.Equal(t, "1; mode=block", resp.Header.Get("X-XSS-Protection")) assert.Equal(t, "DENY", resp.Header.Get("X-Frame-Options")) }) } func TestGetAccounts_Performance(t *testing.T) { t.Run("ResponseTimeUnder500ms", func(t *testing.T) { body := map[string]interface{}{"limit": 10, "page": 1} b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) req.Header.Set("Content-Type", "application/json") start := time.Now() resp, err := http.DefaultClient.Do(req) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("LoadTest100Requests", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done() body := map[string]interface{}{"limit": 10, "page": 1} b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err == nil { resp.Body.Close() } }() } wg.Wait() }) } func TestGetAccounts_BoundaryCases(t *testing.T) { t.Run("LargeLimit", func(t *testing.T) { body := map[string]interface{}{ "limit": 10000, // Очень большой лимит "page": 1, } b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode) }) t.Run("UnicodeCharacters", func(t *testing.T) { body := map[string]interface{}{ "limit": 10, "page": 1, "search": "тест_поиск", } b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) }) } func TestGetAccounts_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} body := map[string]interface{}{ "limit": 10, "page": 1, } b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "Timeout") }) } func TestGetAccounts_SpecialCases(t *testing.T) { t.Run("EmptyResult", func(t *testing.T) { body := map[string]interface{}{ "limit": 10, "page": 999999, // Несуществующая страница } b, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) if accounts, ok := result["accounts"].([]interface{}); ok { assert.Empty(t, accounts) } }) // todo не имеем этого //t.Run("Caching", func(t *testing.T) { // body := map[string]interface{}{ // "limit": 10, // "page": 1, // } // b, err := json.Marshal(body) // assert.NoError(t, err) // // // Первый запрос // req1, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) // assert.NoError(t, err) // req1.Header.Set("Authorization", "Bearer "+validAdminToken) // req1.Header.Set("Content-Type", "application/json") // // resp1, err := http.DefaultClient.Do(req1) // assert.NoError(t, err) // defer resp1.Body.Close() // // // Второй запрос (должен быть быстрее из-за кэша) // req2, err := http.NewRequest("GET", baseURL+"/accounts", bytes.NewReader(b)) // assert.NoError(t, err) // req2.Header.Set("Authorization", "Bearer "+validAdminToken) // req2.Header.Set("Content-Type", "application/json") // // resp2, err := http.DefaultClient.Do(req2) // assert.NoError(t, err) // defer resp2.Body.Close() // // assert.Equal(t, http.StatusOK, resp1.StatusCode) // assert.Equal(t, http.StatusOK, resp2.StatusCode) //}) } func TestGetPrivilege_Success(t *testing.T) { body := map[string]string{"userId": existingUserID} data, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, bytes.NewBuffer(data)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+existingUserIDToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var privileges []model.ShortPrivilege err = json.NewDecoder(resp.Body).Decode(&privileges) assert.NoError(t, err) for _, p := range privileges { assert.NotEmpty(t, p.ID) assert.NotEmpty(t, p.PrivilegeID) assert.NotEmpty(t, p.PrivilegeName) } } func TestGetPrivilege_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, nil) assert.NoError(t, err) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer invalid_token") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+expiredToken) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetPrivilege_InputValidation(t *testing.T) { t.Run("MissingUserID", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/privilege/", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusNotFound, resp.StatusCode) }) t.Run("InvalidUserID", func(t *testing.T) { body := map[string]int{"userId": 111} data, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/privilege/!!!", bytes.NewBuffer(data)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentUserID", func(t *testing.T) { nonExistentID := "non_existent_user_123" body := map[string]string{"userId": nonExistentID} data, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/privilege/"+nonExistentID, bytes.NewBuffer(data)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var privileges []model.ShortPrivilege err = json.NewDecoder(resp.Body).Decode(&privileges) assert.NoError(t, err) assert.Empty(t, privileges) }) } func TestGetPrivilege_BoundaryCases(t *testing.T) { t.Run("LongUserID", func(t *testing.T) { longUserID := strings.Repeat("a", 1000) body := map[string]string{"userId": longUserID} data, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/privilege/"+longUserID, bytes.NewBuffer(data)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode) }) t.Run("UnicodeUserID", func(t *testing.T) { unicodeUserID := "тест_пользователь_123" body := map[string]string{"userId": unicodeUserID} data, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/privilege/"+unicodeUserID, bytes.NewBuffer(data)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) }) } func TestGetPrivilege_Security(t *testing.T) { t.Run("SQLInjection", func(t *testing.T) { injection := "1' OR '1'='1" body := map[string]string{"userId": injection} data, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/privilege/"+injection, bytes.NewBuffer(data)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("XSS", func(t *testing.T) { body := map[string]string{"userId": xssInput} data, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/privilege/"+xssInput, bytes.NewBuffer(data)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestGetPrivilege_Performance(t *testing.T) { t.Run("ResponseTime", func(t *testing.T) { body := map[string]string{"userId": existingUserID} data, err := json.Marshal(body) assert.NoError(t, err) start := time.Now() req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, bytes.NewBuffer(data)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+existingUserIDToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, time.Since(start).Milliseconds(), int64(300)) }) t.Run("LoadTest", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done() body := map[string]string{"userId": existingUserID} data, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, bytes.NewBuffer(data)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+existingUserIDToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) if resp != nil { defer resp.Body.Close() } }() } wg.Wait() }) } func TestGetPrivilege_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} body := map[string]string{"userId": existingUserID} data, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, bytes.NewBuffer(data)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+existingUserIDToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "timeout") }) } func TestGetPrivilege_SpecialCases(t *testing.T) { t.Run("UserWithoutPrivileges", func(t *testing.T) { body := map[string]string{"userId": userWithoutPrivileges} data, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/privilege/"+userWithoutPrivileges, bytes.NewBuffer(data)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var privileges []model.ShortPrivilege err = json.NewDecoder(resp.Body).Decode(&privileges) assert.NoError(t, err) assert.Empty(t, privileges) }) t.Run("MultiplePrivileges", func(t *testing.T) { body := map[string]string{"userId": existingUserID} data, err := json.Marshal(body) assert.NoError(t, err) req, err := http.NewRequest("GET", baseURL+"/privilege/"+existingUserID, bytes.NewBuffer(data)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+existingUserIDToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var privileges []model.ShortPrivilege err = json.NewDecoder(resp.Body).Decode(&privileges) assert.NoError(t, err) for _, privilege := range privileges { assert.NotEmpty(t, privilege.ID) assert.NotEmpty(t, privilege.PrivilegeID) assert.NotEmpty(t, privilege.PrivilegeName) } }) } func deleteAccountByUserIDRequest(token string, body interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("DELETE", baseURL+"/account/"+body.(map[string]string)["userId"], bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } // todo delete //func TestDeleteAccountByUserID_Success(t *testing.T) { // resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": userIDForDelete}) // assert.NoError(t, err) // assert.Equal(t, http.StatusOK, resp.StatusCode) // assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) // // var result map[string]string // err = json.NewDecoder(resp.Body).Decode(&result) // assert.NoError(t, err) // assert.Equal(t, testUserID, result["userId"]) //} // //func TestDeleteAccountByUserID_Auth(t *testing.T) { // t.Run("NoToken", func(t *testing.T) { // req, err := http.NewRequest("DELETE", baseURL+"/account/"+userIDForDelete, nil) // assert.NoError(t, err) // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) // // t.Run("InvalidToken", func(t *testing.T) { // resp, err := deleteAccountByUserIDRequest("invalid_token", map[string]string{"userId": userIDForDelete}) // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) // // t.Run("ExpiredToken", func(t *testing.T) { // resp, err := deleteAccountByUserIDRequest(expiredToken, map[string]string{"userId": userIDForDelete}) // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) //} // //func TestDeleteAccountByUserID_Validation(t *testing.T) { // t.Run("EmptyBody", func(t *testing.T) { // req, err := http.NewRequest("DELETE", baseURL+"/account/", bytes.NewReader([]byte(`{}`))) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validAdminToken) // req.Header.Set("Content-Type", "application/json") // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("InvalidUserID", func(t *testing.T) { // resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": "invalid_id"}) // assert.NoError(t, err) // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) //} // //func TestDeleteAccountByUserID_BoundaryCases(t *testing.T) { // t.Run("LongUserID", func(t *testing.T) { // longUserID := strings.Repeat("a", 1000) // Очень длинный user_id // resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": longUserID}) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode) // }) // // t.Run("UnicodeUserID", func(t *testing.T) { // unicodeUserID := "тест_пользователь_123" // Unicode символы // resp, err := deleteAccountByUserIDRequest(validAdminToken, map[string]string{"userId": unicodeUserID}) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) // }) //} // todo check //func TestDeleteAccountByUserID_Performance(t *testing.T) { // t.Run("ResponseTime", func(t *testing.T) { // start := time.Now() // resp, err := deleteAccountByUserIDRequest(deleteAccountToken, map[string]string{"userId": userIDForDelete}) // assert.NoError(t, err) // defer resp.Body.Close() // // duration := time.Since(start) // assert.Less(t, duration.Milliseconds(), int64(1000)) // < 1s // assert.Equal(t, http.StatusOK, resp.StatusCode) // }) // // t.Run("LoadTest", func(t *testing.T) { // var wg sync.WaitGroup // successCount := 0 // var mu sync.Mutex // // for i := 0; i < 10; i++ { // wg.Add(1) // go func(index int) { // defer wg.Done() // testUserID := fmt.Sprintf("load_test_user_%d", index) // resp, err := deleteAccountByUserIDRequest(deleteAccountToken, map[string]string{"userId": testUserID}) // if err == nil && resp != nil { // defer resp.Body.Close() // if resp.StatusCode == http.StatusOK { // mu.Lock() // successCount++ // mu.Unlock() // } // } // }(i) // } // wg.Wait() // // assert.Greater(t, successCount, 5) // > 50% успешных // }) //} // func TestDeleteAccountByUserID_SQLInjection_XSS(t *testing.T) { // t.Run("SQLInjection", func(t *testing.T) { // resp, err := deleteAccountByUserIDRequest(deleteAccountToken, 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(deleteAccountToken, map[string]string{"userId": xssInput}) // assert.NoError(t, err) // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // } // // todo //func TestDeleteAccountByUserID_ErrorHandling(t *testing.T) { // t.Run("TimeoutHandling", func(t *testing.T) { // client := &http.Client{Timeout: 1 * time.Millisecond} // // body, err := json.Marshal(map[string]string{"userId": userIDForDelete}) // assert.NoError(t, err) // // req, err := http.NewRequest("DELETE", baseURL+"/account/"+userIDForDelete, bytes.NewBuffer(body)) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validAdminToken) // req.Header.Set("Content-Type", "application/json") // // _, err = client.Do(req) // assert.Error(t, err) // assert.Contains(t, err.Error(), "timeout") // }) //} // todo //func TestDeleteAccountByUserID_SpecialCases(t *testing.T) { // t.Run("TransactionAtomicity", func(t *testing.T) { // resp, err := deleteAccountByUserIDRequest(deleteAccountToken, map[string]string{"userId": userIDForDelete}) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError) // }) // // t.Run("CascadeDeletion", func(t *testing.T) { // resp, err := deleteAccountByUserIDRequest(deleteAccountToken, map[string]string{"userId": testUserID}) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, http.StatusOK, resp.StatusCode) // // // Для тестов просто проверяем успешность операции // }) //} func manualDoneRequest(token string, body map[string]string) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/account/manualdone", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestManualDone_Success(t *testing.T) { resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": testUserID}) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, testUserID, result["id"]) } func TestManualDone_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]string{"id": testUserID}) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/account/manualdone", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := manualDoneRequest("invalid_token", map[string]string{"id": testUserID}) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := manualDoneRequest(expiredToken, map[string]string{"id": testUserID}) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestManualDone_Validation(t *testing.T) { t.Run("EmptyBody", func(t *testing.T) { payload := []byte(`{}`) req, err := http.NewRequest("POST", baseURL+"/account/manualdone", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidID", func(t *testing.T) { resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": "invalid_id"}) assert.NoError(t, err) assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentID", func(t *testing.T) { resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": "nonexistent_id"}) assert.NoError(t, err) assert.Equal(t, http.StatusNotFound, resp.StatusCode) }) } func TestManualDone_BoundaryCases(t *testing.T) { t.Run("LongID", func(t *testing.T) { longID := strings.Repeat("a", 1000) // Очень длинный ID resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": longID}) assert.NoError(t, err) defer resp.Body.Close() assert.NotEqual(t, http.StatusInternalServerError, resp.StatusCode) }) t.Run("UnicodeID", func(t *testing.T) { unicodeID := "тест_id_123" // Unicode символы resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": unicodeID}) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) }) } func TestManualDone_Performance(t *testing.T) { t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": testUserID}) assert.NoError(t, err) defer resp.Body.Close() duration := time.Since(start) assert.Less(t, duration.Milliseconds(), int64(500)) // < 500ms assert.Equal(t, http.StatusOK, resp.StatusCode) }) t.Run("LoadTest", func(t *testing.T) { var wg sync.WaitGroup successCount := 0 var mu sync.Mutex for i := 0; i < 20; i++ { wg.Add(1) go func(index int) { defer wg.Done() testID := fmt.Sprintf("load_test_id_%d", index) resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": testID}) if err == nil && resp != nil { defer resp.Body.Close() if resp.StatusCode == http.StatusOK { mu.Lock() successCount++ mu.Unlock() } } }(i) } wg.Wait() assert.Greater(t, successCount, 10) // > 50% успешных }) } func TestManualDone_Security(t *testing.T) { t.Run("SQLInjection", func(t *testing.T) { resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": sqlInjectionInput}) assert.NoError(t, err) assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("XSSAttack", func(t *testing.T) { resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": xssInput}) assert.NoError(t, err) assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestManualDone_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} body, err := json.Marshal(map[string]string{"id": testUserID}) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/account/manualdone", bytes.NewBuffer(body)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validAdminToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "timeout") }) } func TestManualDone_SpecialCases(t *testing.T) { t.Run("TransactionAtomicity", func(t *testing.T) { resp, err := manualDoneRequest(validAdminToken, map[string]string{"id": testUserID}) assert.NoError(t, err) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError) }) t.Run("Idempotency", func(t *testing.T) { resp1, err := manualDoneRequest(validAdminToken, map[string]string{"id": testUserID}) assert.NoError(t, err) defer resp1.Body.Close() resp2, err := manualDoneRequest(validAdminToken, map[string]string{"id": testUserID}) assert.NoError(t, err) defer resp2.Body.Close() assert.Equal(t, resp1.StatusCode, resp2.StatusCode) }) } // TODO ВСЕ ЧТО НИЖЕ ДЕЛАЛ КУРСОР НАДО ВСЕ ТЕСТЫ ПЕРЕПРОВЕРИТЬ ПОКА ЧТО ПРОВЕРЕННО ТОЛЬКО НАЛИЧИЕ ДЛЯ КАЖДОГО ТЕСТ КЕЙСА func createLeadTargetRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/account/leadtarget", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestCreateLeadTarget_Success(t *testing.T) { resp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 123, "target": "example@mail.com", "name": "Example Channel", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result["id"]) } func TestCreateLeadTarget_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "type": "mail", "quizID": 123, "target": "example@mail.com", }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/account/leadtarget", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := createLeadTargetRequest("invalid_token", map[string]interface{}{ "type": "mail", "quizID": 123, "target": "example@mail.com", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := createLeadTargetRequest(expiredToken, map[string]interface{}{ "type": "mail", "quizID": 123, "target": "example@mail.com", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestCreateLeadTarget_InputValidation(t *testing.T) { t.Run("MissingRequiredFields", func(t *testing.T) { resp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidType", func(t *testing.T) { resp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "invalid", "quizID": 123, "target": "example@mail.com", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("EmptyBody", func(t *testing.T) { resp, err := createLeadTargetRequest(validToken, map[string]interface{}{}) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestCreateLeadTarget_Security(t *testing.T) { t.Run("SQLInjection", func(t *testing.T) { resp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": "1' OR '1'='1", "target": "example@mail.com", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("XSSAttack", func(t *testing.T) { resp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 123, "target": xssInput, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestCreateLeadTarget_Performance(t *testing.T) { t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 999, "target": "perf@mail.com", }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(1000)) }) t.Run("LoadTest", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 50; i++ { wg.Add(1) go func(index int) { defer wg.Done() resp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 1000 + index, "target": fmt.Sprintf("load%d@mail.com", index), }) if err == nil && resp != nil { resp.Body.Close() } }(i) } wg.Wait() }) } func TestCreateLeadTarget_BoundaryCases(t *testing.T) { t.Run("MaxLengthFields", func(t *testing.T) { longEmail := strings.Repeat("a", 100) + "@domain.com" longName := strings.Repeat("b", 200) resp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 123, "target": longEmail, "name": longName, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) t.Run("SpecialCharacters", func(t *testing.T) { resp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 123, "target": "special!@#$%^&*()@domain.com", "name": "Special Name!", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) } func TestCreateLeadTarget_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} body, err := json.Marshal(map[string]interface{}{ "type": "mail", "quizID": 123, "target": "example@mail.com", }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/account/leadtarget", bytes.NewBuffer(body)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "timeout") }) } func TestCreateLeadTarget_SpecialCases(t *testing.T) { t.Run("TransactionAtomicity", func(t *testing.T) { resp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 123, "target": "atomic@mail.com", }) assert.NoError(t, err) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError) }) t.Run("DataValidation", func(t *testing.T) { resp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 123, "target": "validation@mail.com", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result["id"]) assert.Equal(t, "mail", result["type"]) assert.Equal(t, float64(123), result["quizID"]) assert.Equal(t, "validation@mail.com", result["target"]) }) } func updateLeadTargetRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("PUT", baseURL+"/account/leadtarget", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestUpdateLeadTarget_Success(t *testing.T) { createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 123, "target": "old@mail.com", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) targetID := createResult["id"] resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{ "id": targetID, "target": "new_target@mail.com", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, targetID, result["id"]) assert.Equal(t, "new_target@mail.com", result["target"]) } func TestUpdateLeadTarget_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "id": 123, "target": "example@mail.com", }) assert.NoError(t, err) req, err := http.NewRequest("PUT", baseURL+"/account/leadtarget", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := updateLeadTargetRequest("invalid_token", map[string]interface{}{ "id": 123, "target": "example@mail.com", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := updateLeadTargetRequest(expiredToken, map[string]interface{}{ "id": 123, "target": "example@mail.com", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestUpdateLeadTarget_InputValidation(t *testing.T) { t.Run("MissingRequiredFields", func(t *testing.T) { resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{ "id": 123, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidID", func(t *testing.T) { resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{ "id": "invalid", "target": "example@mail.com", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) // todo такого нет у нас //t.Run("InvalidTargetFormat", func(t *testing.T) { // resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{ // "id": 123, // "target": "invalid_email", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) //}) t.Run("EmptyBody", func(t *testing.T) { resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{}) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestUpdateLeadTarget_Existence(t *testing.T) { t.Run("NonExistentID", func(t *testing.T) { resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{ "id": 999999, "target": "example@mail.com", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusNotFound, resp.StatusCode) }) } func TestUpdateLeadTarget_Security(t *testing.T) { t.Run("SQLInjection", func(t *testing.T) { resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{ "id": "1' OR '1'='1", "target": "example@mail.com", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("XSSAttack", func(t *testing.T) { resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{ "id": 123, "target": xssInput, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestUpdateLeadTarget_Performance(t *testing.T) { t.Run("ResponseTime", func(t *testing.T) { createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 456, "target": "perf@mail.com", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) targetID := createResult["id"] start := time.Now() resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{ "id": targetID, "target": "updated_perf@mail.com", }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(1000)) }) t.Run("LoadTest", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 30; i++ { wg.Add(1) go func(index int) { defer wg.Done() createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 1000, "target": fmt.Sprintf("load%d@mail.com", index), }) if err != nil { return } defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) if err != nil { return } targetID := createResult["id"] resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{ "id": targetID, "target": fmt.Sprintf("updated_load%d@mail.com", index), }) if err == nil && resp != nil { resp.Body.Close() } }(i) } wg.Wait() }) } func TestUpdateLeadTarget_BoundaryCases(t *testing.T) { t.Run("MaxLengthTarget", func(t *testing.T) { createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 1000, "target": "old@mail.com", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) targetID := createResult["id"] longEmail := strings.Repeat("a", 100) + "@domain.com" resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{ "id": targetID, "target": longEmail, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) t.Run("SpecialCharacters", func(t *testing.T) { createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 999, "target": "old@mail.com", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) targetID := createResult["id"] resp, err := updateLeadTargetRequest(validToken, map[string]interface{}{ "id": targetID, "target": "special!@#$%^&*()@domain.com", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) } func TestDeleteLeadTarget_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} req, err := http.NewRequest("DELETE", baseURL+"/account/leadtarget/123", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+deleteLeadTargetToken) _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "timeout") }) } func TestDeleteLeadTarget_SpecialCases(t *testing.T) { t.Run("TransactionAtomicity", func(t *testing.T) { createResp, err := createLeadTargetRequest(deleteLeadTargetToken, map[string]interface{}{ "type": "mail", "quizID": 123, "target": "atomic@mail.com", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) targetID := fmt.Sprintf("%v", createResult["id"]) resp, err := deleteLeadTargetRequest(validToken, targetID) assert.NoError(t, err) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError) }) } func deleteLeadTargetRequest(token string, targetID string) (*http.Response, error) { req, err := http.NewRequest("DELETE", baseURL+"/account/leadtarget/"+targetID, nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) return http.DefaultClient.Do(req) } func TestDeleteLeadTarget_Success(t *testing.T) { createResp, err := createLeadTargetRequest(deleteLeadTargetToken, map[string]interface{}{ "type": "mail", "quizID": 123, "target": "delete@mail.com", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) targetID := fmt.Sprintf("%v", createResult["id"]) resp, err := deleteLeadTargetRequest(deleteLeadTargetToken, 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(deleteLeadTargetToken, "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 "+deleteLeadTargetToken) 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(deleteLeadTargetToken, "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(deleteLeadTargetToken, "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(deleteLeadTargetToken, xssInput) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestDeleteLeadTarget_Performance(t *testing.T) { t.Run("ResponseTime", func(t *testing.T) { createResp, err := createLeadTargetRequest(deleteLeadTargetToken, 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(deleteLeadTargetToken, targetID) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(1000)) }) t.Run("LoadTest", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 30; i++ { wg.Add(1) go func(index int) { defer wg.Done() createResp, err := createLeadTargetRequest(deleteLeadTargetToken, 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(deleteLeadTargetToken, targetID) if err == nil && resp != nil { resp.Body.Close() } }(i) } wg.Wait() }) } func TestDeleteLeadTarget_AlreadyDeleted(t *testing.T) { createResp, err := createLeadTargetRequest(deleteLeadTargetToken, map[string]interface{}{ "type": "mail", "quizID": 789, "target": "already_deleted@mail.com", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) targetID := fmt.Sprintf("%v", createResult["id"]) resp1, err := deleteLeadTargetRequest(deleteLeadTargetToken, targetID) assert.NoError(t, err) resp1.Body.Close() resp2, err := deleteLeadTargetRequest(deleteLeadTargetToken, targetID) assert.NoError(t, err) defer resp2.Body.Close() assert.Equal(t, http.StatusOK, resp2.StatusCode) } func TestGetLeadTargetByQuizID_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} req, err := http.NewRequest("GET", baseURL+"/account/leadtarget/123", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "timeout") }) } func getLeadTargetByQuizIDRequest(token string, quizID string) (*http.Response, error) { req, err := http.NewRequest("GET", baseURL+"/account/leadtarget/"+quizID, nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) return http.DefaultClient.Do(req) } func TestGetLeadTargetByQuizID_Success(t *testing.T) { createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 123, "target": "get@mail.com", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) resp, err := getLeadTargetByQuizIDRequest(validToken, "123") assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result["id"]) assert.Equal(t, float64(123), result["quizID"]) assert.Equal(t, "mail", result["type"]) assert.Equal(t, "get@mail.com", result["target"]) } func TestGetLeadTargetByQuizID_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/leadtarget/123", nil) assert.NoError(t, err) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := getLeadTargetByQuizIDRequest("invalid_token", "123") assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := getLeadTargetByQuizIDRequest(expiredToken, "123") assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetLeadTargetByQuizID_InputValidation(t *testing.T) { t.Run("InvalidQuizID", func(t *testing.T) { resp, err := getLeadTargetByQuizIDRequest(validToken, "invalid_id") assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("EmptyQuizID", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/leadtarget/", nil) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentQuizID", func(t *testing.T) { resp, err := getLeadTargetByQuizIDRequest(validToken, "999999") assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusNotFound, resp.StatusCode) }) } func TestGetLeadTargetByQuizID_Security(t *testing.T) { t.Run("SQLInjection", func(t *testing.T) { resp, err := getLeadTargetByQuizIDRequest(validToken, "1' OR '1'='1") assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("XSSAttack", func(t *testing.T) { resp, err := getLeadTargetByQuizIDRequest(validToken, xssInput) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestGetLeadTargetByQuizID_Performance(t *testing.T) { t.Run("ResponseTime", func(t *testing.T) { createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 456, "target": "perf_get@mail.com", }) assert.NoError(t, err) defer createResp.Body.Close() start := time.Now() resp, err := getLeadTargetByQuizIDRequest(validToken, "456") duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(300)) }) t.Run("LoadTest", func(t *testing.T) { createResp, err := createLeadTargetRequest(validToken, map[string]interface{}{ "type": "mail", "quizID": 789, "target": "load_get@mail.com", }) assert.NoError(t, err) defer createResp.Body.Close() var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done() resp, err := getLeadTargetByQuizIDRequest(validToken, "789") if err == nil && resp != nil { resp.Body.Close() } }() } wg.Wait() }) } func TestGetLeadTargetByQuizID_DeletedTarget(t *testing.T) { createResp, err := createLeadTargetRequest(deleteLeadTargetToken, map[string]interface{}{ "type": "mail", "quizID": 999, "target": "deleted@mail.com", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) targetID := fmt.Sprintf("%v", createResult["id"]) deleteResp, err := deleteLeadTargetRequest(deleteLeadTargetToken, targetID) assert.NoError(t, err) deleteResp.Body.Close() resp, err := getLeadTargetByQuizIDRequest(deleteLeadTargetToken, "999") assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestCreateQuestion_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} payload, err := json.Marshal(map[string]interface{}{ "quiz_id": 12345, "title": "Test Question", "type": "variant", }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/question/create", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "timeout") }) } func createQuestionRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/question/create", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestCreateQuestion_Success(t *testing.T) { resp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Какой основной компонент воздуха?", "type": "variant", "description": "Выберите один правильный ответ.", "required": true, "page": 1, "content": "{}", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result["id"]) assert.Equal(t, float64(12345), result["quiz_id"]) assert.Equal(t, "Какой основной компонент воздуха?", result["title"]) assert.Equal(t, "variant", result["type"]) assert.Equal(t, "Выберите один правильный ответ.", result["description"]) assert.Equal(t, true, result["required"]) assert.Equal(t, float64(1), result["page"]) assert.Equal(t, "{}", result["content"]) assert.NotEmpty(t, result["created_at"]) } func TestCreateQuestion_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "quiz_id": 12345, "title": "Test Question", "type": "variant", }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/question/create", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := createQuestionRequest("invalid_token", map[string]interface{}{ "quiz_id": 12345, "title": "Test Question", "type": "variant", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := createQuestionRequest(expiredToken, map[string]interface{}{ "quiz_id": 12345, "title": "Test Question", "type": "variant", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestCreateQuestion_InputValidation(t *testing.T) { t.Run("MissingRequiredFields", func(t *testing.T) { resp, err := createQuestionRequest(validToken, map[string]interface{}{ "title": "Test Question", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidQuizID", func(t *testing.T) { resp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": "invalid", "title": "Test Question", "type": "variant", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidType", func(t *testing.T) { resp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Test Question", "type": "invalid_type", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidRequired", func(t *testing.T) { resp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Test Question", "type": "variant", "required": "not_boolean", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidPage", func(t *testing.T) { resp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Test Question", "type": "variant", "page": "not_number", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("EmptyBody", func(t *testing.T) { resp, err := createQuestionRequest(validToken, map[string]interface{}{}) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestCreateQuestion_DifferentTypes(t *testing.T) { questionTypes := []string{"text", "variant", "images", "select", "varimg", "emoji", "date", "number", "page", "rating", "result", "file"} for _, questionType := range questionTypes { t.Run(questionType, func(t *testing.T) { resp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": fmt.Sprintf("Test %s Question", questionType), "type": questionType, "content": "{}", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) } } func TestCreateQuestion_Security(t *testing.T) { t.Run("SQLInjection", func(t *testing.T) { resp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": "1' OR '1'='1", "title": "Test Question", "type": "variant", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("XSSAttack", func(t *testing.T) { resp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": xssInput, "type": "variant", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestCreateQuestion_Performance(t *testing.T) { t.Run("ResponseTime", func(t *testing.T) { 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) }) } func TestGetQuestionList_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} payload, err := json.Marshal(map[string]interface{}{ "limit": 10, "page": 1, }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/question/getList", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "timeout") }) } func getQuestionListRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/question/getList", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestGetQuestionList_Success(t *testing.T) { for i := 0; i < 3; i++ { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": fmt.Sprintf("Test Question %d", i), "type": "variant", }) assert.NoError(t, err) createResp.Body.Close() } resp, err := getQuestionListRequest(validToken, map[string]interface{}{ "limit": 10, "page": 1, "quiz_id": 12345, "type": "variant", "deleted": false, "required": true, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result["count"]) assert.NotEmpty(t, result["items"]) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.LessOrEqual(t, len(items), 10) if len(items) > 0 { firstItem, ok := items[0].(map[string]interface{}) assert.True(t, ok) assert.NotEmpty(t, firstItem["id"]) assert.Equal(t, float64(12345), firstItem["quiz_id"]) assert.Equal(t, "variant", firstItem["type"]) } } func TestGetQuestionList_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "limit": 10, "page": 1, }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/question/getList", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := getQuestionListRequest("invalid_token", map[string]interface{}{ "limit": 10, "page": 1, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := getQuestionListRequest(expiredToken, map[string]interface{}{ "limit": 10, "page": 1, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetQuestionList_InputValidation(t *testing.T) { t.Run("InvalidPagination", func(t *testing.T) { resp, err := getQuestionListRequest(validToken, map[string]interface{}{ "limit": "invalid", "page": "invalid", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidQuizID", func(t *testing.T) { resp, err := getQuestionListRequest(validToken, map[string]interface{}{ "quiz_id": "invalid", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidType", func(t *testing.T) { resp, err := getQuestionListRequest(validToken, map[string]interface{}{ "type": "invalid_type", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidBoolean", func(t *testing.T) { resp, err := getQuestionListRequest(validToken, map[string]interface{}{ "deleted": "not_boolean", "required": "not_boolean", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidTimeRange", func(t *testing.T) { resp, err := getQuestionListRequest(validToken, map[string]interface{}{ "from": 1000, "to": 500, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestGetQuestionList_Pagination(t *testing.T) { for i := 0; i < 15; i++ { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12346, "title": fmt.Sprintf("Pagination Question %d", i), "type": "text", }) assert.NoError(t, err) createResp.Body.Close() } t.Run("FirstPage", func(t *testing.T) { resp, err := getQuestionListRequest(validToken, map[string]interface{}{ "limit": 5, "page": 1, "quiz_id": 12346, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.LessOrEqual(t, len(items), 5) }) t.Run("SecondPage", func(t *testing.T) { resp, err := getQuestionListRequest(validToken, map[string]interface{}{ "limit": 5, "page": 2, "quiz_id": 12346, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.LessOrEqual(t, len(items), 5) }) t.Run("EmptyPage", func(t *testing.T) { resp, err := getQuestionListRequest(validToken, map[string]interface{}{ "limit": 5, "page": 1000, "quiz_id": 12346, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.Empty(t, items) }) } func TestGetQuestionList_Filters(t *testing.T) { questionTypes := []string{"text", "variant", "select"} for _, questionType := range questionTypes { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12347, "title": fmt.Sprintf("Filter Question %s", questionType), "type": questionType, }) assert.NoError(t, err) createResp.Body.Close() } t.Run("FilterByType", func(t *testing.T) { resp, err := getQuestionListRequest(validToken, map[string]interface{}{ "quiz_id": 12347, "type": "text", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) for _, item := range items { question, ok := item.(map[string]interface{}) assert.True(t, ok) assert.Equal(t, "text", question["type"]) } }) 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() }) } func TestEditQuestion_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} payload, err := json.Marshal(map[string]interface{}{ "id": 123, "title": "Test Question", }) assert.NoError(t, err) req, err := http.NewRequest("PATCH", baseURL+"/question/edit", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "timeout") }) } func editQuestionRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("PATCH", baseURL+"/question/edit", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestEditQuestion_Success(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Original Question", "type": "variant", "required": true, }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) questionID := createResult["id"] resp, err := editQuestionRequest(validToken, map[string]interface{}{ "id": questionID, "title": "Обновленный заголовок вопроса?", "desc": "Новое описание для вопроса.", "type": "text", "required": false, "content": "{\"placeholder\":\"Введите ваш ответ здесь\"}", "page": 2, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, questionID, result["updated"]) } func TestEditQuestion_SingleField(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Single Field Question", "type": "variant", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) questionID := createResult["id"] resp, err := editQuestionRequest(validToken, map[string]interface{}{ "id": questionID, "title": "Только заголовок обновлен", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, questionID, result["updated"]) } func TestEditQuestion_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "id": 123, "title": "Test Question", }) assert.NoError(t, err) req, err := http.NewRequest("PATCH", baseURL+"/question/edit", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := editQuestionRequest("invalid_token", map[string]interface{}{ "id": 123, "title": "Test Question", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := editQuestionRequest(expiredToken, map[string]interface{}{ "id": 123, "title": "Test Question", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestEditQuestion_InputValidation(t *testing.T) { t.Run("MissingID", func(t *testing.T) { resp, err := editQuestionRequest(validToken, map[string]interface{}{ "title": "Запрос без ID", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidID", func(t *testing.T) { resp, err := editQuestionRequest(validToken, map[string]interface{}{ "id": "not_an_integer", "title": "Невалидный ID", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentID", func(t *testing.T) { resp, err := editQuestionRequest(validToken, map[string]interface{}{ "id": 99999, "title": "Несуществующий вопрос", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidTitle", func(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Test Question", "type": "variant", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) questionID := createResult["id"] longTitle := strings.Repeat("a", 513) resp, err := editQuestionRequest(validToken, map[string]interface{}{ "id": questionID, "title": longTitle, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidType", func(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Test Question", "type": "variant", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) questionID := createResult["id"] resp, err := editQuestionRequest(validToken, map[string]interface{}{ "id": questionID, "type": "invalid_type", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidRequired", func(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Test Question", "type": "variant", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) questionID := createResult["id"] resp, err := editQuestionRequest(validToken, map[string]interface{}{ "id": questionID, "required": "not_boolean", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestEditQuestion_Security(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Security Test Question", "type": "variant", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) questionID := createResult["id"] t.Run("SQLInjection", func(t *testing.T) { resp, err := editQuestionRequest(validToken, map[string]interface{}{ "id": questionID, "title": "1' OR '1'='1", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, questionID, result["updated"]) }) t.Run("XSSAttack", func(t *testing.T) { resp, err := editQuestionRequest(validToken, map[string]interface{}{ "id": questionID, "title": xssInput, }) 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() }) } func TestCopyQuestion_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} payload, err := json.Marshal(map[string]interface{}{ "id": 101, "quiz_id": 202, }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/question/copy", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "timeout") }) } func copyQuestionRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/question/copy", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestCopyQuestion_Success(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Какой основной компонент воздуха?", "type": "variant", "description": "Выберите один правильный ответ из предложенных.", "required": true, "page": 1, "content": "{}", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) originalID := createResult["id"] resp, err := copyQuestionRequest(validToken, map[string]interface{}{ "id": originalID, "quiz_id": 202, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result["id"]) assert.NotEqual(t, originalID, result["id"]) // Новый ID должен отличаться assert.Equal(t, float64(202), result["quiz_id"]) assert.Equal(t, "Какой основной компонент воздуха?", result["title"]) assert.Equal(t, "variant", result["type"]) assert.Equal(t, "Выберите один правильный ответ из предложенных.", result["description"]) assert.Equal(t, true, result["required"]) assert.Equal(t, float64(1), result["page"]) assert.Equal(t, "{}", result["content"]) assert.Equal(t, false, result["deleted"]) assert.NotEmpty(t, result["created_at"]) assert.NotEmpty(t, result["updated_at"]) } func TestCopyQuestion_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "id": 101, "quiz_id": 202, }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/question/copy", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := copyQuestionRequest("invalid_token", map[string]interface{}{ "id": 101, "quiz_id": 202, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := copyQuestionRequest(expiredToken, map[string]interface{}{ "id": 101, "quiz_id": 202, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestCopyQuestion_InputValidation(t *testing.T) { t.Run("MissingID", func(t *testing.T) { resp, err := copyQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 202, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("MissingQuizID", func(t *testing.T) { resp, err := copyQuestionRequest(validToken, map[string]interface{}{ "id": 101, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidID", func(t *testing.T) { resp, err := copyQuestionRequest(validToken, map[string]interface{}{ "id": "invalid", "quiz_id": 202, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidQuizID", func(t *testing.T) { resp, err := copyQuestionRequest(validToken, map[string]interface{}{ "id": 101, "quiz_id": "invalid", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentID", func(t *testing.T) { resp, err := copyQuestionRequest(validToken, map[string]interface{}{ "id": 99999, "quiz_id": 202, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestCopyQuestion_Security(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Security Test Question", "type": "variant", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) originalID := createResult["id"] t.Run("SQLInjection", func(t *testing.T) { resp, err := copyQuestionRequest(validToken, map[string]interface{}{ "id": "1' OR '1'='1", "quiz_id": 202, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("XSSAttack", func(t *testing.T) { resp, err := copyQuestionRequest(validToken, map[string]interface{}{ "id": originalID, "quiz_id": 202, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result["id"]) }) } func TestCopyQuestion_Performance(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Performance Test Question", "type": "variant", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) originalID := createResult["id"] t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := copyQuestionRequest(validToken, map[string]interface{}{ "id": originalID, "quiz_id": 202, }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("LoadTest", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 30; i++ { wg.Add(1) go func(index int) { defer wg.Done() createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345 + index, "title": fmt.Sprintf("Load Test Question %d", index), "type": "variant", }) if err != nil { return } defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) if err != nil { return } originalID := createResult["id"] resp, err := copyQuestionRequest(validToken, map[string]interface{}{ "id": originalID, "quiz_id": 202 + index, }) if err == nil && resp != nil { resp.Body.Close() } }(i) } wg.Wait() }) } func TestCopyQuestion_OriginalPreserved(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Original Question", "type": "variant", "required": true, }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) originalID := createResult["id"] originalTitle := createResult["title"] resp, err := copyQuestionRequest(validToken, map[string]interface{}{ "id": originalID, "quiz_id": 202, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var copyResult map[string]interface{} err = json.NewDecoder(resp.Body).Decode(©Result) assert.NoError(t, err) copyID := copyResult["id"] // Проверяем, что оригинальный вопрос остался без изменений assert.NotEqual(t, originalID, copyID) assert.Equal(t, originalTitle, copyResult["title"]) } func TestGetQuestionHistory_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} payload, err := json.Marshal(map[string]interface{}{ "id": 101, "l": 10, "p": 1, }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/question/history", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "timeout") }) } func getQuestionHistoryRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/question/history", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestGetQuestionHistory_Success(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Original Question", "type": "variant", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) questionID := createResult["id"] for i := 1; i <= 3; i++ { editResp, err := editQuestionRequest(validToken, map[string]interface{}{ "id": questionID, "title": fmt.Sprintf("Updated Question Version %d", i), }) assert.NoError(t, err) editResp.Body.Close() } resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": questionID, "l": 10, "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result["items"]) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.LessOrEqual(t, len(items), 10) assert.Greater(t, len(items), 0) if len(items) > 0 { firstItem, ok := items[0].(map[string]interface{}) assert.True(t, ok) assert.Equal(t, questionID, firstItem["id"]) assert.Equal(t, float64(12345), firstItem["quiz_id"]) assert.NotEmpty(t, firstItem["version"]) assert.NotEmpty(t, firstItem["created_at"]) assert.NotEmpty(t, firstItem["updated_at"]) } } func TestGetQuestionHistory_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "id": 101, "l": 10, "p": 1, }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/question/history", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := getQuestionHistoryRequest("invalid_token", map[string]interface{}{ "id": 101, "l": 10, "p": 1, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := getQuestionHistoryRequest(expiredToken, map[string]interface{}{ "id": 101, "l": 10, "p": 1, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetQuestionHistory_InputValidation(t *testing.T) { t.Run("MissingID", func(t *testing.T) { resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "l": 10, "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidID", func(t *testing.T) { resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": "not_an_integer", "l": 10, "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidLimit", func(t *testing.T) { resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": 101, "l": "ten", "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidPage", func(t *testing.T) { resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": 101, "l": 10, "p": "one", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NegativeLimit", func(t *testing.T) { resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": 101, "l": -5, "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("ZeroPage", func(t *testing.T) { resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": 101, "l": 10, "p": 0, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentID", func(t *testing.T) { resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": 99999, "l": 10, "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestGetQuestionHistory_Pagination(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Pagination Test Question", "type": "variant", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) questionID := createResult["id"] for i := 1; i <= 15; i++ { editResp, err := editQuestionRequest(validToken, map[string]interface{}{ "id": questionID, "title": fmt.Sprintf("Version %d", i), }) assert.NoError(t, err) editResp.Body.Close() } t.Run("FirstPage", func(t *testing.T) { resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": questionID, "l": 5, "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.LessOrEqual(t, len(items), 5) }) t.Run("SecondPage", func(t *testing.T) { resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": questionID, "l": 5, "p": 2, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.LessOrEqual(t, len(items), 5) }) t.Run("EmptyPage", func(t *testing.T) { resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": questionID, "l": 5, "p": 100, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.Empty(t, items) }) } func TestGetQuestionHistory_NewQuestion(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "New Question", "type": "variant", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) questionID := createResult["id"] resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": questionID, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.Len(t, items, 1) if len(items) > 0 { firstItem, ok := items[0].(map[string]interface{}) assert.True(t, ok) assert.Equal(t, questionID, firstItem["id"]) assert.Equal(t, float64(1), firstItem["version"]) } } func TestGetQuestionHistory_Performance(t *testing.T) { createResp, err := createQuestionRequest(validToken, map[string]interface{}{ "quiz_id": 12345, "title": "Performance Test Question", "type": "variant", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) questionID := createResult["id"] for i := 1; i <= 5; i++ { editResp, err := editQuestionRequest(validToken, map[string]interface{}{ "id": questionID, "title": fmt.Sprintf("Version %d", i), }) assert.NoError(t, err) editResp.Body.Close() } t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": questionID, "l": 10, "p": 1, }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("LoadTest", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done() resp, err := getQuestionHistoryRequest(validToken, map[string]interface{}{ "id": questionID, "l": 5, "p": 1, }) if err == nil && resp != nil { resp.Body.Close() } }() } wg.Wait() }) } func TestDeleteQuestion_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} payload, err := json.Marshal(map[string]interface{}{ "id": 101, }) assert.NoError(t, err) req, err := http.NewRequest("DELETE", baseURL+"/question/delete", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+deleteQuestionToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) assert.Contains(t, err.Error(), "timeout") }) } func deleteQuestionRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("DELETE", baseURL+"/question/delete", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestDeleteQuestion_Success(t *testing.T) { createResp, err := createQuestionRequest(deleteQuestionToken, 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(deleteQuestionToken, 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(deleteQuestionToken, 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(deleteQuestionToken, map[string]interface{}{ "id": questionID, }) assert.NoError(t, err) defer resp1.Body.Close() assert.Equal(t, http.StatusOK, resp1.StatusCode) resp2, err := deleteQuestionRequest(deleteQuestionToken, map[string]interface{}{ "id": questionID, }) assert.NoError(t, err) defer resp2.Body.Close() assert.Equal(t, http.StatusOK, resp2.StatusCode) var result1, result2 map[string]interface{} err = json.NewDecoder(resp1.Body).Decode(&result1) assert.NoError(t, err) err = json.NewDecoder(resp2.Body).Decode(&result2) assert.NoError(t, err) assert.Equal(t, questionID, result1["deactivated"]) assert.Equal(t, questionID, result2["deactivated"]) } func TestDeleteQuestion_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "id": 101, }) assert.NoError(t, err) req, err := http.NewRequest("DELETE", baseURL+"/question/delete", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := deleteQuestionRequest("invalid_token", map[string]interface{}{ "id": 101, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := deleteQuestionRequest(expiredToken, map[string]interface{}{ "id": 101, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestDeleteQuestion_InputValidation(t *testing.T) { t.Run("MissingID", func(t *testing.T) { resp, err := deleteQuestionRequest(deleteQuestionToken, 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(deleteQuestionToken, 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(deleteQuestionToken, 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(deleteQuestionToken, 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(deleteQuestionToken, map[string]interface{}{ "id": questionID, }) assert.NoError(t, err) defer resp1.Body.Close() assert.Equal(t, http.StatusOK, resp1.StatusCode) resp2, err := deleteQuestionRequest(deleteQuestionToken, 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(deleteQuestionToken, 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(deleteQuestionToken, 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(deleteQuestionToken, map[string]interface{}{ "id": id, }) if err == nil && resp != nil { resp.Body.Close() } }(questionIDs[i]) } wg.Wait() }) } func TestCreateQuiz_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} payload, err := json.Marshal(map[string]interface{}{ "name": "Test Quiz", }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/quiz/create", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) // Ожидаем ошибку таймаута }) } func createQuizRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/quiz/create", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestCreateQuiz_Success(t *testing.T) { t.Run("MinimalQuiz", func(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Новый квиз по истории", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusCreated, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result["id"]) assert.NotEmpty(t, result["qid"]) assert.NotEmpty(t, result["accountid"]) assert.Equal(t, "Новый квиз по истории", result["name"]) assert.Equal(t, "draft", result["status"]) // Значение по умолчанию assert.Equal(t, false, result["deleted"]) assert.Equal(t, false, result["archived"]) assert.Equal(t, float64(1), result["version"]) assert.NotEmpty(t, result["created_at"]) assert.NotEmpty(t, result["updated_at"]) }) t.Run("FullQuiz", func(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Полный квиз по географии", "description": "Детальный тест на знание столиц и стран.", "fingerprinting": true, "repeatable": true, "note_prevented": false, "mail_notifications": true, "unique_answers": false, "config": "{\"showCorrectAnswers\": true}", "status": "start", "limit": 100, "due_to": 1700000000, "question_cnt": 10, "time_of_passing": 3600, "pausable": true, "super": false, "group_id": nil, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusCreated, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result["id"]) assert.Equal(t, "Полный квиз по географии", result["name"]) assert.Equal(t, "Детальный тест на знание столиц и стран.", result["description"]) assert.Equal(t, true, result["fingerprinting"]) assert.Equal(t, true, result["repeatable"]) assert.Equal(t, false, result["note_prevented"]) assert.Equal(t, true, result["mail_notifications"]) assert.Equal(t, false, result["unique_answers"]) assert.Equal(t, "{\"showCorrectAnswers\": true}", result["config"]) assert.Equal(t, "start", result["status"]) assert.Equal(t, float64(100), result["limit"]) assert.Equal(t, float64(1700000000), result["due_to"]) assert.Equal(t, float64(10), result["question_cnt"]) assert.Equal(t, float64(3600), result["time_of_passing"]) assert.Equal(t, true, result["pausable"]) assert.Equal(t, false, result["super"]) assert.Nil(t, result["group_id"]) }) } func TestCreateQuiz_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "name": "Test Quiz", }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/quiz/create", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := createQuizRequest("invalid_token", map[string]interface{}{ "name": "Test Quiz", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := createQuizRequest(expiredToken, map[string]interface{}{ "name": "Test Quiz", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestCreateQuiz_InputValidation(t *testing.T) { t.Run("MissingName", func(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "description": "Test description", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NameTooLong", func(t *testing.T) { longName := strings.Repeat("a", 701) // Больше 700 символов resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": longName, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidStatus", func(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Test Quiz", "status": "invalid_status", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidFingerprinting", func(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Test Quiz", "fingerprinting": "not_boolean", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NegativeLimit", func(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Test Quiz", "limit": -5, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidDueTo", func(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Test Quiz", "due_to": "not_timestamp", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestCreateQuiz_StatusValues(t *testing.T) { statuses := []string{"draft", "template", "stop", "start"} for _, status := range statuses { t.Run("Status_"+status, func(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": fmt.Sprintf("Quiz with status %s", status), "status": status, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusCreated, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, status, result["status"]) }) } } func TestCreateQuiz_DefaultValues(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Quiz with defaults", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusCreated, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, "draft", result["status"]) assert.Equal(t, false, result["fingerprinting"]) assert.Equal(t, false, result["repeatable"]) assert.Equal(t, false, result["note_prevented"]) assert.Equal(t, true, result["pausable"]) assert.Equal(t, false, result["super"]) } func TestCreateQuiz_Conflict(t *testing.T) { resp1, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Duplicate Quiz Name", }) assert.NoError(t, err) defer resp1.Body.Close() assert.Equal(t, http.StatusCreated, resp1.StatusCode) resp2, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Duplicate Quiz Name", }) assert.NoError(t, err) defer resp2.Body.Close() assert.Equal(t, http.StatusConflict, resp2.StatusCode) } func TestCreateQuiz_Security(t *testing.T) { t.Run("SQLInjection", func(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": sqlInjectionInput, "description": sqlInjectionInput, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("XSS", func(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": xssInput, "description": xssInput, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestCreateQuiz_Performance(t *testing.T) { t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Performance Test Quiz", }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("LoadTest", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 50; i++ { wg.Add(1) go func(index int) { defer wg.Done() resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": fmt.Sprintf("Load Test Quiz %d", index), }) if err == nil && resp != nil { resp.Body.Close() } }(i) } wg.Wait() }) } func TestCreateQuiz_SuperQuiz(t *testing.T) { t.Run("SuperQuizWithoutGroup", func(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Super Quiz", "super": true, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusCreated, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, true, result["super"]) assert.Nil(t, result["group_id"]) }) t.Run("NonSuperQuizWithGroup", func(t *testing.T) { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Group Quiz", "super": false, "group_id": 123, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusCreated, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, false, result["super"]) assert.Equal(t, float64(123), result["group_id"]) }) } func TestGetQuizList_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} payload, err := json.Marshal(map[string]interface{}{ "limit": 10, "page": 1, }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/quiz/getList", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) }) t.Run("DatabaseConnectionError", func(t *testing.T) { resp, err := getQuizListRequest("db_error_token", map[string]interface{}{ "limit": 10, "page": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) }) t.Run("MemoryError", func(t *testing.T) { resp, err := getQuizListRequest("memory_error_token", map[string]interface{}{ "limit": 10, "page": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) }) } func getQuizListRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/quiz/getList", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestGetQuizList_Success(t *testing.T) { quizNames := []string{"Квиз по географии", "Квиз по истории", "Квиз по математике"} var quizIDs []interface{} for _, name := range quizNames { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": name, "status": "start", }) assert.NoError(t, err) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) resp.Body.Close() assert.NoError(t, err) quizIDs = append(quizIDs, result["id"]) } t.Run("BasicList", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "limit": 5, "page": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result["count"]) assert.NotEmpty(t, result["items"]) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.LessOrEqual(t, len(items), 5) if len(items) > 0 { firstItem, ok := items[0].(map[string]interface{}) assert.True(t, ok) assert.NotEmpty(t, firstItem["id"]) assert.NotEmpty(t, firstItem["name"]) assert.NotEmpty(t, firstItem["status"]) } }) t.Run("WithFilters", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "limit": 10, "page": 1, "search": "география", "status": "start", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) for _, item := range items { quiz, ok := item.(map[string]interface{}) assert.True(t, ok) name, ok := quiz["name"].(string) assert.True(t, ok) assert.Contains(t, strings.ToLower(name), "география") } }) } func TestGetQuizList_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "limit": 10, "page": 1, }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/quiz/getList", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := getQuizListRequest("invalid_token", map[string]interface{}{ "limit": 10, "page": 1, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := getQuizListRequest(expiredToken, map[string]interface{}{ "limit": 10, "page": 1, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetQuizList_InputValidation(t *testing.T) { t.Run("InvalidLimit", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "limit": "not_integer", "page": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidPage", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "limit": 10, "page": "not_integer", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("ZeroLimit", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "limit": 0, "page": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("ZeroPage", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "limit": 10, "page": 0, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidFrom", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "from": "not_timestamp", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidTo", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "to": "not_timestamp", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidStatus", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "status": "invalid_status", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidDeleted", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "deleted": "not_boolean", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidArchived", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "archived": "not_boolean", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidSuper", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "super": "not_boolean", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidGroupID", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "group_id": "not_integer", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestGetQuizList_Pagination(t *testing.T) { for i := 0; i < 15; i++ { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": fmt.Sprintf("Pagination Test Quiz %d", i), "status": "draft", }) assert.NoError(t, err) resp.Body.Close() } t.Run("FirstPage", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "limit": 5, "page": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.LessOrEqual(t, len(items), 5) }) t.Run("SecondPage", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "limit": 5, "page": 2, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.LessOrEqual(t, len(items), 5) }) // todo со временм бдшка же заполнится t.Run("EmptyPage", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "limit": 5, "page": 100, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.Empty(t, items) }) } func TestGetQuizList_Filters(t *testing.T) { statuses := []string{"draft", "start", "stop", "template"} for _, status := range statuses { resp, err := createQuizRequest(validToken, map[string]interface{}{ "name": fmt.Sprintf("Filter Test Quiz %s", status), "status": status, }) assert.NoError(t, err) resp.Body.Close() } t.Run("StatusFilter", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "status": "draft", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) for _, item := range items { quiz, ok := item.(map[string]interface{}) assert.True(t, ok) assert.Equal(t, "draft", quiz["status"]) } }) t.Run("SearchFilter", func(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{ "search": "Filter Test", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) for _, item := range items { quiz, ok := item.(map[string]interface{}) assert.True(t, ok) name, ok := quiz["name"].(string) assert.True(t, ok) assert.Contains(t, name, "Filter Test") } }) t.Run("TimeRangeFilter", func(t *testing.T) { now := time.Now().Unix() resp, err := getQuizListRequest(validToken, map[string]interface{}{ "from": now - 86400, "to": now, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.GreaterOrEqual(t, len(items), 0) }) } func TestGetQuizList_DefaultValues(t *testing.T) { resp, err := getQuizListRequest(validToken, map[string]interface{}{}) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) items, ok := result["items"].([]interface{}) assert.True(t, ok) assert.LessOrEqual(t, len(items), 10) } func TestGetQuizList_Performance(t *testing.T) { t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := getQuizListRequest(validToken, map[string]interface{}{ "limit": 10, "page": 1, }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(1000)) }) t.Run("LoadTest", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done() resp, err := getQuizListRequest(validToken, map[string]interface{}{ "limit": 5, "page": 1, }) if err == nil && resp != nil { resp.Body.Close() } }() } wg.Wait() }) } func editQuizRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("PATCH", baseURL+"/quiz/edit", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestEditQuiz_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для редактирования", "status": "draft", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := editQuizRequest(validToken, map[string]interface{}{ "id": quizID, "name": "Обновленное название квиза", "desc": "Новое описание", "status": "start", "limit": 150, "fp": true, "rep": false, "conf": "{}", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, quizID, result["updated"]) } func TestEditQuiz_OneField(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для смены статуса", "status": "draft", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := editQuizRequest(validToken, map[string]interface{}{ "id": quizID, "status": "stop", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, quizID, result["updated"]) } func TestEditQuiz_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "id": 101, "name": "Test Quiz", }) assert.NoError(t, err) req, err := http.NewRequest("PATCH", baseURL+"/quiz/edit", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := editQuizRequest("invalid_token", map[string]interface{}{ "id": 101, "name": "Test Quiz", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := editQuizRequest(expiredToken, map[string]interface{}{ "id": 101, "name": "Test Quiz", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestEditQuiz_InputValidation(t *testing.T) { t.Run("MissingID", func(t *testing.T) { resp, err := editQuizRequest(validToken, map[string]interface{}{ "name": "Без ID", }) assert.NoError(t, err) defer resp.Body.Close() assert.Contains(t, []int{http.StatusBadRequest, 422}, resp.StatusCode) }) t.Run("InvalidID", func(t *testing.T) { resp, err := editQuizRequest(validToken, map[string]interface{}{ "id": "not_an_integer", "name": "Невалидный ID", }) assert.NoError(t, err) defer resp.Body.Close() assert.Contains(t, []int{http.StatusBadRequest, 422}, resp.StatusCode) }) t.Run("NonExistentID", func(t *testing.T) { resp, err := editQuizRequest(validToken, map[string]interface{}{ "id": 99999, "name": "Несуществующий квиз", }) assert.NoError(t, err) defer resp.Body.Close() assert.Contains(t, []int{http.StatusBadRequest, 422}, resp.StatusCode) }) t.Run("NameTooLong", func(t *testing.T) { longName := strings.Repeat("a", 701) resp, err := editQuizRequest(validToken, map[string]interface{}{ "id": 101, "name": longName, }) assert.NoError(t, err) defer resp.Body.Close() assert.Contains(t, []int{http.StatusBadRequest, 422}, resp.StatusCode) }) t.Run("InvalidStatus", func(t *testing.T) { resp, err := editQuizRequest(validToken, map[string]interface{}{ "id": 101, "status": "invalid_status", }) assert.NoError(t, err) defer resp.Body.Close() assert.Contains(t, []int{http.StatusBadRequest, 422}, resp.StatusCode) }) t.Run("InvalidFP", func(t *testing.T) { resp, err := editQuizRequest(validToken, map[string]interface{}{ "id": 101, "fp": "not_boolean", }) assert.NoError(t, err) defer resp.Body.Close() assert.Contains(t, []int{http.StatusBadRequest, 422}, resp.StatusCode) }) t.Run("InvalidLimit", func(t *testing.T) { resp, err := editQuizRequest(validToken, map[string]interface{}{ "id": 101, "limit": -5, }) assert.NoError(t, err) defer resp.Body.Close() assert.Contains(t, []int{http.StatusBadRequest, 422}, resp.StatusCode) }) } func TestEditQuiz_Security(t *testing.T) { t.Run("SQLInjection", func(t *testing.T) { resp, err := editQuizRequest(validToken, map[string]interface{}{ "id": 101, "name": sqlInjectionInput, "desc": sqlInjectionInput, "conf": "{}", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("XSS", func(t *testing.T) { resp, err := editQuizRequest(validToken, map[string]interface{}{ "id": 101, "name": xssInput, "desc": xssInput, "conf": "{}", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestEditQuiz_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности", "status": "draft", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult 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() }) } 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() }) } func TestGetQuizHistory_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} payload, err := json.Marshal(map[string]interface{}{ "id": 101, "l": 10, "p": 1, }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/quiz/history", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) // Ожидаем ошибку таймаута }) } func getQuizHistoryRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/quiz/history", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestGetQuizHistory_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для истории", "status": "draft", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] for i := 1; i <= 3; i++ { editResp, err := editQuizRequest(validToken, map[string]interface{}{ "id": quizID, "name": fmt.Sprintf("Обновленный квиз версия %d", i), "status": "start", }) assert.NoError(t, err) editResp.Body.Close() } resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{ "id": quizID, "l": 5, "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result []map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.LessOrEqual(t, len(result), 5) assert.Greater(t, len(result), 0) if len(result) > 0 { firstItem := result[0] assert.Equal(t, quizID, firstItem["id"]) assert.NotEmpty(t, firstItem["version"]) assert.NotEmpty(t, firstItem["created_at"]) assert.NotEmpty(t, firstItem["updated_at"]) } } func TestGetQuizHistory_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "id": 101, "l": 10, "p": 1, }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/quiz/history", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := getQuizHistoryRequest("invalid_token", map[string]interface{}{ "id": 101, "l": 10, "p": 1, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := getQuizHistoryRequest(expiredToken, map[string]interface{}{ "id": 101, "l": 10, "p": 1, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetQuizHistory_InputValidation(t *testing.T) { t.Run("MissingID", func(t *testing.T) { resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{ "l": 10, "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidID", func(t *testing.T) { resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{ "id": "not_an_integer", "l": 10, "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidLimit", func(t *testing.T) { resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{ "id": 101, "l": "ten", "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidPage", func(t *testing.T) { resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{ "id": 101, "l": 10, "p": "one", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentID", func(t *testing.T) { resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{ "id": 99999, "l": 10, "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result []map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Empty(t, result) }) } func TestGetQuizHistory_Pagination(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для пагинации истории", "status": "draft", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] for i := 1; i <= 15; i++ { editResp, err := editQuizRequest(validToken, map[string]interface{}{ "id": quizID, "name": fmt.Sprintf("Версия %d", i), }) assert.NoError(t, err) editResp.Body.Close() } t.Run("FirstPage", func(t *testing.T) { resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{ "id": quizID, "l": 5, "p": 1, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result []map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.LessOrEqual(t, len(result), 5) }) t.Run("SecondPage", func(t *testing.T) { resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{ "id": quizID, "l": 5, "p": 2, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result []map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.LessOrEqual(t, len(result), 5) }) t.Run("EmptyPage", func(t *testing.T) { resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{ "id": quizID, "l": 5, "p": 100, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result []map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Empty(t, result) }) } func TestGetQuizHistory_NewQuiz(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Новый квиз для истории", "status": "draft", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{ "id": quizID, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result []map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Len(t, result, 1) // Должна быть только одна запись для нового квиза if len(result) > 0 { firstItem := result[0] assert.Equal(t, quizID, firstItem["id"]) assert.Equal(t, float64(1), firstItem["version"]) } } func TestGetQuizHistory_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности истории", "status": "draft", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] for i := 1; i <= 5; i++ { editResp, err := editQuizRequest(validToken, map[string]interface{}{ "id": quizID, "name": fmt.Sprintf("Версия %d", i), }) assert.NoError(t, err) editResp.Body.Close() } t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{ "id": quizID, "l": 10, "p": 1, }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("LoadTest", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done() resp, err := getQuizHistoryRequest(validToken, map[string]interface{}{ "id": quizID, "l": 5, "p": 1, }) if err == nil && resp != nil { resp.Body.Close() } }() } wg.Wait() }) } func TestDeleteQuiz_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} payload, err := json.Marshal(map[string]interface{}{ "id": 101, }) assert.NoError(t, err) req, err := http.NewRequest("DELETE", baseURL+"/quiz/delete", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+deleteQuizToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) // Ожидаем ошибку таймаута }) } func deleteQuizRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("DELETE", baseURL+"/quiz/delete", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestDeleteQuiz_Success(t *testing.T) { createResp, err := createQuizRequest(deleteQuizToken, 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(deleteQuizToken, 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(deleteQuizToken, 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(deleteQuizToken, map[string]interface{}{ "id": quizID, }) assert.NoError(t, err) defer resp1.Body.Close() assert.Equal(t, http.StatusOK, resp1.StatusCode) resp2, err := deleteQuizRequest(deleteQuizToken, map[string]interface{}{ "id": quizID, }) assert.NoError(t, err) defer resp2.Body.Close() assert.Equal(t, http.StatusOK, resp2.StatusCode) var result1, result2 map[string]interface{} err = json.NewDecoder(resp1.Body).Decode(&result1) assert.NoError(t, err) err = json.NewDecoder(resp2.Body).Decode(&result2) assert.NoError(t, err) assert.Equal(t, quizID, result1["deactivated"]) assert.Equal(t, quizID, result2["deactivated"]) } func TestDeleteQuiz_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "id": 101, }) assert.NoError(t, err) req, err := http.NewRequest("DELETE", baseURL+"/quiz/delete", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := deleteQuizRequest("invalid_token", map[string]interface{}{ "id": 101, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := deleteQuizRequest(expiredToken, map[string]interface{}{ "id": 101, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestDeleteQuiz_InputValidation(t *testing.T) { t.Run("MissingID", func(t *testing.T) { resp, err := deleteQuizRequest(deleteQuizToken, 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(deleteQuizToken, 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(deleteQuizToken, 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(deleteQuizToken, 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(deleteQuizToken, 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(deleteQuizToken, map[string]interface{}{ "id": id, }) if err == nil && resp != nil { resp.Body.Close() } }(quizIDs[i]) } wg.Wait() }) } func TestArchiveQuiz_ErrorHandling(t *testing.T) { t.Run("TimeoutHandling", func(t *testing.T) { client := &http.Client{Timeout: 1 * time.Millisecond} payload, err := json.Marshal(map[string]interface{}{ "id": 101, }) assert.NoError(t, err) req, err := http.NewRequest("PATCH", baseURL+"/quiz/archive", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") _, err = client.Do(req) assert.Error(t, err) // Ожидаем ошибку таймаута }) } func archiveQuizRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("PATCH", baseURL+"/quiz/archive", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestArchiveQuiz_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для архивации", "status": "draft", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := archiveQuizRequest(validToken, map[string]interface{}{ "id": quizID, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, quizID, result["deactivated"]) } func TestArchiveQuiz_Idempotency(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для архивации (идемпотентность)", "status": "draft", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp1, err := archiveQuizRequest(validToken, map[string]interface{}{ "id": quizID, }) assert.NoError(t, err) defer resp1.Body.Close() assert.Equal(t, http.StatusOK, resp1.StatusCode) resp2, err := archiveQuizRequest(validToken, map[string]interface{}{ "id": quizID, }) assert.NoError(t, err) defer resp2.Body.Close() assert.Equal(t, http.StatusOK, resp2.StatusCode) var result1, result2 map[string]interface{} err = json.NewDecoder(resp1.Body).Decode(&result1) assert.NoError(t, err) err = json.NewDecoder(resp2.Body).Decode(&result2) assert.NoError(t, err) assert.Equal(t, quizID, result1["deactivated"]) assert.Equal(t, quizID, result2["deactivated"]) } func TestArchiveQuiz_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "id": 101, }) assert.NoError(t, err) req, err := http.NewRequest("PATCH", baseURL+"/quiz/archive", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := archiveQuizRequest("invalid_token", map[string]interface{}{ "id": 101, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("ExpiredToken", func(t *testing.T) { resp, err := archiveQuizRequest(expiredToken, map[string]interface{}{ "id": 101, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestArchiveQuiz_InputValidation(t *testing.T) { t.Run("MissingID", func(t *testing.T) { resp, err := archiveQuizRequest(validToken, map[string]interface{}{}) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidID", func(t *testing.T) { resp, err := archiveQuizRequest(validToken, map[string]interface{}{ "id": "not_an_integer", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentID", func(t *testing.T) { resp, err := archiveQuizRequest(validToken, map[string]interface{}{ "id": 99999, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) // Идемпотентность }) } func TestArchiveQuiz_Performance(t *testing.T) { var quizIDs []interface{} for i := 0; i < 10; i++ { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": fmt.Sprintf("Квиз для архивации %d", i), "status": "draft", }) assert.NoError(t, err) var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) createResp.Body.Close() assert.NoError(t, err) quizIDs = append(quizIDs, createResult["id"]) } t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := archiveQuizRequest(validToken, map[string]interface{}{ "id": quizIDs[0], }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("BulkArchive", func(t *testing.T) { var wg sync.WaitGroup for i := 1; i < len(quizIDs); i++ { wg.Add(1) go func(id interface{}) { defer wg.Done() resp, err := archiveQuizRequest(validToken, map[string]interface{}{ "id": id, }) if err == nil && resp != nil { resp.Body.Close() } }(quizIDs[i]) } wg.Wait() }) } func TestMoveQuiz_BoundaryCases(t *testing.T) { t.Run("EmptyQIDAndAccountID", func(t *testing.T) { resp, err := moveQuizRequest(validToken, map[string]interface{}{ "qid": "", "accountID": "", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("LongQIDAndAccountID", func(t *testing.T) { longStr := make([]byte, 1025) for i := range longStr { longStr[i] = 'a' } resp, err := moveQuizRequest(validToken, map[string]interface{}{ "qid": string(longStr), "accountID": string(longStr), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestMoveQuiz_ErrorHandling(t *testing.T) { t.Run("MalformedJSON", func(t *testing.T) { req, err := http.NewRequest("POST", baseURL+"/quiz/move", bytes.NewReader([]byte("{invalid_json}"))) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func moveQuizRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/quiz/move", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestMoveQuiz_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для переноса", "status": "draft", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizQID := createResult["qid"] resp, err := moveQuizRequest(validToken, map[string]interface{}{ "qid": quizQID, "accountID": "new-account-id", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) } func TestMoveQuiz_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "qid": "test-qid", "accountID": "new-account", }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/quiz/move", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := moveQuizRequest("invalid_token", map[string]interface{}{ "qid": "test-qid", "accountID": "new-account", }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestMoveQuiz_InputValidation(t *testing.T) { t.Run("MissingQID", func(t *testing.T) { resp, err := moveQuizRequest(validToken, map[string]interface{}{ "accountID": "new-account", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("MissingAccountID", func(t *testing.T) { resp, err := moveQuizRequest(validToken, map[string]interface{}{ "qid": "test-qid", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentQID", func(t *testing.T) { resp, err := moveQuizRequest(validToken, map[string]interface{}{ "qid": "non-existent-qid", "accountID": "new-account", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentAccountID", func(t *testing.T) { resp, err := moveQuizRequest(validToken, map[string]interface{}{ "qid": "test-qid", "accountID": "non-existent-account", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestMoveQuiz_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности переноса", "status": "draft", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizQID := createResult["qid"] t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := moveQuizRequest(validToken, map[string]interface{}{ "qid": quizQID, "accountID": "performance-test-account", }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) } func TestCreateQuizTemplate_BoundaryCases(t *testing.T) { t.Run("ManyRequests", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 20; i++ { wg.Add(1) go func() { defer wg.Done() resp, err := createQuizTemplateRequest(validToken) if err == nil && resp != nil { resp.Body.Close() } }() } wg.Wait() }) } func createQuizTemplateRequest(token string) (*http.Response, error) { req, err := http.NewRequest("POST", baseURL+"/quiz/template", nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestCreateQuizTemplate_Success(t *testing.T) { resp, err := createQuizTemplateRequest(validToken) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result["id"]) assert.IsType(t, float64(0), result["id"]) } func TestCreateQuizTemplate_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { req, err := http.NewRequest("POST", baseURL+"/quiz/template", nil) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := createQuizTemplateRequest("invalid_token") assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestCreateQuizTemplate_Performance(t *testing.T) { t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := createQuizTemplateRequest(validToken) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("MultipleTemplates", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go func() { defer wg.Done() resp, err := createQuizTemplateRequest(validToken) if err == nil && resp != nil { resp.Body.Close() } }() } wg.Wait() }) } func getResultsRequest(token string, quizId string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/results/getResults/"+quizId, bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestGetResults_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для результатов", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "Page": 1, "Limit": 10, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotNil(t, result["total_count"]) assert.NotNil(t, result["results"]) assert.IsType(t, []interface{}{}, result["results"]) } func TestGetResults_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "Page": 1, "Limit": 10, }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/results/getResults/12345", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := getResultsRequest("invalid_token", "12345", map[string]interface{}{ "Page": 1, "Limit": 10, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetResults_InputValidation(t *testing.T) { t.Run("InvalidQuizID", func(t *testing.T) { resp, err := getResultsRequest(validToken, "invalid-quiz-id", map[string]interface{}{ "Page": 1, "Limit": 10, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentQuizID", func(t *testing.T) { resp, err := getResultsRequest(validToken, "99999", map[string]interface{}{ "Page": 1, "Limit": 10, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.Equal(t, float64(0), result["total_count"]) assert.Empty(t, result["results"]) }) } func TestGetResults_Pagination(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для пагинации результатов", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] t.Run("FirstPage", func(t *testing.T) { resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "Page": 1, "Limit": 5, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) results := result["results"].([]interface{}) assert.LessOrEqual(t, len(results), 5) }) t.Run("SecondPage", func(t *testing.T) { resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "Page": 2, "Limit": 5, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) t.Run("EmptyPage", func(t *testing.T) { resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "Page": 100, "Limit": 5, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) results := result["results"].([]interface{}) assert.Empty(t, results) }) } func TestGetResults_Filtering(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для фильтрации результатов", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] t.Run("WithDateRange", func(t *testing.T) { resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": "2023-01-01", "To": "2023-12-31", "Page": 1, "Limit": 10, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) t.Run("NewResultsOnly", func(t *testing.T) { resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "New": true, "Page": 1, "Limit": 10, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) } func TestGetResults_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности результатов", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "Page": 1, "Limit": 10, }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("ConcurrentRequests", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "Page": 1, "Limit": 5, }) if err == nil && resp != nil { resp.Body.Close() } }() } wg.Wait() }) } func TestGetResults_ErrorHandling(t *testing.T) { t.Run("MalformedJSON", func(t *testing.T) { req, err := http.NewRequest("POST", baseURL+"/results/getResults/12345", bytes.NewReader([]byte("{invalid_json}"))) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("ServerError", func(t *testing.T) { // Тестируем с некорректным quizId, который может вызвать серверную ошибку resp, err := getResultsRequest(validToken, "invalid-id", map[string]interface{}{ "Page": 1, "Limit": 10, }) assert.NoError(t, err) defer resp.Body.Close() assert.NotEqual(t, http.StatusOK, resp.StatusCode) }) } func deleteResultRequest(token string, resultId string) (*http.Response, error) { req, err := http.NewRequest("DELETE", baseURL+"/results/"+resultId, nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestDeleteResult_Success(t *testing.T) { createResp, err := createQuizRequest(deleteResultToken, 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(deleteResultToken, 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(deleteResultToken, 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(deleteResultToken, 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(deleteResultToken, 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(deleteResultToken, resultID) assert.NoError(t, err) defer resp1.Body.Close() assert.Equal(t, http.StatusOK, resp1.StatusCode) resp2, err := deleteResultRequest(deleteResultToken, 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(deleteResultToken, "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(deleteResultToken, "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(deleteResultToken, 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(deleteResultToken, 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(deleteResultToken, 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(deleteResultToken, 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(deleteResultToken, resultID) if err == nil && resp != nil { resp.Body.Close() } }(moreResults[i]) } wg.Wait() }) } func TestDeleteResult_BoundaryCases(t *testing.T) { t.Run("VeryLongResultID", func(t *testing.T) { longID := make([]byte, 1025) for i := range longID { longID[i] = '1' } resp, err := deleteResultRequest(deleteResultToken, string(longID)) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NegativeResultID", func(t *testing.T) { resp, err := deleteResultRequest(deleteResultToken, "-123") assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestDeleteResult_ErrorHandling(t *testing.T) { t.Run("MalformedRequest", func(t *testing.T) { req, err := http.NewRequest("DELETE", baseURL+"/results/invalid-id", bytes.NewReader([]byte("invalid_body"))) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+deleteResultToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.NotEqual(t, http.StatusOK, resp.StatusCode) }) t.Run("ServerError", func(t *testing.T) { resp, err := deleteResultRequest(deleteResultToken, "invalid-id") assert.NoError(t, err) defer resp.Body.Close() assert.NotEqual(t, http.StatusOK, resp.StatusCode) }) } func updateResultsStatusRequest(token string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("PATCH", baseURL+"/result/seen", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestUpdateResultsStatus_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для обновления статуса результатов", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "Page": 1, "Limit": 10, }) assert.NoError(t, err) defer getResultsResp.Body.Close() var resultsData map[string]interface{} err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData) assert.NoError(t, err) results := resultsData["results"].([]interface{}) if len(results) >= 3 { var answerIDs []int64 for i := 0; i < 3; i++ { result := results[i].(map[string]interface{}) answerIDs = append(answerIDs, int64(result["id"].(float64))) } resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ "Answers": answerIDs, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) } } func TestUpdateResultsStatus_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "Answers": []int64{101, 102, 103}, }) assert.NoError(t, err) req, err := http.NewRequest("PATCH", baseURL+"/result/seen", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := updateResultsStatusRequest("invalid_token", map[string]interface{}{ "Answers": []int64{101, 102, 103}, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestUpdateResultsStatus_InputValidation(t *testing.T) { t.Run("MissingAnswers", func(t *testing.T) { resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{}) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("EmptyAnswers", func(t *testing.T) { resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ "Answers": []int64{}, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidAnswersType", func(t *testing.T) { resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ "Answers": "not_an_array", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentAnswers", func(t *testing.T) { resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ "Answers": []int64{999999, 999998, 999997}, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) // Идемпотентность }) } func TestUpdateResultsStatus_Idempotency(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для идемпотентности обновления статуса", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "Page": 1, "Limit": 5, }) assert.NoError(t, err) defer getResultsResp.Body.Close() var resultsData map[string]interface{} err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData) assert.NoError(t, err) results := resultsData["results"].([]interface{}) if len(results) >= 2 { var answerIDs []int64 for i := 0; i < 2; i++ { result := results[i].(map[string]interface{}) answerIDs = append(answerIDs, int64(result["id"].(float64))) } resp1, err := updateResultsStatusRequest(validToken, map[string]interface{}{ "Answers": answerIDs, }) assert.NoError(t, err) defer resp1.Body.Close() assert.Equal(t, http.StatusOK, resp1.StatusCode) resp2, err := updateResultsStatusRequest(validToken, map[string]interface{}{ "Answers": answerIDs, }) assert.NoError(t, err) defer resp2.Body.Close() assert.Equal(t, http.StatusOK, resp2.StatusCode) } } func TestUpdateResultsStatus_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности обновления статуса", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "Page": 1, "Limit": 20, }) assert.NoError(t, err) defer getResultsResp.Body.Close() var resultsData map[string]interface{} err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData) assert.NoError(t, err) results := resultsData["results"].([]interface{}) if len(results) > 0 { var answerIDs []int64 for i := 0; i < len(results) && i < 10; i++ { result := results[i].(map[string]interface{}) answerIDs = append(answerIDs, int64(result["id"].(float64))) } t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ "Answers": answerIDs, }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("LargeBatch", func(t *testing.T) { largeBatch := make([]int64, 100) for i := 0; i < 100; i++ { largeBatch[i] = int64(1000000 + i) // Используем несуществующие ID } resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ "Answers": largeBatch, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) } } func TestUpdateResultsStatus_BoundaryCases(t *testing.T) { t.Run("VeryLargeAnswersArray", func(t *testing.T) { largeAnswers := make([]int64, 10000) for i := 0; i < 10000; i++ { largeAnswers[i] = int64(1000000 + i) } resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ "Answers": largeAnswers, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) t.Run("NegativeAnswerIDs", func(t *testing.T) { resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ "Answers": []int64{-1, -2, -3}, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) } func TestUpdateResultsStatus_ErrorHandling(t *testing.T) { t.Run("MalformedJSON", func(t *testing.T) { req, err := http.NewRequest("PATCH", baseURL+"/result/seen", bytes.NewReader([]byte("{invalid_json}"))) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("ServerError", func(t *testing.T) { resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ "Answers": "not_an_array", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestUpdateResultsStatus_SpecialCases(t *testing.T) { t.Run("DuplicateAnswerIDs", func(t *testing.T) { resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ "Answers": []int64{101, 101, 102, 102, 103}, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) } func exportResultsRequest(token string, quizID string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/results/"+quizID+"/export", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestExportResults_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для экспорта результатов", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": "2023-01-01T00:00:00Z", "To": "2023-12-31T23:59:59Z", "New": false, "Page": 1, "Limit": 100, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", resp.Header.Get("Content-Type")) } func TestExportResults_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "Page": 1, "Limit": 10, }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/results/123/export", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := exportResultsRequest("invalid_token", "123", map[string]interface{}{ "Page": 1, "Limit": 10, }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestExportResults_InputValidation(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для валидации экспорта", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] t.Run("InvalidDateFormat", func(t *testing.T) { resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": "not-a-date", }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidQuizID", func(t *testing.T) { resp, err := exportResultsRequest(validToken, "invalid-quiz-id", map[string]interface{}{ "Page": 1, "Limit": 10, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentQuizID", func(t *testing.T) { resp, err := exportResultsRequest(validToken, "99999", map[string]interface{}{ "Page": 1, "Limit": 10, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) // Возвращает пустой файл }) } // todo nned check //func TestExportResults_Performance(t *testing.T) { // // Создаем квиз для тестирования производительности // createResp, err := createQuizRequest(validToken, map[string]interface{}{ // "name": "Квиз для теста производительности экспорта", // "status": "start", // }) // assert.NoError(t, err) // defer createResp.Body.Close() // var createResult map[string]interface{} // err = json.NewDecoder(createResp.Body).Decode(&createResult) // assert.NoError(t, err) // quizID := createResult["id"] // // t.Run("ResponseTime", func(t *testing.T) { // start := time.Now() // resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ // "Page": 1, // "Limit": 100, // }) // duration := time.Since(start) // // assert.NoError(t, err) // defer resp.Body.Close() // assert.Less(t, duration.Milliseconds(), int64(2000)) // Экспорт может занимать больше времени // }) // // t.Run("LargeExport", func(t *testing.T) { // resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ // "Page": 1, // "Limit": 1000, // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // }) //} // //func TestExportResults_BoundaryCases(t *testing.T) { // t.Run("VeryLargeLimit", func(t *testing.T) { // // Создаем квиз для тестирования // createResp, err := createQuizRequest(validToken, map[string]interface{}{ // "name": "Квиз для граничных случаев экспорта", // "status": "start", // }) // assert.NoError(t, err) // defer createResp.Body.Close() // var createResult map[string]interface{} // err = json.NewDecoder(createResp.Body).Decode(&createResult) // assert.NoError(t, err) // quizID := createResult["id"] // // resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ // "Page": 1, // "Limit": 100000, // Очень большой лимит // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // }) // // t.Run("VeryOldDateRange", func(t *testing.T) { // // Создаем квиз для тестирования // createResp, err := createQuizRequest(validToken, map[string]interface{}{ // "name": "Квиз для старых дат экспорта", // "status": "start", // }) // assert.NoError(t, err) // defer createResp.Body.Close() // var createResult map[string]interface{} // err = json.NewDecoder(createResp.Body).Decode(&createResult) // assert.NoError(t, err) // quizID := createResult["id"] // // resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ // "From": "1900-01-01T00:00:00Z", // "To": "1950-12-31T23:59:59Z", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // }) //} // //func TestExportResults_ErrorHandling(t *testing.T) { // t.Run("MalformedJSON", func(t *testing.T) { // req, err := http.NewRequest("POST", baseURL+"/results/123/export", bytes.NewReader([]byte("{invalid_json}"))) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validToken) // req.Header.Set("Content-Type", "application/json") // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("ServerError", func(t *testing.T) { // // Тестируем с некорректным quizId, который может вызвать серверную ошибку // resp, err := exportResultsRequest(validToken, "invalid-id", map[string]interface{}{ // "Page": 1, // "Limit": 10, // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.NotEqual(t, http.StatusOK, resp.StatusCode) // }) //} // //func TestExportResults_AuditMetricsConsistency(t *testing.T) { // t.Run("AuditLog", func(t *testing.T) { // // Создаем квиз для тестирования аудита // createResp, err := createQuizRequest(validToken, map[string]interface{}{ // "name": "Квиз для аудита экспорта", // "status": "start", // }) // assert.NoError(t, err) // defer createResp.Body.Close() // var createResult map[string]interface{} // err = json.NewDecoder(createResp.Body).Decode(&createResult) // assert.NoError(t, err) // quizID := createResult["id"] // // resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ // "Page": 1, // "Limit": 50, // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // // Здесь можно добавить проверку на появление записи аудита, если есть доступ к логам/БД // }) // // t.Run("Consistency", func(t *testing.T) { // // Проверяем консистентность данных при повторных экспортах // createResp, err := createQuizRequest(validToken, map[string]interface{}{ // "name": "Квиз для консистентности экспорта", // "status": "start", // }) // assert.NoError(t, err) // defer createResp.Body.Close() // var createResult map[string]interface{} // err = json.NewDecoder(createResp.Body).Decode(&createResult) // assert.NoError(t, err) // quizID := createResult["id"] // // // Первый экспорт // resp1, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ // "Page": 1, // "Limit": 10, // }) // assert.NoError(t, err) // defer resp1.Body.Close() // assert.Equal(t, http.StatusOK, resp1.StatusCode) // // // Второй экспорт с теми же параметрами // resp2, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ // "Page": 1, // "Limit": 10, // }) // assert.NoError(t, err) // defer resp2.Body.Close() // assert.Equal(t, http.StatusOK, resp2.StatusCode) // // // Проверяем, что размеры файлов одинаковые (консистентность) // body1, err := io.ReadAll(resp1.Body) // assert.NoError(t, err) // body2, err := io.ReadAll(resp2.Body) // assert.NoError(t, err) // assert.Equal(t, len(body1), len(body2)) // }) //} // //func TestExportResults_IdempotencyAndTransactions(t *testing.T) { // t.Run("Idempotency", func(t *testing.T) { // // Создаем квиз для тестирования идемпотентности // createResp, err := createQuizRequest(validToken, map[string]interface{}{ // "name": "Квиз для идемпотентности экспорта", // "status": "start", // }) // assert.NoError(t, err) // defer createResp.Body.Close() // var createResult map[string]interface{} // err = json.NewDecoder(createResp.Body).Decode(&createResult) // assert.NoError(t, err) // quizID := createResult["id"] // // // Первый экспорт // resp1, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ // "Page": 1, // "Limit": 5, // }) // assert.NoError(t, err) // defer resp1.Body.Close() // assert.Equal(t, http.StatusOK, resp1.StatusCode) // // // Повторный экспорт с теми же параметрами // resp2, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ // "Page": 1, // "Limit": 5, // }) // assert.NoError(t, err) // defer resp2.Body.Close() // assert.Equal(t, http.StatusOK, resp2.StatusCode) // assert.Equal(t, resp1.StatusCode, resp2.StatusCode) // }) // // t.Run("TransactionRollback", func(t *testing.T) { // // Пытаемся экспортировать результаты несуществующего квиза // resp, err := exportResultsRequest(validToken, "999999", map[string]interface{}{ // "Page": 1, // "Limit": 10, // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // // // Проверяем, что файл пустой или содержит заголовки // body, err := io.ReadAll(resp.Body) // assert.NoError(t, err) // // Файл может быть пустым или содержать только заголовки // assert.NotNil(t, body) // }) //} func getResultRequest(token string, resultID string) (*http.Response, error) { req, err := http.NewRequest("GET", baseURL+"/result/"+resultID, nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestGetResult_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для получения результата", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "Page": 1, "Limit": 5, }) assert.NoError(t, err) defer getResultsResp.Body.Close() var resultsData map[string]interface{} err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData) assert.NoError(t, err) results := resultsData["results"].([]interface{}) if len(results) > 0 { firstResult := results[0].(map[string]interface{}) resultID := fmt.Sprintf("%v", firstResult["id"]) resp, err := getResultRequest(validToken, resultID) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var answers []map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&answers) assert.NoError(t, err) if len(answers) > 0 { answer := answers[0] assert.NotEmpty(t, answer["Id"]) assert.NotEmpty(t, answer["content"]) assert.NotEmpty(t, answer["question_id"]) assert.NotEmpty(t, answer["QuizId"]) assert.NotEmpty(t, answer["CreatedAt"]) assert.IsType(t, false, answer["Result"]) assert.IsType(t, false, answer["new"]) assert.IsType(t, false, answer["Deleted"]) assert.IsType(t, false, answer["Start"]) } } } func TestGetResult_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/result/abc123xyz", nil) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := getResultRequest("invalid_token", "abc123xyz") assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetResult_InputValidation(t *testing.T) { t.Run("EmptyResultID", func(t *testing.T) { resp, err := getResultRequest(validToken, "") assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentResultID", func(t *testing.T) { resp, err := getResultRequest(validToken, "nonexistent123") assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var answers []map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&answers) assert.NoError(t, err) assert.Empty(t, answers) }) t.Run("InvalidResultID", func(t *testing.T) { resp, err := getResultRequest(validToken, "invalid-result-id!") assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestGetResult_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности результата", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "Page": 1, "Limit": 1, }) assert.NoError(t, err) defer getResultsResp.Body.Close() var resultsData map[string]interface{} err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData) assert.NoError(t, err) results := resultsData["results"].([]interface{}) if len(results) > 0 { firstResult := results[0].(map[string]interface{}) resultID := fmt.Sprintf("%v", firstResult["id"]) t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := getResultRequest(validToken, resultID) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) } } func TestGetResult_BoundaryCases(t *testing.T) { t.Run("VeryLongResultID", func(t *testing.T) { longID := make([]byte, 1025) for i := range longID { longID[i] = 'a' } resp, err := getResultRequest(validToken, string(longID)) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NegativeResultID", func(t *testing.T) { resp, err := getResultRequest(validToken, "-123") assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("SpecialCharactersInResultID", func(t *testing.T) { resp, err := getResultRequest(validToken, "result@#$%^&*()") assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } func TestGetResult_ErrorHandling(t *testing.T) { t.Run("MalformedRequest", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/result/invalid-id", bytes.NewReader([]byte("invalid_body"))) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.NotEqual(t, http.StatusOK, resp.StatusCode) }) t.Run("ServerError", func(t *testing.T) { resp, err := getResultRequest(validToken, "invalid-id") assert.NoError(t, err) defer resp.Body.Close() assert.NotEqual(t, http.StatusOK, resp.StatusCode) }) } func getDeviceStatsRequest(token string, quizID string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/statistic/"+quizID+"/devices", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestGetDeviceStats_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для статистики устройств", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotNil(t, result["Device"]) assert.NotNil(t, result["OS"]) assert.NotNil(t, result["Browser"]) deviceStats := result["Device"].(map[string]interface{}) osStats := result["OS"].(map[string]interface{}) browserStats := result["Browser"].(map[string]interface{}) for _, value := range deviceStats { assert.IsType(t, float64(0), value) } for _, value := range osStats { assert.IsType(t, float64(0), value) } for _, value := range browserStats { assert.IsType(t, float64(0), value) } } func TestGetDeviceStats_WithoutDateRange(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для статистики без диапазона дат", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{}) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotNil(t, result["Device"]) assert.NotNil(t, result["OS"]) assert.NotNil(t, result["Browser"]) } func TestGetDeviceStats_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/statistic/12345/devices", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := getDeviceStatsRequest("invalid_token", "12345", map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetDeviceStats_InputValidation(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для валидации статистики устройств", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] t.Run("InvalidDateFormat", func(t *testing.T) { resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": "not_a_timestamp", "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidQuizID", func(t *testing.T) { resp, err := getDeviceStatsRequest(validToken, "invalid-quiz-id", map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentQuizID", func(t *testing.T) { resp, err := getDeviceStatsRequest(validToken, "99999", map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) deviceStats := result["Device"].(map[string]interface{}) osStats := result["OS"].(map[string]interface{}) browserStats := result["Browser"].(map[string]interface{}) assert.Empty(t, deviceStats) assert.Empty(t, osStats) assert.Empty(t, browserStats) }) } func TestGetDeviceStats_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности статистики устройств", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("ConcurrentRequests", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) if err == nil && resp != nil { resp.Body.Close() } }() } wg.Wait() }) } func TestGetDeviceStats_ErrorHandling(t *testing.T) { t.Run("MalformedJSON", func(t *testing.T) { req, err := http.NewRequest("POST", baseURL+"/statistic/12345/devices", bytes.NewReader([]byte("{invalid_json}"))) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("ServerError", func(t *testing.T) { resp, err := getDeviceStatsRequest(validToken, "invalid-id", map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.NotEqual(t, http.StatusOK, resp.StatusCode) }) } func getGeneralStatsRequest(token string, quizID string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/statistic/"+quizID+"/general", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestGetGeneralStats_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для общей статистики", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotNil(t, result["Open"]) assert.NotNil(t, result["Result"]) assert.NotNil(t, result["AvTime"]) assert.NotNil(t, result["Conversion"]) openStats := result["Open"].(map[string]interface{}) resultStats := result["Result"].(map[string]interface{}) avTimeStats := result["AvTime"].(map[string]interface{}) conversionStats := result["Conversion"].(map[string]interface{}) for _, value := range openStats { assert.IsType(t, float64(0), value) assert.GreaterOrEqual(t, value.(float64), float64(0)) } for _, value := range resultStats { assert.IsType(t, float64(0), value) assert.GreaterOrEqual(t, value.(float64), float64(0)) } for _, value := range avTimeStats { assert.IsType(t, float64(0), value) assert.GreaterOrEqual(t, value.(float64), float64(0)) } for _, value := range conversionStats { assert.IsType(t, float64(0), value) assert.GreaterOrEqual(t, value.(float64), float64(0)) assert.LessOrEqual(t, value.(float64), float64(100)) } } func TestGetGeneralStats_WithoutDateRange(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для общей статистики без диапазона", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{}) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotNil(t, result["Open"]) assert.NotNil(t, result["Result"]) assert.NotNil(t, result["AvTime"]) assert.NotNil(t, result["Conversion"]) } func TestGetGeneralStats_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/statistic/12345/general", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := getGeneralStatsRequest("invalid_token", "12345", map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetGeneralStats_InputValidation(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для валидации общей статистики", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] t.Run("InvalidDateFormat", func(t *testing.T) { resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": "not_a_timestamp", "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidQuizID", func(t *testing.T) { resp, err := getGeneralStatsRequest(validToken, "invalid-quiz-id", map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentQuizID", func(t *testing.T) { resp, err := getGeneralStatsRequest(validToken, "99999", map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) openStats := result["Open"].(map[string]interface{}) resultStats := result["Result"].(map[string]interface{}) avTimeStats := result["AvTime"].(map[string]interface{}) conversionStats := result["Conversion"].(map[string]interface{}) assert.Empty(t, openStats) assert.Empty(t, resultStats) assert.Empty(t, avTimeStats) assert.Empty(t, conversionStats) }) } func TestGetGeneralStats_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности общей статистики", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("ConcurrentRequests", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) if err == nil && resp != nil { resp.Body.Close() } }() } wg.Wait() }) } func TestGetGeneralStats_BoundaryCases(t *testing.T) { t.Run("VeryLargeDateRange", func(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для граничных случаев общей статистики", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": 0, "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) t.Run("NegativeTimestamps", func(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для отрицательных временных меток", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := getGeneralStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": -1000000, "To": -500000, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) } func getQuestionStatsRequest(token string, quizID string, body map[string]interface{}) (*http.Response, error) { payload, err := json.Marshal(body) if err != nil { return nil, err } req, err := http.NewRequest("POST", baseURL+"/statistic/"+quizID+"/questions", bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return http.DefaultClient.Do(req) } func TestGetQuestionStats_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для статистики вопросов", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result []map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result) if len(result) > 0 { questionStat := result[0] assert.NotNil(t, questionStat["Funnel"]) assert.NotNil(t, questionStat["FunnelData"]) assert.NotNil(t, questionStat["Results"]) assert.NotNil(t, questionStat["Questions"]) funnel := questionStat["Funnel"].([]interface{}) funnelData := questionStat["FunnelData"].([]interface{}) results := questionStat["Results"].(map[string]interface{}) questions := questionStat["Questions"].(map[string]interface{}) assert.LessOrEqual(t, len(funnel), 3) assert.LessOrEqual(t, len(funnelData), 4) for _, value := range funnel { assert.IsType(t, float64(0), value) assert.GreaterOrEqual(t, value.(float64), float64(0)) assert.LessOrEqual(t, value.(float64), float64(100)) } for _, value := range funnelData { assert.IsType(t, float64(0), value) assert.GreaterOrEqual(t, value.(float64), float64(0)) } for _, value := range results { assert.IsType(t, float64(0), value) assert.GreaterOrEqual(t, value.(float64), float64(0)) assert.LessOrEqual(t, value.(float64), float64(100)) } for _, questionData := range questions { questionMap := questionData.(map[string]interface{}) for _, percentage := range questionMap { assert.IsType(t, float64(0), percentage) assert.GreaterOrEqual(t, percentage.(float64), float64(0)) assert.LessOrEqual(t, percentage.(float64), float64(100)) } } } } func TestGetQuestionStats_WithoutDateRange(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для статистики вопросов без диапазона", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{}) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var result []map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result) } func TestGetQuestionStats_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) req, err := http.NewRequest("POST", baseURL+"/statistic/12345/questions", bytes.NewReader(payload)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("InvalidToken", func(t *testing.T) { resp, err := getQuestionStatsRequest("invalid_token", "12345", map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) } func TestGetQuestionStats_InputValidation(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для валидации статистики вопросов", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] t.Run("InvalidDateFormat", func(t *testing.T) { resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": "not_a_timestamp", "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidQuizID", func(t *testing.T) { resp, err := getQuestionStatsRequest(validToken, "invalid-quiz-id", map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("NonExistentQuizID", func(t *testing.T) { resp, err := getQuestionStatsRequest(validToken, "99999", map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result []map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) assert.NotEmpty(t, result) if len(result) > 0 { questionStat := result[0] funnel := questionStat["Funnel"].([]interface{}) funnelData := questionStat["FunnelData"].([]interface{}) results := questionStat["Results"].(map[string]interface{}) questions := questionStat["Questions"].(map[string]interface{}) assert.Empty(t, funnel) assert.Empty(t, funnelData) assert.Empty(t, results) assert.Empty(t, questions) } }) } func TestGetQuestionStats_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности статистики вопросов", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) duration := time.Since(start) assert.NoError(t, err) defer resp.Body.Close() assert.Less(t, duration.Milliseconds(), int64(500)) }) t.Run("ConcurrentRequests", func(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) if err == nil && resp != nil { resp.Body.Close() } }() } wg.Wait() }) } func TestGetQuestionStats_BoundaryCases(t *testing.T) { t.Run("VeryLargeDateRange", func(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для граничных случаев статистики вопросов", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": 0, "To": time.Now().Unix(), }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) t.Run("NegativeTimestamps", func(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для отрицательных временных меток вопросов", "status": "start", }) assert.NoError(t, err) defer createResp.Body.Close() var createResult map[string]interface{} err = json.NewDecoder(createResp.Body).Decode(&createResult) assert.NoError(t, err) quizID := createResult["id"] resp, err := getQuestionStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ "From": -1000000, "To": -500000, }) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) }) } // todo ПРОПУСК 35 36 тест кесов // todo ждет когда будет применятся //func getTelegramPoolRequest(token string) (*http.Response, error) { // req, err := http.NewRequest("GET", baseURL+"/telegram/pool", nil) // if err != nil { // return nil, err // } // req.Header.Set("Authorization", "Bearer "+token) // req.Header.Set("Content-Type", "application/json") // return http.DefaultClient.Do(req) //} // //func TestGetTelegramPool_Success(t *testing.T) { // resp, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, http.StatusOK, resp.StatusCode) // assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) // // var accounts []map[string]interface{} // err = json.NewDecoder(resp.Body).Decode(&accounts) // assert.NoError(t, err) // // for _, account := range accounts { // assert.NotEmpty(t, account["ID"]) // assert.NotEmpty(t, account["ApiID"]) // assert.NotEmpty(t, account["ApiHash"]) // assert.NotEmpty(t, account["PhoneNumber"]) // assert.NotEmpty(t, account["Status"]) // assert.NotEmpty(t, account["CreatedAt"]) // // assert.IsType(t, float64(0), account["ID"]) // assert.IsType(t, float64(0), account["ApiID"]) // assert.IsType(t, "", account["ApiHash"]) // assert.IsType(t, "", account["PhoneNumber"]) // assert.IsType(t, "", account["Status"]) // assert.IsType(t, "", account["CreatedAt"]) // assert.IsType(t, false, account["Deleted"]) // // assert.Equal(t, false, account["Deleted"]) // // status := account["Status"].(string) // assert.Contains(t, []string{"active", "inactive", "ban"}, status) // // createdAt := account["CreatedAt"].(string) // _, err := time.Parse(time.RFC3339, createdAt) // assert.NoError(t, err) // } //} // //func TestGetTelegramPool_Auth(t *testing.T) { // t.Run("NoToken", func(t *testing.T) { // req, err := http.NewRequest("GET", baseURL+"/telegram/pool", nil) // assert.NoError(t, err) // req.Header.Set("Content-Type", "application/json") // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) // // t.Run("InvalidToken", func(t *testing.T) { // resp, err := getTelegramPoolRequest("invalid_token") // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) // // t.Run("ExpiredToken", func(t *testing.T) { // resp, err := getTelegramPoolRequest(expiredToken) // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) //} // //func TestGetTelegramPool_Security(t *testing.T) { // t.Run("SQLInjectionAttempt", func(t *testing.T) { // resp, err := getTelegramPoolRequest("' OR 1=1 --") // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) // // t.Run("XSSAttempt", func(t *testing.T) { // resp, err := getTelegramPoolRequest("") // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) //} // //func TestGetTelegramPool_Performance(t *testing.T) { // t.Run("ResponseTime", func(t *testing.T) { // start := time.Now() // resp, err := getTelegramPoolRequest(validToken) // duration := time.Since(start) // // assert.NoError(t, err) // defer resp.Body.Close() // assert.Less(t, duration.Milliseconds(), int64(1000)) // Менее 1 секунды // }) // // t.Run("ConcurrentRequests", func(t *testing.T) { // var wg sync.WaitGroup // for i := 0; i < 10; i++ { // wg.Add(1) // go func() { // defer wg.Done() // resp, err := getTelegramPoolRequest(validToken) // if err == nil && resp != nil { // resp.Body.Close() // } // }() // } // wg.Wait() // }) //} // //func TestGetTelegramPool_BoundaryCases(t *testing.T) { // t.Run("EmptyPool", func(t *testing.T) { // resp, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // // var accounts []map[string]interface{} // err = json.NewDecoder(resp.Body).Decode(&accounts) // assert.NoError(t, err) // assert.NotNil(t, accounts) // }) // // t.Run("LargePool", func(t *testing.T) { // resp, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // // var accounts []map[string]interface{} // err = json.NewDecoder(resp.Body).Decode(&accounts) // assert.NoError(t, err) // // Проверяем, что система может обработать большое количество аккаунтов // assert.NotNil(t, accounts) // }) //} // //func TestGetTelegramPool_ErrorHandling(t *testing.T) { // t.Run("MalformedRequest", func(t *testing.T) { // // Тестируем с некорректным запросом // req, err := http.NewRequest("GET", baseURL+"/telegram/pool", bytes.NewReader([]byte("invalid_body"))) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validToken) // req.Header.Set("Content-Type", "application/json") // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // assert.NotEqual(t, http.StatusOK, resp.StatusCode) // }) // // t.Run("ServerError", func(t *testing.T) { // // Тестируем с некорректным токеном, который может вызвать серверную ошибку // resp, err := getTelegramPoolRequest("invalid-token-with-special-chars@#$%") // assert.NoError(t, err) // defer resp.Body.Close() // assert.NotEqual(t, http.StatusOK, resp.StatusCode) // }) //} // //func TestGetTelegramPool_AuditMetricsConsistency(t *testing.T) { // t.Run("AuditLog", func(t *testing.T) { // resp, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // // Здесь можно добавить проверку на появление записи аудита, если есть доступ к логам/БД // }) // // t.Run("Consistency", func(t *testing.T) { // // Проверяем консистентность данных при повторных запросах // // Первый запрос // resp1, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer resp1.Body.Close() // assert.Equal(t, http.StatusOK, resp1.StatusCode) // // // Второй запрос с теми же параметрами // resp2, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer resp2.Body.Close() // assert.Equal(t, http.StatusOK, resp2.StatusCode) // // // Проверяем, что результаты консистентны // var accounts1, accounts2 []map[string]interface{} // err = json.NewDecoder(resp1.Body).Decode(&accounts1) // assert.NoError(t, err) // err = json.NewDecoder(resp2.Body).Decode(&accounts2) // assert.NoError(t, err) // assert.Equal(t, len(accounts1), len(accounts2)) // }) //} // //func TestGetTelegramPool_IdempotencyAndTransactions(t *testing.T) { // t.Run("Idempotency", func(t *testing.T) { // // Первый запрос // resp1, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer resp1.Body.Close() // assert.Equal(t, http.StatusOK, resp1.StatusCode) // // // Повторный запрос с теми же параметрами // resp2, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer resp2.Body.Close() // assert.Equal(t, http.StatusOK, resp2.StatusCode) // assert.Equal(t, resp1.StatusCode, resp2.StatusCode) // }) // // t.Run("TransactionRollback", func(t *testing.T) { // // Пытаемся получить пул с некорректным токеном // resp, err := getTelegramPoolRequest("invalid-token") // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // // // Проверяем, что система корректно обрабатывает ошибки авторизации // assert.NotEqual(t, http.StatusOK, resp.StatusCode) // }) //} // //func createTelegramRequest(token string, body map[string]interface{}) (*http.Response, error) { // payload, err := json.Marshal(body) // if err != nil { // return nil, err // } // req, err := http.NewRequest("POST", baseURL+"/telegram/create", bytes.NewReader(payload)) // if err != nil { // return nil, err // } // req.Header.Set("Authorization", "Bearer "+token) // req.Header.Set("Content-Type", "application/json") // return http.DefaultClient.Do(req) //} // //func TestCreateTelegram_Success(t *testing.T) { // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678901", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, http.StatusOK, resp.StatusCode) // assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) // // var result map[string]interface{} // err = json.NewDecoder(resp.Body).Decode(&result) // assert.NoError(t, err) // // // Проверяем, что возвращается signature // assert.NotEmpty(t, result["signature"]) // assert.IsType(t, "", result["signature"]) //} // //func TestCreateTelegram_Auth(t *testing.T) { // t.Run("NoToken", func(t *testing.T) { // payload, err := json.Marshal(map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678901", // "password": "secure_password", // }) // assert.NoError(t, err) // // req, err := http.NewRequest("POST", baseURL+"/telegram/create", bytes.NewReader(payload)) // assert.NoError(t, err) // req.Header.Set("Content-Type", "application/json") // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) // // t.Run("InvalidToken", func(t *testing.T) { // resp, err := createTelegramRequest("invalid_token", map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678901", // "password": "secure_password", // }) // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) //} // //func TestCreateTelegram_InputValidation(t *testing.T) { // t.Run("MissingApiID", func(t *testing.T) { // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678901", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("MissingApiHash", func(t *testing.T) { // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "phone_number": "+12345678901", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("MissingPhoneNumber", func(t *testing.T) { // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("InvalidApiID", func(t *testing.T) { // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": "not_a_number", // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678901", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("InvalidPhoneNumber", func(t *testing.T) { // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "invalid_phone", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) //} // //func TestCreateTelegram_Performance(t *testing.T) { // t.Run("ResponseTime", func(t *testing.T) { // start := time.Now() // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678901", // "password": "secure_password", // }) // duration := time.Since(start) // // assert.NoError(t, err) // defer resp.Body.Close() // assert.Less(t, duration.Milliseconds(), int64(500)) // }) //} // //func TestCreateTelegram_ErrorHandling(t *testing.T) { // t.Run("MalformedJSON", func(t *testing.T) { // req, err := http.NewRequest("POST", baseURL+"/telegram/create", bytes.NewReader([]byte("{invalid_json}"))) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validToken) // req.Header.Set("Content-Type", "application/json") // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("ServerError", func(t *testing.T) { // // Тестируем с некорректными данными, которые могут вызвать серверную ошибку // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": "invalid_api_id", // "api_hash": "invalid_hash", // "phone_number": "invalid_phone", // "password": "invalid_password", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.NotEqual(t, http.StatusOK, resp.StatusCode) // }) //} // //func TestCreateTelegram_BoundaryCases(t *testing.T) { // t.Run("VeryLongApiHash", func(t *testing.T) { // longHash := make([]byte, 1025) // for i := range longHash { // longHash[i] = 'a' // } // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": string(longHash), // "phone_number": "+12345678901", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("VeryLongPhoneNumber", func(t *testing.T) { // longPhone := make([]byte, 1025) // for i := range longPhone { // longPhone[i] = '1' // } // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": string(longPhone), // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("NegativeApiID", func(t *testing.T) { // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": -123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678901", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) //} // //func TestCreateTelegram_SpecialCases(t *testing.T) { // t.Run("DuplicatePhoneNumber", func(t *testing.T) { // // Создаем первый аккаунт // resp1, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678901", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp1.Body.Close() // assert.Equal(t, http.StatusOK, resp1.StatusCode) // // // Пытаемся создать второй аккаунт с тем же номером телефона // resp2, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678901", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp2.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp2.StatusCode) // }) // // t.Run("SpecialCharactersInPassword", func(t *testing.T) { // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678901", // "password": "pass@#$%^&*()word", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // }) //} // //func TestCreateTelegram_AuditMetricsConsistency(t *testing.T) { // t.Run("AuditLog", func(t *testing.T) { // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678901", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // // Здесь можно добавить проверку на появление записи аудита, если есть доступ к логам/БД // }) // // t.Run("Consistency", func(t *testing.T) { // // Проверяем консистентность данных при создании аккаунтов // resp1, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678902", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp1.Body.Close() // assert.Equal(t, http.StatusOK, resp1.StatusCode) // // var result1 map[string]interface{} // err = json.NewDecoder(resp1.Body).Decode(&result1) // assert.NoError(t, err) // assert.NotEmpty(t, result1["signature"]) // // // Проверяем, что signature уникален // resp2, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678903", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp2.Body.Close() // assert.Equal(t, http.StatusOK, resp2.StatusCode) // // var result2 map[string]interface{} // err = json.NewDecoder(resp2.Body).Decode(&result2) // assert.NoError(t, err) // assert.NotEmpty(t, result2["signature"]) // assert.NotEqual(t, result1["signature"], result2["signature"]) // }) //} // //func TestCreateTelegram_IdempotencyAndTransactions(t *testing.T) { // t.Run("Idempotency", func(t *testing.T) { // // Создаем аккаунт // resp1, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678904", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp1.Body.Close() // assert.Equal(t, http.StatusOK, resp1.StatusCode) // // // Повторно создаем аккаунт с теми же данными // resp2, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": 123456, // "api_hash": "abcdef1234567890abcdef1234567890", // "phone_number": "+12345678904", // "password": "secure_password", // }) // assert.NoError(t, err) // defer resp2.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp2.StatusCode) // Дубликат не должен создаваться // }) // // t.Run("TransactionRollback", func(t *testing.T) { // // Пытаемся создать аккаунт с некорректными данными // resp, err := createTelegramRequest(validToken, map[string]interface{}{ // "api_id": "invalid", // "api_hash": "invalid", // "phone_number": "invalid", // "password": "invalid", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // // // Проверяем, что транзакция откатилась и аккаунт не создался // poolResp, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer poolResp.Body.Close() // assert.Equal(t, http.StatusOK, poolResp.StatusCode) // }) //} // //func deleteTelegramRequest(token string, id string) (*http.Response, error) { // req, err := http.NewRequest("DELETE", baseURL+"/telegram/"+id, nil) // if err != nil { // return nil, err // } // req.Header.Set("Authorization", "Bearer "+token) // req.Header.Set("Content-Type", "application/json") // return http.DefaultClient.Do(req) //} // //func TestDeleteTelegram_Success(t *testing.T) { // // Сначала получаем список аккаунтов, чтобы найти существующий ID // poolResp, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer poolResp.Body.Close() // // var accounts []map[string]interface{} // err = json.NewDecoder(poolResp.Body).Decode(&accounts) // assert.NoError(t, err) // // // Если есть аккаунты, удаляем первый // if len(accounts) > 0 { // firstAccount := accounts[0] // accountID := fmt.Sprintf("%v", firstAccount["ID"]) // // // Удаляем аккаунт // resp, err := deleteTelegramRequest(validToken, accountID) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, http.StatusOK, resp.StatusCode) // } //} // //func TestDeleteTelegram_Auth(t *testing.T) { // t.Run("NoToken", func(t *testing.T) { // req, err := http.NewRequest("DELETE", baseURL+"/telegram/123", nil) // assert.NoError(t, err) // req.Header.Set("Content-Type", "application/json") // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) // // t.Run("InvalidToken", func(t *testing.T) { // resp, err := deleteTelegramRequest("invalid_token", "123") // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) // // t.Run("ExpiredToken", func(t *testing.T) { // resp, err := deleteTelegramRequest(expiredToken, "123") // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) //} // //func TestDeleteTelegram_InputValidation(t *testing.T) { // t.Run("NonExistentID", func(t *testing.T) { // resp, err := deleteTelegramRequest(validToken, "99999") // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("InvalidID", func(t *testing.T) { // resp, err := deleteTelegramRequest(validToken, "!@#") // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("EmptyID", func(t *testing.T) { // resp, err := deleteTelegramRequest(validToken, "") // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) //} // //func TestDeleteTelegram_Security(t *testing.T) { // t.Run("SQLInjectionAttempt", func(t *testing.T) { // resp, err := deleteTelegramRequest(validToken, "' OR 1=1 --") // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("XSSAttempt", func(t *testing.T) { // resp, err := deleteTelegramRequest(validToken, "") // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) //} // //func TestDeleteTelegram_Performance(t *testing.T) { // t.Run("ResponseTime", func(t *testing.T) { // start := time.Now() // resp, err := deleteTelegramRequest(validToken, "123") // duration := time.Since(start) // // assert.NoError(t, err) // defer resp.Body.Close() // assert.Less(t, duration.Milliseconds(), int64(500)) // }) //} // //func TestDeleteTelegram_BoundaryCases(t *testing.T) { // t.Run("VeryLongID", func(t *testing.T) { // longID := make([]byte, 1025) // for i := range longID { // longID[i] = '1' // } // resp, err := deleteTelegramRequest(validToken, string(longID)) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("NegativeID", func(t *testing.T) { // resp, err := deleteTelegramRequest(validToken, "-123") // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("ZeroID", func(t *testing.T) { // resp, err := deleteTelegramRequest(validToken, "0") // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) //} // //func TestDeleteTelegram_ErrorHandling(t *testing.T) { // t.Run("MalformedRequest", func(t *testing.T) { // // Тестируем с некорректным запросом // req, err := http.NewRequest("DELETE", baseURL+"/telegram/123", bytes.NewReader([]byte("invalid_body"))) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validToken) // req.Header.Set("Content-Type", "application/json") // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // assert.NotEqual(t, http.StatusOK, resp.StatusCode) // }) // // t.Run("ServerError", func(t *testing.T) { // // Тестируем с некорректным ID, который может вызвать серверную ошибку // resp, err := deleteTelegramRequest(validToken, "invalid-id-with-special-chars@#$%") // assert.NoError(t, err) // defer resp.Body.Close() // assert.NotEqual(t, http.StatusOK, resp.StatusCode) // }) //} // //func TestDeleteTelegram_AuditMetricsConsistency(t *testing.T) { // t.Run("AuditLog", func(t *testing.T) { // // Сначала получаем список аккаунтов, чтобы найти существующий ID // poolResp, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer poolResp.Body.Close() // // var accounts []map[string]interface{} // err = json.NewDecoder(poolResp.Body).Decode(&accounts) // assert.NoError(t, err) // // // Если есть аккаунты, удаляем первый для тестирования аудита // if len(accounts) > 0 { // firstAccount := accounts[0] // accountID := fmt.Sprintf("%v", firstAccount["ID"]) // // resp, err := deleteTelegramRequest(validToken, accountID) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // // Здесь можно добавить проверку на появление записи аудита, если есть доступ к логам/БД // } // }) // // t.Run("Consistency", func(t *testing.T) { // // Проверяем консистентность данных при удалении // // Сначала получаем список аккаунтов // poolResp1, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer poolResp1.Body.Close() // // var accounts1 []map[string]interface{} // err = json.NewDecoder(poolResp1.Body).Decode(&accounts1) // assert.NoError(t, err) // // // Если есть аккаунты, удаляем один // if len(accounts1) > 0 { // firstAccount := accounts1[0] // accountID := fmt.Sprintf("%v", firstAccount["ID"]) // // resp, err := deleteTelegramRequest(validToken, accountID) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // // // Проверяем, что аккаунт действительно удален // poolResp2, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer poolResp2.Body.Close() // // var accounts2 []map[string]interface{} // err = json.NewDecoder(poolResp2.Body).Decode(&accounts2) // assert.NoError(t, err) // // // Проверяем, что количество аккаунтов уменьшилось // assert.LessOrEqual(t, len(accounts2), len(accounts1)) // } // }) //} // //func TestDeleteTelegram_IdempotencyAndTransactions(t *testing.T) { // t.Run("Idempotency", func(t *testing.T) { // // Сначала получаем список аккаунтов, чтобы найти существующий ID // poolResp, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer poolResp.Body.Close() // // var accounts []map[string]interface{} // err = json.NewDecoder(poolResp.Body).Decode(&accounts) // assert.NoError(t, err) // // // Если есть аккаунты, удаляем первый // if len(accounts) > 0 { // firstAccount := accounts[0] // accountID := fmt.Sprintf("%v", firstAccount["ID"]) // // // Первое удаление // resp1, err := deleteTelegramRequest(validToken, accountID) // assert.NoError(t, err) // defer resp1.Body.Close() // assert.Equal(t, http.StatusOK, resp1.StatusCode) // // // Повторное удаление того же аккаунта // resp2, err := deleteTelegramRequest(validToken, accountID) // assert.NoError(t, err) // defer resp2.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp2.StatusCode) // Уже удален // } // }) // // t.Run("TransactionRollback", func(t *testing.T) { // // Пытаемся удалить несуществующий аккаунт // resp, err := deleteTelegramRequest(validToken, "999999") // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // // // Проверяем, что система корректно обрабатывает ошибки // assert.NotEqual(t, http.StatusOK, resp.StatusCode) // }) //} // //func setTelegramCodeRequest(token string, body map[string]interface{}) (*http.Response, error) { // payload, err := json.Marshal(body) // if err != nil { // return nil, err // } // req, err := http.NewRequest("POST", baseURL+"/telegram/setCode", bytes.NewReader(payload)) // if err != nil { // return nil, err // } // req.Header.Set("Authorization", "Bearer "+token) // req.Header.Set("Content-Type", "application/json") // return http.DefaultClient.Do(req) //} // //func TestSetTelegramCode_Success(t *testing.T) { // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "123456", // "signature": "abc123signature", // }) // assert.NoError(t, err) // defer resp.Body.Close() // // assert.Equal(t, http.StatusOK, resp.StatusCode) // assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) // // var result map[string]interface{} // err = json.NewDecoder(resp.Body).Decode(&result) // assert.NoError(t, err) // // // Проверяем, что возвращается ID // assert.NotEmpty(t, result["id"]) // assert.IsType(t, float64(0), result["id"]) //} // //func TestSetTelegramCode_Auth(t *testing.T) { // t.Run("NoToken", func(t *testing.T) { // payload, err := json.Marshal(map[string]interface{}{ // "code": "123456", // "signature": "abc123signature", // }) // assert.NoError(t, err) // // req, err := http.NewRequest("POST", baseURL+"/telegram/setCode", bytes.NewReader(payload)) // assert.NoError(t, err) // req.Header.Set("Content-Type", "application/json") // // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) // // t.Run("InvalidToken", func(t *testing.T) { // resp, err := setTelegramCodeRequest("invalid_token", map[string]interface{}{ // "code": "123456", // "signature": "abc123signature", // }) // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) // // t.Run("ExpiredToken", func(t *testing.T) { // resp, err := setTelegramCodeRequest(expiredToken, map[string]interface{}{ // "code": "123456", // "signature": "abc123signature", // }) // assert.NoError(t, err) // assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // }) //} // //func TestSetTelegramCode_InputValidation(t *testing.T) { // t.Run("MissingCode", func(t *testing.T) { // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "signature": "abc123signature", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("MissingSignature", func(t *testing.T) { // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "123456", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("InvalidCodeType", func(t *testing.T) { // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": 123456, // "signature": "abc123signature", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("InvalidSignatureType", func(t *testing.T) { // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "123456", // "signature": 12345, // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) //} // //func TestSetTelegramCode_Security(t *testing.T) { // t.Run("XSSInCode", func(t *testing.T) { // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "", // "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": "", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) //} // //func TestSetTelegramCode_Performance(t *testing.T) { // t.Run("ResponseTime", func(t *testing.T) { // start := time.Now() // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "123456", // "signature": "abc123signature", // }) // duration := time.Since(start) // // assert.NoError(t, err) // defer resp.Body.Close() // assert.Less(t, duration.Milliseconds(), int64(500)) // }) // // t.Run("ConcurrentRequests", func(t *testing.T) { // var wg sync.WaitGroup // for i := 0; i < 10; i++ { // wg.Add(1) // go func(index int) { // defer wg.Done() // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": fmt.Sprintf("12345%d", index), // "signature": fmt.Sprintf("signature%d", index), // }) // if err == nil && resp != nil { // resp.Body.Close() // } // }(i) // } // wg.Wait() // }) //} // //func TestSetTelegramCode_BoundaryCases(t *testing.T) { // t.Run("VeryLongCode", func(t *testing.T) { // longCode := make([]byte, 1025) // for i := range longCode { // longCode[i] = '1' // } // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": string(longCode), // "signature": "abc123signature", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("VeryLongSignature", func(t *testing.T) { // longSignature := make([]byte, 1025) // for i := range longSignature { // longSignature[i] = 'a' // } // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "123456", // "signature": string(longSignature), // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("EmptyCode", func(t *testing.T) { // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "", // "signature": "abc123signature", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("EmptySignature", func(t *testing.T) { // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "123456", // "signature": "", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) //} // //func TestSetTelegramCode_ErrorHandling(t *testing.T) { // t.Run("MalformedJSON", func(t *testing.T) { // req, err := http.NewRequest("POST", baseURL+"/telegram/setCode", bytes.NewReader([]byte("{invalid_json}"))) // assert.NoError(t, err) // req.Header.Set("Authorization", "Bearer "+validToken) // req.Header.Set("Content-Type", "application/json") // resp, err := http.DefaultClient.Do(req) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // }) // // t.Run("ServerError", func(t *testing.T) { // // Тестируем с некорректными данными, которые могут вызвать серверную ошибку // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "invalid_code", // "signature": "invalid_signature", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.NotEqual(t, http.StatusOK, resp.StatusCode) // }) //} // //func TestSetTelegramCode_AuditMetricsConsistency(t *testing.T) { // t.Run("AuditLog", func(t *testing.T) { // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "123456", // "signature": "abc123signature", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusOK, resp.StatusCode) // // Здесь можно добавить проверку на появление записи аудита, если есть доступ к логам/БД // }) // // t.Run("Consistency", func(t *testing.T) { // // Проверяем консистентность данных при установке кода // resp1, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "123456", // "signature": "abc123signature", // }) // assert.NoError(t, err) // defer resp1.Body.Close() // assert.Equal(t, http.StatusOK, resp1.StatusCode) // // var result1 map[string]interface{} // err = json.NewDecoder(resp1.Body).Decode(&result1) // assert.NoError(t, err) // assert.NotEmpty(t, result1["id"]) // // // Проверяем, что ID уникален при разных кодах // resp2, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "654321", // "signature": "abc123signature", // }) // assert.NoError(t, err) // defer resp2.Body.Close() // assert.Equal(t, http.StatusOK, resp2.StatusCode) // // var result2 map[string]interface{} // err = json.NewDecoder(resp2.Body).Decode(&result2) // assert.NoError(t, err) // assert.NotEmpty(t, result2["id"]) // assert.NotEqual(t, result1["id"], result2["id"]) // }) //} // //func TestSetTelegramCode_IdempotencyAndTransactions(t *testing.T) { // t.Run("Idempotency", func(t *testing.T) { // // Первая установка кода // resp1, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "123456", // "signature": "abc123signature", // }) // assert.NoError(t, err) // defer resp1.Body.Close() // assert.Equal(t, http.StatusOK, resp1.StatusCode) // // // Повторная установка того же кода // resp2, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "123456", // "signature": "abc123signature", // }) // assert.NoError(t, err) // defer resp2.Body.Close() // assert.Equal(t, http.StatusOK, resp2.StatusCode) // assert.Equal(t, resp1.StatusCode, resp2.StatusCode) // }) // // t.Run("TransactionRollback", func(t *testing.T) { // // Пытаемся установить код с некорректными данными // resp, err := setTelegramCodeRequest(validToken, map[string]interface{}{ // "code": "invalid", // "signature": "invalid", // }) // assert.NoError(t, err) // defer resp.Body.Close() // assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // // // Проверяем, что транзакция откатилась и код не установился // poolResp, err := getTelegramPoolRequest(validToken) // assert.NoError(t, err) // defer poolResp.Body.Close() // assert.Equal(t, http.StatusOK, poolResp.StatusCode) // }) //}