Files
breakpilot-compliance/ai-compliance-sdk/internal/api/handlers/training_handlers_test.go
Benjamin Admin 4f6bc8f6f6
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
feat(training+controls): interactive video pipeline, training blocks, control generator, CE libraries
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>
2026-03-16 21:41:48 +01:00

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)
}
})
}
}