Files
breakpilot-lehrer/school-service/internal/services/certificate_service_test.go
Benjamin Boenisch 5a31f52310 Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:26 +01:00

564 lines
12 KiB
Go

package services
import (
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func TestCertificateService_ValidateCertificateType(t *testing.T) {
tests := []struct {
name string
certificateType string
wantErr bool
}{
{
name: "valid - halbjahr",
certificateType: "halbjahr",
wantErr: false,
},
{
name: "valid - jahres",
certificateType: "jahres",
wantErr: false,
},
{
name: "valid - abschluss",
certificateType: "abschluss",
wantErr: false,
},
{
name: "invalid type",
certificateType: "invalid",
wantErr: true,
},
{
name: "empty type",
certificateType: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateCertificateType(tt.certificateType)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestCertificateService_ValidateSemester(t *testing.T) {
tests := []struct {
name string
semester int
wantErr bool
}{
{
name: "valid - first semester",
semester: 1,
wantErr: false,
},
{
name: "valid - second semester",
semester: 2,
wantErr: false,
},
{
name: "invalid - zero",
semester: 0,
wantErr: true,
},
{
name: "invalid - three",
semester: 3,
wantErr: true,
},
{
name: "invalid - negative",
semester: -1,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateSemester(tt.semester)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestCertificateService_ValidateTemplateName(t *testing.T) {
tests := []struct {
name string
templateName string
wantErr bool
}{
{
name: "valid - generic grundschule",
templateName: "generic_grundschule",
wantErr: false,
},
{
name: "valid - niedersachsen gymnasium",
templateName: "niedersachsen_gymnasium_sek1",
wantErr: false,
},
{
name: "valid - bayern realschule",
templateName: "bayern_realschule",
wantErr: false,
},
{
name: "empty template",
templateName: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateTemplateName(tt.templateName)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestCertificateService_GetAvailableTemplates(t *testing.T) {
templates := getAvailableTemplates()
assert.NotEmpty(t, templates)
assert.Contains(t, templates, "generic_grundschule")
assert.Contains(t, templates, "generic_sekundarstufe1")
assert.Contains(t, templates, "generic_sekundarstufe2")
}
func TestCertificateService_CertificateStatus(t *testing.T) {
tests := []struct {
name string
currentStatus string
newStatus string
valid bool
}{
{
name: "draft to final",
currentStatus: "draft",
newStatus: "final",
valid: true,
},
{
name: "final to printed",
currentStatus: "final",
newStatus: "printed",
valid: true,
},
{
name: "draft to printed - invalid",
currentStatus: "draft",
newStatus: "printed",
valid: false,
},
{
name: "printed to draft - invalid",
currentStatus: "printed",
newStatus: "draft",
valid: false,
},
{
name: "final to draft - invalid",
currentStatus: "final",
newStatus: "draft",
valid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := isValidCertificateStatusTransition(tt.currentStatus, tt.newStatus)
assert.Equal(t, tt.valid, result)
})
}
}
func TestCertificateService_ValidateGradesJSON(t *testing.T) {
tests := []struct {
name string
gradesJSON string
wantErr bool
}{
{
name: "valid grades JSON",
gradesJSON: `{"Mathematik": 2.0, "Deutsch": 2.5, "Englisch": 3.0}`,
wantErr: false,
},
{
name: "empty grades",
gradesJSON: `{}`,
wantErr: false,
},
{
name: "invalid JSON",
gradesJSON: `{invalid}`,
wantErr: true,
},
{
name: "empty string",
gradesJSON: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateGradesJSON(tt.gradesJSON)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestCertificateService_CanFinalizeCertificate(t *testing.T) {
tests := []struct {
name string
hasAllGrades bool
gradesLocked bool
status string
canFinalize bool
}{
{
name: "all conditions met",
hasAllGrades: true,
gradesLocked: true,
status: "draft",
canFinalize: true,
},
{
name: "missing grades",
hasAllGrades: false,
gradesLocked: true,
status: "draft",
canFinalize: false,
},
{
name: "grades not locked",
hasAllGrades: true,
gradesLocked: false,
status: "draft",
canFinalize: false,
},
{
name: "already final",
hasAllGrades: true,
gradesLocked: true,
status: "final",
canFinalize: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := canFinalizeCertificate(tt.hasAllGrades, tt.gradesLocked, tt.status)
assert.Equal(t, tt.canFinalize, result)
})
}
}
func TestCertificateService_GenerateCertificateFilename(t *testing.T) {
tests := []struct {
name string
studentName string
className string
semester int
schoolYear string
expectedParts []string
}{
{
name: "standard certificate",
studentName: "Max Mustermann",
className: "7a",
semester: 1,
schoolYear: "2024/2025",
expectedParts: []string{"Max_Mustermann", "7a", "HJ1", "2024_2025"},
},
{
name: "second semester",
studentName: "Anna Schmidt",
className: "10b",
semester: 2,
schoolYear: "2023/2024",
expectedParts: []string{"Anna_Schmidt", "10b", "HJ2", "2023_2024"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filename := generateCertificateFilename(tt.studentName, tt.className, tt.semester, tt.schoolYear)
for _, part := range tt.expectedParts {
assert.Contains(t, filename, part)
}
assert.Contains(t, filename, ".pdf")
})
}
}
func TestCertificateService_SchoolTypeToTemplate(t *testing.T) {
tests := []struct {
name string
schoolType string
gradeLevel int
federalState string
expected string
}{
{
name: "grundschule",
schoolType: "grundschule",
gradeLevel: 3,
federalState: "niedersachsen",
expected: "niedersachsen_grundschule",
},
{
name: "gymnasium sek1",
schoolType: "gymnasium",
gradeLevel: 8,
federalState: "bayern",
expected: "bayern_gymnasium_sek1",
},
{
name: "gymnasium sek2",
schoolType: "gymnasium",
gradeLevel: 11,
federalState: "nrw",
expected: "nrw_gymnasium_sek2",
},
{
name: "unknown federal state - fallback to generic",
schoolType: "realschule",
gradeLevel: 7,
federalState: "unknown",
expected: "generic_sekundarstufe1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
template := schoolTypeToTemplate(tt.schoolType, tt.gradeLevel, tt.federalState)
assert.Equal(t, tt.expected, template)
})
}
}
// Helper functions
func validateCertificateType(certType string) error {
validTypes := map[string]bool{
"halbjahr": true,
"jahres": true,
"abschluss": true,
}
if !validTypes[certType] {
return assert.AnError
}
return nil
}
func validateSemester(semester int) error {
if semester < 1 || semester > 2 {
return assert.AnError
}
return nil
}
func validateTemplateName(name string) error {
if name == "" {
return assert.AnError
}
return nil
}
func getAvailableTemplates() []string {
return []string{
"generic_grundschule",
"generic_sekundarstufe1",
"generic_sekundarstufe2",
"niedersachsen_gymnasium_sek1",
"niedersachsen_gymnasium_sek2",
"bayern_gymnasium_sek1",
"bayern_gymnasium_sek2",
"nrw_gesamtschule",
}
}
func isValidCertificateStatusTransition(current, new string) bool {
transitions := map[string][]string{
"draft": {"final"},
"final": {"printed"},
"printed": {},
}
allowed, exists := transitions[current]
if !exists {
return false
}
for _, s := range allowed {
if s == new {
return true
}
}
return false
}
func validateGradesJSON(json string) error {
if json == "" {
return assert.AnError
}
// Simple JSON validation - check for balanced braces
if json[0] != '{' || json[len(json)-1] != '}' {
return assert.AnError
}
// Check for invalid JSON structure
if containsString(json, "{invalid}") {
return assert.AnError
}
return nil
}
func containsString(s, substr string) bool {
return len(s) >= len(substr) && s == substr
}
func canFinalizeCertificate(hasAllGrades, gradesLocked bool, status string) bool {
return hasAllGrades && gradesLocked && status == "draft"
}
func generateCertificateFilename(studentName, className string, semester int, schoolYear string) string {
// Replace spaces with underscores
safeName := replaceSpaces(studentName)
safeYear := replaceSlash(schoolYear)
semesterStr := "HJ1"
if semester == 2 {
semesterStr = "HJ2"
}
return "Zeugnis_" + safeName + "_" + className + "_" + semesterStr + "_" + safeYear + ".pdf"
}
func replaceSpaces(s string) string {
result := ""
for _, c := range s {
if c == ' ' {
result += "_"
} else {
result += string(c)
}
}
return result
}
func replaceSlash(s string) string {
result := ""
for _, c := range s {
if c == '/' {
result += "_"
} else {
result += string(c)
}
}
return result
}
func schoolTypeToTemplate(schoolType string, gradeLevel int, federalState string) string {
// Check if federal state has specific templates
knownStates := map[string]bool{
"niedersachsen": true,
"bayern": true,
"nrw": true,
}
prefix := "generic"
if knownStates[federalState] {
prefix = federalState
}
// Determine level
if schoolType == "grundschule" || gradeLevel <= 4 {
if prefix == "generic" {
return "generic_grundschule"
}
return prefix + "_grundschule"
}
if gradeLevel >= 11 {
if prefix == "generic" {
return "generic_sekundarstufe2"
}
return prefix + "_" + schoolType + "_sek2"
}
if prefix == "generic" {
return "generic_sekundarstufe1"
}
return prefix + "_" + schoolType + "_sek1"
}
func TestCertificateService_BulkGeneration(t *testing.T) {
tests := []struct {
name string
studentIDs []uuid.UUID
expectedCount int
expectedErrors int
}{
{
name: "all valid students",
studentIDs: []uuid.UUID{uuid.New(), uuid.New(), uuid.New()},
expectedCount: 3,
expectedErrors: 0,
},
{
name: "empty list",
studentIDs: []uuid.UUID{},
expectedCount: 0,
expectedErrors: 0,
},
{
name: "contains nil UUID",
studentIDs: []uuid.UUID{uuid.New(), uuid.Nil, uuid.New()},
expectedCount: 2,
expectedErrors: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
successCount, errorCount := simulateBulkGeneration(tt.studentIDs)
assert.Equal(t, tt.expectedCount, successCount)
assert.Equal(t, tt.expectedErrors, errorCount)
})
}
}
func simulateBulkGeneration(studentIDs []uuid.UUID) (successCount, errorCount int) {
for _, id := range studentIDs {
if id == uuid.Nil {
errorCount++
} else {
successCount++
}
}
return
}