e958f88a2d
Phase 1 — Stammdaten (7 tables):
tt_class, tt_period, tt_room, tt_subject, tt_teacher,
tt_curriculum, tt_assignment with CRUD endpoints.
Phase 2 — Constraints (15 typed tables):
Teacher (6): unavailable_day, unavailable_window, max_hours_day,
max_hours_week, excluded_subject, excluded_room
Subject (5): min_day_gap, max_consecutive, contiguous_when_repeated,
preferred_period, double_lesson
Class (2): max_hours_day, no_gaps
Room (2): requires_type, unavailable
Each constraint row carries is_hard / weight / active / note /
created_by_user_id; ownership enforced via WHERE EXISTS against the
parent tt_teacher/tt_class/tt_subject/tt_room row.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
110 lines
4.2 KiB
Go
110 lines
4.2 KiB
Go
package services
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/go-playground/validator/v10"
|
|
|
|
"github.com/breakpilot/school-service/internal/models"
|
|
)
|
|
|
|
// validate is a singleton used to exercise the same struct tags Gin uses for
|
|
// request validation. The DB tests live in integration tests against a real
|
|
// database; this test pins the contract for the request DTOs.
|
|
var validate = func() *validator.Validate {
|
|
v := validator.New()
|
|
v.SetTagName("binding")
|
|
return v
|
|
}()
|
|
|
|
func TestNewTimetableService_Constructs(t *testing.T) {
|
|
s := NewTimetableService(nil)
|
|
if s == nil {
|
|
t.Fatal("expected non-nil service")
|
|
}
|
|
}
|
|
|
|
func TestCreateTimetableClassRequest_Validation(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
req models.CreateTimetableClassRequest
|
|
wantErr bool
|
|
}{
|
|
{"valid", models.CreateTimetableClassRequest{Name: "5a", GradeLevel: 5, StudentCount: 24}, false},
|
|
{"missing name", models.CreateTimetableClassRequest{GradeLevel: 5}, true},
|
|
{"grade too low", models.CreateTimetableClassRequest{Name: "0a", GradeLevel: 0}, true},
|
|
{"grade too high", models.CreateTimetableClassRequest{Name: "14a", GradeLevel: 14}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validate.Struct(tt.req)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("got err=%v, wantErr=%v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreateTimetablePeriodRequest_Validation(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
req models.CreateTimetablePeriodRequest
|
|
wantErr bool
|
|
}{
|
|
{"valid monday first", models.CreateTimetablePeriodRequest{DayOfWeek: 1, PeriodIndex: 1, StartTime: "08:00", EndTime: "08:45"}, false},
|
|
{"day too low", models.CreateTimetablePeriodRequest{DayOfWeek: 0, PeriodIndex: 1, StartTime: "08:00", EndTime: "08:45"}, true},
|
|
{"day too high", models.CreateTimetablePeriodRequest{DayOfWeek: 8, PeriodIndex: 1, StartTime: "08:00", EndTime: "08:45"}, true},
|
|
{"missing times", models.CreateTimetablePeriodRequest{DayOfWeek: 1, PeriodIndex: 1}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validate.Struct(tt.req)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("got err=%v, wantErr=%v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreateTimetableTeacherRequest_Validation(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
req models.CreateTimetableTeacherRequest
|
|
wantErr bool
|
|
}{
|
|
{"valid full-time", models.CreateTimetableTeacherRequest{FirstName: "Anna", LastName: "Schmidt", ShortCode: "SCH", EmploymentPercentage: 100, MaxHoursWeek: 28}, false},
|
|
{"valid part-time", models.CreateTimetableTeacherRequest{FirstName: "Bea", LastName: "Mueller", ShortCode: "MUE", EmploymentPercentage: 50, MaxHoursWeek: 14}, false},
|
|
{"missing names", models.CreateTimetableTeacherRequest{ShortCode: "XX"}, true},
|
|
{"employment above 100", models.CreateTimetableTeacherRequest{FirstName: "X", LastName: "Y", ShortCode: "Z", EmploymentPercentage: 150}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validate.Struct(tt.req)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("got err=%v, wantErr=%v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreateTimetableCurriculumRequest_Validation(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
req models.CreateTimetableCurriculumRequest
|
|
wantErr bool
|
|
}{
|
|
{"valid", models.CreateTimetableCurriculumRequest{ClassID: "00000000-0000-0000-0000-000000000001", SubjectID: "00000000-0000-0000-0000-000000000002", WeeklyHours: 4}, false},
|
|
{"non-uuid class", models.CreateTimetableCurriculumRequest{ClassID: "not-a-uuid", SubjectID: "00000000-0000-0000-0000-000000000002", WeeklyHours: 4}, true},
|
|
{"hours below 1", models.CreateTimetableCurriculumRequest{ClassID: "00000000-0000-0000-0000-000000000001", SubjectID: "00000000-0000-0000-0000-000000000002", WeeklyHours: 0}, true},
|
|
{"hours above 10", models.CreateTimetableCurriculumRequest{ClassID: "00000000-0000-0000-0000-000000000001", SubjectID: "00000000-0000-0000-0000-000000000002", WeeklyHours: 11}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validate.Struct(tt.req)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("got err=%v, wantErr=%v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|