feat: tests
This commit is contained in:
parent
3de76916c9
commit
1346abf733
32 changed files with 3772 additions and 11 deletions
245
internal/repository/expense_test.go
Normal file
245
internal/repository/expense_test.go
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/testutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestExpenseRepository_Create(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewExpenseRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
tag := testutil.CreateTestTag(t, dbi.DB, space.ID, "Food", nil)
|
||||
|
||||
now := time.Now()
|
||||
expense := &model.Expense{
|
||||
ID: uuid.NewString(),
|
||||
SpaceID: space.ID,
|
||||
CreatedBy: user.ID,
|
||||
Description: "Lunch",
|
||||
AmountCents: 1500,
|
||||
Type: model.ExpenseTypeExpense,
|
||||
Date: now,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := repo.Create(expense, []string{tag.ID}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByID(expense.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expense.ID, fetched.ID)
|
||||
assert.Equal(t, "Lunch", fetched.Description)
|
||||
assert.Equal(t, 1500, fetched.AmountCents)
|
||||
assert.Equal(t, model.ExpenseTypeExpense, fetched.Type)
|
||||
})
|
||||
}
|
||||
|
||||
func TestExpenseRepository_GetBySpaceIDPaginated(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewExpenseRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
testutil.CreateTestExpense(t, dbi.DB, space.ID, user.ID, "Expense 1", 1000, model.ExpenseTypeExpense)
|
||||
testutil.CreateTestExpense(t, dbi.DB, space.ID, user.ID, "Expense 2", 2000, model.ExpenseTypeExpense)
|
||||
testutil.CreateTestExpense(t, dbi.DB, space.ID, user.ID, "Expense 3", 3000, model.ExpenseTypeExpense)
|
||||
|
||||
expenses, err := repo.GetBySpaceIDPaginated(space.ID, 2, 0)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, expenses, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestExpenseRepository_CountBySpaceID(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewExpenseRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
testutil.CreateTestExpense(t, dbi.DB, space.ID, user.ID, "Expense 1", 1000, model.ExpenseTypeExpense)
|
||||
testutil.CreateTestExpense(t, dbi.DB, space.ID, user.ID, "Expense 2", 2000, model.ExpenseTypeExpense)
|
||||
|
||||
count, err := repo.CountBySpaceID(space.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, count)
|
||||
})
|
||||
}
|
||||
|
||||
func TestExpenseRepository_GetTagsByExpenseIDs(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewExpenseRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
tag := testutil.CreateTestTag(t, dbi.DB, space.ID, "Groceries", nil)
|
||||
|
||||
now := time.Now()
|
||||
expense := &model.Expense{
|
||||
ID: uuid.NewString(),
|
||||
SpaceID: space.ID,
|
||||
CreatedBy: user.ID,
|
||||
Description: "Weekly groceries",
|
||||
AmountCents: 5000,
|
||||
Type: model.ExpenseTypeExpense,
|
||||
Date: now,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := repo.Create(expense, []string{tag.ID}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
tagMap, err := repo.GetTagsByExpenseIDs([]string{expense.ID})
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, tagMap, expense.ID)
|
||||
require.Len(t, tagMap[expense.ID], 1)
|
||||
assert.Equal(t, tag.ID, tagMap[expense.ID][0].ID)
|
||||
assert.Equal(t, "Groceries", tagMap[expense.ID][0].Name)
|
||||
})
|
||||
}
|
||||
|
||||
func TestExpenseRepository_GetPaymentMethodsByExpenseIDs(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewExpenseRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
method := testutil.CreateTestPaymentMethod(t, dbi.DB, space.ID, "Visa", model.PaymentMethodTypeCredit, user.ID)
|
||||
|
||||
now := time.Now()
|
||||
expense := &model.Expense{
|
||||
ID: uuid.NewString(),
|
||||
SpaceID: space.ID,
|
||||
CreatedBy: user.ID,
|
||||
Description: "Online purchase",
|
||||
AmountCents: 3000,
|
||||
Type: model.ExpenseTypeExpense,
|
||||
Date: now,
|
||||
PaymentMethodID: &method.ID,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := repo.Create(expense, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
methodMap, err := repo.GetPaymentMethodsByExpenseIDs([]string{expense.ID})
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, methodMap, expense.ID)
|
||||
assert.Equal(t, method.ID, methodMap[expense.ID].ID)
|
||||
assert.Equal(t, "Visa", methodMap[expense.ID].Name)
|
||||
assert.Equal(t, model.PaymentMethodTypeCredit, methodMap[expense.ID].Type)
|
||||
})
|
||||
}
|
||||
|
||||
func TestExpenseRepository_GetExpensesByTag(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewExpenseRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
color := "#ff0000"
|
||||
tag := testutil.CreateTestTag(t, dbi.DB, space.ID, "Food", &color)
|
||||
|
||||
now := time.Now()
|
||||
fromDate := now.Add(-24 * time.Hour)
|
||||
toDate := now.Add(24 * time.Hour)
|
||||
|
||||
expense1 := &model.Expense{
|
||||
ID: uuid.NewString(),
|
||||
SpaceID: space.ID,
|
||||
CreatedBy: user.ID,
|
||||
Description: "Lunch",
|
||||
AmountCents: 1500,
|
||||
Type: model.ExpenseTypeExpense,
|
||||
Date: now,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
err := repo.Create(expense1, []string{tag.ID}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
expense2 := &model.Expense{
|
||||
ID: uuid.NewString(),
|
||||
SpaceID: space.ID,
|
||||
CreatedBy: user.ID,
|
||||
Description: "Dinner",
|
||||
AmountCents: 2500,
|
||||
Type: model.ExpenseTypeExpense,
|
||||
Date: now,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
err = repo.Create(expense2, []string{tag.ID}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
summaries, err := repo.GetExpensesByTag(space.ID, fromDate, toDate)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, summaries, 1)
|
||||
assert.Equal(t, tag.ID, summaries[0].TagID)
|
||||
assert.Equal(t, "Food", summaries[0].TagName)
|
||||
assert.Equal(t, 4000, summaries[0].TotalAmount)
|
||||
})
|
||||
}
|
||||
|
||||
func TestExpenseRepository_Update(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewExpenseRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
tag1 := testutil.CreateTestTag(t, dbi.DB, space.ID, "Tag A", nil)
|
||||
tag2 := testutil.CreateTestTag(t, dbi.DB, space.ID, "Tag B", nil)
|
||||
|
||||
now := time.Now()
|
||||
expense := &model.Expense{
|
||||
ID: uuid.NewString(),
|
||||
SpaceID: space.ID,
|
||||
CreatedBy: user.ID,
|
||||
Description: "Original",
|
||||
AmountCents: 1000,
|
||||
Type: model.ExpenseTypeExpense,
|
||||
Date: now,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := repo.Create(expense, []string{tag1.ID}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
expense.Description = "Updated"
|
||||
expense.UpdatedAt = time.Now()
|
||||
err = repo.Update(expense, []string{tag2.ID})
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByID(expense.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Updated", fetched.Description)
|
||||
|
||||
tagMap, err := repo.GetTagsByExpenseIDs([]string{expense.ID})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, tagMap[expense.ID], 1)
|
||||
assert.Equal(t, tag2.ID, tagMap[expense.ID][0].ID)
|
||||
})
|
||||
}
|
||||
|
||||
func TestExpenseRepository_Delete(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewExpenseRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
expense := testutil.CreateTestExpense(t, dbi.DB, space.ID, user.ID, "To Delete", 500, model.ExpenseTypeExpense)
|
||||
|
||||
err := repo.Delete(expense.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = repo.GetByID(expense.ID)
|
||||
assert.ErrorIs(t, err, ErrExpenseNotFound)
|
||||
})
|
||||
}
|
||||
100
internal/repository/invitation_test.go
Normal file
100
internal/repository/invitation_test.go
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/testutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInvitationRepository_Create(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewInvitationRepository(dbi.DB)
|
||||
|
||||
owner := testutil.CreateTestUser(t, dbi.DB, "inv-owner@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, owner.ID, "Invite Space")
|
||||
|
||||
now := time.Now()
|
||||
invitation := &model.SpaceInvitation{
|
||||
Token: uuid.NewString(),
|
||||
SpaceID: space.ID,
|
||||
InviterID: owner.ID,
|
||||
Email: "invitee@example.com",
|
||||
Status: model.InvitationStatusPending,
|
||||
ExpiresAt: now.Add(48 * time.Hour),
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := repo.Create(invitation)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByToken(invitation.Token)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, invitation.Token, fetched.Token)
|
||||
assert.Equal(t, space.ID, fetched.SpaceID)
|
||||
assert.Equal(t, "invitee@example.com", fetched.Email)
|
||||
assert.Equal(t, model.InvitationStatusPending, fetched.Status)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInvitationRepository_GetBySpaceID(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewInvitationRepository(dbi.DB)
|
||||
|
||||
owner := testutil.CreateTestUser(t, dbi.DB, "inv-space-owner@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, owner.ID, "Space Invites")
|
||||
testutil.CreateTestInvitation(t, dbi.DB, space.ID, owner.ID, "one@example.com")
|
||||
|
||||
invitations, err := repo.GetBySpaceID(space.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, invitations, 1)
|
||||
assert.Equal(t, "one@example.com", invitations[0].Email)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInvitationRepository_UpdateStatus(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewInvitationRepository(dbi.DB)
|
||||
|
||||
owner := testutil.CreateTestUser(t, dbi.DB, "inv-status-owner@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, owner.ID, "Status Space")
|
||||
invitation := testutil.CreateTestInvitation(t, dbi.DB, space.ID, owner.ID, "status@example.com")
|
||||
|
||||
err := repo.UpdateStatus(invitation.Token, model.InvitationStatusAccepted)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByToken(invitation.Token)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, model.InvitationStatusAccepted, fetched.Status)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInvitationRepository_Delete(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewInvitationRepository(dbi.DB)
|
||||
|
||||
owner := testutil.CreateTestUser(t, dbi.DB, "inv-delete-owner@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, owner.ID, "Delete Space")
|
||||
invitation := testutil.CreateTestInvitation(t, dbi.DB, space.ID, owner.ID, "delete@example.com")
|
||||
|
||||
err := repo.Delete(invitation.Token)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = repo.GetByToken(invitation.Token)
|
||||
assert.ErrorIs(t, err, ErrInvitationNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInvitationRepository_NotFound(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewInvitationRepository(dbi.DB)
|
||||
|
||||
_, err := repo.GetByToken("nonexistent-token")
|
||||
assert.ErrorIs(t, err, ErrInvitationNotFound)
|
||||
})
|
||||
}
|
||||
161
internal/repository/list_item_test.go
Normal file
161
internal/repository/list_item_test.go
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/testutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestListItemRepository_Create(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewListItemRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
list := testutil.CreateTestShoppingList(t, dbi.DB, space.ID, "Test List")
|
||||
|
||||
now := time.Now()
|
||||
item := &model.ListItem{
|
||||
ID: uuid.NewString(),
|
||||
ListID: list.ID,
|
||||
Name: "Apples",
|
||||
IsChecked: false,
|
||||
CreatedBy: user.ID,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := repo.Create(item)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByID(item.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, item.ID, fetched.ID)
|
||||
assert.Equal(t, list.ID, fetched.ListID)
|
||||
assert.Equal(t, "Apples", fetched.Name)
|
||||
assert.False(t, fetched.IsChecked)
|
||||
assert.Equal(t, user.ID, fetched.CreatedBy)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListItemRepository_GetByListID(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewListItemRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
list := testutil.CreateTestShoppingList(t, dbi.DB, space.ID, "Test List")
|
||||
|
||||
item1 := testutil.CreateTestListItem(t, dbi.DB, list.ID, "Item A", user.ID)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
item2 := testutil.CreateTestListItem(t, dbi.DB, list.ID, "Item B", user.ID)
|
||||
|
||||
items, err := repo.GetByListID(list.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, items, 2)
|
||||
|
||||
// Ordered by created_at ASC, so item1 should be first.
|
||||
assert.Equal(t, item1.ID, items[0].ID)
|
||||
assert.Equal(t, item2.ID, items[1].ID)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListItemRepository_GetByListIDPaginated(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewListItemRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
list := testutil.CreateTestShoppingList(t, dbi.DB, space.ID, "Test List")
|
||||
|
||||
testutil.CreateTestListItem(t, dbi.DB, list.ID, "Item A", user.ID)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
testutil.CreateTestListItem(t, dbi.DB, list.ID, "Item B", user.ID)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
testutil.CreateTestListItem(t, dbi.DB, list.ID, "Item C", user.ID)
|
||||
|
||||
items, err := repo.GetByListIDPaginated(list.ID, 2, 0)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, items, 2)
|
||||
|
||||
count, err := repo.CountByListID(list.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 3, count)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListItemRepository_CountByListID(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewListItemRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
list := testutil.CreateTestShoppingList(t, dbi.DB, space.ID, "Test List")
|
||||
|
||||
testutil.CreateTestListItem(t, dbi.DB, list.ID, "Item A", user.ID)
|
||||
testutil.CreateTestListItem(t, dbi.DB, list.ID, "Item B", user.ID)
|
||||
testutil.CreateTestListItem(t, dbi.DB, list.ID, "Item C", user.ID)
|
||||
|
||||
count, err := repo.CountByListID(list.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 3, count)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListItemRepository_Update(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewListItemRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
list := testutil.CreateTestShoppingList(t, dbi.DB, space.ID, "Test List")
|
||||
|
||||
item := testutil.CreateTestListItem(t, dbi.DB, list.ID, "Original", user.ID)
|
||||
|
||||
item.Name = "Updated"
|
||||
item.IsChecked = true
|
||||
err := repo.Update(item)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByID(item.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Updated", fetched.Name)
|
||||
assert.True(t, fetched.IsChecked)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListItemRepository_Delete(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewListItemRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
list := testutil.CreateTestShoppingList(t, dbi.DB, space.ID, "Test List")
|
||||
|
||||
item := testutil.CreateTestListItem(t, dbi.DB, list.ID, "To Delete", user.ID)
|
||||
|
||||
err := repo.Delete(item.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = repo.GetByID(item.ID)
|
||||
assert.ErrorIs(t, err, ErrListItemNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListItemRepository_DeleteByListID(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewListItemRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
list := testutil.CreateTestShoppingList(t, dbi.DB, space.ID, "Test List")
|
||||
|
||||
testutil.CreateTestListItem(t, dbi.DB, list.ID, "Item A", user.ID)
|
||||
testutil.CreateTestListItem(t, dbi.DB, list.ID, "Item B", user.ID)
|
||||
|
||||
err := repo.DeleteByListID(list.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
items, err := repo.GetByListID(list.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, items)
|
||||
})
|
||||
}
|
||||
169
internal/repository/money_account_test.go
Normal file
169
internal/repository/money_account_test.go
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/testutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMoneyAccountRepository_Create(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewMoneyAccountRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
now := time.Now()
|
||||
account := &model.MoneyAccount{
|
||||
ID: uuid.NewString(),
|
||||
SpaceID: space.ID,
|
||||
Name: "Savings",
|
||||
CreatedBy: user.ID,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := repo.Create(account)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByID(account.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, account.ID, fetched.ID)
|
||||
assert.Equal(t, space.ID, fetched.SpaceID)
|
||||
assert.Equal(t, "Savings", fetched.Name)
|
||||
assert.Equal(t, user.ID, fetched.CreatedBy)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMoneyAccountRepository_GetBySpaceID(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewMoneyAccountRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
testutil.CreateTestMoneyAccount(t, dbi.DB, space.ID, "Account A", user.ID)
|
||||
testutil.CreateTestMoneyAccount(t, dbi.DB, space.ID, "Account B", user.ID)
|
||||
|
||||
accounts, err := repo.GetBySpaceID(space.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, accounts, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMoneyAccountRepository_Update(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewMoneyAccountRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
account := testutil.CreateTestMoneyAccount(t, dbi.DB, space.ID, "Original", user.ID)
|
||||
|
||||
account.Name = "Renamed"
|
||||
err := repo.Update(account)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByID(account.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Renamed", fetched.Name)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMoneyAccountRepository_Delete(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewMoneyAccountRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
account := testutil.CreateTestMoneyAccount(t, dbi.DB, space.ID, "To Delete", user.ID)
|
||||
|
||||
err := repo.Delete(account.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = repo.GetByID(account.ID)
|
||||
assert.ErrorIs(t, err, ErrMoneyAccountNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMoneyAccountRepository_CreateTransfer(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewMoneyAccountRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
account := testutil.CreateTestMoneyAccount(t, dbi.DB, space.ID, "Checking", user.ID)
|
||||
|
||||
transfer := &model.AccountTransfer{
|
||||
ID: uuid.NewString(),
|
||||
AccountID: account.ID,
|
||||
AmountCents: 5000,
|
||||
Direction: model.TransferDirectionDeposit,
|
||||
Note: "Initial deposit",
|
||||
CreatedBy: user.ID,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
err := repo.CreateTransfer(transfer)
|
||||
require.NoError(t, err)
|
||||
|
||||
transfers, err := repo.GetTransfersByAccountID(account.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, transfers, 1)
|
||||
assert.Equal(t, transfer.ID, transfers[0].ID)
|
||||
assert.Equal(t, 5000, transfers[0].AmountCents)
|
||||
assert.Equal(t, model.TransferDirectionDeposit, transfers[0].Direction)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMoneyAccountRepository_DeleteTransfer(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewMoneyAccountRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
account := testutil.CreateTestMoneyAccount(t, dbi.DB, space.ID, "Checking", user.ID)
|
||||
transfer := testutil.CreateTestTransfer(t, dbi.DB, account.ID, 1000, model.TransferDirectionDeposit, user.ID)
|
||||
|
||||
err := repo.DeleteTransfer(transfer.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
transfers, err := repo.GetTransfersByAccountID(account.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, transfers)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMoneyAccountRepository_GetAccountBalance(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewMoneyAccountRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
account := testutil.CreateTestMoneyAccount(t, dbi.DB, space.ID, "Checking", user.ID)
|
||||
|
||||
testutil.CreateTestTransfer(t, dbi.DB, account.ID, 1000, model.TransferDirectionDeposit, user.ID)
|
||||
testutil.CreateTestTransfer(t, dbi.DB, account.ID, 300, model.TransferDirectionWithdrawal, user.ID)
|
||||
|
||||
balance, err := repo.GetAccountBalance(account.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 700, balance)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMoneyAccountRepository_GetTotalAllocatedForSpace(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewMoneyAccountRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
account1 := testutil.CreateTestMoneyAccount(t, dbi.DB, space.ID, "Account A", user.ID)
|
||||
account2 := testutil.CreateTestMoneyAccount(t, dbi.DB, space.ID, "Account B", user.ID)
|
||||
|
||||
testutil.CreateTestTransfer(t, dbi.DB, account1.ID, 2000, model.TransferDirectionDeposit, user.ID)
|
||||
testutil.CreateTestTransfer(t, dbi.DB, account2.ID, 3000, model.TransferDirectionDeposit, user.ID)
|
||||
|
||||
total, err := repo.GetTotalAllocatedForSpace(space.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 5000, total)
|
||||
})
|
||||
}
|
||||
97
internal/repository/payment_method_test.go
Normal file
97
internal/repository/payment_method_test.go
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/testutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPaymentMethodRepository_Create(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewPaymentMethodRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
lastFour := "4242"
|
||||
now := time.Now()
|
||||
method := &model.PaymentMethod{
|
||||
ID: uuid.NewString(),
|
||||
SpaceID: space.ID,
|
||||
Name: "Visa Gold",
|
||||
Type: model.PaymentMethodTypeCredit,
|
||||
LastFour: &lastFour,
|
||||
CreatedBy: user.ID,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := repo.Create(method)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByID(method.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, method.ID, fetched.ID)
|
||||
assert.Equal(t, space.ID, fetched.SpaceID)
|
||||
assert.Equal(t, "Visa Gold", fetched.Name)
|
||||
assert.Equal(t, model.PaymentMethodTypeCredit, fetched.Type)
|
||||
require.NotNil(t, fetched.LastFour)
|
||||
assert.Equal(t, "4242", *fetched.LastFour)
|
||||
assert.Equal(t, user.ID, fetched.CreatedBy)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPaymentMethodRepository_GetBySpaceID(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewPaymentMethodRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
testutil.CreateTestPaymentMethod(t, dbi.DB, space.ID, "Visa", model.PaymentMethodTypeCredit, user.ID)
|
||||
testutil.CreateTestPaymentMethod(t, dbi.DB, space.ID, "Debit Card", model.PaymentMethodTypeDebit, user.ID)
|
||||
|
||||
methods, err := repo.GetBySpaceID(space.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, methods, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPaymentMethodRepository_Update(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewPaymentMethodRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
method := testutil.CreateTestPaymentMethod(t, dbi.DB, space.ID, "Old Card", model.PaymentMethodTypeCredit, user.ID)
|
||||
|
||||
method.Name = "New Card"
|
||||
method.Type = model.PaymentMethodTypeDebit
|
||||
err := repo.Update(method)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByID(method.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "New Card", fetched.Name)
|
||||
assert.Equal(t, model.PaymentMethodTypeDebit, fetched.Type)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPaymentMethodRepository_Delete(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewPaymentMethodRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
method := testutil.CreateTestPaymentMethod(t, dbi.DB, space.ID, "To Delete", model.PaymentMethodTypeCredit, user.ID)
|
||||
|
||||
err := repo.Delete(method.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = repo.GetByID(method.ID)
|
||||
assert.ErrorIs(t, err, ErrPaymentMethodNotFound)
|
||||
})
|
||||
}
|
||||
63
internal/repository/profile_test.go
Normal file
63
internal/repository/profile_test.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/testutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestProfileRepository_Create(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewProfileRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "profile-create@example.com", nil)
|
||||
|
||||
now := time.Now()
|
||||
profile := &model.Profile{
|
||||
ID: uuid.NewString(),
|
||||
UserID: user.ID,
|
||||
Name: "Test User",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
id, err := repo.Create(profile)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, profile.ID, id)
|
||||
|
||||
fetched, err := repo.ByUserID(user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Test User", fetched.Name)
|
||||
assert.Equal(t, user.ID, fetched.UserID)
|
||||
})
|
||||
}
|
||||
|
||||
func TestProfileRepository_UpdateName(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewProfileRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "profile-update@example.com", nil)
|
||||
testutil.CreateTestProfile(t, dbi.DB, user.ID, "Old Name")
|
||||
|
||||
err := repo.UpdateName(user.ID, "New Name")
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.ByUserID(user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "New Name", fetched.Name)
|
||||
})
|
||||
}
|
||||
|
||||
func TestProfileRepository_NotFound(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewProfileRepository(dbi.DB)
|
||||
|
||||
_, err := repo.ByUserID("nonexistent-id")
|
||||
assert.ErrorIs(t, err, ErrProfileNotFound)
|
||||
})
|
||||
}
|
||||
93
internal/repository/shopping_list_test.go
Normal file
93
internal/repository/shopping_list_test.go
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/testutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestShoppingListRepository_Create(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewShoppingListRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
now := time.Now()
|
||||
list := &model.ShoppingList{
|
||||
ID: uuid.NewString(),
|
||||
SpaceID: space.ID,
|
||||
Name: "Groceries",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := repo.Create(list)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByID(list.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, list.ID, fetched.ID)
|
||||
assert.Equal(t, space.ID, fetched.SpaceID)
|
||||
assert.Equal(t, "Groceries", fetched.Name)
|
||||
})
|
||||
}
|
||||
|
||||
func TestShoppingListRepository_GetBySpaceID(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewShoppingListRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
list1 := testutil.CreateTestShoppingList(t, dbi.DB, space.ID, "List A")
|
||||
// Small delay to ensure distinct created_at timestamps for ordering.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
list2 := testutil.CreateTestShoppingList(t, dbi.DB, space.ID, "List B")
|
||||
|
||||
lists, err := repo.GetBySpaceID(space.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, lists, 2)
|
||||
|
||||
// Ordered by created_at DESC, so list2 should be first.
|
||||
assert.Equal(t, list2.ID, lists[0].ID)
|
||||
assert.Equal(t, list1.ID, lists[1].ID)
|
||||
})
|
||||
}
|
||||
|
||||
func TestShoppingListRepository_Update(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewShoppingListRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
list := testutil.CreateTestShoppingList(t, dbi.DB, space.ID, "Original Name")
|
||||
|
||||
list.Name = "Updated Name"
|
||||
err := repo.Update(list)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByID(list.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Updated Name", fetched.Name)
|
||||
})
|
||||
}
|
||||
|
||||
func TestShoppingListRepository_Delete(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewShoppingListRepository(dbi.DB)
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "test@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Test Space")
|
||||
|
||||
list := testutil.CreateTestShoppingList(t, dbi.DB, space.ID, "To Delete")
|
||||
|
||||
err := repo.Delete(list.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = repo.GetByID(list.ID)
|
||||
assert.ErrorIs(t, err, ErrShoppingListNotFound)
|
||||
})
|
||||
}
|
||||
140
internal/repository/space_test.go
Normal file
140
internal/repository/space_test.go
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/testutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSpaceRepository_Create(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewSpaceRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "space-create@example.com", nil)
|
||||
|
||||
now := time.Now()
|
||||
space := &model.Space{
|
||||
ID: uuid.NewString(),
|
||||
Name: "My Space",
|
||||
OwnerID: user.ID,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := repo.Create(space)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.ByID(space.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "My Space", fetched.Name)
|
||||
assert.Equal(t, user.ID, fetched.OwnerID)
|
||||
|
||||
isMember, err := repo.IsMember(space.ID, user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, isMember, "owner should be a member after Create")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSpaceRepository_ByUserID(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewSpaceRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "space-byuser@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "User Space")
|
||||
|
||||
spaces, err := repo.ByUserID(user.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, spaces, 1)
|
||||
assert.Equal(t, space.ID, spaces[0].ID)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSpaceRepository_AddMember(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewSpaceRepository(dbi.DB)
|
||||
|
||||
owner := testutil.CreateTestUser(t, dbi.DB, "space-owner@example.com", nil)
|
||||
member := testutil.CreateTestUser(t, dbi.DB, "space-member@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, owner.ID, "Shared Space")
|
||||
|
||||
err := repo.AddMember(space.ID, member.ID, model.RoleMember)
|
||||
require.NoError(t, err)
|
||||
|
||||
isMember, err := repo.IsMember(space.ID, member.ID)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, isMember)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSpaceRepository_RemoveMember(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewSpaceRepository(dbi.DB)
|
||||
|
||||
owner := testutil.CreateTestUser(t, dbi.DB, "remove-owner@example.com", nil)
|
||||
member := testutil.CreateTestUser(t, dbi.DB, "remove-member@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, owner.ID, "Remove Test")
|
||||
|
||||
err := repo.AddMember(space.ID, member.ID, model.RoleMember)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = repo.RemoveMember(space.ID, member.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
isMember, err := repo.IsMember(space.ID, member.ID)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, isMember)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSpaceRepository_GetMembers(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewSpaceRepository(dbi.DB)
|
||||
|
||||
owner, _ := testutil.CreateTestUserWithProfile(t, dbi.DB, "members-owner@example.com", "Owner")
|
||||
member, _ := testutil.CreateTestUserWithProfile(t, dbi.DB, "members-member@example.com", "Member")
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, owner.ID, "Members Space")
|
||||
|
||||
err := repo.AddMember(space.ID, member.ID, model.RoleMember)
|
||||
require.NoError(t, err)
|
||||
|
||||
members, err := repo.GetMembers(space.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, members, 2)
|
||||
|
||||
// The query orders by role DESC (owner first), then joined_at ASC.
|
||||
assert.Equal(t, model.RoleOwner, members[0].Role)
|
||||
assert.Equal(t, "Owner", members[0].Name)
|
||||
assert.Equal(t, model.RoleMember, members[1].Role)
|
||||
assert.Equal(t, "Member", members[1].Name)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSpaceRepository_UpdateName(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewSpaceRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "space-rename@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Old Name")
|
||||
|
||||
err := repo.UpdateName(space.ID, "New Name")
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.ByID(space.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "New Name", fetched.Name)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSpaceRepository_NotFound(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewSpaceRepository(dbi.DB)
|
||||
|
||||
_, err := repo.ByID("nonexistent-id")
|
||||
assert.ErrorIs(t, err, ErrSpaceNotFound)
|
||||
})
|
||||
}
|
||||
120
internal/repository/tag_test.go
Normal file
120
internal/repository/tag_test.go
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/testutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTagRepository_Create(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewTagRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "tag-create@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Tag Space")
|
||||
|
||||
color := "#ff0000"
|
||||
now := time.Now()
|
||||
tag := &model.Tag{
|
||||
ID: uuid.NewString(),
|
||||
SpaceID: space.ID,
|
||||
Name: "Groceries",
|
||||
Color: &color,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := repo.Create(tag)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByID(tag.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Groceries", fetched.Name)
|
||||
assert.Equal(t, &color, fetched.Color)
|
||||
assert.Equal(t, space.ID, fetched.SpaceID)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTagRepository_GetBySpaceID(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewTagRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "tag-list@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Tag List Space")
|
||||
|
||||
// Create tags with names that sort alphabetically: "Alpha" < "Beta".
|
||||
testutil.CreateTestTag(t, dbi.DB, space.ID, "Beta", nil)
|
||||
testutil.CreateTestTag(t, dbi.DB, space.ID, "Alpha", nil)
|
||||
|
||||
tags, err := repo.GetBySpaceID(space.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, tags, 2)
|
||||
assert.Equal(t, "Alpha", tags[0].Name)
|
||||
assert.Equal(t, "Beta", tags[1].Name)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTagRepository_Update(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewTagRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "tag-update@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Tag Update Space")
|
||||
tag := testutil.CreateTestTag(t, dbi.DB, space.ID, "Old Tag", nil)
|
||||
|
||||
newColor := "#00ff00"
|
||||
tag.Name = "New Tag"
|
||||
tag.Color = &newColor
|
||||
|
||||
err := repo.Update(tag)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.GetByID(tag.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "New Tag", fetched.Name)
|
||||
assert.Equal(t, &newColor, fetched.Color)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTagRepository_Delete(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewTagRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "tag-delete@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Tag Delete Space")
|
||||
tag := testutil.CreateTestTag(t, dbi.DB, space.ID, "Doomed Tag", nil)
|
||||
|
||||
err := repo.Delete(tag.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = repo.GetByID(tag.ID)
|
||||
assert.ErrorIs(t, err, ErrTagNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTagRepository_DuplicateTagName(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewTagRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "tag-dup@example.com", nil)
|
||||
space := testutil.CreateTestSpace(t, dbi.DB, user.ID, "Tag Dup Space")
|
||||
testutil.CreateTestTag(t, dbi.DB, space.ID, "Duplicate", nil)
|
||||
|
||||
now := time.Now()
|
||||
duplicate := &model.Tag{
|
||||
ID: uuid.NewString(),
|
||||
SpaceID: space.ID,
|
||||
Name: "Duplicate",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := repo.Create(duplicate)
|
||||
assert.ErrorIs(t, err, ErrDuplicateTagName)
|
||||
})
|
||||
}
|
||||
78
internal/repository/token_test.go
Normal file
78
internal/repository/token_test.go
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/testutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTokenRepository_Create(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewTokenRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "token-create@example.com", nil)
|
||||
|
||||
token := &model.Token{
|
||||
ID: uuid.NewString(),
|
||||
UserID: user.ID,
|
||||
Type: model.TokenTypeEmailVerify,
|
||||
Token: uuid.NewString(),
|
||||
ExpiresAt: time.Now().Add(1 * time.Hour),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
id, err := repo.Create(token)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, token.ID, id)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTokenRepository_ConsumeToken(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewTokenRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "token-consume@example.com", nil)
|
||||
tokenString := uuid.NewString()
|
||||
testutil.CreateTestToken(t, dbi.DB, user.ID, model.TokenTypeEmailVerify, tokenString, time.Now().Add(1*time.Hour))
|
||||
|
||||
consumed, err := repo.ConsumeToken(tokenString)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, consumed.UsedAt)
|
||||
assert.Equal(t, user.ID, consumed.UserID)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTokenRepository_ConsumeExpiredToken(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewTokenRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "token-expired@example.com", nil)
|
||||
tokenString := uuid.NewString()
|
||||
testutil.CreateTestToken(t, dbi.DB, user.ID, model.TokenTypeEmailVerify, tokenString, time.Now().Add(-1*time.Hour))
|
||||
|
||||
_, err := repo.ConsumeToken(tokenString)
|
||||
assert.ErrorIs(t, err, ErrTokenNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTokenRepository_DeleteByUserAndType(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewTokenRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "token-delete@example.com", nil)
|
||||
tokenString := uuid.NewString()
|
||||
testutil.CreateTestToken(t, dbi.DB, user.ID, model.TokenTypeEmailVerify, tokenString, time.Now().Add(1*time.Hour))
|
||||
|
||||
err := repo.DeleteByUserAndType(user.ID, model.TokenTypeEmailVerify)
|
||||
require.NoError(t, err)
|
||||
|
||||
// The token should no longer be consumable since it was deleted.
|
||||
_, err = repo.ConsumeToken(tokenString)
|
||||
assert.ErrorIs(t, err, ErrTokenNotFound)
|
||||
})
|
||||
}
|
||||
101
internal/repository/user_test.go
Normal file
101
internal/repository/user_test.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/testutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUserRepository_Create(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewUserRepository(dbi.DB)
|
||||
|
||||
user := &model.User{
|
||||
ID: uuid.NewString(),
|
||||
Email: "create@example.com",
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
id, err := repo.Create(user)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, user.ID, id)
|
||||
|
||||
fetched, err := repo.ByID(id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, user.Email, fetched.Email)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserRepository_ByEmail(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewUserRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "byemail@example.com", nil)
|
||||
|
||||
fetched, err := repo.ByEmail("byemail@example.com")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, user.ID, fetched.ID)
|
||||
assert.Equal(t, "byemail@example.com", fetched.Email)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserRepository_Update(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewUserRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "before@example.com", nil)
|
||||
|
||||
user.Email = "after@example.com"
|
||||
err := repo.Update(user)
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := repo.ByID(user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "after@example.com", fetched.Email)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserRepository_Delete(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewUserRepository(dbi.DB)
|
||||
|
||||
user := testutil.CreateTestUser(t, dbi.DB, "delete@example.com", nil)
|
||||
|
||||
err := repo.Delete(user.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = repo.ByID(user.ID)
|
||||
assert.ErrorIs(t, err, ErrUserNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserRepository_DuplicateEmail(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewUserRepository(dbi.DB)
|
||||
|
||||
testutil.CreateTestUser(t, dbi.DB, "dup@example.com", nil)
|
||||
|
||||
duplicate := &model.User{
|
||||
ID: uuid.NewString(),
|
||||
Email: "dup@example.com",
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
_, err := repo.Create(duplicate)
|
||||
assert.ErrorIs(t, err, ErrDuplicateEmail)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserRepository_NotFound(t *testing.T) {
|
||||
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||
repo := NewUserRepository(dbi.DB)
|
||||
|
||||
_, err := repo.ByID("nonexistent-id")
|
||||
assert.ErrorIs(t, err, ErrUserNotFound)
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue