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