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

440 lines
10 KiB
Go

package services
import (
"testing"
"time"
"github.com/google/uuid"
)
// TestDeadlineService_CreateDeadline tests creating consent deadlines
func TestDeadlineService_CreateDeadline(t *testing.T) {
tests := []struct {
name string
userID uuid.UUID
versionID uuid.UUID
deadlineAt time.Time
expectError bool
}{
{
name: "valid deadline - 30 days",
userID: uuid.New(),
versionID: uuid.New(),
deadlineAt: time.Now().AddDate(0, 0, 30),
expectError: false,
},
{
name: "valid deadline - 14 days",
userID: uuid.New(),
versionID: uuid.New(),
deadlineAt: time.Now().AddDate(0, 0, 14),
expectError: false,
},
{
name: "invalid user ID",
userID: uuid.Nil,
versionID: uuid.New(),
deadlineAt: time.Now().AddDate(0, 0, 30),
expectError: true,
},
{
name: "invalid version ID",
userID: uuid.New(),
versionID: uuid.Nil,
deadlineAt: time.Now().AddDate(0, 0, 30),
expectError: true,
},
{
name: "deadline in past",
userID: uuid.New(),
versionID: uuid.New(),
deadlineAt: time.Now().AddDate(0, 0, -1),
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var err error
if tt.userID == uuid.Nil {
err = &ValidationError{Field: "user ID", Message: "required"}
} else if tt.versionID == uuid.Nil {
err = &ValidationError{Field: "version ID", Message: "required"}
} else if tt.deadlineAt.Before(time.Now()) {
err = &ValidationError{Field: "deadline", Message: "must be in the future"}
}
if tt.expectError && err == nil {
t.Error("Expected error, got nil")
}
if !tt.expectError && err != nil {
t.Errorf("Expected no error, got %v", err)
}
})
}
}
// TestDeadlineService_CheckDeadlineStatus tests deadline status checking
func TestDeadlineService_CheckDeadlineStatus(t *testing.T) {
now := time.Now()
tests := []struct {
name string
deadlineAt time.Time
isOverdue bool
daysLeft int
urgency string
}{
{
name: "30 days left",
deadlineAt: now.AddDate(0, 0, 30),
isOverdue: false,
daysLeft: 30,
urgency: "normal",
},
{
name: "7 days left - warning",
deadlineAt: now.AddDate(0, 0, 7),
isOverdue: false,
daysLeft: 7,
urgency: "warning",
},
{
name: "3 days left - urgent",
deadlineAt: now.AddDate(0, 0, 3),
isOverdue: false,
daysLeft: 3,
urgency: "urgent",
},
{
name: "1 day left - critical",
deadlineAt: now.AddDate(0, 0, 1),
isOverdue: false,
daysLeft: 1,
urgency: "critical",
},
{
name: "overdue by 1 day",
deadlineAt: now.AddDate(0, 0, -1),
isOverdue: true,
daysLeft: -1,
urgency: "overdue",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isOverdue := tt.deadlineAt.Before(now)
daysLeft := int(tt.deadlineAt.Sub(now).Hours() / 24)
var urgency string
if isOverdue {
urgency = "overdue"
} else if daysLeft <= 1 {
urgency = "critical"
} else if daysLeft <= 3 {
urgency = "urgent"
} else if daysLeft <= 7 {
urgency = "warning"
} else {
urgency = "normal"
}
if isOverdue != tt.isOverdue {
t.Errorf("Expected isOverdue=%v, got %v", tt.isOverdue, isOverdue)
}
if abs(daysLeft-tt.daysLeft) > 1 { // Allow 1 day difference
t.Errorf("Expected daysLeft=%d, got %d", tt.daysLeft, daysLeft)
}
if urgency != tt.urgency {
t.Errorf("Expected urgency=%s, got %s", tt.urgency, urgency)
}
})
}
}
// TestDeadlineService_SendReminders tests reminder scheduling
func TestDeadlineService_SendReminders(t *testing.T) {
now := time.Now()
tests := []struct {
name string
deadlineAt time.Time
lastReminderAt *time.Time
reminderCount int
shouldSend bool
nextReminder int // days before deadline
}{
{
name: "first reminder - 14 days before",
deadlineAt: now.AddDate(0, 0, 14),
lastReminderAt: nil,
reminderCount: 0,
shouldSend: true,
nextReminder: 14,
},
{
name: "second reminder - 7 days before",
deadlineAt: now.AddDate(0, 0, 7),
lastReminderAt: ptrTime(now.AddDate(0, 0, -7)),
reminderCount: 1,
shouldSend: true,
nextReminder: 7,
},
{
name: "third reminder - 3 days before",
deadlineAt: now.AddDate(0, 0, 3),
lastReminderAt: ptrTime(now.AddDate(0, 0, -4)),
reminderCount: 2,
shouldSend: true,
nextReminder: 3,
},
{
name: "final reminder - 1 day before",
deadlineAt: now.AddDate(0, 0, 1),
lastReminderAt: ptrTime(now.AddDate(0, 0, -2)),
reminderCount: 3,
shouldSend: true,
nextReminder: 1,
},
{
name: "too soon for next reminder",
deadlineAt: now.AddDate(0, 0, 10),
lastReminderAt: ptrTime(now.AddDate(0, 0, -1)),
reminderCount: 1,
shouldSend: false,
nextReminder: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
daysUntilDeadline := int(tt.deadlineAt.Sub(now).Hours() / 24)
// Reminder schedule: 14, 7, 3, 1 days before deadline
reminderDays := []int{14, 7, 3, 1}
shouldSend := false
for _, day := range reminderDays {
if daysUntilDeadline == day {
// Check if enough time passed since last reminder
if tt.lastReminderAt == nil || now.Sub(*tt.lastReminderAt) > 12*time.Hour {
shouldSend = true
break
}
}
}
if shouldSend != tt.shouldSend {
t.Errorf("Expected shouldSend=%v, got %v", tt.shouldSend, shouldSend)
}
})
}
}
// TestDeadlineService_SuspendAccount tests account suspension logic
func TestDeadlineService_SuspendAccount(t *testing.T) {
tests := []struct {
name string
userID uuid.UUID
reason string
shouldSuspend bool
expectError bool
}{
{
name: "suspend for missed deadline",
userID: uuid.New(),
reason: "consent_deadline_exceeded",
shouldSuspend: true,
expectError: false,
},
{
name: "invalid user ID",
userID: uuid.Nil,
reason: "consent_deadline_exceeded",
shouldSuspend: false,
expectError: true,
},
{
name: "invalid reason",
userID: uuid.New(),
reason: "",
shouldSuspend: false,
expectError: true,
},
}
validReasons := map[string]bool{
"consent_deadline_exceeded": true,
"mandatory_consent_missing": true,
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var err error
if tt.userID == uuid.Nil {
err = &ValidationError{Field: "user ID", Message: "required"}
} else if !validReasons[tt.reason] && tt.reason != "" {
err = &ValidationError{Field: "reason", Message: "invalid suspension reason"}
} else if tt.reason == "" {
err = &ValidationError{Field: "reason", 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)
}
})
}
}
// TestDeadlineService_LiftSuspension tests lifting account suspension
func TestDeadlineService_LiftSuspension(t *testing.T) {
tests := []struct {
name string
userID uuid.UUID
adminID uuid.UUID
reason string
expectError bool
}{
{
name: "lift valid suspension",
userID: uuid.New(),
adminID: uuid.New(),
reason: "consent provided",
expectError: false,
},
{
name: "invalid user ID",
userID: uuid.Nil,
adminID: uuid.New(),
reason: "consent provided",
expectError: true,
},
{
name: "invalid admin ID",
userID: uuid.New(),
adminID: uuid.Nil,
reason: "consent provided",
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var err error
if tt.userID == uuid.Nil {
err = &ValidationError{Field: "user ID", Message: "required"}
} else if tt.adminID == uuid.Nil {
err = &ValidationError{Field: "admin 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)
}
})
}
}
// TestDeadlineService_GetOverdueDeadlines tests finding overdue deadlines
func TestDeadlineService_GetOverdueDeadlines(t *testing.T) {
now := time.Now()
tests := []struct {
name string
deadlines []time.Time
expected int // number of overdue
}{
{
name: "no overdue deadlines",
deadlines: []time.Time{
now.AddDate(0, 0, 1),
now.AddDate(0, 0, 7),
now.AddDate(0, 0, 30),
},
expected: 0,
},
{
name: "some overdue",
deadlines: []time.Time{
now.AddDate(0, 0, -1),
now.AddDate(0, 0, -5),
now.AddDate(0, 0, 7),
},
expected: 2,
},
{
name: "all overdue",
deadlines: []time.Time{
now.AddDate(0, 0, -1),
now.AddDate(0, 0, -7),
now.AddDate(0, 0, -30),
},
expected: 3,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
overdueCount := 0
for _, deadline := range tt.deadlines {
if deadline.Before(now) {
overdueCount++
}
}
if overdueCount != tt.expected {
t.Errorf("Expected %d overdue, got %d", tt.expected, overdueCount)
}
})
}
}
// TestDeadlineService_ProcessScheduledTasks tests scheduled task processing
func TestDeadlineService_ProcessScheduledTasks(t *testing.T) {
now := time.Now()
tests := []struct {
name string
task string
scheduledAt time.Time
shouldProcess bool
}{
{
name: "process due task",
task: "send_reminder",
scheduledAt: now.Add(-1 * time.Hour),
shouldProcess: true,
},
{
name: "skip future task",
task: "send_reminder",
scheduledAt: now.Add(1 * time.Hour),
shouldProcess: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
shouldProcess := tt.scheduledAt.Before(now) || tt.scheduledAt.Equal(now)
if shouldProcess != tt.shouldProcess {
t.Errorf("Expected shouldProcess=%v, got %v", tt.shouldProcess, shouldProcess)
}
})
}
}
// Helper functions
func ptrTime(t time.Time) *time.Time {
return &t
}