Task creation/update responses were using UTC time for kanban column categorization, causing tasks to incorrectly appear as overdue when the server had passed midnight UTC but the user's local time was still the previous day. Changes: - Add timezone-aware response functions (NewTaskResponseWithTime, etc.) - Pass userNow from middleware to all task service methods - Update handlers to use timezone-aware time from X-Timezone header - Update tests to pass the now parameter 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
522 lines
18 KiB
Go
522 lines
18 KiB
Go
package services
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/shopspring/decimal"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/treytartt/casera-api/internal/dto/requests"
|
|
"github.com/treytartt/casera-api/internal/models"
|
|
"github.com/treytartt/casera-api/internal/repositories"
|
|
"github.com/treytartt/casera-api/internal/testutil"
|
|
)
|
|
|
|
func setupTaskService(t *testing.T) (*TaskService, *repositories.TaskRepository, *repositories.ResidenceRepository) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
return service, taskRepo, residenceRepo
|
|
}
|
|
|
|
func TestTaskService_CreateTask(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
|
|
req := &requests.CreateTaskRequest{
|
|
ResidenceID: residence.ID,
|
|
Title: "Fix leaky faucet",
|
|
Description: "Kitchen faucet is dripping",
|
|
}
|
|
|
|
now := time.Now().UTC()
|
|
resp, err := service.CreateTask(req, user.ID, now)
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, resp.Data.ID)
|
|
assert.Equal(t, "Fix leaky faucet", resp.Data.Title)
|
|
assert.Equal(t, "Kitchen faucet is dripping", resp.Data.Description)
|
|
}
|
|
|
|
func TestTaskService_CreateTask_WithOptionalFields(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
|
|
// Get category and priority IDs
|
|
var category models.TaskCategory
|
|
var priority models.TaskPriority
|
|
db.First(&category)
|
|
db.First(&priority)
|
|
|
|
dueDate := requests.FlexibleDate{Time: time.Now().Add(7 * 24 * time.Hour).UTC()}
|
|
cost := decimal.NewFromFloat(150.50)
|
|
|
|
req := &requests.CreateTaskRequest{
|
|
ResidenceID: residence.ID,
|
|
Title: "Fix leaky faucet",
|
|
CategoryID: &category.ID,
|
|
PriorityID: &priority.ID,
|
|
DueDate: &dueDate,
|
|
EstimatedCost: &cost,
|
|
}
|
|
|
|
now := time.Now().UTC()
|
|
resp, err := service.CreateTask(req, user.ID, now)
|
|
require.NoError(t, err)
|
|
// Note: Category and Priority are no longer preloaded for performance
|
|
// Client resolves from cache using CategoryID and PriorityID
|
|
assert.NotNil(t, resp.Data.CategoryID, "CategoryID should be set")
|
|
assert.NotNil(t, resp.Data.PriorityID, "PriorityID should be set")
|
|
assert.NotNil(t, resp.Data.DueDate)
|
|
assert.NotNil(t, resp.Data.EstimatedCost)
|
|
}
|
|
|
|
func TestTaskService_CreateTask_AccessDenied(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
otherUser := testutil.CreateTestUser(t, db, "other", "other@test.com", "password")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
|
|
req := &requests.CreateTaskRequest{
|
|
ResidenceID: residence.ID,
|
|
Title: "Test Task",
|
|
}
|
|
|
|
now := time.Now().UTC()
|
|
_, err := service.CreateTask(req, otherUser.ID, now)
|
|
// When creating a task, residence access is checked first
|
|
assert.ErrorIs(t, err, ErrResidenceAccessDenied)
|
|
}
|
|
|
|
func TestTaskService_GetTask(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
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, "Test Task")
|
|
|
|
resp, err := service.GetTask(task.ID, user.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, task.ID, resp.ID)
|
|
assert.Equal(t, "Test Task", resp.Title)
|
|
}
|
|
|
|
func TestTaskService_GetTask_AccessDenied(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
otherUser := testutil.CreateTestUser(t, db, "other", "other@test.com", "password")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
task := testutil.CreateTestTask(t, db, residence.ID, owner.ID, "Test Task")
|
|
|
|
_, err := service.GetTask(task.ID, otherUser.ID)
|
|
assert.ErrorIs(t, err, ErrTaskAccessDenied)
|
|
}
|
|
|
|
func TestTaskService_ListTasks(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
testutil.CreateTestTask(t, db, residence.ID, user.ID, "Task 1")
|
|
testutil.CreateTestTask(t, db, residence.ID, user.ID, "Task 2")
|
|
testutil.CreateTestTask(t, db, residence.ID, user.ID, "Task 3")
|
|
|
|
resp, err := service.ListTasks(user.ID, time.Now().UTC())
|
|
require.NoError(t, err)
|
|
// ListTasks returns a KanbanBoardResponse with columns
|
|
// Count total tasks across all columns
|
|
totalTasks := 0
|
|
for _, col := range resp.Columns {
|
|
totalTasks += col.Count
|
|
}
|
|
assert.Equal(t, 3, totalTasks)
|
|
}
|
|
|
|
func TestTaskService_UpdateTask(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
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, "Original Title")
|
|
|
|
newTitle := "Updated Title"
|
|
newDesc := "Updated description"
|
|
req := &requests.UpdateTaskRequest{
|
|
Title: &newTitle,
|
|
Description: &newDesc,
|
|
}
|
|
|
|
now := time.Now().UTC()
|
|
resp, err := service.UpdateTask(task.ID, user.ID, req, now)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Updated Title", resp.Data.Title)
|
|
assert.Equal(t, "Updated description", resp.Data.Description)
|
|
}
|
|
|
|
func TestTaskService_DeleteTask(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
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, "Test Task")
|
|
|
|
_, err := service.DeleteTask(task.ID, user.ID)
|
|
require.NoError(t, err)
|
|
|
|
_, err = service.GetTask(task.ID, user.ID)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestTaskService_CancelTask(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
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, "Test Task")
|
|
|
|
now := time.Now().UTC()
|
|
resp, err := service.CancelTask(task.ID, user.ID, now)
|
|
require.NoError(t, err)
|
|
assert.True(t, resp.Data.IsCancelled)
|
|
}
|
|
|
|
func TestTaskService_CancelTask_AlreadyCancelled(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
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, "Test Task")
|
|
|
|
now := time.Now().UTC()
|
|
service.CancelTask(task.ID, user.ID, now)
|
|
_, err := service.CancelTask(task.ID, user.ID, now)
|
|
assert.ErrorIs(t, err, ErrTaskAlreadyCancelled)
|
|
}
|
|
|
|
func TestTaskService_UncancelTask(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
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, "Test Task")
|
|
|
|
now := time.Now().UTC()
|
|
service.CancelTask(task.ID, user.ID, now)
|
|
resp, err := service.UncancelTask(task.ID, user.ID, now)
|
|
require.NoError(t, err)
|
|
assert.False(t, resp.Data.IsCancelled)
|
|
}
|
|
|
|
func TestTaskService_ArchiveTask(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
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, "Test Task")
|
|
|
|
now := time.Now().UTC()
|
|
resp, err := service.ArchiveTask(task.ID, user.ID, now)
|
|
require.NoError(t, err)
|
|
assert.True(t, resp.Data.IsArchived)
|
|
}
|
|
|
|
func TestTaskService_UnarchiveTask(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
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, "Test Task")
|
|
|
|
now := time.Now().UTC()
|
|
service.ArchiveTask(task.ID, user.ID, now)
|
|
resp, err := service.UnarchiveTask(task.ID, user.ID, now)
|
|
require.NoError(t, err)
|
|
assert.False(t, resp.Data.IsArchived)
|
|
}
|
|
|
|
func TestTaskService_MarkInProgress(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
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, "Test Task")
|
|
|
|
now := time.Now().UTC()
|
|
resp, err := service.MarkInProgress(task.ID, user.ID, now)
|
|
require.NoError(t, err)
|
|
assert.True(t, resp.Data.InProgress)
|
|
}
|
|
|
|
func TestTaskService_CreateCompletion(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
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, "Test Task")
|
|
|
|
req := &requests.CreateTaskCompletionRequest{
|
|
TaskID: task.ID,
|
|
Notes: "Completed successfully",
|
|
}
|
|
|
|
now := time.Now().UTC()
|
|
resp, err := service.CreateCompletion(req, user.ID, now)
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, resp.Data.ID)
|
|
assert.Equal(t, task.ID, resp.Data.TaskID)
|
|
assert.Equal(t, "Completed successfully", resp.Data.Notes)
|
|
}
|
|
|
|
func TestTaskService_CreateCompletion_RecurringTask_ResetsInProgress(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
|
|
var monthlyFrequency models.TaskFrequency
|
|
db.Where("name = ?", "Monthly").First(&monthlyFrequency)
|
|
|
|
// Create a recurring task that is in progress
|
|
dueDate := time.Now().AddDate(0, 0, 7) // Due in 7 days
|
|
task := &models.Task{
|
|
ResidenceID: residence.ID,
|
|
CreatedByID: user.ID,
|
|
Title: "Recurring Task",
|
|
InProgress: true,
|
|
FrequencyID: &monthlyFrequency.ID,
|
|
DueDate: &dueDate,
|
|
NextDueDate: &dueDate,
|
|
IsCancelled: false,
|
|
IsArchived: false,
|
|
}
|
|
err := db.Create(task).Error
|
|
require.NoError(t, err)
|
|
|
|
// Complete the task
|
|
req := &requests.CreateTaskCompletionRequest{
|
|
TaskID: task.ID,
|
|
Notes: "Monthly maintenance done",
|
|
}
|
|
|
|
now := time.Now().UTC()
|
|
resp, err := service.CreateCompletion(req, user.ID, now)
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, resp.Data.ID)
|
|
|
|
// Verify the task in the response has InProgress reset to false
|
|
require.NotNil(t, resp.Data.Task, "Response should include the updated task")
|
|
assert.False(t, resp.Data.Task.InProgress, "Recurring task InProgress should be reset to false after completion")
|
|
|
|
// Verify NextDueDate was updated (should be ~30 days from now for monthly)
|
|
require.NotNil(t, resp.Data.Task.NextDueDate, "Recurring task should have NextDueDate set")
|
|
expectedNextDue := time.Now().AddDate(0, 0, 30) // Monthly = 30 days
|
|
assert.WithinDuration(t, expectedNextDue, *resp.Data.Task.NextDueDate, 24*time.Hour, "NextDueDate should be approximately 30 days from now")
|
|
|
|
// Also verify by reloading from database directly
|
|
var reloadedTask models.Task
|
|
db.First(&reloadedTask, task.ID)
|
|
assert.False(t, reloadedTask.InProgress, "Database should show InProgress=false")
|
|
}
|
|
|
|
func TestTaskService_GetCompletion(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
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, "Test Task")
|
|
|
|
completion := &models.TaskCompletion{
|
|
TaskID: task.ID,
|
|
CompletedByID: user.ID,
|
|
CompletedAt: time.Now().UTC(),
|
|
Notes: "Test notes",
|
|
}
|
|
db.Create(completion)
|
|
|
|
resp, err := service.GetCompletion(completion.ID, user.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, completion.ID, resp.ID)
|
|
assert.Equal(t, "Test notes", resp.Notes)
|
|
}
|
|
|
|
func TestTaskService_DeleteCompletion(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
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, "Test Task")
|
|
|
|
completion := &models.TaskCompletion{
|
|
TaskID: task.ID,
|
|
CompletedByID: user.ID,
|
|
CompletedAt: time.Now().UTC(),
|
|
}
|
|
db.Create(completion)
|
|
|
|
_, err := service.DeleteCompletion(completion.ID, user.ID)
|
|
require.NoError(t, err)
|
|
|
|
_, err = service.GetCompletion(completion.ID, user.ID)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestTaskService_GetCategories(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
categories, err := service.GetCategories()
|
|
require.NoError(t, err)
|
|
assert.Greater(t, len(categories), 0)
|
|
|
|
// Check JSON structure
|
|
for _, cat := range categories {
|
|
assert.NotZero(t, cat.ID)
|
|
assert.NotEmpty(t, cat.Name)
|
|
}
|
|
}
|
|
|
|
func TestTaskService_GetPriorities(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
priorities, err := service.GetPriorities()
|
|
require.NoError(t, err)
|
|
assert.Greater(t, len(priorities), 0)
|
|
|
|
// Check order by level
|
|
for i := 1; i < len(priorities); i++ {
|
|
assert.GreaterOrEqual(t, priorities[i].Level, priorities[i-1].Level)
|
|
}
|
|
}
|
|
|
|
func TestTaskService_GetFrequencies(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
frequencies, err := service.GetFrequencies()
|
|
require.NoError(t, err)
|
|
assert.Greater(t, len(frequencies), 0)
|
|
}
|
|
|
|
func TestTaskService_SharedUserAccess(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
taskRepo := repositories.NewTaskRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewTaskService(taskRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
sharedUser := testutil.CreateTestUser(t, db, "shared", "shared@test.com", "password")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
|
|
// Share residence
|
|
residenceRepo.AddUser(residence.ID, sharedUser.ID)
|
|
|
|
// Create task as owner
|
|
task := testutil.CreateTestTask(t, db, residence.ID, owner.ID, "Test Task")
|
|
|
|
// Shared user should be able to see the task
|
|
resp, err := service.GetTask(task.ID, sharedUser.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, task.ID, resp.ID)
|
|
|
|
// Shared user should be able to create tasks
|
|
req := &requests.CreateTaskRequest{
|
|
ResidenceID: residence.ID,
|
|
Title: "Shared User Task",
|
|
}
|
|
now := time.Now().UTC()
|
|
_, err = service.CreateTask(req, sharedUser.ID, now)
|
|
require.NoError(t, err)
|
|
}
|