Files
BoostAI/Backend/internal/handlers/api/questions/handler_test.go
2026-05-25 17:05:06 +01:00

141 lines
3.7 KiB
Go

package questions
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"boostai-backend/internal/http/respond"
"boostai-backend/internal/questiongen"
"boostai-backend/internal/sqlc"
"github.com/gofiber/fiber/v2"
)
func TestGenerateQuestionsReturnsGeneratorUnavailable(t *testing.T) {
t.Parallel()
handler := NewHandler(nil, nil)
status, body := performGenerateRequest(t, handler, map[string]any{
"topic": "fractions",
"difficulty": "easy",
"count": 1,
}, true)
if status != fiber.StatusServiceUnavailable {
t.Fatalf("expected status %d, got %d", fiber.StatusServiceUnavailable, status)
}
if body.Error != "generator_unavailable" {
t.Fatalf("expected generator_unavailable error, got %#v", body)
}
}
func TestGenerateQuestionsRequiresTeacherAuthentication(t *testing.T) {
t.Parallel()
handler := NewHandler(nil, questiongen.NewService())
status, body := performGenerateRequest(t, handler, map[string]any{
"topic": "fractions",
"difficulty": "easy",
"count": 1,
}, false)
if status != fiber.StatusUnauthorized {
t.Fatalf("expected status %d, got %d", fiber.StatusUnauthorized, status)
}
if body.Error != "unauthorized" {
t.Fatalf("expected unauthorized error, got %#v", body)
}
}
func TestGenerateQuestionsRejectsZeroCount(t *testing.T) {
t.Parallel()
handler := NewHandler(nil, questiongen.NewService())
status, body := performGenerateRequest(t, handler, map[string]any{
"topic": "fractions",
"difficulty": "easy",
"count": 0,
}, true)
if status != fiber.StatusBadRequest {
t.Fatalf("expected status %d, got %d", fiber.StatusBadRequest, status)
}
if body.Message != "count must be between 1 and 25" {
t.Fatalf("expected count validation message, got %#v", body)
}
}
func TestGenerateQuestionsRejectsInvalidStatus(t *testing.T) {
t.Parallel()
handler := NewHandler(nil, questiongen.NewService())
status, body := performGenerateRequest(t, handler, map[string]any{
"topic": "fractions",
"difficulty": "easy",
"count": 1,
"status": "invalid",
}, true)
if status != fiber.StatusBadRequest {
t.Fatalf("expected status %d, got %d", fiber.StatusBadRequest, status)
}
if body.Message != "status must be draft, published, or archived" {
t.Fatalf("expected invalid status message, got %#v", body)
}
}
func TestGenerateQuestionsRejectsInvalidTopic(t *testing.T) {
t.Parallel()
handler := NewHandler(nil, questiongen.NewService())
status, body := performGenerateRequest(t, handler, map[string]any{
"topic": "not_a_topic",
"difficulty": "easy",
"count": 1,
}, true)
if status != fiber.StatusBadRequest {
t.Fatalf("expected status %d, got %d", fiber.StatusBadRequest, status)
}
if body.Error != "invalid_request" {
t.Fatalf("expected invalid_request error, got %#v", body)
}
}
func performGenerateRequest(t *testing.T, handler *Handler, payload map[string]any, authenticated bool) (int, respond.ErrorBody) {
t.Helper()
app := fiber.New()
app.Post("/questions/generate", func(c *fiber.Ctx) error {
if authenticated {
c.Locals("auth.user_id", int64(42))
c.Locals("auth.role", sqlc.UserRoleTeacher)
}
return handler.GenerateQuestions(c)
})
bodyBytes, err := json.Marshal(payload)
if err != nil {
t.Fatalf("marshal payload: %v", err)
}
req := httptest.NewRequest(http.MethodPost, "/questions/generate", bytes.NewReader(bodyBytes))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
if err != nil {
t.Fatalf("app.Test returned error: %v", err)
}
defer resp.Body.Close()
var errorBody respond.ErrorBody
if err := json.NewDecoder(resp.Body).Decode(&errorBody); err != nil {
t.Fatalf("decode error response: %v", err)
}
return resp.StatusCode, errorBody
}