b02c40f716
Add relative date formatting (today, tomorrow, in 3d, etc.) for list and detail views. Add structured feedback helpers for add/complete/delete operations showing display IDs and parsed modifiers. Change Complete() to return spawned recurring instance so callers can display recurrence info. Add AppendTask to working set for immediate display ID assignment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
294 lines
6.0 KiB
Go
294 lines
6.0 KiB
Go
package engine
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
// Setup test database with explicit directory overrides
|
|
testDir := "/tmp/opal-test"
|
|
|
|
// Set directory overrides to use test directory
|
|
SetConfigDirOverride(testDir)
|
|
SetDataDirOverride(testDir)
|
|
|
|
// Ensure test directory exists
|
|
if err := os.MkdirAll(testDir, 0755); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := InitDB(); err != nil {
|
|
panic(err)
|
|
}
|
|
defer CloseDB()
|
|
|
|
// Run tests
|
|
code := m.Run()
|
|
|
|
// Cleanup
|
|
os.RemoveAll(testDir)
|
|
os.Exit(code)
|
|
}
|
|
|
|
func TestCreateTask(t *testing.T) {
|
|
task, err := CreateTask("Test task")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create task: %v", err)
|
|
}
|
|
|
|
if task.UUID == uuid.Nil {
|
|
t.Error("Task UUID should not be nil")
|
|
}
|
|
|
|
if task.Description != "Test task" {
|
|
t.Errorf("Expected description 'Test task', got '%s'", task.Description)
|
|
}
|
|
|
|
if task.Status != StatusPending {
|
|
t.Errorf("Expected status Pending, got %v", task.Status)
|
|
}
|
|
|
|
if task.Priority != PriorityDefault {
|
|
t.Errorf("Expected priority Default, got %v", task.Priority)
|
|
}
|
|
}
|
|
|
|
func TestGetTask(t *testing.T) {
|
|
// Create a task
|
|
created, err := CreateTask("Test get task")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create task: %v", err)
|
|
}
|
|
|
|
// Retrieve it
|
|
retrieved, err := GetTask(created.UUID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get task: %v", err)
|
|
}
|
|
|
|
if retrieved.UUID != created.UUID {
|
|
t.Error("UUIDs don't match")
|
|
}
|
|
|
|
if retrieved.Description != created.Description {
|
|
t.Error("Descriptions don't match")
|
|
}
|
|
}
|
|
|
|
func TestTaskSave(t *testing.T) {
|
|
task, err := CreateTask("Test save")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create task: %v", err)
|
|
}
|
|
|
|
// Modify task
|
|
task.Description = "Modified description"
|
|
task.Priority = PriorityHigh
|
|
project := "test-project"
|
|
task.Project = &project
|
|
|
|
if err := task.Save(); err != nil {
|
|
t.Fatalf("Failed to save task: %v", err)
|
|
}
|
|
|
|
// Retrieve and verify
|
|
retrieved, err := GetTask(task.UUID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get task: %v", err)
|
|
}
|
|
|
|
if retrieved.Description != "Modified description" {
|
|
t.Error("Description not updated")
|
|
}
|
|
|
|
if retrieved.Priority != PriorityHigh {
|
|
t.Error("Priority not updated")
|
|
}
|
|
|
|
if retrieved.Project == nil || *retrieved.Project != "test-project" {
|
|
t.Error("Project not updated")
|
|
}
|
|
}
|
|
|
|
func TestTaskTags(t *testing.T) {
|
|
task, err := CreateTask("Test tags")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create task: %v", err)
|
|
}
|
|
|
|
// Add tags
|
|
if err := task.AddTag("urgent"); err != nil {
|
|
t.Fatalf("Failed to add tag: %v", err)
|
|
}
|
|
|
|
if err := task.AddTag("work"); err != nil {
|
|
t.Fatalf("Failed to add tag: %v", err)
|
|
}
|
|
|
|
// Retrieve and verify tags
|
|
retrieved, err := GetTask(task.UUID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get task: %v", err)
|
|
}
|
|
|
|
if len(retrieved.Tags) != 2 {
|
|
t.Errorf("Expected 2 tags, got %d", len(retrieved.Tags))
|
|
}
|
|
|
|
// Remove a tag
|
|
if err := retrieved.RemoveTag("urgent"); err != nil {
|
|
t.Fatalf("Failed to remove tag: %v", err)
|
|
}
|
|
|
|
// Verify removal
|
|
retrieved2, err := GetTask(task.UUID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get task: %v", err)
|
|
}
|
|
|
|
if len(retrieved2.Tags) != 1 {
|
|
t.Errorf("Expected 1 tag after removal, got %d", len(retrieved2.Tags))
|
|
}
|
|
|
|
if retrieved2.Tags[0] != "work" {
|
|
t.Errorf("Expected remaining tag 'work', got '%s'", retrieved2.Tags[0])
|
|
}
|
|
}
|
|
|
|
func TestTaskComplete(t *testing.T) {
|
|
task, err := CreateTask("Test complete")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create task: %v", err)
|
|
}
|
|
|
|
if _, err := task.Complete(); err != nil {
|
|
t.Fatalf("Failed to complete task: %v", err)
|
|
}
|
|
|
|
if task.Status != StatusCompleted {
|
|
t.Error("Status should be Completed")
|
|
}
|
|
|
|
if task.End == nil {
|
|
t.Error("End time should be set")
|
|
}
|
|
|
|
// Verify in database
|
|
retrieved, err := GetTask(task.UUID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get task: %v", err)
|
|
}
|
|
|
|
if retrieved.Status != StatusCompleted {
|
|
t.Error("Retrieved task should be completed")
|
|
}
|
|
}
|
|
|
|
func TestTaskDelete(t *testing.T) {
|
|
// Test soft delete
|
|
task1, err := CreateTask("Test soft delete")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create task: %v", err)
|
|
}
|
|
|
|
if err := task1.Delete(false); err != nil {
|
|
t.Fatalf("Failed to soft delete: %v", err)
|
|
}
|
|
|
|
retrieved, err := GetTask(task1.UUID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get deleted task: %v", err)
|
|
}
|
|
|
|
if retrieved.Status != StatusDeleted {
|
|
t.Error("Task should be marked as deleted")
|
|
}
|
|
|
|
// Test hard delete
|
|
task2, err := CreateTask("Test hard delete")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create task: %v", err)
|
|
}
|
|
|
|
if err := task2.Delete(true); err != nil {
|
|
t.Fatalf("Failed to hard delete: %v", err)
|
|
}
|
|
|
|
_, err = GetTask(task2.UUID)
|
|
if err == nil {
|
|
t.Error("Should not be able to retrieve hard-deleted task")
|
|
}
|
|
}
|
|
|
|
func TestTaskStartStop(t *testing.T) {
|
|
task, err := CreateTask("Test start/stop")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create task: %v", err)
|
|
}
|
|
|
|
// Start task
|
|
if err := task.StartTask(); err != nil {
|
|
t.Fatalf("Failed to start task: %v", err)
|
|
}
|
|
|
|
if task.Start == nil {
|
|
t.Error("Start time should be set")
|
|
}
|
|
|
|
// Stop task
|
|
if err := task.StopTask(); err != nil {
|
|
t.Fatalf("Failed to stop task: %v", err)
|
|
}
|
|
|
|
if task.Start != nil {
|
|
t.Error("Start time should be nil after stop")
|
|
}
|
|
}
|
|
|
|
func TestGetTasks(t *testing.T) {
|
|
// Create multiple tasks
|
|
CreateTask("Task 1")
|
|
CreateTask("Task 2")
|
|
CreateTask("Task 3")
|
|
|
|
tasks, err := GetTasks(nil)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get tasks: %v", err)
|
|
}
|
|
|
|
if len(tasks) < 3 {
|
|
t.Errorf("Expected at least 3 tasks, got %d", len(tasks))
|
|
}
|
|
}
|
|
|
|
func TestTaskWithDueDate(t *testing.T) {
|
|
task, err := CreateTask("Task with due date")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create task: %v", err)
|
|
}
|
|
|
|
dueDate := time.Now().Add(24 * time.Hour)
|
|
task.Due = &dueDate
|
|
|
|
if err := task.Save(); err != nil {
|
|
t.Fatalf("Failed to save task with due date: %v", err)
|
|
}
|
|
|
|
retrieved, err := GetTask(task.UUID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get task: %v", err)
|
|
}
|
|
|
|
if retrieved.Due == nil {
|
|
t.Error("Due date should be set")
|
|
}
|
|
|
|
if retrieved.Due.Unix() != dueDate.Unix() {
|
|
t.Error("Due dates don't match")
|
|
}
|
|
}
|