Add timetable scheduler Phases 1 + 2 to school-service
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>
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/breakpilot/school-service/internal/models"
|
||||
)
|
||||
|
||||
// These tests exercise the request DTO binding tags (the same the Gin layer
|
||||
// uses). They don't hit the database — DB-level checks live in integration
|
||||
// tests against a real Postgres.
|
||||
|
||||
func TestCreateTeacherUnavailableDayRequest_Validation(t *testing.T) {
|
||||
uid := "00000000-0000-0000-0000-000000000001"
|
||||
tests := []struct {
|
||||
name string
|
||||
req models.CreateTeacherUnavailableDayRequest
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid monday", models.CreateTeacherUnavailableDayRequest{TeacherID: uid, DayOfWeek: 1, IsHard: true, Weight: 100, Active: true}, false},
|
||||
{"day too low", models.CreateTeacherUnavailableDayRequest{TeacherID: uid, DayOfWeek: 0}, true},
|
||||
{"day too high", models.CreateTeacherUnavailableDayRequest{TeacherID: uid, DayOfWeek: 8}, true},
|
||||
{"non-uuid teacher", models.CreateTeacherUnavailableDayRequest{TeacherID: "not-a-uuid", DayOfWeek: 1}, true},
|
||||
{"weight above 100", models.CreateTeacherUnavailableDayRequest{TeacherID: uid, DayOfWeek: 1, Weight: 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 TestCreateTeacherUnavailableWindowRequest_Validation(t *testing.T) {
|
||||
uid := "00000000-0000-0000-0000-000000000001"
|
||||
tests := []struct {
|
||||
name string
|
||||
req models.CreateTeacherUnavailableWindowRequest
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid", models.CreateTeacherUnavailableWindowRequest{TeacherID: uid, DayOfWeek: 2, StartTime: "13:00", EndTime: "17:00"}, false},
|
||||
{"missing times", models.CreateTeacherUnavailableWindowRequest{TeacherID: uid, DayOfWeek: 2}, true},
|
||||
{"day too high", models.CreateTeacherUnavailableWindowRequest{TeacherID: uid, DayOfWeek: 8, StartTime: "13:00", EndTime: "17:00"}, 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 TestCreateSubjectMaxConsecutiveRequest_Validation(t *testing.T) {
|
||||
uid := "00000000-0000-0000-0000-000000000002"
|
||||
tests := []struct {
|
||||
name string
|
||||
req models.CreateSubjectMaxConsecutiveRequest
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid 2 in a row", models.CreateSubjectMaxConsecutiveRequest{SubjectID: uid, MaxConsecutive: 2, IsHard: true, Weight: 100}, false},
|
||||
{"below 1", models.CreateSubjectMaxConsecutiveRequest{SubjectID: uid, MaxConsecutive: 0}, true},
|
||||
{"above 5", models.CreateSubjectMaxConsecutiveRequest{SubjectID: uid, MaxConsecutive: 6}, true},
|
||||
{"non-uuid subject", models.CreateSubjectMaxConsecutiveRequest{SubjectID: "x", MaxConsecutive: 2}, 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 TestCreateSubjectPreferredPeriodRequest_Validation(t *testing.T) {
|
||||
uid := "00000000-0000-0000-0000-000000000002"
|
||||
tests := []struct {
|
||||
name string
|
||||
req models.CreateSubjectPreferredPeriodRequest
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid morning", models.CreateSubjectPreferredPeriodRequest{SubjectID: uid, PeriodFrom: 1, PeriodTo: 4, IsHard: false, Weight: 40}, false},
|
||||
{"from missing", models.CreateSubjectPreferredPeriodRequest{SubjectID: uid, PeriodTo: 4}, true},
|
||||
{"to too high", models.CreateSubjectPreferredPeriodRequest{SubjectID: uid, PeriodFrom: 1, PeriodTo: 13}, 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 TestCreateClassMaxHoursDayRequest_Validation(t *testing.T) {
|
||||
uid := "00000000-0000-0000-0000-000000000003"
|
||||
tests := []struct {
|
||||
name string
|
||||
req models.CreateClassMaxHoursDayRequest
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid", models.CreateClassMaxHoursDayRequest{ClassID: uid, MaxHours: 6, IsHard: true, Weight: 100}, false},
|
||||
{"hours below 1", models.CreateClassMaxHoursDayRequest{ClassID: uid, MaxHours: 0}, true},
|
||||
{"hours above 12", models.CreateClassMaxHoursDayRequest{ClassID: uid, MaxHours: 13}, 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 TestCreateRoomUnavailableRequest_Validation(t *testing.T) {
|
||||
uid := "00000000-0000-0000-0000-000000000004"
|
||||
tests := []struct {
|
||||
name string
|
||||
req models.CreateRoomUnavailableRequest
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid", models.CreateRoomUnavailableRequest{RoomID: uid, DayOfWeek: 3, PeriodIndex: 4, IsHard: true, Weight: 100}, false},
|
||||
{"missing day", models.CreateRoomUnavailableRequest{RoomID: uid, PeriodIndex: 4}, true},
|
||||
{"period too high", models.CreateRoomUnavailableRequest{RoomID: uid, DayOfWeek: 3, PeriodIndex: 13}, 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user