Some checks failed
Clients that send users through a multi-task onboarding step no longer loop N POST /api/tasks/ calls and no longer create "orphan" tasks with no reference to the TaskTemplate they came from. Task model - New task_template_id column + GORM FK (migration 000016) - CreateTaskRequest.template_id, TaskResponse.template_id - task_service.CreateTask persists the backlink Bulk endpoint - POST /api/tasks/bulk/ — 1-50 tasks in a single transaction, returns every created row + TotalSummary. Single residence access check, per-entry residence_id is overridden with batch value - task_handler.BulkCreateTasks + task_service.BulkCreateTasks using db.Transaction; task_repo.CreateTx + FindByIDTx helpers Climate-region scoring - templateConditions gains ClimateRegionID; suggestion_service scores residence.PostalCode -> ZipToState -> GetClimateRegionIDByState against the template's conditions JSON (no penalty on mismatch / unknown ZIP) - regionMatchBonus 0.35, totalProfileFields 14 -> 15 - Standalone GET /api/tasks/templates/by-region/ removed; legacy task_tasktemplate_regions many-to-many dropped (migration 000017). Region affinity now lives entirely in the template's conditions JSON Tests - +11 cases across task_service_test, task_handler_test, suggestion_ service_test: template_id persistence, bulk rollback + cap + auth, region match / mismatch / no-ZIP / unknown-ZIP / stacks-with-others Docs - docs/openapi.yaml: /tasks/bulk/ + BulkCreateTasks schemas, template_id on TaskResponse + CreateTaskRequest, /templates/by-region/ removed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
210 lines
6.6 KiB
Go
210 lines
6.6 KiB
Go
package repositories
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"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 TestTaskTemplateRepository_Create(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
repo := NewTaskTemplateRepository(db)
|
|
|
|
var cat models.TaskCategory
|
|
require.NoError(t, db.First(&cat).Error)
|
|
|
|
var freq models.TaskFrequency
|
|
require.NoError(t, db.First(&freq).Error)
|
|
|
|
template := &models.TaskTemplate{
|
|
Title: "Change HVAC Filter",
|
|
Description: "Replace the HVAC air filter",
|
|
CategoryID: &cat.ID,
|
|
FrequencyID: &freq.ID,
|
|
IsActive: true,
|
|
Tags: "hvac,filter,maintenance",
|
|
}
|
|
err := repo.Create(template)
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, template.ID)
|
|
}
|
|
|
|
func TestTaskTemplateRepository_GetAll(t *testing.T) {
|
|
t.Skip("requires PostgreSQL: SQLite cannot scan jsonb default into json.RawMessage")
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
repo := NewTaskTemplateRepository(db)
|
|
|
|
// Create active and inactive templates
|
|
t1 := &models.TaskTemplate{Title: "Active Template", IsActive: true}
|
|
t2 := &models.TaskTemplate{Title: "Inactive Template", IsActive: false}
|
|
require.NoError(t, db.Create(t1).Error)
|
|
require.NoError(t, db.Create(t2).Error)
|
|
|
|
templates, err := repo.GetAll()
|
|
require.NoError(t, err)
|
|
assert.Len(t, templates, 1) // Only active
|
|
assert.Equal(t, "Active Template", templates[0].Title)
|
|
}
|
|
|
|
func TestTaskTemplateRepository_GetByCategory(t *testing.T) {
|
|
t.Skip("requires PostgreSQL: SQLite cannot scan jsonb default into json.RawMessage")
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
repo := NewTaskTemplateRepository(db)
|
|
|
|
var cat models.TaskCategory
|
|
require.NoError(t, db.First(&cat).Error)
|
|
|
|
t1 := &models.TaskTemplate{Title: "Cat Template", CategoryID: &cat.ID, IsActive: true}
|
|
t2 := &models.TaskTemplate{Title: "No Cat Template", IsActive: true}
|
|
require.NoError(t, db.Create(t1).Error)
|
|
require.NoError(t, db.Create(t2).Error)
|
|
|
|
templates, err := repo.GetByCategory(cat.ID)
|
|
require.NoError(t, err)
|
|
assert.Len(t, templates, 1)
|
|
assert.Equal(t, "Cat Template", templates[0].Title)
|
|
}
|
|
|
|
func TestTaskTemplateRepository_Search(t *testing.T) {
|
|
t.Skip("requires PostgreSQL: SQLite cannot scan jsonb default into json.RawMessage")
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewTaskTemplateRepository(db)
|
|
|
|
t1 := &models.TaskTemplate{Title: "Change HVAC Filter", Tags: "hvac,filter", IsActive: true}
|
|
t2 := &models.TaskTemplate{Title: "Fix Faucet", Tags: "plumbing", IsActive: true}
|
|
require.NoError(t, db.Create(t1).Error)
|
|
require.NoError(t, db.Create(t2).Error)
|
|
|
|
// Search by title
|
|
results, err := repo.Search("hvac")
|
|
require.NoError(t, err)
|
|
assert.Len(t, results, 1)
|
|
assert.Equal(t, "Change HVAC Filter", results[0].Title)
|
|
|
|
// Search by tag
|
|
results, err = repo.Search("plumbing")
|
|
require.NoError(t, err)
|
|
assert.Len(t, results, 1)
|
|
assert.Equal(t, "Fix Faucet", results[0].Title)
|
|
|
|
// No match
|
|
results, err = repo.Search("nonexistent")
|
|
require.NoError(t, err)
|
|
assert.Empty(t, results)
|
|
}
|
|
|
|
func TestTaskTemplateRepository_GetByID(t *testing.T) {
|
|
t.Skip("requires PostgreSQL: SQLite cannot scan jsonb default into json.RawMessage")
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewTaskTemplateRepository(db)
|
|
|
|
tmpl := &models.TaskTemplate{Title: "Test Template", IsActive: true}
|
|
require.NoError(t, db.Create(tmpl).Error)
|
|
|
|
found, err := repo.GetByID(tmpl.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Test Template", found.Title)
|
|
}
|
|
|
|
func TestTaskTemplateRepository_GetByID_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewTaskTemplateRepository(db)
|
|
|
|
_, err := repo.GetByID(9999)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestTaskTemplateRepository_Update(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewTaskTemplateRepository(db)
|
|
|
|
tmpl := &models.TaskTemplate{Title: "Original", IsActive: true}
|
|
require.NoError(t, db.Create(tmpl).Error)
|
|
|
|
tmpl.Title = "Updated"
|
|
err := repo.Update(tmpl)
|
|
require.NoError(t, err)
|
|
|
|
found, err := repo.GetByID(tmpl.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Updated", found.Title)
|
|
}
|
|
|
|
func TestTaskTemplateRepository_Delete(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewTaskTemplateRepository(db)
|
|
|
|
tmpl := &models.TaskTemplate{Title: "To Delete", IsActive: true}
|
|
require.NoError(t, db.Create(tmpl).Error)
|
|
|
|
err := repo.Delete(tmpl.ID)
|
|
require.NoError(t, err)
|
|
|
|
_, err = repo.GetByID(tmpl.ID)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestTaskTemplateRepository_GetAllIncludingInactive(t *testing.T) {
|
|
t.Skip("requires PostgreSQL: SQLite cannot scan jsonb default into json.RawMessage")
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewTaskTemplateRepository(db)
|
|
|
|
t1 := &models.TaskTemplate{Title: "Active", IsActive: true}
|
|
t2 := &models.TaskTemplate{Title: "Inactive", IsActive: false}
|
|
require.NoError(t, db.Create(t1).Error)
|
|
require.NoError(t, db.Create(t2).Error)
|
|
|
|
templates, err := repo.GetAllIncludingInactive()
|
|
require.NoError(t, err)
|
|
assert.Len(t, templates, 2) // Both active and inactive
|
|
}
|
|
|
|
func TestTaskTemplateRepository_Count(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewTaskTemplateRepository(db)
|
|
|
|
t1 := &models.TaskTemplate{Title: "Active 1", IsActive: true}
|
|
t2 := &models.TaskTemplate{Title: "Active 2", IsActive: true}
|
|
require.NoError(t, db.Create(t1).Error)
|
|
require.NoError(t, db.Create(t2).Error)
|
|
// Use raw SQL for inactive record to avoid GORM default:true overriding IsActive=false
|
|
require.NoError(t, db.Exec(
|
|
"INSERT INTO task_tasktemplate (title, is_active, display_order, created_at, updated_at) VALUES (?, ?, ?, datetime('now'), datetime('now'))",
|
|
"Inactive", false, 0,
|
|
).Error)
|
|
|
|
count, err := repo.Count()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(2), count) // Only active
|
|
}
|
|
|
|
func TestTaskTemplateRepository_GetGroupedByCategory(t *testing.T) {
|
|
t.Skip("requires PostgreSQL: SQLite cannot scan jsonb default into json.RawMessage")
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
repo := NewTaskTemplateRepository(db)
|
|
|
|
var cat models.TaskCategory
|
|
require.NoError(t, db.First(&cat).Error)
|
|
|
|
t1 := &models.TaskTemplate{Title: "Categorized", CategoryID: &cat.ID, IsActive: true}
|
|
t2 := &models.TaskTemplate{Title: "Uncategorized", IsActive: true}
|
|
require.NoError(t, db.Create(t1).Error)
|
|
require.NoError(t, db.Create(t2).Error)
|
|
|
|
grouped, err := repo.GetGroupedByCategory()
|
|
require.NoError(t, err)
|
|
assert.GreaterOrEqual(t, len(grouped), 2) // At least the category + "Uncategorized"
|
|
|
|
// Uncategorized should have the template without category
|
|
assert.Len(t, grouped["Uncategorized"], 1)
|
|
assert.Equal(t, "Uncategorized", grouped["Uncategorized"][0].Title)
|
|
}
|