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/document_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

729 lines
18 KiB
Go

package services
import (
"regexp"
"testing"
"time"
"github.com/google/uuid"
)
// TestDocumentService_CreateDocument tests creating a new legal document
func TestDocumentService_CreateDocument(t *testing.T) {
tests := []struct {
name string
docType string
docName string
description string
isMandatory bool
expectError bool
errorContains string
}{
{
name: "valid mandatory document",
docType: "terms",
docName: "Terms of Service",
description: "Our terms and conditions",
isMandatory: true,
expectError: false,
},
{
name: "valid optional document",
docType: "cookies",
docName: "Cookie Policy",
description: "How we use cookies",
isMandatory: false,
expectError: false,
},
{
name: "empty document type",
docType: "",
docName: "Test Document",
description: "Test",
isMandatory: true,
expectError: true,
errorContains: "type",
},
{
name: "empty document name",
docType: "privacy",
docName: "",
description: "Test",
isMandatory: true,
expectError: true,
errorContains: "name",
},
{
name: "invalid document type",
docType: "invalid_type",
docName: "Test",
description: "Test",
isMandatory: false,
expectError: true,
errorContains: "type",
},
}
validTypes := map[string]bool{
"terms": true,
"privacy": true,
"cookies": true,
"community_guidelines": true,
"imprint": true,
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Validate inputs
var err error
if tt.docType == "" {
err = &ValidationError{Field: "type", Message: "required"}
} else if !validTypes[tt.docType] {
err = &ValidationError{Field: "type", Message: "invalid document type"}
} else if tt.docName == "" {
err = &ValidationError{Field: "name", Message: "required"}
}
// Assert
if tt.expectError {
if err == nil {
t.Errorf("Expected error containing '%s', got nil", tt.errorContains)
}
} else {
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
})
}
}
// TestDocumentService_UpdateDocument tests updating a document
func TestDocumentService_UpdateDocument(t *testing.T) {
tests := []struct {
name string
documentID uuid.UUID
newName string
newActive bool
expectError bool
}{
{
name: "valid update",
documentID: uuid.New(),
newName: "Updated Name",
newActive: true,
expectError: false,
},
{
name: "deactivate document",
documentID: uuid.New(),
newName: "Test",
newActive: false,
expectError: false,
},
{
name: "invalid document ID",
documentID: uuid.Nil,
newName: "Test",
newActive: true,
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var err error
if tt.documentID == uuid.Nil {
err = &ValidationError{Field: "document ID", Message: "required"}
}
if tt.expectError && err == nil {
t.Error("Expected error, got nil")
}
if !tt.expectError && err != nil {
t.Errorf("Expected no error, got %v", err)
}
})
}
}
// TestDocumentService_CreateVersion tests creating a document version
func TestDocumentService_CreateVersion(t *testing.T) {
tests := []struct {
name string
documentID uuid.UUID
version string
language string
title string
content string
expectError bool
errorContains string
}{
{
name: "valid version - German",
documentID: uuid.New(),
version: "1.0.0",
language: "de",
title: "Nutzungsbedingungen",
content: "<h1>Terms</h1><p>Content...</p>",
expectError: false,
},
{
name: "valid version - English",
documentID: uuid.New(),
version: "1.0.0",
language: "en",
title: "Terms of Service",
content: "<h1>Terms</h1><p>Content...</p>",
expectError: false,
},
{
name: "invalid version format",
documentID: uuid.New(),
version: "1.0",
language: "de",
title: "Test",
content: "Content",
expectError: true,
errorContains: "version",
},
{
name: "invalid language",
documentID: uuid.New(),
version: "1.0.0",
language: "fr",
title: "Test",
content: "Content",
expectError: true,
errorContains: "language",
},
{
name: "empty title",
documentID: uuid.New(),
version: "1.0.0",
language: "de",
title: "",
content: "Content",
expectError: true,
errorContains: "title",
},
{
name: "empty content",
documentID: uuid.New(),
version: "1.0.0",
language: "de",
title: "Test",
content: "",
expectError: true,
errorContains: "content",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Validate semver format (X.Y.Z pattern)
validVersion := regexp.MustCompile(`^\d+\.\d+\.\d+$`).MatchString(tt.version)
validLanguage := tt.language == "de" || tt.language == "en"
var err error
if !validVersion {
err = &ValidationError{Field: "version", Message: "invalid format"}
} else if !validLanguage {
err = &ValidationError{Field: "language", Message: "must be 'de' or 'en'"}
} else if tt.title == "" {
err = &ValidationError{Field: "title", Message: "required"}
} else if tt.content == "" {
err = &ValidationError{Field: "content", Message: "required"}
}
if tt.expectError {
if err == nil {
t.Errorf("Expected error containing '%s', got nil", tt.errorContains)
}
} else {
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
})
}
}
// TestDocumentService_VersionStatusTransitions tests version status workflow
func TestDocumentService_VersionStatusTransitions(t *testing.T) {
tests := []struct {
name string
fromStatus string
toStatus string
isAllowed bool
}{
// Valid transitions
{"draft to review", "draft", "review", true},
{"review to approved", "review", "approved", true},
{"review to rejected", "review", "rejected", true},
{"approved to published", "approved", "published", true},
{"approved to scheduled", "approved", "scheduled", true},
{"scheduled to published", "scheduled", "published", true},
{"published to archived", "published", "archived", true},
{"rejected to draft", "rejected", "draft", true},
// Invalid transitions
{"draft to published", "draft", "published", false},
{"draft to approved", "draft", "approved", false},
{"review to published", "review", "published", false},
{"published to draft", "published", "draft", false},
{"published to review", "published", "review", false},
{"archived to draft", "archived", "draft", false},
{"archived to published", "archived", "published", false},
}
// Define valid transitions
validTransitions := map[string][]string{
"draft": {"review"},
"review": {"approved", "rejected"},
"approved": {"published", "scheduled"},
"scheduled": {"published"},
"published": {"archived"},
"rejected": {"draft"},
"archived": {}, // terminal state
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Check if transition is allowed
allowed := false
if transitions, ok := validTransitions[tt.fromStatus]; ok {
for _, validTo := range transitions {
if validTo == tt.toStatus {
allowed = true
break
}
}
}
if allowed != tt.isAllowed {
t.Errorf("Transition %s->%s: expected allowed=%v, got %v",
tt.fromStatus, tt.toStatus, tt.isAllowed, allowed)
}
})
}
}
// TestDocumentService_PublishVersion tests publishing a version
func TestDocumentService_PublishVersion(t *testing.T) {
tests := []struct {
name string
versionID uuid.UUID
currentStatus string
expectError bool
errorContains string
}{
{
name: "publish approved version",
versionID: uuid.New(),
currentStatus: "approved",
expectError: false,
},
{
name: "publish scheduled version",
versionID: uuid.New(),
currentStatus: "scheduled",
expectError: false,
},
{
name: "cannot publish draft",
versionID: uuid.New(),
currentStatus: "draft",
expectError: true,
errorContains: "draft",
},
{
name: "cannot publish review",
versionID: uuid.New(),
currentStatus: "review",
expectError: true,
errorContains: "review",
},
{
name: "invalid version ID",
versionID: uuid.Nil,
currentStatus: "approved",
expectError: true,
errorContains: "ID",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var err error
if tt.versionID == uuid.Nil {
err = &ValidationError{Field: "version ID", Message: "required"}
} else if tt.currentStatus != "approved" && tt.currentStatus != "scheduled" {
err = &ValidationError{Field: "status", Message: "only approved or scheduled versions can be published"}
}
if tt.expectError {
if err == nil {
t.Errorf("Expected error containing '%s', got nil", tt.errorContains)
}
} else {
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
})
}
}
// TestDocumentService_ArchiveVersion tests archiving a version
func TestDocumentService_ArchiveVersion(t *testing.T) {
tests := []struct {
name string
versionID uuid.UUID
expectError bool
}{
{
name: "archive valid version",
versionID: uuid.New(),
expectError: false,
},
{
name: "invalid version ID",
versionID: uuid.Nil,
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var err error
if tt.versionID == uuid.Nil {
err = &ValidationError{Field: "version ID", Message: "required"}
}
if tt.expectError && err == nil {
t.Error("Expected error, got nil")
}
if !tt.expectError && err != nil {
t.Errorf("Expected no error, got %v", err)
}
})
}
}
// TestDocumentService_DeleteVersion tests deleting a version
func TestDocumentService_DeleteVersion(t *testing.T) {
tests := []struct {
name string
versionID uuid.UUID
status string
canDelete bool
expectError bool
}{
{
name: "delete draft version",
versionID: uuid.New(),
status: "draft",
canDelete: true,
expectError: false,
},
{
name: "delete rejected version",
versionID: uuid.New(),
status: "rejected",
canDelete: true,
expectError: false,
},
{
name: "cannot delete published version",
versionID: uuid.New(),
status: "published",
canDelete: false,
expectError: true,
},
{
name: "cannot delete approved version",
versionID: uuid.New(),
status: "approved",
canDelete: false,
expectError: true,
},
{
name: "cannot delete archived version",
versionID: uuid.New(),
status: "archived",
canDelete: false,
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Only draft and rejected can be deleted
canDelete := tt.status == "draft" || tt.status == "rejected"
var err error
if !canDelete {
err = &ValidationError{Field: "status", Message: "only draft or rejected versions can be deleted"}
}
if tt.expectError {
if err == nil {
t.Error("Expected error, got nil")
}
} else {
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
if canDelete != tt.canDelete {
t.Errorf("Expected canDelete=%v, got %v", tt.canDelete, canDelete)
}
})
}
}
// TestDocumentService_GetLatestVersion tests retrieving the latest version
func TestDocumentService_GetLatestVersion(t *testing.T) {
tests := []struct {
name string
documentID uuid.UUID
language string
status string
expectError bool
}{
{
name: "get latest German version",
documentID: uuid.New(),
language: "de",
status: "published",
expectError: false,
},
{
name: "get latest English version",
documentID: uuid.New(),
language: "en",
status: "published",
expectError: false,
},
{
name: "invalid document ID",
documentID: uuid.Nil,
language: "de",
status: "published",
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var err error
if tt.documentID == uuid.Nil {
err = &ValidationError{Field: "document ID", Message: "required"}
}
if tt.expectError && err == nil {
t.Error("Expected error, got nil")
}
if !tt.expectError && err != nil {
t.Errorf("Expected no error, got %v", err)
}
})
}
}
// TestDocumentService_CompareVersions tests version comparison
func TestDocumentService_CompareVersions(t *testing.T) {
tests := []struct {
name string
version1 string
version2 string
isDifferent bool
}{
{
name: "same version",
version1: "1.0.0",
version2: "1.0.0",
isDifferent: false,
},
{
name: "different major version",
version1: "2.0.0",
version2: "1.0.0",
isDifferent: true,
},
{
name: "different minor version",
version1: "1.1.0",
version2: "1.0.0",
isDifferent: true,
},
{
name: "different patch version",
version1: "1.0.1",
version2: "1.0.0",
isDifferent: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isDifferent := tt.version1 != tt.version2
if isDifferent != tt.isDifferent {
t.Errorf("Expected isDifferent=%v, got %v", tt.isDifferent, isDifferent)
}
})
}
}
// TestDocumentService_ScheduledPublishing tests scheduled publishing
func TestDocumentService_ScheduledPublishing(t *testing.T) {
now := time.Now()
tests := []struct {
name string
scheduledAt time.Time
shouldPublish bool
}{
{
name: "scheduled for past - should publish",
scheduledAt: now.Add(-1 * time.Hour),
shouldPublish: true,
},
{
name: "scheduled for now - should publish",
scheduledAt: now,
shouldPublish: true,
},
{
name: "scheduled for future - should not publish",
scheduledAt: now.Add(1 * time.Hour),
shouldPublish: false,
},
{
name: "scheduled for tomorrow - should not publish",
scheduledAt: now.AddDate(0, 0, 1),
shouldPublish: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
shouldPublish := tt.scheduledAt.Before(now) || tt.scheduledAt.Equal(now)
if shouldPublish != tt.shouldPublish {
t.Errorf("Expected shouldPublish=%v, got %v", tt.shouldPublish, shouldPublish)
}
})
}
}
// TestDocumentService_ApprovalWorkflow tests the approval workflow
func TestDocumentService_ApprovalWorkflow(t *testing.T) {
tests := []struct {
name string
action string
userRole string
isAllowed bool
}{
// Admin permissions
{"admin submit for review", "submit_review", "admin", true},
{"admin cannot approve", "approve", "admin", false},
{"admin can publish", "publish", "admin", true},
// DSB permissions
{"dsb can approve", "approve", "data_protection_officer", true},
{"dsb can reject", "reject", "data_protection_officer", true},
{"dsb can publish", "publish", "data_protection_officer", true},
// User permissions
{"user cannot submit", "submit_review", "user", false},
{"user cannot approve", "approve", "user", false},
{"user cannot publish", "publish", "user", false},
}
permissions := map[string]map[string]bool{
"admin": {
"submit_review": true,
"approve": false,
"reject": false,
"publish": true,
},
"data_protection_officer": {
"submit_review": true,
"approve": true,
"reject": true,
"publish": true,
},
"user": {
"submit_review": false,
"approve": false,
"reject": false,
"publish": false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rolePerms, ok := permissions[tt.userRole]
if !ok {
t.Fatalf("Unknown role: %s", tt.userRole)
}
isAllowed := rolePerms[tt.action]
if isAllowed != tt.isAllowed {
t.Errorf("Role %s action %s: expected allowed=%v, got %v",
tt.userRole, tt.action, tt.isAllowed, isAllowed)
}
})
}
}
// TestDocumentService_FourEyesPrinciple tests the four-eyes principle
func TestDocumentService_FourEyesPrinciple(t *testing.T) {
tests := []struct {
name string
createdBy uuid.UUID
approver uuid.UUID
approverRole string
canApprove bool
}{
{
name: "different users - DSB can approve",
createdBy: uuid.New(),
approver: uuid.New(),
approverRole: "data_protection_officer",
canApprove: true,
},
{
name: "same user - DSB cannot approve own",
createdBy: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"),
approver: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"),
approverRole: "data_protection_officer",
canApprove: false,
},
{
name: "same user - admin CAN approve own (exception)",
createdBy: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"),
approver: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"),
approverRole: "admin",
canApprove: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Four-eyes principle: DSB cannot approve their own work
// Exception: Admins can (for development/testing)
canApprove := tt.createdBy != tt.approver || tt.approverRole == "admin"
if canApprove != tt.canApprove {
t.Errorf("Expected canApprove=%v, got %v", tt.canApprove, canApprove)
}
})
}
}