This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/consent-service/internal/services/jitsi/jitsi_service_test.go
Benjamin Admin bfdaf63ba9 fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:51:32 +01:00

688 lines
17 KiB
Go

package jitsi
import (
"context"
"encoding/base64"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
// ========================================
// Test Helpers
// ========================================
func createTestService() *JitsiService {
return NewJitsiService(Config{
BaseURL: "http://localhost:8443",
AppID: "breakpilot",
AppSecret: "test-secret-key",
})
}
func createTestServiceWithoutAuth() *JitsiService {
return NewJitsiService(Config{
BaseURL: "http://localhost:8443",
})
}
// ========================================
// Unit Tests: Service Creation
// ========================================
func TestNewJitsiService_ValidConfig_CreatesService(t *testing.T) {
cfg := Config{
BaseURL: "http://localhost:8443",
AppID: "test-app",
AppSecret: "test-secret",
}
service := NewJitsiService(cfg)
if service == nil {
t.Fatal("Expected service to be created, got nil")
}
if service.baseURL != cfg.BaseURL {
t.Errorf("Expected baseURL %s, got %s", cfg.BaseURL, service.baseURL)
}
if service.appID != cfg.AppID {
t.Errorf("Expected appID %s, got %s", cfg.AppID, service.appID)
}
if service.appSecret != cfg.AppSecret {
t.Errorf("Expected appSecret %s, got %s", cfg.AppSecret, service.appSecret)
}
if service.httpClient == nil {
t.Error("Expected httpClient to be initialized")
}
}
func TestNewJitsiService_TrailingSlash_Removed(t *testing.T) {
service := NewJitsiService(Config{
BaseURL: "http://localhost:8443/",
})
if service.baseURL != "http://localhost:8443" {
t.Errorf("Expected trailing slash to be removed, got %s", service.baseURL)
}
}
func TestGetBaseURL_ReturnsConfiguredURL(t *testing.T) {
service := createTestService()
result := service.GetBaseURL()
if result != "http://localhost:8443" {
t.Errorf("Expected 'http://localhost:8443', got '%s'", result)
}
}
func TestIsAuthEnabled_WithSecret_ReturnsTrue(t *testing.T) {
service := createTestService()
if !service.IsAuthEnabled() {
t.Error("Expected auth to be enabled when secret is configured")
}
}
func TestIsAuthEnabled_WithoutSecret_ReturnsFalse(t *testing.T) {
service := createTestServiceWithoutAuth()
if service.IsAuthEnabled() {
t.Error("Expected auth to be disabled when secret is not configured")
}
}
// ========================================
// Unit Tests: Room Name Generation
// ========================================
func TestSanitizeRoomName_ValidInput_ReturnsCleanName(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{
name: "simple name",
input: "meeting",
expected: "meeting",
},
{
name: "with spaces",
input: "My Meeting Room",
expected: "my-meeting-room",
},
{
name: "german umlauts",
input: "Schüler Müller",
expected: "schueler-mueller",
},
{
name: "special characters",
input: "Test@#$%Meeting!",
expected: "testmeeting",
},
{
name: "consecutive hyphens",
input: "test---meeting",
expected: "test-meeting",
},
{
name: "leading trailing hyphens",
input: "-test-meeting-",
expected: "test-meeting",
},
{
name: "eszett",
input: "Straße",
expected: "strasse",
},
{
name: "numbers",
input: "Klasse 5a",
expected: "klasse-5a",
},
}
service := createTestService()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := service.sanitizeRoomName(tt.input)
if result != tt.expected {
t.Errorf("Expected '%s', got '%s'", tt.expected, result)
}
})
}
}
func TestSanitizeRoomName_LongName_Truncated(t *testing.T) {
service := createTestService()
longName := strings.Repeat("a", 100)
result := service.sanitizeRoomName(longName)
if len(result) > 50 {
t.Errorf("Expected max 50 chars, got %d", len(result))
}
}
func TestGenerateRoomName_ReturnsUniqueNames(t *testing.T) {
service := createTestService()
name1 := service.generateRoomName()
name2 := service.generateRoomName()
if name1 == name2 {
t.Error("Expected unique room names")
}
if !strings.HasPrefix(name1, "breakpilot-") {
t.Errorf("Expected prefix 'breakpilot-', got '%s'", name1)
}
}
func TestGenerateTrainingRoomName_IncludesTitle(t *testing.T) {
service := createTestService()
result := service.generateTrainingRoomName("Go Workshop")
if !strings.HasPrefix(result, "go-workshop-") {
t.Errorf("Expected to start with 'go-workshop-', got '%s'", result)
}
}
func TestGeneratePassword_ReturnsValidPassword(t *testing.T) {
service := createTestService()
password := service.generatePassword()
if len(password) != 8 {
t.Errorf("Expected 8 char password, got %d", len(password))
}
}
// ========================================
// Unit Tests: Meeting Link Creation
// ========================================
func TestCreateMeetingLink_BasicMeeting_ReturnsValidLink(t *testing.T) {
service := createTestServiceWithoutAuth()
meeting := Meeting{
RoomName: "test-room",
DisplayName: "Test User",
}
link, err := service.CreateMeetingLink(context.Background(), meeting)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if link.RoomName != "test-room" {
t.Errorf("Expected room name 'test-room', got '%s'", link.RoomName)
}
if link.URL != "http://localhost:8443/test-room" {
t.Errorf("Expected URL 'http://localhost:8443/test-room', got '%s'", link.URL)
}
if link.JoinURL != "http://localhost:8443/test-room" {
t.Errorf("Expected JoinURL 'http://localhost:8443/test-room', got '%s'", link.JoinURL)
}
}
func TestCreateMeetingLink_NoRoomName_GeneratesName(t *testing.T) {
service := createTestServiceWithoutAuth()
meeting := Meeting{
DisplayName: "Test User",
}
link, err := service.CreateMeetingLink(context.Background(), meeting)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if link.RoomName == "" {
t.Error("Expected room name to be generated")
}
if !strings.HasPrefix(link.RoomName, "breakpilot-") {
t.Errorf("Expected generated room name to start with 'breakpilot-', got '%s'", link.RoomName)
}
}
func TestCreateMeetingLink_WithPassword_IncludesPassword(t *testing.T) {
service := createTestServiceWithoutAuth()
meeting := Meeting{
RoomName: "test-room",
Password: "secret123",
}
link, err := service.CreateMeetingLink(context.Background(), meeting)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if link.Password != "secret123" {
t.Errorf("Expected password 'secret123', got '%s'", link.Password)
}
}
func TestCreateMeetingLink_WithAuth_IncludesJWT(t *testing.T) {
service := createTestService()
meeting := Meeting{
RoomName: "test-room",
DisplayName: "Test User",
Email: "test@example.com",
}
link, err := service.CreateMeetingLink(context.Background(), meeting)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if link.JWT == "" {
t.Error("Expected JWT to be generated")
}
if !strings.Contains(link.JoinURL, "jwt=") {
t.Error("Expected JoinURL to contain JWT parameter")
}
if link.ExpiresAt == nil {
t.Error("Expected ExpiresAt to be set")
}
}
func TestCreateMeetingLink_WithConfig_IncludesParams(t *testing.T) {
service := createTestServiceWithoutAuth()
meeting := Meeting{
RoomName: "test-room",
Config: &MeetingConfig{
StartWithAudioMuted: true,
StartWithVideoMuted: true,
RequireDisplayName: true,
},
}
link, err := service.CreateMeetingLink(context.Background(), meeting)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if !strings.Contains(link.JoinURL, "startWithAudioMuted=true") {
t.Error("Expected JoinURL to contain audio muted config")
}
if !strings.Contains(link.JoinURL, "startWithVideoMuted=true") {
t.Error("Expected JoinURL to contain video muted config")
}
}
func TestCreateMeetingLink_Moderator_SetsModeratorURL(t *testing.T) {
service := createTestService()
meeting := Meeting{
RoomName: "test-room",
DisplayName: "Admin",
Moderator: true,
}
link, err := service.CreateMeetingLink(context.Background(), meeting)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if link.ModeratorURL == "" {
t.Error("Expected ModeratorURL to be set for moderator")
}
}
// ========================================
// Unit Tests: Specialized Meeting Types
// ========================================
func TestCreateTrainingSession_ReturnsOptimizedConfig(t *testing.T) {
service := createTestServiceWithoutAuth()
link, err := service.CreateTrainingSession(
context.Background(),
"Go Grundlagen",
"Max Trainer",
"trainer@example.com",
60,
)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if !strings.Contains(link.RoomName, "go-grundlagen") {
t.Errorf("Expected room name to contain 'go-grundlagen', got '%s'", link.RoomName)
}
// Config should have lobby enabled for training
if !strings.Contains(link.JoinURL, "enableLobby=true") {
t.Error("Expected training to have lobby enabled")
}
}
func TestCreateQuickMeeting_ReturnsSimpleMeeting(t *testing.T) {
service := createTestServiceWithoutAuth()
link, err := service.CreateQuickMeeting(context.Background(), "Quick User")
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if link.RoomName == "" {
t.Error("Expected room name to be generated")
}
}
func TestCreateParentTeacherMeeting_ReturnsSecureMeeting(t *testing.T) {
service := createTestServiceWithoutAuth()
scheduledTime := time.Now().Add(24 * time.Hour)
link, err := service.CreateParentTeacherMeeting(
context.Background(),
"Frau Müller",
"Herr Schmidt",
"Max Mustermann",
scheduledTime,
)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if !strings.Contains(link.RoomName, "elterngespraech") {
t.Errorf("Expected room name to contain 'elterngespraech', got '%s'", link.RoomName)
}
if link.Password == "" {
t.Error("Expected password for parent-teacher meeting")
}
if !strings.Contains(link.JoinURL, "enableLobby=true") {
t.Error("Expected lobby to be enabled")
}
}
func TestCreateClassMeeting_ReturnsMeetingForClass(t *testing.T) {
service := createTestServiceWithoutAuth()
link, err := service.CreateClassMeeting(
context.Background(),
"5a",
"Herr Lehrer",
"Mathematik",
)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if !strings.Contains(link.RoomName, "klasse-5a") {
t.Errorf("Expected room name to contain 'klasse-5a', got '%s'", link.RoomName)
}
// Students should be muted by default
if !strings.Contains(link.JoinURL, "startWithAudioMuted=true") {
t.Error("Expected students to start muted")
}
}
// ========================================
// Unit Tests: JWT Generation
// ========================================
func TestGenerateJWT_ValidClaims_ReturnsValidToken(t *testing.T) {
service := createTestService()
meeting := Meeting{
RoomName: "test-room",
DisplayName: "Test User",
Email: "test@example.com",
Moderator: true,
Duration: 60,
}
token, expiresAt, err := service.generateJWT(meeting, "test-room")
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if token == "" {
t.Error("Expected token to be generated")
}
if expiresAt == nil {
t.Error("Expected expiration time to be set")
}
// Verify token structure (header.payload.signature)
parts := strings.Split(token, ".")
if len(parts) != 3 {
t.Errorf("Expected 3 JWT parts, got %d", len(parts))
}
// Decode and verify payload
payloadJSON, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
t.Fatalf("Failed to decode payload: %v", err)
}
var claims JWTClaims
if err := json.Unmarshal(payloadJSON, &claims); err != nil {
t.Fatalf("Failed to unmarshal claims: %v", err)
}
if claims.Room != "test-room" {
t.Errorf("Expected room 'test-room', got '%s'", claims.Room)
}
if !claims.Moderator {
t.Error("Expected moderator to be true")
}
if claims.Context == nil || claims.Context.User == nil {
t.Error("Expected user context to be set")
}
if claims.Context.User.Name != "Test User" {
t.Errorf("Expected user name 'Test User', got '%s'", claims.Context.User.Name)
}
}
func TestGenerateJWT_WithFeatures_IncludesFeatures(t *testing.T) {
service := createTestService()
meeting := Meeting{
RoomName: "test-room",
Features: &MeetingFeatures{
Recording: true,
Transcription: true,
},
}
token, _, err := service.generateJWT(meeting, "test-room")
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
parts := strings.Split(token, ".")
payloadJSON, _ := base64.RawURLEncoding.DecodeString(parts[1])
var claims JWTClaims
json.Unmarshal(payloadJSON, &claims)
if claims.Features == nil {
t.Error("Expected features to be set")
}
if claims.Features.Recording != "true" {
t.Errorf("Expected recording 'true', got '%s'", claims.Features.Recording)
}
}
func TestGenerateJWT_NoSecret_ReturnsError(t *testing.T) {
service := createTestServiceWithoutAuth()
meeting := Meeting{RoomName: "test"}
_, _, err := service.generateJWT(meeting, "test")
if err == nil {
t.Error("Expected error when secret is not configured")
}
}
// ========================================
// Unit Tests: Health Check
// ========================================
func TestHealthCheck_ServerAvailable_ReturnsNil(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
service := NewJitsiService(Config{BaseURL: server.URL})
err := service.HealthCheck(context.Background())
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
func TestHealthCheck_ServerError_ReturnsError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer server.Close()
service := NewJitsiService(Config{BaseURL: server.URL})
err := service.HealthCheck(context.Background())
if err == nil {
t.Error("Expected error for server error response")
}
}
func TestHealthCheck_ServerUnreachable_ReturnsError(t *testing.T) {
service := NewJitsiService(Config{BaseURL: "http://localhost:59999"})
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
err := service.HealthCheck(ctx)
if err == nil {
t.Error("Expected error for unreachable server")
}
}
// ========================================
// Unit Tests: URL Building
// ========================================
func TestBuildEmbedURL_BasicRoom_ReturnsURL(t *testing.T) {
service := createTestService()
url := service.BuildEmbedURL("test-room", "", nil)
if url != "http://localhost:8443/test-room" {
t.Errorf("Expected 'http://localhost:8443/test-room', got '%s'", url)
}
}
func TestBuildEmbedURL_WithDisplayName_IncludesParam(t *testing.T) {
service := createTestService()
url := service.BuildEmbedURL("test-room", "Max Mustermann", nil)
if !strings.Contains(url, "displayName=Max") {
t.Errorf("Expected URL to contain display name, got '%s'", url)
}
}
func TestBuildEmbedURL_WithConfig_IncludesParams(t *testing.T) {
service := createTestService()
config := &MeetingConfig{
StartWithAudioMuted: true,
StartWithVideoMuted: true,
}
url := service.BuildEmbedURL("test-room", "", config)
if !strings.Contains(url, "startWithAudioMuted=true") {
t.Error("Expected URL to contain audio muted config")
}
if !strings.Contains(url, "startWithVideoMuted=true") {
t.Error("Expected URL to contain video muted config")
}
}
func TestBuildIFrameCode_DefaultSize_Returns800x600(t *testing.T) {
service := createTestService()
code := service.BuildIFrameCode("test-room", 0, 0)
if !strings.Contains(code, "width=\"800\"") {
t.Error("Expected default width 800")
}
if !strings.Contains(code, "height=\"600\"") {
t.Error("Expected default height 600")
}
if !strings.Contains(code, "test-room") {
t.Error("Expected room name in iframe")
}
if !strings.Contains(code, "allow=\"camera; microphone") {
t.Error("Expected camera/microphone permissions")
}
}
func TestBuildIFrameCode_CustomSize_ReturnsCorrectDimensions(t *testing.T) {
service := createTestService()
code := service.BuildIFrameCode("test-room", 1920, 1080)
if !strings.Contains(code, "width=\"1920\"") {
t.Error("Expected width 1920")
}
if !strings.Contains(code, "height=\"1080\"") {
t.Error("Expected height 1080")
}
}
// ========================================
// Unit Tests: Server Info
// ========================================
func TestGetServerInfo_ReturnsInfo(t *testing.T) {
service := createTestService()
info := service.GetServerInfo()
if info["base_url"] != "http://localhost:8443" {
t.Errorf("Expected base_url, got '%s'", info["base_url"])
}
if info["app_id"] != "breakpilot" {
t.Errorf("Expected app_id 'breakpilot', got '%s'", info["app_id"])
}
if info["auth_enabled"] != "true" {
t.Errorf("Expected auth_enabled 'true', got '%s'", info["auth_enabled"])
}
}
// ========================================
// Unit Tests: Helper Functions
// ========================================
func TestBoolToString_True_ReturnsTrue(t *testing.T) {
result := boolToString(true)
if result != "true" {
t.Errorf("Expected 'true', got '%s'", result)
}
}
func TestBoolToString_False_ReturnsFalse(t *testing.T) {
result := boolToString(false)
if result != "false" {
t.Errorf("Expected 'false', got '%s'", result)
}
}