diff --git a/tests/main_test.go b/tests/main_test.go index 1d7af2e..5373139 100644 --- a/tests/main_test.go +++ b/tests/main_test.go @@ -7,6 +7,7 @@ import ( "gitea.pena/SQuiz/common/model" "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" @@ -17,6 +18,8 @@ import ( ) // todo нужно перепроверить все тесты связанные с результатами и удалениями раскоментить и переписать +// todo также нужно взять и сделать "константы" для тестирования результатов с ответами +// todo нужно определить из кликхауса на чем будем тестировать статистику var PublicKey = `-----BEGIN PUBLIC KEY-----MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAE=-----END PUBLIC KEY-----` @@ -50,6 +53,7 @@ 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) @@ -71,6 +75,7 @@ func TestGetAccount_Success(t *testing.T) { 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") @@ -97,6 +102,7 @@ func TestGetAccount_Auth(t *testing.T) { }) } +// отсмотрено func TestGetAccount_NotFound(t *testing.T) { t.Run("DeletedAccount", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/get", nil) @@ -108,6 +114,7 @@ func TestGetAccount_NotFound(t *testing.T) { }) } +// отсмотрено func TestGetAccount_Privileges(t *testing.T) { t.Run("NoPrivileges", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/get", nil) @@ -142,6 +149,7 @@ func TestGetAccount_Privileges(t *testing.T) { }) } +// отсмотрено func TestAccount_Performance(t *testing.T) { t.Run("AccountResponseTime", func(t *testing.T) { start := time.Now() @@ -175,36 +183,37 @@ func TestAccount_Performance(t *testing.T) { }) } -// 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) -// }) -//} +// 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) @@ -224,20 +233,21 @@ func TestGetAccount_BoundaryCases(t *testing.T) { } }) // todo - //t.Run("UnicodeCharacters", func(t *testing.T) { - // req, err := http.NewRequest("GET", baseURL+"/account/get", nil) - // assert.NoError(t, err) - // req.Header.Set("Authorization", "Bearer "+validToken) - // - // resp, err := http.DefaultClient.Do(req) - // assert.NoError(t, err) - // defer resp.Body.Close() - // - // assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) - // assert.Equal(t, "utf-8", resp.Header.Get("Content-Type")) - //}) + t.Run("UnicodeCharacters", func(t *testing.T) { + req, err := http.NewRequest("GET", baseURL+"/account/get", nil) + assert.NoError(t, err) + req.Header.Set("Authorization", "Bearer "+validToken) + + resp, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) + assert.Equal(t, "utf-8", resp.Header.Get("Content-Type")) + }) } +// отсмотрено func TestGetAccount_SpecialCases(t *testing.T) { t.Run("AccountWithoutPrivileges", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/account/get", nil) @@ -432,6 +442,7 @@ func createAccountRequest(t *testing.T, token string, payload map[string]interfa // assert.NotEmpty(t, result["accountId"]) //} +// отсмотрено func TestDeleteAccount_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { req, err := http.NewRequest("DELETE", baseURL+"/account/delete", nil) @@ -7253,20 +7264,59 @@ func getResultsRequest(token string, quizId string, body map[string]interface{}) return http.DefaultClient.Do(req) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestGetResults_Success(t *testing.T) { - createResp, err := createQuizRequest(validToken, map[string]interface{}{ - "name": "Квиз для результатов", + quizResp, err := createQuizRequest(validToken, map[string]interface{}{ + "name": "Квиз для тестирования результатов", "status": "start", }) assert.NoError(t, err) - defer createResp.Body.Close() - var createResult map[string]interface{} - err = json.NewDecoder(createResp.Body).Decode(&createResult) - assert.NoError(t, err) - quizID := createResult["id"] + defer quizResp.Body.Close() + var quizResult model.Quiz + err = json.NewDecoder(quizResp.Body).Decode(&quizResult) + assert.NoError(t, err) + quizID := quizResult.Id + + question1Resp, err := createQuestionRequest(validToken, map[string]interface{}{ + "quiz_id": quizID, + "title": "Какой основной компонент воздуха?", + "type": "variant", + "description": "Выберите один правильный ответ.", + "required": true, + "page": 1, + "content": `{"variants": ["Кислород", "Азот", "Углекислый газ", "Водород"]}`, + }) + assert.NoError(t, err) + defer question1Resp.Body.Close() + + var question1Result model.Question + err = json.NewDecoder(question1Resp.Body).Decode(&question1Result) + assert.NoError(t, err) + + question2Resp, err := createQuestionRequest(validToken, map[string]interface{}{ + "quiz_id": quizID, + "title": "Столица России?", + "type": "text", + "description": "Введите название города.", + "required": true, + "page": 1, + "content": `{"placeholder": "Введите ответ"}`, + }) + assert.NoError(t, err) + defer question2Resp.Body.Close() + + var question2Result model.Question + err = json.NewDecoder(question2Resp.Body).Decode(&question2Result) + assert.NoError(t, err) + + // Создаем тестовые ответы (симулируем прохождение квиза) + // В реальном приложении ответы создаются через отдельный API + // Здесь мы будем тестировать получение результатов без ответов + + // Тестируем получение результатов resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ - "Page": 1, + "Page": 0, "Limit": 10, }) assert.NoError(t, err) @@ -7275,13 +7325,28 @@ func TestGetResults_Success(t *testing.T) { assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) - var result map[string]interface{} + var result result2.ReqExportResponse err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) - assert.NotNil(t, result["total_count"]) - assert.NotNil(t, result["results"]) + assert.NotNil(t, result.TotalCount) + assert.NotNil(t, result.Results) + + // Проверяем, что результаты возвращаются в правильном формате + assert.IsType(t, uint64(0), result.TotalCount) + assert.IsType(t, []model.AnswerExport{}, result.Results) + + // Если есть результаты, проверяем их структуру + if len(result.Results) > 0 { + for _, answer := range result.Results { + assert.NotEmpty(t, answer.Id) + assert.NotEmpty(t, answer.CreatedAt) + assert.IsType(t, bool(false), answer.New) + assert.IsType(t, int32(0), answer.Version) + } + } } +// отсмотрено func TestGetResults_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ @@ -7309,6 +7374,7 @@ func TestGetResults_Auth(t *testing.T) { }) } +// отсмотрено func TestGetResults_InputValidation(t *testing.T) { t.Run("InvalidQuizID", func(t *testing.T) { resp, err := getResultsRequest(validToken, "invalid-quiz-id", map[string]interface{}{ @@ -7337,20 +7403,57 @@ func TestGetResults_InputValidation(t *testing.T) { }) } -// todo +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestGetResults_Pagination(t *testing.T) { - createResp, err := createQuizRequest(validToken, map[string]interface{}{ - "name": "Квиз для пагинации результатов", + // Создаем квиз для тестирования пагинации + quizResp, err := createQuizRequest(validToken, map[string]interface{}{ + "name": "Квиз для тестирования пагинации результатов", "status": "start", }) assert.NoError(t, err) - defer createResp.Body.Close() - var createResult map[string]interface{} - err = json.NewDecoder(createResp.Body).Decode(&createResult) + defer quizResp.Body.Close() + + var quizResult model.Quiz + err = json.NewDecoder(quizResp.Body).Decode(&quizResult) + assert.NoError(t, err) + quizID := quizResult.Id + + // Создаем несколько вопросов для квиза + questionResp, err := createQuestionRequest(validToken, map[string]interface{}{ + "quiz_id": quizID, + "title": "Тестовый вопрос для пагинации", + "type": "text", + "description": "Введите ответ.", + "required": true, + "page": 1, + "content": `{"placeholder": "Введите ответ"}`, + }) + assert.NoError(t, err) + defer questionResp.Body.Close() + + var questionResult model.Question + err = json.NewDecoder(questionResp.Body).Decode(&questionResult) assert.NoError(t, err) - quizID := createResult["id"] t.Run("FirstPage", func(t *testing.T) { + resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ + "Page": 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", quizID), map[string]interface{}{ "Page": 1, "Limit": 5, @@ -7359,21 +7462,13 @@ func TestGetResults_Pagination(t *testing.T) { defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) - var result map[string]interface{} + var result result2.ReqExportResponse err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) - //results := result["results"].([]interface{}) - //assert.LessOrEqual(t, len(results), 5) - }) - t.Run("SecondPage", func(t *testing.T) { - resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ - "Page": 2, - "Limit": 5, - }) - assert.NoError(t, err) - defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode) + // Проверяем структуру ответа + assert.IsType(t, uint64(0), result.TotalCount) + assert.IsType(t, []model.AnswerExport{}, result.Results) }) t.Run("EmptyPage", func(t *testing.T) { @@ -7385,97 +7480,317 @@ func TestGetResults_Pagination(t *testing.T) { defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) - var result map[string]interface{} + var result result2.ReqExportResponse err = json.NewDecoder(resp.Body).Decode(&result) assert.NoError(t, err) - //results := result["results"].([]interface{}) - //assert.Empty(t, results) + + // Проверяем, что на несуществующей странице нет результатов + assert.Empty(t, result.Results) + assert.Equal(t, uint64(0), result.TotalCount) + }) + + t.Run("ZeroLimit", func(t *testing.T) { + resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), 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) + + // Проверяем, что при лимите 0 возвращается пустой результат + assert.Empty(t, result.Results) }) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestGetResults_Filtering(t *testing.T) { - createResp, err := createQuizRequest(validToken, map[string]interface{}{ - "name": "Квиз для фильтрации результатов", + // Создаем квиз для тестирования фильтрации + quizResp, err := createQuizRequest(validToken, map[string]interface{}{ + "name": "Квиз для тестирования фильтрации результатов", "status": "start", }) assert.NoError(t, err) - defer createResp.Body.Close() - var createResult map[string]interface{} - err = json.NewDecoder(createResp.Body).Decode(&createResult) + defer quizResp.Body.Close() + + var quizResult model.Quiz + err = json.NewDecoder(quizResp.Body).Decode(&quizResult) + assert.NoError(t, err) + quizID := quizResult.Id + + // Создаем вопрос для квиза + questionResp, err := createQuestionRequest(validToken, map[string]interface{}{ + "quiz_id": quizID, + "title": "Тестовый вопрос для фильтрации", + "type": "text", + "description": "Введите ответ.", + "required": true, + "page": 1, + "content": `{"placeholder": "Введите ответ"}`, + }) + assert.NoError(t, err) + defer questionResp.Body.Close() + + var questionResult model.Question + err = json.NewDecoder(questionResp.Body).Decode(&questionResult) assert.NoError(t, err) - quizID := createResult["id"] 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", quizID), map[string]interface{}{ - "From": "2023-01-01", - "To": "2023-12-31", - "Page": 1, + "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", quizID), map[string]interface{}{ "New": true, - "Page": 1, + "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", quizID), 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) { + // Тестируем некорректный диапазон дат (From > To) + 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", quizID), 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) }) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestGetResults_Performance(t *testing.T) { - createResp, err := createQuizRequest(validToken, map[string]interface{}{ - "name": "Квиз для теста производительности результатов", + // Создаем квиз для тестирования производительности + quizResp, err := createQuizRequest(validToken, map[string]interface{}{ + "name": "Квиз для тестирования производительности результатов", "status": "start", }) assert.NoError(t, err) - defer createResp.Body.Close() - var createResult map[string]interface{} - err = json.NewDecoder(createResp.Body).Decode(&createResult) + defer quizResp.Body.Close() + + var quizResult model.Quiz + err = json.NewDecoder(quizResp.Body).Decode(&quizResult) assert.NoError(t, err) - quizID := createResult["id"] + quizID := quizResult.Id + + // Создаем несколько вопросов для квиза + for i := 1; i <= 5; i++ { + questionResp, err := createQuestionRequest(validToken, map[string]interface{}{ + "quiz_id": quizID, + "title": fmt.Sprintf("Вопрос %d для производительности", i), + "type": "text", + "description": fmt.Sprintf("Описание вопроса %d", i), + "required": true, + "page": i, + "content": `{"placeholder": "Введите ответ"}`, + }) + assert.NoError(t, err) + defer questionResp.Body.Close() + + var questionResult model.Question + err = json.NewDecoder(questionResp.Body).Decode(&questionResult) + assert.NoError(t, err) + } t.Run("ResponseTime", func(t *testing.T) { start := time.Now() resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ - "Page": 1, + "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) + 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() { + go func(requestID int) { defer wg.Done() resp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ - "Page": 1, + "Page": 0, "Limit": 5, }) - if err == nil && resp != nil { - resp.Body.Close() + 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", quizID), 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) + assert.IsType(t, uint64(0), result.TotalCount) + assert.IsType(t, []model.AnswerExport{}, result.Results) }) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestGetResults_ErrorHandling(t *testing.T) { + // Создаем квиз для тестирования обработки ошибок + quizResp, err := createQuizRequest(validToken, map[string]interface{}{ + "name": "Квиз для тестирования обработки ошибок", + "status": "start", + }) + assert.NoError(t, err) + defer quizResp.Body.Close() + + var quizResult model.Quiz + err = json.NewDecoder(quizResp.Body).Decode(&quizResult) + assert.NoError(t, err) + quizID := quizResult.Id + + // Создаем вопрос для квиза + questionResp, err := createQuestionRequest(validToken, map[string]interface{}{ + "quiz_id": quizID, + "title": "Тестовый вопрос для обработки ошибок", + "type": "text", + "description": "Введите ответ.", + "required": true, + "page": 1, + "content": `{"placeholder": "Введите ответ"}`, + }) + assert.NoError(t, err) + defer questionResp.Body.Close() + + var questionResult model.Question + err = json.NewDecoder(questionResp.Body).Decode(&questionResult) + assert.NoError(t, err) + t.Run("MalformedJSON", func(t *testing.T) { - req, err := http.NewRequest("POST", baseURL+"/results/getResults/12345", bytes.NewReader([]byte("{invalid_json}"))) + req, err := http.NewRequest("POST", baseURL+fmt.Sprintf("/results/getResults/%v", quizID), bytes.NewReader([]byte("{invalid_json}"))) assert.NoError(t, err) req.Header.Set("Authorization", "Bearer "+validToken) req.Header.Set("Content-Type", "application/json") @@ -7485,20 +7800,91 @@ func TestGetResults_ErrorHandling(t *testing.T) { assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) - t.Run("ServerError", func(t *testing.T) { - // Тестируем с некорректным quizId, который может вызвать серверную ошибку - resp, err := getResultsRequest(validToken, "invalid-id", map[string]interface{}{ - "Page": 1, + t.Run("InvalidQuizID", func(t *testing.T) { + // Тестируем некорректный ID квиза + resp, err := getResultsRequest(validToken, "invalid-quiz-id", map[string]interface{}{ + "Page": 0, "Limit": 10, }) assert.NoError(t, err) defer resp.Body.Close() - assert.NotEqual(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + }) + + t.Run("NonExistentQuizID", func(t *testing.T) { + // Тестируем несуществующий ID квиза + 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", quizID), 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", quizID), 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.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("EmptyBody", func(t *testing.T) { + // Тестируем пустое тело запроса + req, err := http.NewRequest("POST", baseURL+fmt.Sprintf("/results/getResults/%v", quizID), 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/"+resultId, nil) + req, err := http.NewRequest("DELETE", baseURL+"/results/delete/"+resultId, nil) if err != nil { return nil, err } @@ -7507,42 +7893,104 @@ func deleteResultRequest(token string, resultId string) (*http.Response, error) return http.DefaultClient.Do(req) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestDeleteResult_Success(t *testing.T) { - createResp, err := createQuizRequest(deleteResultToken, map[string]interface{}{ - "name": "Квиз для удаления результатов", + // Создаем квиз для тестирования удаления результатов + quizResp, err := createQuizRequest(deleteResultToken, map[string]interface{}{ + "name": "Квиз для тестирования удаления результатов", "status": "start", }) assert.NoError(t, err) - defer createResp.Body.Close() - var createResult map[string]interface{} - err = json.NewDecoder(createResp.Body).Decode(&createResult) - assert.NoError(t, err) - quizID := createResult["id"] + defer quizResp.Body.Close() + var quizResult model.Quiz + err = json.NewDecoder(quizResp.Body).Decode(&quizResult) + assert.NoError(t, err) + quizID := quizResult.Id + + // Создаем несколько вопросов для квиза + question1Resp, err := createQuestionRequest(deleteResultToken, map[string]interface{}{ + "quiz_id": quizID, + "title": "Вопрос 1 для удаления результатов", + "type": "text", + "description": "Введите ответ.", + "required": true, + "page": 1, + "content": `{"placeholder": "Введите ответ"}`, + }) + assert.NoError(t, err) + defer question1Resp.Body.Close() + + var question1Result model.Question + err = json.NewDecoder(question1Resp.Body).Decode(&question1Result) + assert.NoError(t, err) + + question2Resp, err := createQuestionRequest(deleteResultToken, map[string]interface{}{ + "quiz_id": quizID, + "title": "Вопрос 2 для удаления результатов", + "type": "variant", + "description": "Выберите ответ.", + "required": true, + "page": 1, + "content": `{"variants": ["Вариант 1", "Вариант 2", "Вариант 3"]}`, + }) + assert.NoError(t, err) + defer question2Resp.Body.Close() + + var question2Result model.Question + err = json.NewDecoder(question2Resp.Body).Decode(&question2Result) + assert.NoError(t, err) + + // Получаем результаты квиза getResultsResp, err := getResultsRequest(deleteResultToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ - "Page": 1, + "Page": 0, "Limit": 10, }) assert.NoError(t, err) defer getResultsResp.Body.Close() - var resultsData map[string]interface{} + var resultsData result2.ReqExportResponse err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData) assert.NoError(t, err) - //results := resultsData["results"].([]interface{}) - //if len(results) > 0 { - // firstResult := results[0].(map[string]interface{}) - // resultID := fmt.Sprintf("%v", firstResult["id"]) - // - // resp, err := deleteResultRequest(deleteResultToken, resultID) - // assert.NoError(t, err) - // defer resp.Body.Close() - // - // assert.Equal(t, http.StatusOK, resp.StatusCode) - //} + // Если есть результаты, тестируем их удаление + if len(resultsData.Results) > 0 { + firstResult := resultsData.Results[0] + resultID := fmt.Sprintf("%v", firstResult.Id) + + // Удаляем результат + resp, err := deleteResultRequest(deleteResultToken, resultID) + assert.NoError(t, err) + defer resp.Body.Close() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Проверяем, что результат действительно удален + getResultsAfterDeleteResp, err := getResultsRequest(deleteResultToken, fmt.Sprintf("%v", quizID), 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) + } else { + // Если результатов нет, тестируем удаление несуществующего результата + resp, err := deleteResultRequest(deleteResultToken, "999999") + assert.NoError(t, err) + defer resp.Body.Close() + + // Должен вернуться успешный статус (мягкое удаление) + assert.Equal(t, http.StatusOK, resp.StatusCode) + } } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestDeleteResult_Idempotency(t *testing.T) { createResp, err := createQuizRequest(deleteResultToken, map[string]interface{}{ "name": "Квиз для идемпотентности удаления результатов", @@ -7583,6 +8031,7 @@ func TestDeleteResult_Idempotency(t *testing.T) { //} } +// отсмотрено func TestDeleteResult_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { req, err := http.NewRequest("DELETE", baseURL+"/results/123456", nil) @@ -7601,6 +8050,7 @@ func TestDeleteResult_Auth(t *testing.T) { }) } +// отсмотрено func TestDeleteResult_InputValidation(t *testing.T) { t.Run("InvalidResultID", func(t *testing.T) { resp, err := deleteResultRequest(deleteResultToken, "not_a_number") @@ -7608,7 +8058,7 @@ func TestDeleteResult_InputValidation(t *testing.T) { defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) - + // todo need 404 t.Run("NonExistentResultID", func(t *testing.T) { resp, err := deleteResultRequest(deleteResultToken, "99999999") assert.NoError(t, err) @@ -7617,6 +8067,7 @@ func TestDeleteResult_InputValidation(t *testing.T) { }) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestDeleteResult_Performance(t *testing.T) { createResp, err := createQuizRequest(deleteResultToken, map[string]interface{}{ "name": "Квиз для теста производительности удаления результатов", @@ -7686,6 +8137,7 @@ func TestDeleteResult_Performance(t *testing.T) { }) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestDeleteResult_BoundaryCases(t *testing.T) { t.Run("VeryLongResultID", func(t *testing.T) { longID := make([]byte, 1025) @@ -7706,6 +8158,7 @@ func TestDeleteResult_BoundaryCases(t *testing.T) { }) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestDeleteResult_ErrorHandling(t *testing.T) { t.Run("MalformedRequest", func(t *testing.T) { req, err := http.NewRequest("DELETE", baseURL+"/results/invalid-id", bytes.NewReader([]byte("invalid_body"))) @@ -7740,47 +8193,116 @@ func updateResultsStatusRequest(token string, body map[string]interface{}) (*htt return http.DefaultClient.Do(req) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestUpdateResultsStatus_Success(t *testing.T) { - createResp, err := createQuizRequest(validToken, map[string]interface{}{ - "name": "Квиз для обновления статуса результатов", + // Создаем квиз для тестирования обновления статуса результатов + quizResp, err := createQuizRequest(validToken, map[string]interface{}{ + "name": "Квиз для тестирования обновления статуса результатов", "status": "start", }) assert.NoError(t, err) - defer createResp.Body.Close() - var createResult map[string]interface{} - err = json.NewDecoder(createResp.Body).Decode(&createResult) - assert.NoError(t, err) - quizID := createResult["id"] + defer quizResp.Body.Close() + var quizResult model.Quiz + err = json.NewDecoder(quizResp.Body).Decode(&quizResult) + assert.NoError(t, err) + quizID := quizResult.Id + + // Создаем несколько вопросов для квиза + question1Resp, err := createQuestionRequest(validToken, map[string]interface{}{ + "quiz_id": quizID, + "title": "Вопрос 1 для обновления статуса", + "type": "text", + "description": "Введите ответ.", + "required": true, + "page": 1, + "content": `{"placeholder": "Введите ответ"}`, + }) + assert.NoError(t, err) + defer question1Resp.Body.Close() + + var question1Result model.Question + err = json.NewDecoder(question1Resp.Body).Decode(&question1Result) + assert.NoError(t, err) + + question2Resp, err := createQuestionRequest(validToken, map[string]interface{}{ + "quiz_id": quizID, + "title": "Вопрос 2 для обновления статуса", + "type": "variant", + "description": "Выберите ответ.", + "required": true, + "page": 1, + "content": `{"variants": ["Вариант 1", "Вариант 2", "Вариант 3"]}`, + }) + assert.NoError(t, err) + defer question2Resp.Body.Close() + + var question2Result model.Question + err = json.NewDecoder(question2Resp.Body).Decode(&question2Result) + assert.NoError(t, err) + + // Получаем результаты квиза getResultsResp, err := getResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ - "Page": 1, + "Page": 0, "Limit": 10, }) assert.NoError(t, err) defer getResultsResp.Body.Close() - var resultsData map[string]interface{} + var resultsData result2.ReqExportResponse err = json.NewDecoder(getResultsResp.Body).Decode(&resultsData) assert.NoError(t, err) - //results := resultsData["results"].([]interface{}) - // - //if len(results) >= 3 { - // var answerIDs []int64 - // for i := 0; i < 3; i++ { - // result := results[i].(map[string]interface{}) - // answerIDs = append(answerIDs, int64(result["id"].(float64))) - // } - // - // resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ - // "Answers": answerIDs, - // }) - // assert.NoError(t, err) - // defer resp.Body.Close() - // - // assert.Equal(t, http.StatusOK, resp.StatusCode) - //} + + // Если есть результаты, тестируем обновление их статуса + 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", quizID), 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, "Результат должен быть помечен как просмотренный") + } + } + } + } else { + // Если результатов нет, тестируем обновление статуса несуществующих результатов + resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ + "Answers": []int64{999999, 999998}, + }) + assert.NoError(t, err) + defer resp.Body.Close() + + // Должен вернуться статус NotAcceptable, так как у пользователя нет прав на эти результаты + assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode) + } } +// отсмотрено func TestUpdateResultsStatus_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ @@ -7806,6 +8328,7 @@ func TestUpdateResultsStatus_Auth(t *testing.T) { }) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestUpdateResultsStatus_InputValidation(t *testing.T) { t.Run("MissingAnswers", func(t *testing.T) { resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{}) @@ -7842,6 +8365,7 @@ func TestUpdateResultsStatus_InputValidation(t *testing.T) { }) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestUpdateResultsStatus_Idempotency(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для идемпотентности обновления статуса", @@ -7889,6 +8413,7 @@ func TestUpdateResultsStatus_Idempotency(t *testing.T) { //} } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestUpdateResultsStatus_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности обновления статуса", @@ -7948,6 +8473,7 @@ func TestUpdateResultsStatus_Performance(t *testing.T) { //} } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestUpdateResultsStatus_BoundaryCases(t *testing.T) { t.Run("VeryLargeAnswersArray", func(t *testing.T) { largeAnswers := make([]int64, 10000) @@ -7973,6 +8499,7 @@ func TestUpdateResultsStatus_BoundaryCases(t *testing.T) { }) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы 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}"))) @@ -7995,6 +8522,7 @@ func TestUpdateResultsStatus_ErrorHandling(t *testing.T) { }) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestUpdateResultsStatus_SpecialCases(t *testing.T) { t.Run("DuplicateAnswerIDs", func(t *testing.T) { resp, err := updateResultsStatusRequest(validToken, map[string]interface{}{ @@ -8021,6 +8549,7 @@ func exportResultsRequest(token string, quizID string, body map[string]interface return http.DefaultClient.Do(req) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestExportResults_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для экспорта результатов", @@ -8047,6 +8576,7 @@ func TestExportResults_Success(t *testing.T) { 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{}{ @@ -8074,6 +8604,7 @@ func TestExportResults_Auth(t *testing.T) { }) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestExportResults_InputValidation(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для валидации экспорта", @@ -8116,230 +8647,6 @@ func TestExportResults_InputValidation(t *testing.T) { }) } -// todo nned check -//func TestExportResults_Performance(t *testing.T) { -// // Создаем квиз для тестирования производительности -// createResp, err := createQuizRequest(validToken, map[string]interface{}{ -// "name": "Квиз для теста производительности экспорта", -// "status": "start", -// }) -// assert.NoError(t, err) -// defer createResp.Body.Close() -// var createResult map[string]interface{} -// err = json.NewDecoder(createResp.Body).Decode(&createResult) -// assert.NoError(t, err) -// quizID := createResult["id"] -// -// t.Run("ResponseTime", func(t *testing.T) { -// start := time.Now() -// resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ -// "Page": 1, -// "Limit": 100, -// }) -// duration := time.Since(start) -// -// assert.NoError(t, err) -// defer resp.Body.Close() -// assert.Less(t, duration.Milliseconds(), int64(2000)) // Экспорт может занимать больше времени -// }) -// -// t.Run("LargeExport", func(t *testing.T) { -// resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ -// "Page": 1, -// "Limit": 1000, -// }) -// assert.NoError(t, err) -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// }) -//} -// -//func TestExportResults_BoundaryCases(t *testing.T) { -// t.Run("VeryLargeLimit", func(t *testing.T) { -// // Создаем квиз для тестирования -// createResp, err := createQuizRequest(validToken, map[string]interface{}{ -// "name": "Квиз для граничных случаев экспорта", -// "status": "start", -// }) -// assert.NoError(t, err) -// defer createResp.Body.Close() -// var createResult map[string]interface{} -// err = json.NewDecoder(createResp.Body).Decode(&createResult) -// assert.NoError(t, err) -// quizID := createResult["id"] -// -// resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ -// "Page": 1, -// "Limit": 100000, // Очень большой лимит -// }) -// assert.NoError(t, err) -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// }) -// -// t.Run("VeryOldDateRange", func(t *testing.T) { -// // Создаем квиз для тестирования -// createResp, err := createQuizRequest(validToken, map[string]interface{}{ -// "name": "Квиз для старых дат экспорта", -// "status": "start", -// }) -// assert.NoError(t, err) -// defer createResp.Body.Close() -// var createResult map[string]interface{} -// err = json.NewDecoder(createResp.Body).Decode(&createResult) -// assert.NoError(t, err) -// quizID := createResult["id"] -// -// resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ -// "From": "1900-01-01T00:00:00Z", -// "To": "1950-12-31T23:59:59Z", -// }) -// assert.NoError(t, err) -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// }) -//} -// -//func TestExportResults_ErrorHandling(t *testing.T) { -// t.Run("MalformedJSON", func(t *testing.T) { -// req, err := http.NewRequest("POST", baseURL+"/results/123/export", bytes.NewReader([]byte("{invalid_json}"))) -// assert.NoError(t, err) -// req.Header.Set("Authorization", "Bearer "+validToken) -// req.Header.Set("Content-Type", "application/json") -// resp, err := http.DefaultClient.Do(req) -// assert.NoError(t, err) -// defer resp.Body.Close() -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -// }) -// -// t.Run("ServerError", func(t *testing.T) { -// // Тестируем с некорректным quizId, который может вызвать серверную ошибку -// resp, err := exportResultsRequest(validToken, "invalid-id", map[string]interface{}{ -// "Page": 1, -// "Limit": 10, -// }) -// assert.NoError(t, err) -// defer resp.Body.Close() -// assert.NotEqual(t, http.StatusOK, resp.StatusCode) -// }) -//} -// -//func TestExportResults_AuditMetricsConsistency(t *testing.T) { -// t.Run("AuditLog", func(t *testing.T) { -// // Создаем квиз для тестирования аудита -// createResp, err := createQuizRequest(validToken, map[string]interface{}{ -// "name": "Квиз для аудита экспорта", -// "status": "start", -// }) -// assert.NoError(t, err) -// defer createResp.Body.Close() -// var createResult map[string]interface{} -// err = json.NewDecoder(createResp.Body).Decode(&createResult) -// assert.NoError(t, err) -// quizID := createResult["id"] -// -// resp, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ -// "Page": 1, -// "Limit": 50, -// }) -// assert.NoError(t, err) -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// // Здесь можно добавить проверку на появление записи аудита, если есть доступ к логам/БД -// }) -// -// t.Run("Consistency", func(t *testing.T) { -// // Проверяем консистентность данных при повторных экспортах -// createResp, err := createQuizRequest(validToken, map[string]interface{}{ -// "name": "Квиз для консистентности экспорта", -// "status": "start", -// }) -// assert.NoError(t, err) -// defer createResp.Body.Close() -// var createResult map[string]interface{} -// err = json.NewDecoder(createResp.Body).Decode(&createResult) -// assert.NoError(t, err) -// quizID := createResult["id"] -// -// // Первый экспорт -// resp1, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ -// "Page": 1, -// "Limit": 10, -// }) -// assert.NoError(t, err) -// defer resp1.Body.Close() -// assert.Equal(t, http.StatusOK, resp1.StatusCode) -// -// // Второй экспорт с теми же параметрами -// resp2, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ -// "Page": 1, -// "Limit": 10, -// }) -// assert.NoError(t, err) -// defer resp2.Body.Close() -// assert.Equal(t, http.StatusOK, resp2.StatusCode) -// -// // Проверяем, что размеры файлов одинаковые (консистентность) -// body1, err := io.ReadAll(resp1.Body) -// assert.NoError(t, err) -// body2, err := io.ReadAll(resp2.Body) -// assert.NoError(t, err) -// assert.Equal(t, len(body1), len(body2)) -// }) -//} -// -//func TestExportResults_IdempotencyAndTransactions(t *testing.T) { -// t.Run("Idempotency", func(t *testing.T) { -// // Создаем квиз для тестирования идемпотентности -// createResp, err := createQuizRequest(validToken, map[string]interface{}{ -// "name": "Квиз для идемпотентности экспорта", -// "status": "start", -// }) -// assert.NoError(t, err) -// defer createResp.Body.Close() -// var createResult map[string]interface{} -// err = json.NewDecoder(createResp.Body).Decode(&createResult) -// assert.NoError(t, err) -// quizID := createResult["id"] -// -// // Первый экспорт -// resp1, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ -// "Page": 1, -// "Limit": 5, -// }) -// assert.NoError(t, err) -// defer resp1.Body.Close() -// assert.Equal(t, http.StatusOK, resp1.StatusCode) -// -// // Повторный экспорт с теми же параметрами -// resp2, err := exportResultsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ -// "Page": 1, -// "Limit": 5, -// }) -// assert.NoError(t, err) -// defer resp2.Body.Close() -// assert.Equal(t, http.StatusOK, resp2.StatusCode) -// assert.Equal(t, resp1.StatusCode, resp2.StatusCode) -// }) -// -// t.Run("TransactionRollback", func(t *testing.T) { -// // Пытаемся экспортировать результаты несуществующего квиза -// resp, err := exportResultsRequest(validToken, "999999", map[string]interface{}{ -// "Page": 1, -// "Limit": 10, -// }) -// assert.NoError(t, err) -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// -// // Проверяем, что файл пустой или содержит заголовки -// body, err := io.ReadAll(resp.Body) -// assert.NoError(t, err) -// // Файл может быть пустым или содержать только заголовки -// assert.NotNil(t, body) -// }) -//} - func getResultRequest(token string, resultID string) (*http.Response, error) { req, err := http.NewRequest("GET", baseURL+"/result/"+resultID, nil) if err != nil { @@ -8350,6 +8657,7 @@ func getResultRequest(token string, resultID string) (*http.Response, error) { return http.DefaultClient.Do(req) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestGetResult_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для получения результата", @@ -8404,6 +8712,7 @@ func TestGetResult_Success(t *testing.T) { //} } +// отсмотрено func TestGetResult_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/result/abc123xyz", nil) @@ -8422,24 +8731,20 @@ func TestGetResult_Auth(t *testing.T) { }) } +// отсмотрено func TestGetResult_InputValidation(t *testing.T) { t.Run("EmptyResultID", func(t *testing.T) { resp, err := getResultRequest(validToken, "") assert.NoError(t, err) defer resp.Body.Close() - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + 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.StatusOK, resp.StatusCode) - - var answers []map[string]interface{} - err = json.NewDecoder(resp.Body).Decode(&answers) - assert.NoError(t, err) - assert.Empty(t, answers) + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("InvalidResultID", func(t *testing.T) { @@ -8450,6 +8755,7 @@ func TestGetResult_InputValidation(t *testing.T) { }) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestGetResult_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности результата", @@ -8490,6 +8796,7 @@ func TestGetResult_Performance(t *testing.T) { //} } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestGetResult_BoundaryCases(t *testing.T) { t.Run("VeryLongResultID", func(t *testing.T) { longID := make([]byte, 1025) @@ -8517,6 +8824,7 @@ func TestGetResult_BoundaryCases(t *testing.T) { //}) } +// todo для этих тестов нужно заранее заполнять бдшку ответами на вопросы func TestGetResult_ErrorHandling(t *testing.T) { t.Run("MalformedRequest", func(t *testing.T) { req, err := http.NewRequest("GET", baseURL+"/result/invalid-id", bytes.NewReader([]byte("invalid_body"))) @@ -8551,19 +8859,9 @@ func getDeviceStatsRequest(token string, quizID string, body map[string]interfac return http.DefaultClient.Do(req) } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetDeviceStats_Success(t *testing.T) { - createResp, err := createQuizRequest(validToken, map[string]interface{}{ - "name": "Квиз для статистики устройств", - "status": "start", - }) - assert.NoError(t, err) - defer createResp.Body.Close() - var createResult map[string]interface{} - err = json.NewDecoder(createResp.Body).Decode(&createResult) - assert.NoError(t, err) - quizID := createResult["id"] - - resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", quizID), map[string]interface{}{ + resp, err := getDeviceStatsRequest(validToken, fmt.Sprintf("%v", 111), map[string]interface{}{ "From": time.Now().Unix() - 100, "To": time.Now().Unix(), }) @@ -8596,6 +8894,7 @@ func TestGetDeviceStats_Success(t *testing.T) { } } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetDeviceStats_WithoutDateRange(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для статистики без диапазона дат", @@ -8623,6 +8922,7 @@ func TestGetDeviceStats_WithoutDateRange(t *testing.T) { 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{}{ @@ -8650,6 +8950,7 @@ func TestGetDeviceStats_Auth(t *testing.T) { }) } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetDeviceStats_InputValidation(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для валидации статистики устройств", @@ -8703,6 +9004,7 @@ func TestGetDeviceStats_InputValidation(t *testing.T) { }) } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetDeviceStats_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности статистики устройств", @@ -8747,6 +9049,7 @@ func TestGetDeviceStats_Performance(t *testing.T) { }) } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetDeviceStats_ErrorHandling(t *testing.T) { t.Run("MalformedJSON", func(t *testing.T) { req, err := http.NewRequest("POST", baseURL+"/statistic/12345/devices", bytes.NewReader([]byte("{invalid_json}"))) @@ -8784,6 +9087,7 @@ func getGeneralStatsRequest(token string, quizID string, body map[string]interfa return http.DefaultClient.Do(req) } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetGeneralStats_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для общей статистики", @@ -8839,6 +9143,7 @@ func TestGetGeneralStats_Success(t *testing.T) { } } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetGeneralStats_WithoutDateRange(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для общей статистики без диапазона", @@ -8867,6 +9172,7 @@ func TestGetGeneralStats_WithoutDateRange(t *testing.T) { 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{}{ @@ -8894,6 +9200,7 @@ func TestGetGeneralStats_Auth(t *testing.T) { }) } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetGeneralStats_InputValidation(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для валидации общей статистики", @@ -8949,6 +9256,7 @@ func TestGetGeneralStats_InputValidation(t *testing.T) { }) } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetGeneralStats_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности общей статистики", @@ -8993,6 +9301,7 @@ func TestGetGeneralStats_Performance(t *testing.T) { }) } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetGeneralStats_BoundaryCases(t *testing.T) { t.Run("VeryLargeDateRange", func(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ @@ -9051,6 +9360,7 @@ func getQuestionStatsRequest(token string, quizID string, body map[string]interf return http.DefaultClient.Do(req) } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetQuestionStats_Success(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для статистики вопросов", @@ -9123,6 +9433,7 @@ func TestGetQuestionStats_Success(t *testing.T) { } } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetQuestionStats_WithoutDateRange(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для статистики вопросов без диапазона", @@ -9148,6 +9459,7 @@ func TestGetQuestionStats_WithoutDateRange(t *testing.T) { assert.NotEmpty(t, result) } +// отсмотрено func TestGetQuestionStats_Auth(t *testing.T) { t.Run("NoToken", func(t *testing.T) { payload, err := json.Marshal(map[string]interface{}{ @@ -9175,6 +9487,7 @@ func TestGetQuestionStats_Auth(t *testing.T) { }) } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetQuestionStats_InputValidation(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для валидации статистики вопросов", @@ -9235,6 +9548,7 @@ func TestGetQuestionStats_InputValidation(t *testing.T) { }) } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetQuestionStats_Performance(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{ "name": "Квиз для теста производительности статистики вопросов", @@ -9279,6 +9593,7 @@ func TestGetQuestionStats_Performance(t *testing.T) { }) } +// todo нужно заранее в кликхаусе выбрать на чем честить будем func TestGetQuestionStats_BoundaryCases(t *testing.T) { t.Run("VeryLargeDateRange", func(t *testing.T) { createResp, err := createQuizRequest(validToken, map[string]interface{}{