package handlers import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" ) // TestGetCommunicationStatus_NoServices tests status with no services configured func TestGetCommunicationStatus_NoServices_ReturnsDisabled(t *testing.T) { gin.SetMode(gin.TestMode) // Create handler with no services handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.GET("/api/v1/communication/status", handler.GetCommunicationStatus) req, _ := http.NewRequest("GET", "/api/v1/communication/status", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status 200, got %d", w.Code) } var response map[string]interface{} if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("Failed to parse response: %v", err) } // Check matrix is disabled matrix, ok := response["matrix"].(map[string]interface{}) if !ok { t.Fatal("Expected matrix in response") } if matrix["enabled"] != false { t.Error("Expected matrix.enabled to be false") } // Check jitsi is disabled jitsi, ok := response["jitsi"].(map[string]interface{}) if !ok { t.Fatal("Expected jitsi in response") } if jitsi["enabled"] != false { t.Error("Expected jitsi.enabled to be false") } // Check timestamp exists if _, ok := response["timestamp"]; !ok { t.Error("Expected timestamp in response") } } // TestCreateRoom_NoMatrixService tests room creation without Matrix func TestCreateRoom_NoMatrixService_Returns503(t *testing.T) { gin.SetMode(gin.TestMode) handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.POST("/api/v1/communication/rooms", handler.CreateRoom) body := `{"type": "class_info", "class_name": "5b"}` req, _ := http.NewRequest("POST", "/api/v1/communication/rooms", bytes.NewBufferString(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusServiceUnavailable { t.Errorf("Expected status 503, got %d", w.Code) } var response map[string]string if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("Failed to parse response: %v", err) } if response["error"] != "Matrix service not configured" { t.Errorf("Unexpected error message: %s", response["error"]) } } // TestCreateRoom_InvalidBody tests room creation with invalid body func TestCreateRoom_InvalidBody_Returns400(t *testing.T) { gin.SetMode(gin.TestMode) handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.POST("/api/v1/communication/rooms", handler.CreateRoom) req, _ := http.NewRequest("POST", "/api/v1/communication/rooms", bytes.NewBufferString("{invalid")) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) // Service unavailable check happens first, so we get 503 // This is expected behavior - service check before body validation if w.Code != http.StatusServiceUnavailable { t.Errorf("Expected status 503, got %d", w.Code) } } // TestInviteUser_NoMatrixService tests invite without Matrix func TestInviteUser_NoMatrixService_Returns503(t *testing.T) { gin.SetMode(gin.TestMode) handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.POST("/api/v1/communication/rooms/invite", handler.InviteUser) body := `{"room_id": "!abc:server", "user_id": "@user:server"}` req, _ := http.NewRequest("POST", "/api/v1/communication/rooms/invite", bytes.NewBufferString(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusServiceUnavailable { t.Errorf("Expected status 503, got %d", w.Code) } } // TestSendMessage_NoMatrixService tests message sending without Matrix func TestSendMessage_NoMatrixService_Returns503(t *testing.T) { gin.SetMode(gin.TestMode) handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.POST("/api/v1/communication/messages", handler.SendMessage) body := `{"room_id": "!abc:server", "message": "Hello"}` req, _ := http.NewRequest("POST", "/api/v1/communication/messages", bytes.NewBufferString(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusServiceUnavailable { t.Errorf("Expected status 503, got %d", w.Code) } } // TestSendNotification_NoMatrixService tests notification without Matrix func TestSendNotification_NoMatrixService_Returns503(t *testing.T) { gin.SetMode(gin.TestMode) handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.POST("/api/v1/communication/notifications", handler.SendNotification) body := `{"room_id": "!abc:server", "type": "absence", "student_name": "Max"}` req, _ := http.NewRequest("POST", "/api/v1/communication/notifications", bytes.NewBufferString(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusServiceUnavailable { t.Errorf("Expected status 503, got %d", w.Code) } } // TestCreateMeeting_NoJitsiService tests meeting creation without Jitsi func TestCreateMeeting_NoJitsiService_Returns503(t *testing.T) { gin.SetMode(gin.TestMode) handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.POST("/api/v1/communication/meetings", handler.CreateMeeting) body := `{"type": "quick", "display_name": "Teacher"}` req, _ := http.NewRequest("POST", "/api/v1/communication/meetings", bytes.NewBufferString(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusServiceUnavailable { t.Errorf("Expected status 503, got %d", w.Code) } var response map[string]string if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("Failed to parse response: %v", err) } if response["error"] != "Jitsi service not configured" { t.Errorf("Unexpected error message: %s", response["error"]) } } // TestGetEmbedURL_NoJitsiService tests embed URL without Jitsi func TestGetEmbedURL_NoJitsiService_Returns503(t *testing.T) { gin.SetMode(gin.TestMode) handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.POST("/api/v1/communication/meetings/embed", handler.GetEmbedURL) body := `{"room_name": "test-room", "display_name": "User"}` req, _ := http.NewRequest("POST", "/api/v1/communication/meetings/embed", bytes.NewBufferString(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusServiceUnavailable { t.Errorf("Expected status 503, got %d", w.Code) } } // TestGetJitsiInfo_NoJitsiService tests Jitsi info without service func TestGetJitsiInfo_NoJitsiService_Returns503(t *testing.T) { gin.SetMode(gin.TestMode) handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.GET("/api/v1/communication/jitsi/info", handler.GetJitsiInfo) req, _ := http.NewRequest("GET", "/api/v1/communication/jitsi/info", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusServiceUnavailable { t.Errorf("Expected status 503, got %d", w.Code) } } // TestRegisterMatrixUser_NoMatrixService tests user registration without Matrix func TestRegisterMatrixUser_NoMatrixService_Returns503(t *testing.T) { gin.SetMode(gin.TestMode) handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.POST("/api/v1/communication/admin/matrix/users", handler.RegisterMatrixUser) body := `{"username": "testuser", "display_name": "Test User"}` req, _ := http.NewRequest("POST", "/api/v1/communication/admin/matrix/users", bytes.NewBufferString(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusServiceUnavailable { t.Errorf("Expected status 503, got %d", w.Code) } } // TestGetAdminStats_NoServices tests admin stats without services func TestGetAdminStats_NoServices_ReturnsDisabledStats(t *testing.T) { gin.SetMode(gin.TestMode) handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.GET("/api/v1/communication/admin/stats", handler.GetAdminStats) req, _ := http.NewRequest("GET", "/api/v1/communication/admin/stats", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status 200, got %d", w.Code) } var response CommunicationStats if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("Failed to parse response: %v", err) } if response.Matrix.Enabled { t.Error("Expected matrix.enabled to be false") } if response.Jitsi.Enabled { t.Error("Expected jitsi.enabled to be false") } } // TestErrToString tests the helper function func TestErrToString_NilError_ReturnsEmpty(t *testing.T) { result := errToString(nil) if result != "" { t.Errorf("Expected empty string, got %s", result) } } // TestErrToString_WithError_ReturnsMessage tests error string conversion func TestErrToString_WithError_ReturnsMessage(t *testing.T) { err := &testError{"test error message"} result := errToString(err) if result != "test error message" { t.Errorf("Expected 'test error message', got %s", result) } } // testError is a simple error implementation for testing type testError struct { message string } func (e *testError) Error() string { return e.message } // TestCreateRoomRequest_Types tests different room types validation func TestCreateRoom_InvalidType_Returns400(t *testing.T) { gin.SetMode(gin.TestMode) // Since we don't have Matrix service, we get 503 first // This test documents expected behavior when Matrix IS available handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.POST("/api/v1/communication/rooms", handler.CreateRoom) body := `{"type": "invalid_type", "class_name": "5b"}` req, _ := http.NewRequest("POST", "/api/v1/communication/rooms", bytes.NewBufferString(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) // Without Matrix service, we get 503 before type validation if w.Code != http.StatusServiceUnavailable { t.Errorf("Expected status 503, got %d", w.Code) } } // TestCreateMeeting_InvalidType tests invalid meeting type func TestCreateMeeting_InvalidType_Returns503(t *testing.T) { gin.SetMode(gin.TestMode) handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.POST("/api/v1/communication/meetings", handler.CreateMeeting) body := `{"type": "invalid", "display_name": "User"}` req, _ := http.NewRequest("POST", "/api/v1/communication/meetings", bytes.NewBufferString(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) // Without Jitsi service, we get 503 before type validation if w.Code != http.StatusServiceUnavailable { t.Errorf("Expected status 503, got %d", w.Code) } } // TestSendNotification_InvalidType tests invalid notification type func TestSendNotification_InvalidType_Returns503(t *testing.T) { gin.SetMode(gin.TestMode) handler := NewCommunicationHandlers(nil, nil) router := gin.New() router.POST("/api/v1/communication/notifications", handler.SendNotification) body := `{"room_id": "!abc:server", "type": "invalid", "student_name": "Max"}` req, _ := http.NewRequest("POST", "/api/v1/communication/notifications", bytes.NewBufferString(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) // Without Matrix service, we get 503 before type validation if w.Code != http.StatusServiceUnavailable { t.Errorf("Expected status 503, got %d", w.Code) } } // TestNewCommunicationHandlers tests constructor func TestNewCommunicationHandlers_WithNilServices_CreatesHandler(t *testing.T) { handler := NewCommunicationHandlers(nil, nil) if handler == nil { t.Fatal("Expected handler to be created") } if handler.matrixService != nil { t.Error("Expected matrixService to be nil") } if handler.jitsiService != nil { t.Error("Expected jitsiService to be nil") } }