Some checks failed
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Failing after 37s
CI/CD / test-python-backend-compliance (push) Successful in 39s
CI/CD / test-python-document-crawler (push) Successful in 26s
CI/CD / test-python-dsms-gateway (push) Successful in 23s
CI/CD / validate-canonical-controls (push) Successful in 12s
CI/CD / Deploy (push) Has been skipped
Interactive Training Videos (CP-TRAIN): - DB migration 022: training_checkpoints + checkpoint_progress tables - NarratorScript generation via Anthropic (AI Teacher persona, German) - TTS batch synthesis + interactive video pipeline (slides + checkpoint slides + FFmpeg) - 4 new API endpoints: generate-interactive, interactive-manifest, checkpoint submit, checkpoint progress - InteractiveVideoPlayer component (HTML5 Video, quiz overlay, seek protection, progress tracking) - Learner portal integration with automatic completion on all checkpoints passed - 30 new tests (handler validation + grading logic + manifest/progress + seek protection) Training Blocks: - Block generator, block store, block config CRUD + preview/generate endpoints - Migration 021: training_blocks schema Control Generator + Canonical Library: - Control generator routes + service enhancements - Canonical control library helpers, sidebar entry - Citation backfill service + tests - CE libraries data (hazard, protection, evidence, lifecycle, components) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
692 lines
24 KiB
Go
692 lines
24 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// newTestContext, parseResponse, and gin.SetMode are defined in iace_handler_test.go
|
|
|
|
// ============================================================================
|
|
// Module Endpoint Tests
|
|
// ============================================================================
|
|
|
|
func TestGetModule_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/modules/not-a-uuid", nil, nil, gin.Params{{Key: "id", Value: "not-a-uuid"}})
|
|
h.GetModule(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestGetModule_EmptyID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/modules/", nil, nil, gin.Params{{Key: "id", Value: ""}})
|
|
h.GetModule(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestCreateModule_EmptyBody_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/modules", nil, nil, nil)
|
|
h.CreateModule(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestCreateModule_MissingTitle_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
body := map[string]interface{}{"module_code": "T01", "regulation_area": "dsgvo", "frequency_type": "annual"}
|
|
w, c := newTestContext("POST", "/modules", body, nil, nil)
|
|
h.CreateModule(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestCreateModule_MissingModuleCode_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
body := map[string]interface{}{"title": "Test", "regulation_area": "dsgvo", "frequency_type": "annual"}
|
|
w, c := newTestContext("POST", "/modules", body, nil, nil)
|
|
h.CreateModule(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestCreateModule_MissingRegulationArea_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
body := map[string]interface{}{"module_code": "T01", "title": "Test", "frequency_type": "annual"}
|
|
w, c := newTestContext("POST", "/modules", body, nil, nil)
|
|
h.CreateModule(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestUpdateModule_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("PUT", "/modules/bad", map[string]interface{}{"title": "x"}, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.UpdateModule(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestDeleteModule_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("DELETE", "/modules/bad", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.DeleteModule(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestDeleteModule_EmptyID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("DELETE", "/modules/", nil, nil, gin.Params{{Key: "id", Value: ""}})
|
|
h.DeleteModule(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Matrix Endpoint Tests
|
|
// ============================================================================
|
|
|
|
func TestSetMatrixEntry_EmptyBody_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/matrix", nil, nil, nil)
|
|
h.SetMatrixEntry(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestSetMatrixEntry_MissingRoleCode_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
body := map[string]interface{}{"module_id": "00000000-0000-0000-0000-000000000001"}
|
|
w, c := newTestContext("POST", "/matrix", body, nil, nil)
|
|
h.SetMatrixEntry(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestDeleteMatrixEntry_InvalidModuleID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("DELETE", "/matrix/R1/bad", nil, nil, gin.Params{
|
|
{Key: "role", Value: "R1"},
|
|
{Key: "moduleId", Value: "bad"},
|
|
})
|
|
h.DeleteMatrixEntry(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Assignment Endpoint Tests
|
|
// ============================================================================
|
|
|
|
func TestGetAssignment_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/assignments/bad", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.GetAssignment(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestStartAssignment_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/assignments/bad/start", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.StartAssignment(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestUpdateAssignmentProgress_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/assignments/bad/progress", map[string]interface{}{"progress": 50}, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.UpdateAssignmentProgress(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestUpdateAssignmentProgress_EmptyBody_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/assignments/00000000-0000-0000-0000-000000000001/progress", nil, nil,
|
|
gin.Params{{Key: "id", Value: "00000000-0000-0000-0000-000000000001"}})
|
|
h.UpdateAssignmentProgress(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestCompleteAssignment_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/assignments/bad/complete", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.CompleteAssignment(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestComputeAssignments_EmptyBody_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/assignments/compute", nil, nil, nil)
|
|
h.ComputeAssignments(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestComputeAssignments_MissingUserID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
body := map[string]interface{}{"user_name": "Test", "user_email": "test@test.de", "roles": []string{"R1"}}
|
|
w, c := newTestContext("POST", "/assignments/compute", body, nil, nil)
|
|
h.ComputeAssignments(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Quiz Endpoint Tests
|
|
// ============================================================================
|
|
|
|
func TestGetQuiz_InvalidModuleID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/quiz/bad", nil, nil, gin.Params{{Key: "moduleId", Value: "bad"}})
|
|
h.GetQuiz(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestSubmitQuiz_InvalidModuleID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/quiz/bad/submit", map[string]interface{}{}, nil, gin.Params{{Key: "moduleId", Value: "bad"}})
|
|
h.SubmitQuiz(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestSubmitQuiz_EmptyBody_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/quiz/00000000-0000-0000-0000-000000000001/submit", nil, nil,
|
|
gin.Params{{Key: "moduleId", Value: "00000000-0000-0000-0000-000000000001"}})
|
|
h.SubmitQuiz(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestGetQuizAttempts_InvalidAssignmentID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/quiz/attempts/bad", nil, nil, gin.Params{{Key: "assignmentId", Value: "bad"}})
|
|
h.GetQuizAttempts(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Content Endpoint Tests
|
|
// ============================================================================
|
|
|
|
func TestGetContent_InvalidModuleID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/content/bad", nil, nil, gin.Params{{Key: "moduleId", Value: "bad"}})
|
|
h.GetContent(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestPublishContent_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/content/bad/publish", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.PublishContent(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestGenerateContent_EmptyBody_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/content/generate", nil, nil, nil)
|
|
h.GenerateContent(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestGenerateQuiz_EmptyBody_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/content/generate-quiz", nil, nil, nil)
|
|
h.GenerateQuiz(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Media Endpoint Tests
|
|
// ============================================================================
|
|
|
|
func TestGetModuleMedia_InvalidModuleID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/media/module/bad", nil, nil, gin.Params{{Key: "moduleId", Value: "bad"}})
|
|
h.GetModuleMedia(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestGetMediaURL_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/media/bad/url", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.GetMediaURL(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestPublishMedia_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/media/bad/publish", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.PublishMedia(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestStreamMedia_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/media/bad/stream", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.StreamMedia(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestGenerateAudio_InvalidModuleID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/content/bad/generate-audio", nil, nil, gin.Params{{Key: "moduleId", Value: "bad"}})
|
|
h.GenerateAudio(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestGenerateVideo_InvalidModuleID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/content/bad/generate-video", nil, nil, gin.Params{{Key: "moduleId", Value: "bad"}})
|
|
h.GenerateVideo(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestPreviewVideoScript_InvalidModuleID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/content/bad/preview-script", nil, nil, gin.Params{{Key: "moduleId", Value: "bad"}})
|
|
h.PreviewVideoScript(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Certificate Endpoint Tests
|
|
// ============================================================================
|
|
|
|
func TestGenerateCertificate_InvalidAssignmentID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/certificates/generate/bad", nil, nil, gin.Params{{Key: "assignmentId", Value: "bad"}})
|
|
h.GenerateCertificate(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestDownloadCertificatePDF_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/certificates/bad/pdf", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.DownloadCertificatePDF(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestVerifyCertificate_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/certificates/bad/verify", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.VerifyCertificate(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestListCertificates_NilStore_Panics(t *testing.T) {
|
|
// This tests that a nil store doesn't silently succeed
|
|
defer func() {
|
|
if r := recover(); r == nil {
|
|
t.Error("Expected panic with nil store")
|
|
}
|
|
}()
|
|
h := &TrainingHandlers{}
|
|
_, c := newTestContext("GET", "/certificates", nil, nil, nil)
|
|
h.ListCertificates(c)
|
|
}
|
|
|
|
// ============================================================================
|
|
// Interactive Video Endpoint Tests (User Journey: Admin generates video)
|
|
// ============================================================================
|
|
|
|
func TestGenerateInteractiveVideo_InvalidModuleID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/content/bad/generate-interactive", nil, nil, gin.Params{{Key: "moduleId", Value: "bad"}})
|
|
h.GenerateInteractiveVideo(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
resp := parseResponse(w)
|
|
if resp["error"] == nil {
|
|
t.Error("Response should contain 'error' key")
|
|
}
|
|
}
|
|
|
|
func TestGenerateInteractiveVideo_EmptyModuleID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/content//generate-interactive", nil, nil, gin.Params{{Key: "moduleId", Value: ""}})
|
|
h.GenerateInteractiveVideo(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestGetInteractiveManifest_InvalidModuleID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/content/bad/interactive-manifest", nil, nil, gin.Params{{Key: "moduleId", Value: "bad"}})
|
|
h.GetInteractiveManifest(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
resp := parseResponse(w)
|
|
if resp["error"] == nil {
|
|
t.Error("Response should contain 'error' key")
|
|
}
|
|
}
|
|
|
|
func TestGetInteractiveManifest_EmptyModuleID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/content//interactive-manifest", nil, nil, gin.Params{{Key: "moduleId", Value: ""}})
|
|
h.GetInteractiveManifest(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Checkpoint Quiz Endpoint Tests (User Journey: Learner takes quiz)
|
|
// ============================================================================
|
|
|
|
func TestSubmitCheckpointQuiz_InvalidCheckpointID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
body := map[string]interface{}{
|
|
"assignment_id": "00000000-0000-0000-0000-000000000001",
|
|
"answers": []int{0, 1, 2},
|
|
}
|
|
w, c := newTestContext("POST", "/checkpoints/bad/submit", body, nil, gin.Params{{Key: "checkpointId", Value: "bad"}})
|
|
h.SubmitCheckpointQuiz(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
resp := parseResponse(w)
|
|
if resp["error"] == nil {
|
|
t.Error("Response should contain 'error' key")
|
|
}
|
|
}
|
|
|
|
func TestSubmitCheckpointQuiz_EmptyCheckpointID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
body := map[string]interface{}{
|
|
"assignment_id": "00000000-0000-0000-0000-000000000001",
|
|
"answers": []int{0},
|
|
}
|
|
w, c := newTestContext("POST", "/checkpoints//submit", body, nil, gin.Params{{Key: "checkpointId", Value: ""}})
|
|
h.SubmitCheckpointQuiz(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestSubmitCheckpointQuiz_EmptyBody_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/checkpoints/00000000-0000-0000-0000-000000000001/submit", nil, nil,
|
|
gin.Params{{Key: "checkpointId", Value: "00000000-0000-0000-0000-000000000001"}})
|
|
h.SubmitCheckpointQuiz(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestSubmitCheckpointQuiz_InvalidAssignmentID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
body := map[string]interface{}{
|
|
"assignment_id": "not-a-uuid",
|
|
"answers": []int{0},
|
|
}
|
|
w, c := newTestContext("POST", "/checkpoints/00000000-0000-0000-0000-000000000001/submit", body, nil,
|
|
gin.Params{{Key: "checkpointId", Value: "00000000-0000-0000-0000-000000000001"}})
|
|
h.SubmitCheckpointQuiz(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestSubmitCheckpointQuiz_ValidIDs_NilStore_Panics(t *testing.T) {
|
|
// When both IDs are valid, handler reaches store → panic with nil store
|
|
defer func() {
|
|
if r := recover(); r == nil {
|
|
t.Error("Expected panic with nil store")
|
|
}
|
|
}()
|
|
h := &TrainingHandlers{}
|
|
body := map[string]interface{}{
|
|
"assignment_id": "00000000-0000-0000-0000-000000000001",
|
|
"answers": []int{0},
|
|
}
|
|
_, c := newTestContext("POST", "/checkpoints/00000000-0000-0000-0000-000000000001/submit", body, nil,
|
|
gin.Params{{Key: "checkpointId", Value: "00000000-0000-0000-0000-000000000001"}})
|
|
h.SubmitCheckpointQuiz(c)
|
|
}
|
|
|
|
// ============================================================================
|
|
// Checkpoint Progress Endpoint Tests (User Journey: Learner views progress)
|
|
// ============================================================================
|
|
|
|
func TestGetCheckpointProgress_InvalidAssignmentID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/checkpoints/progress/bad", nil, nil, gin.Params{{Key: "assignmentId", Value: "bad"}})
|
|
h.GetCheckpointProgress(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
resp := parseResponse(w)
|
|
if resp["error"] == nil {
|
|
t.Error("Response should contain 'error' key")
|
|
}
|
|
}
|
|
|
|
func TestGetCheckpointProgress_EmptyAssignmentID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/checkpoints/progress/", nil, nil, gin.Params{{Key: "assignmentId", Value: ""}})
|
|
h.GetCheckpointProgress(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Interactive Video Error Format Tests (table-driven)
|
|
// ============================================================================
|
|
|
|
func TestInteractiveEndpoints_InvalidID_ResponseContainsErrorKey(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
method string
|
|
handler func(h *TrainingHandlers, c *gin.Context)
|
|
params gin.Params
|
|
}{
|
|
{"GenerateInteractiveVideo", "POST",
|
|
func(h *TrainingHandlers, c *gin.Context) { h.GenerateInteractiveVideo(c) },
|
|
gin.Params{{Key: "moduleId", Value: "x"}}},
|
|
{"GetInteractiveManifest", "GET",
|
|
func(h *TrainingHandlers, c *gin.Context) { h.GetInteractiveManifest(c) },
|
|
gin.Params{{Key: "moduleId", Value: "x"}}},
|
|
{"SubmitCheckpointQuiz", "POST",
|
|
func(h *TrainingHandlers, c *gin.Context) { h.SubmitCheckpointQuiz(c) },
|
|
gin.Params{{Key: "checkpointId", Value: "x"}}},
|
|
{"GetCheckpointProgress", "GET",
|
|
func(h *TrainingHandlers, c *gin.Context) { h.GetCheckpointProgress(c) },
|
|
gin.Params{{Key: "assignmentId", Value: "x"}}},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext(tt.method, "/test", nil, nil, tt.params)
|
|
tt.handler(h, c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("%s: Expected 400, got %d", tt.name, w.Code)
|
|
}
|
|
resp := parseResponse(w)
|
|
if resp["error"] == nil {
|
|
t.Errorf("%s: response should contain 'error' key", tt.name)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Block Endpoint Tests
|
|
// ============================================================================
|
|
|
|
func TestGetBlockConfig_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/blocks/bad", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.GetBlockConfig(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestCreateBlockConfig_EmptyBody_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/blocks", nil, nil, nil)
|
|
h.CreateBlockConfig(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestCreateBlockConfig_MissingName_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
body := map[string]interface{}{"regulation_area": "dsgvo", "module_code_prefix": "BLK"}
|
|
w, c := newTestContext("POST", "/blocks", body, nil, nil)
|
|
h.CreateBlockConfig(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestUpdateBlockConfig_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("PUT", "/blocks/bad", map[string]interface{}{"name": "x"}, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.UpdateBlockConfig(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestDeleteBlockConfig_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("DELETE", "/blocks/bad", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.DeleteBlockConfig(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestPreviewBlock_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/blocks/bad/preview", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.PreviewBlock(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestGenerateBlock_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("POST", "/blocks/bad/generate", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.GenerateBlock(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestGetBlockControls_InvalidID_Returns400(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext("GET", "/blocks/bad/controls", nil, nil, gin.Params{{Key: "id", Value: "bad"}})
|
|
h.GetBlockControls(c)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Response Error Format Tests
|
|
// ============================================================================
|
|
|
|
func TestInvalidID_ResponseContainsErrorKey(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
method string
|
|
handler func(h *TrainingHandlers, c *gin.Context)
|
|
params gin.Params
|
|
}{
|
|
{"GetModule", "GET", func(h *TrainingHandlers, c *gin.Context) { h.GetModule(c) }, gin.Params{{Key: "id", Value: "x"}}},
|
|
{"DeleteModule", "DELETE", func(h *TrainingHandlers, c *gin.Context) { h.DeleteModule(c) }, gin.Params{{Key: "id", Value: "x"}}},
|
|
{"StreamMedia", "GET", func(h *TrainingHandlers, c *gin.Context) { h.StreamMedia(c) }, gin.Params{{Key: "id", Value: "x"}}},
|
|
{"GenerateCertificate", "POST", func(h *TrainingHandlers, c *gin.Context) { h.GenerateCertificate(c) }, gin.Params{{Key: "assignmentId", Value: "x"}}},
|
|
{"DownloadCertificatePDF", "GET", func(h *TrainingHandlers, c *gin.Context) { h.DownloadCertificatePDF(c) }, gin.Params{{Key: "id", Value: "x"}}},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
h := &TrainingHandlers{}
|
|
w, c := newTestContext(tt.method, "/test", nil, nil, tt.params)
|
|
tt.handler(h, c)
|
|
resp := parseResponse(w)
|
|
if resp["error"] == nil {
|
|
t.Errorf("%s: response should contain 'error' key", tt.name)
|
|
}
|
|
})
|
|
}
|
|
}
|