- Add completion_summary endpoint data to residence detail response - Track completed_from_column on task completions (overdue/due_soon/upcoming) - Add GetCompletionSummary repo method with monthly aggregation - Add one-time data migration framework (data_migrations table + registry) - Add backfill migration to classify historical completions - Add standalone backfill script for manual/dry-run usage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
175 lines
6.1 KiB
Go
175 lines
6.1 KiB
Go
package repositories
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/treytartt/honeydue-api/internal/models"
|
|
"github.com/treytartt/honeydue-api/internal/testutil"
|
|
)
|
|
|
|
func TestGetCompletionSummary_EmptyResidence(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewTaskRepository(db)
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
now := time.Date(2026, 3, 15, 0, 0, 0, 0, time.UTC)
|
|
|
|
summary, err := repo.GetCompletionSummary(residence.ID, now, 10)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, summary.TotalAllTime)
|
|
assert.Equal(t, 0, summary.TotalLast12Months)
|
|
assert.Len(t, summary.Months, 12)
|
|
// First month should be March 2025
|
|
assert.Equal(t, "2025-03", summary.Months[0].Month)
|
|
// Last month should be February 2026
|
|
assert.Equal(t, "2026-02", summary.Months[11].Month)
|
|
}
|
|
|
|
func TestGetCompletionSummary_CountsByMonthAndColumn(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewTaskRepository(db)
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
task := testutil.CreateTestTask(t, db, residence.ID, user.ID, "Fix roof")
|
|
now := time.Date(2026, 3, 15, 0, 0, 0, 0, time.UTC)
|
|
|
|
completions := []models.TaskCompletion{
|
|
{TaskID: task.ID, CompletedByID: user.ID, CompletedAt: time.Date(2026, 1, 10, 12, 0, 0, 0, time.UTC), CompletedFromColumn: "overdue_tasks"},
|
|
{TaskID: task.ID, CompletedByID: user.ID, CompletedAt: time.Date(2026, 1, 20, 12, 0, 0, 0, time.UTC), CompletedFromColumn: "completed_tasks"},
|
|
{TaskID: task.ID, CompletedByID: user.ID, CompletedAt: time.Date(2026, 2, 5, 12, 0, 0, 0, time.UTC), CompletedFromColumn: "due_soon_tasks"},
|
|
{TaskID: task.ID, CompletedByID: user.ID, CompletedAt: time.Date(2025, 6, 1, 12, 0, 0, 0, time.UTC), CompletedFromColumn: "upcoming_tasks"},
|
|
}
|
|
for i := range completions {
|
|
require.NoError(t, db.Create(&completions[i]).Error)
|
|
}
|
|
|
|
summary, err := repo.GetCompletionSummary(residence.ID, now, 10)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, 4, summary.TotalAllTime)
|
|
assert.Equal(t, 4, summary.TotalLast12Months)
|
|
|
|
for _, m := range summary.Months {
|
|
switch m.Month {
|
|
case "2026-01":
|
|
assert.Equal(t, 2, m.Total, "January should have 2 completions")
|
|
assert.Len(t, m.Completions, 2, "January should have 2 column entries")
|
|
case "2026-02":
|
|
assert.Equal(t, 1, m.Total, "February should have 1 completion")
|
|
case "2025-06":
|
|
assert.Equal(t, 1, m.Total, "June 2025 should have 1 completion")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetCompletionSummary_Overflow(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewTaskRepository(db)
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Busy House")
|
|
task := testutil.CreateTestTask(t, db, residence.ID, user.ID, "Lots of tasks")
|
|
now := time.Date(2026, 3, 15, 0, 0, 0, 0, time.UTC)
|
|
|
|
for i := 0; i < 15; i++ {
|
|
c := models.TaskCompletion{
|
|
TaskID: task.ID,
|
|
CompletedByID: user.ID,
|
|
CompletedAt: time.Date(2026, 2, 1+i, 12, 0, 0, 0, time.UTC),
|
|
CompletedFromColumn: "completed_tasks",
|
|
}
|
|
require.NoError(t, db.Create(&c).Error)
|
|
}
|
|
|
|
summary, err := repo.GetCompletionSummary(residence.ID, now, 10)
|
|
require.NoError(t, err)
|
|
|
|
for _, m := range summary.Months {
|
|
if m.Month == "2026-02" {
|
|
assert.Equal(t, 15, m.Total)
|
|
assert.Equal(t, 5, m.Overflow)
|
|
return
|
|
}
|
|
}
|
|
t.Fatal("February 2026 not found")
|
|
}
|
|
|
|
func TestGetCompletionSummary_OldCompletionsExcludedFromMonths(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewTaskRepository(db)
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Old House")
|
|
task := testutil.CreateTestTask(t, db, residence.ID, user.ID, "Old task")
|
|
now := time.Date(2026, 3, 15, 0, 0, 0, 0, time.UTC)
|
|
|
|
old := models.TaskCompletion{
|
|
TaskID: task.ID, CompletedByID: user.ID,
|
|
CompletedAt: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), CompletedFromColumn: "completed_tasks",
|
|
}
|
|
require.NoError(t, db.Create(&old).Error)
|
|
|
|
recent := models.TaskCompletion{
|
|
TaskID: task.ID, CompletedByID: user.ID,
|
|
CompletedAt: time.Date(2026, 2, 1, 12, 0, 0, 0, time.UTC), CompletedFromColumn: "completed_tasks",
|
|
}
|
|
require.NoError(t, db.Create(&recent).Error)
|
|
|
|
summary, err := repo.GetCompletionSummary(residence.ID, now, 10)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, 2, summary.TotalAllTime)
|
|
assert.Equal(t, 1, summary.TotalLast12Months)
|
|
}
|
|
|
|
func TestGetCompletionSummary_CompletionColors(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewTaskRepository(db)
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Color House")
|
|
task := testutil.CreateTestTask(t, db, residence.ID, user.ID, "Color task")
|
|
now := time.Date(2026, 3, 15, 0, 0, 0, 0, time.UTC)
|
|
|
|
c := models.TaskCompletion{
|
|
TaskID: task.ID, CompletedByID: user.ID,
|
|
CompletedAt: time.Date(2026, 1, 5, 12, 0, 0, 0, time.UTC), CompletedFromColumn: "overdue_tasks",
|
|
}
|
|
require.NoError(t, db.Create(&c).Error)
|
|
|
|
summary, err := repo.GetCompletionSummary(residence.ID, now, 10)
|
|
require.NoError(t, err)
|
|
|
|
for _, m := range summary.Months {
|
|
if m.Month == "2026-01" {
|
|
require.Len(t, m.Completions, 1)
|
|
assert.Equal(t, "overdue_tasks", m.Completions[0].Column)
|
|
assert.Equal(t, "#FF3B30", m.Completions[0].Color)
|
|
assert.Equal(t, 1, m.Completions[0].Count)
|
|
return
|
|
}
|
|
}
|
|
t.Fatal("January 2026 not found")
|
|
}
|
|
|
|
func TestKanbanColumnColor(t *testing.T) {
|
|
tests := []struct {
|
|
column string
|
|
expected string
|
|
}{
|
|
{"overdue_tasks", "#FF3B30"},
|
|
{"in_progress_tasks", "#5856D6"},
|
|
{"due_soon_tasks", "#FF9500"},
|
|
{"upcoming_tasks", "#007AFF"},
|
|
{"completed_tasks", "#34C759"},
|
|
{"cancelled_tasks", "#8E8E93"},
|
|
{"unknown_column", "#34C759"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.column, func(t *testing.T) {
|
|
assert.Equal(t, tt.expected, KanbanColumnColor(tt.column))
|
|
})
|
|
}
|
|
}
|