Files
gems/opal-task/internal/engine/modifier_relative_test.go
T
joakim 2afa4c6ee0 Phase 4: Implement relative date expressions
- Add parseRelativeExpression() to detect pattern: attr+/-duration
- Add resolveDateValue() to resolve absolute or relative dates
- Add applyDateAttribute() helper for date attributes with relative support
- Track attribute order in Modifier struct (AttributeOrder field)
- Refactor Apply() and ApplyToNew() to process attrs in order
- Support chaining: due:mon scheduled:due-3d wait:scheduled-1d
- Support addition and subtraction: due+1y, wait-2d
- Add comprehensive test suite for relative expressions
- Error if referencing undefined date attribute
- All 38+ tests passing
2026-01-05 10:05:20 +01:00

135 lines
3.1 KiB
Go

package engine
import (
"testing"
"time"
)
func TestRelativeDateExpressions(t *testing.T) {
task, err := CreateTask("Test task with relative dates")
if err != nil {
t.Fatal(err)
}
mods := []string{"due:mon", "wait:due-1d"}
modifier, err := ParseModifier(mods)
if err != nil {
t.Fatal(err)
}
err = modifier.Apply(task)
if err != nil {
t.Fatal(err)
}
if task.Due == nil || task.Wait == nil {
t.Fatal("Due and wait should be set")
}
expectedWait := task.Due.AddDate(0, 0, -1)
if !task.Wait.Equal(expectedWait) {
t.Errorf("Wait should be 1 day before due. Expected %v, got %v", expectedWait, *task.Wait)
}
}
func TestRelativeDateChaining(t *testing.T) {
task, err := CreateTask("Test chained relative dates")
if err != nil {
t.Fatal(err)
}
mods := []string{"due:mon", "scheduled:due-3d", "wait:scheduled-1d"}
modifier, err := ParseModifier(mods)
if err != nil {
t.Fatal(err)
}
err = modifier.Apply(task)
if err != nil {
t.Fatal(err)
}
if task.Due == nil || task.Scheduled == nil || task.Wait == nil {
t.Fatal("All dates should be set")
}
expectedScheduled := task.Due.AddDate(0, 0, -3)
if !task.Scheduled.Equal(expectedScheduled) {
t.Errorf("Scheduled should be 3 days before due. Expected %v, got %v", expectedScheduled, *task.Scheduled)
}
expectedWait := task.Scheduled.AddDate(0, 0, -1)
if !task.Wait.Equal(expectedWait) {
t.Errorf("Wait should be 1 day before scheduled. Expected %v, got %v", expectedWait, *task.Wait)
}
}
func TestRelativeDateAddition(t *testing.T) {
task, err := CreateTask("Test relative date addition")
if err != nil {
t.Fatal(err)
}
mods := []string{"due:today", "until:due+1y"}
modifier, err := ParseModifier(mods)
if err != nil {
t.Fatal(err)
}
err = modifier.Apply(task)
if err != nil {
t.Fatal(err)
}
if task.Due == nil || task.Until == nil {
t.Fatal("Due and until should be set")
}
expectedUntil := task.Due.AddDate(1, 0, 0)
diff := task.Until.Sub(expectedUntil)
if diff < -24*time.Hour || diff > 24*time.Hour {
t.Errorf("Until should be ~1 year after due. Expected %v, got %v (diff: %v)", expectedUntil, *task.Until, diff)
}
}
func TestRelativeDateReferenceError(t *testing.T) {
task, err := CreateTask("Test reference error")
if err != nil {
t.Fatal(err)
}
mods := []string{"wait:due-1d"}
modifier, err := ParseModifier(mods)
if err != nil {
t.Fatal(err)
}
err = modifier.Apply(task)
if err == nil {
t.Error("Should error when referencing unset date")
}
}
func TestRelativeDateInNewTask(t *testing.T) {
// Test relative dates when creating a new task via CreateTaskWithModifier
mods := []string{"due:tomorrow", "wait:due-2d"}
modifier, err := ParseModifier(mods)
if err != nil {
t.Fatal(err)
}
task, err := CreateTaskWithModifier("New task with relative dates", modifier)
if err != nil {
t.Fatal(err)
}
if task.Due == nil || task.Wait == nil {
t.Fatal("Due and wait should be set")
}
expectedWait := task.Due.AddDate(0, 0, -2)
if !task.Wait.Equal(expectedWait) {
t.Errorf("Wait should be 2 days before due. Expected %v, got %v", expectedWait, *task.Wait)
}
}