- Holiday service with German federal holidays, Easter calculation, DB loading - Deadline calculator adapted from youpc.org (duration calc + non-working day adjustment) - Deadline CRUD service (tenant-scoped: list, create, update, complete, delete) - Deadline rule service (list, filter by proceeding type, hierarchical rule trees) - HTTP handlers for all endpoints with tenant resolution via X-Tenant-ID header - Router wired with all new endpoints under /api/ - Tests for holiday and calculator services (8 passing)
142 lines
3.8 KiB
Go
142 lines
3.8 KiB
Go
package services
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"mgit.msbls.de/m/KanzlAI-mGMT/internal/models"
|
|
)
|
|
|
|
func TestCalculateEndDateAfterDays(t *testing.T) {
|
|
holidays := NewHolidayService(nil)
|
|
calc := NewDeadlineCalculator(holidays)
|
|
|
|
eventDate := time.Date(2026, 3, 25, 0, 0, 0, 0, time.UTC) // Wednesday
|
|
timing := "after"
|
|
rule := models.DeadlineRule{
|
|
ID: uuid.New(),
|
|
Name: "Test 10 days",
|
|
DurationValue: 10,
|
|
DurationUnit: "days",
|
|
Timing: &timing,
|
|
}
|
|
|
|
adjusted, original, wasAdjusted := calc.CalculateEndDate(eventDate, rule)
|
|
|
|
// 25 March + 10 days = 4 April 2026 (Saturday)
|
|
// Apr 5 = Easter Sunday (holiday), Apr 6 = Easter Monday (holiday) -> adjusted to 7 April (Tuesday)
|
|
expectedOriginal := time.Date(2026, 4, 4, 0, 0, 0, 0, time.UTC)
|
|
expectedAdjusted := time.Date(2026, 4, 7, 0, 0, 0, 0, time.UTC)
|
|
|
|
if original != expectedOriginal {
|
|
t.Errorf("original should be %s, got %s", expectedOriginal, original)
|
|
}
|
|
if adjusted != expectedAdjusted {
|
|
t.Errorf("adjusted should be %s, got %s", expectedAdjusted, adjusted)
|
|
}
|
|
if !wasAdjusted {
|
|
t.Error("should have been adjusted (Saturday)")
|
|
}
|
|
}
|
|
|
|
func TestCalculateEndDateBeforeMonths(t *testing.T) {
|
|
holidays := NewHolidayService(nil)
|
|
calc := NewDeadlineCalculator(holidays)
|
|
|
|
eventDate := time.Date(2026, 6, 15, 0, 0, 0, 0, time.UTC) // Monday
|
|
timing := "before"
|
|
rule := models.DeadlineRule{
|
|
ID: uuid.New(),
|
|
Name: "Test 2 months before",
|
|
DurationValue: 2,
|
|
DurationUnit: "months",
|
|
Timing: &timing,
|
|
}
|
|
|
|
adjusted, original, wasAdjusted := calc.CalculateEndDate(eventDate, rule)
|
|
|
|
// 15 June - 2 months = 15 April 2026 (Wednesday)
|
|
expected := time.Date(2026, 4, 15, 0, 0, 0, 0, time.UTC)
|
|
|
|
if original != expected {
|
|
t.Errorf("original should be %s, got %s", expected, original)
|
|
}
|
|
if adjusted != expected {
|
|
t.Errorf("adjusted should be %s (not a holiday/weekend), got %s", expected, adjusted)
|
|
}
|
|
if wasAdjusted {
|
|
t.Error("should not have been adjusted (Wednesday)")
|
|
}
|
|
}
|
|
|
|
func TestCalculateEndDateWeeks(t *testing.T) {
|
|
holidays := NewHolidayService(nil)
|
|
calc := NewDeadlineCalculator(holidays)
|
|
|
|
eventDate := time.Date(2026, 3, 25, 0, 0, 0, 0, time.UTC) // Wednesday
|
|
timing := "after"
|
|
rule := models.DeadlineRule{
|
|
ID: uuid.New(),
|
|
Name: "Test 2 weeks",
|
|
DurationValue: 2,
|
|
DurationUnit: "weeks",
|
|
Timing: &timing,
|
|
}
|
|
|
|
adjusted, original, _ := calc.CalculateEndDate(eventDate, rule)
|
|
|
|
// 25 March + 14 days = 8 April 2026 (Wednesday)
|
|
expected := time.Date(2026, 4, 8, 0, 0, 0, 0, time.UTC)
|
|
if original != expected {
|
|
t.Errorf("original should be %s, got %s", expected, original)
|
|
}
|
|
if adjusted != expected {
|
|
t.Errorf("adjusted should be %s, got %s", expected, adjusted)
|
|
}
|
|
}
|
|
|
|
func TestCalculateFromRules(t *testing.T) {
|
|
holidays := NewHolidayService(nil)
|
|
calc := NewDeadlineCalculator(holidays)
|
|
|
|
eventDate := time.Date(2026, 3, 25, 0, 0, 0, 0, time.UTC)
|
|
timing := "after"
|
|
code := "TEST-1"
|
|
|
|
rules := []models.DeadlineRule{
|
|
{
|
|
ID: uuid.New(),
|
|
Code: &code,
|
|
Name: "Rule A",
|
|
DurationValue: 7,
|
|
DurationUnit: "days",
|
|
Timing: &timing,
|
|
},
|
|
{
|
|
ID: uuid.New(),
|
|
Name: "Rule B (zero duration)",
|
|
DurationValue: 0,
|
|
DurationUnit: "days",
|
|
},
|
|
}
|
|
|
|
results := calc.CalculateFromRules(eventDate, rules)
|
|
if len(results) != 2 {
|
|
t.Fatalf("expected 2 results, got %d", len(results))
|
|
}
|
|
|
|
// Rule A: 25 March + 7 = 1 April (Wednesday)
|
|
if results[0].DueDate != "2026-04-01" {
|
|
t.Errorf("Rule A due date should be 2026-04-01, got %s", results[0].DueDate)
|
|
}
|
|
if results[0].RuleCode != "TEST-1" {
|
|
t.Errorf("Rule A code should be TEST-1, got %s", results[0].RuleCode)
|
|
}
|
|
|
|
// Rule B: zero duration -> event date
|
|
if results[1].DueDate != "2026-03-25" {
|
|
t.Errorf("Rule B due date should be 2026-03-25, got %s", results[1].DueDate)
|
|
}
|
|
}
|