Add PDF reports, file uploads, admin auth, and comprehensive tests
Features: - PDF service for generating task reports with ReportLab-style formatting - Storage service for file uploads (local and S3-compatible) - Admin authentication middleware with JWT support - Admin user model and repository Infrastructure: - Updated Docker configuration for admin panel builds - Email service enhancements for task notifications - Updated router with admin and file upload routes - Environment configuration updates Tests: - Unit tests for handlers (auth, residence, task) - Unit tests for models (user, residence, task) - Unit tests for repositories (user, residence, task) - Unit tests for services (residence, task) - Integration test setup - Test utilities for mocking database and services Database: - Admin user seed data - Updated test data seeds 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
330
internal/repositories/residence_repo_test.go
Normal file
330
internal/repositories/residence_repo_test.go
Normal file
@@ -0,0 +1,330 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/treytartt/mycrib-api/internal/models"
|
||||
"github.com/treytartt/mycrib-api/internal/testutil"
|
||||
)
|
||||
|
||||
func TestResidenceRepository_Create(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
|
||||
residence := &models.Residence{
|
||||
OwnerID: user.ID,
|
||||
Name: "Test House",
|
||||
StreetAddress: "123 Main St",
|
||||
City: "Austin",
|
||||
StateProvince: "TX",
|
||||
PostalCode: "78701",
|
||||
Country: "USA",
|
||||
IsActive: true,
|
||||
IsPrimary: true,
|
||||
}
|
||||
|
||||
err := repo.Create(residence)
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, residence.ID)
|
||||
}
|
||||
|
||||
func TestResidenceRepository_FindByID(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
|
||||
found, err := repo.FindByID(residence.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, residence.ID, found.ID)
|
||||
assert.Equal(t, "Test House", found.Name)
|
||||
assert.Equal(t, user.ID, found.OwnerID)
|
||||
}
|
||||
|
||||
func TestResidenceRepository_FindByID_NotFound(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
_, err := repo.FindByID(9999)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestResidenceRepository_FindByID_InactiveNotFound(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
|
||||
// Deactivate
|
||||
db.Model(residence).Update("is_active", false)
|
||||
|
||||
_, err := repo.FindByID(residence.ID)
|
||||
assert.Error(t, err) // Should not find inactive residences
|
||||
}
|
||||
|
||||
func TestResidenceRepository_FindByUser(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
sharedUser := testutil.CreateTestUser(t, db, "shared", "shared@test.com", "password")
|
||||
|
||||
// Create residences
|
||||
r1 := testutil.CreateTestResidence(t, db, owner.ID, "House 1")
|
||||
r2 := testutil.CreateTestResidence(t, db, owner.ID, "House 2")
|
||||
|
||||
// Share r1 with sharedUser
|
||||
repo.AddUser(r1.ID, sharedUser.ID)
|
||||
|
||||
// Owner should see both
|
||||
ownerResidences, err := repo.FindByUser(owner.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, ownerResidences, 2)
|
||||
|
||||
// Shared user should see only r1
|
||||
sharedResidences, err := repo.FindByUser(sharedUser.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, sharedResidences, 1)
|
||||
assert.Equal(t, r1.ID, sharedResidences[0].ID)
|
||||
|
||||
// Another user should see nothing
|
||||
otherUser := testutil.CreateTestUser(t, db, "other", "other@test.com", "password")
|
||||
otherResidences, err := repo.FindByUser(otherUser.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, otherResidences, 0)
|
||||
|
||||
_ = r2 // suppress unused
|
||||
}
|
||||
|
||||
func TestResidenceRepository_FindOwnedByUser(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
otherOwner := testutil.CreateTestUser(t, db, "other", "other@test.com", "password")
|
||||
|
||||
testutil.CreateTestResidence(t, db, owner.ID, "House 1")
|
||||
testutil.CreateTestResidence(t, db, owner.ID, "House 2")
|
||||
testutil.CreateTestResidence(t, db, otherOwner.ID, "Other House")
|
||||
|
||||
residences, err := repo.FindOwnedByUser(owner.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, residences, 2)
|
||||
}
|
||||
|
||||
func TestResidenceRepository_Update(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
|
||||
residence.Name = "Updated House"
|
||||
residence.City = "Dallas"
|
||||
err := repo.Update(residence)
|
||||
require.NoError(t, err)
|
||||
|
||||
found, err := repo.FindByID(residence.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Updated House", found.Name)
|
||||
assert.Equal(t, "Dallas", found.City)
|
||||
}
|
||||
|
||||
func TestResidenceRepository_Delete(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
|
||||
err := repo.Delete(residence.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should not be found (soft delete sets is_active = false)
|
||||
_, err = repo.FindByID(residence.ID)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestResidenceRepository_HasAccess(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
sharedUser := testutil.CreateTestUser(t, db, "shared", "shared@test.com", "password")
|
||||
otherUser := testutil.CreateTestUser(t, db, "other", "other@test.com", "password")
|
||||
|
||||
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
||||
repo.AddUser(residence.ID, sharedUser.ID)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
userID uint
|
||||
expected bool
|
||||
}{
|
||||
{"owner has access", owner.ID, true},
|
||||
{"shared user has access", sharedUser.ID, true},
|
||||
{"other user no access", otherUser.ID, false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
hasAccess, err := repo.HasAccess(residence.ID, tt.userID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, hasAccess)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResidenceRepository_IsOwner(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
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")
|
||||
repo.AddUser(residence.ID, sharedUser.ID)
|
||||
|
||||
// Owner should be owner
|
||||
isOwner, err := repo.IsOwner(residence.ID, owner.ID)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, isOwner)
|
||||
|
||||
// Shared user should not be owner
|
||||
isOwner, err = repo.IsOwner(residence.ID, sharedUser.ID)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, isOwner)
|
||||
}
|
||||
|
||||
func TestResidenceRepository_AddAndRemoveUser(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
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")
|
||||
|
||||
// Initially no access
|
||||
hasAccess, _ := repo.HasAccess(residence.ID, sharedUser.ID)
|
||||
assert.False(t, hasAccess)
|
||||
|
||||
// Add user
|
||||
err := repo.AddUser(residence.ID, sharedUser.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
hasAccess, _ = repo.HasAccess(residence.ID, sharedUser.ID)
|
||||
assert.True(t, hasAccess)
|
||||
|
||||
// Remove user
|
||||
err = repo.RemoveUser(residence.ID, sharedUser.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
hasAccess, _ = repo.HasAccess(residence.ID, sharedUser.ID)
|
||||
assert.False(t, hasAccess)
|
||||
}
|
||||
|
||||
func TestResidenceRepository_GetResidenceUsers(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
user1 := testutil.CreateTestUser(t, db, "user1", "user1@test.com", "password")
|
||||
user2 := testutil.CreateTestUser(t, db, "user2", "user2@test.com", "password")
|
||||
|
||||
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
||||
repo.AddUser(residence.ID, user1.ID)
|
||||
repo.AddUser(residence.ID, user2.ID)
|
||||
|
||||
users, err := repo.GetResidenceUsers(residence.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, users, 3) // owner + 2 shared users
|
||||
}
|
||||
|
||||
func TestResidenceRepository_CountByOwner(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
|
||||
testutil.CreateTestResidence(t, db, owner.ID, "House 1")
|
||||
testutil.CreateTestResidence(t, db, owner.ID, "House 2")
|
||||
testutil.CreateTestResidence(t, db, owner.ID, "House 3")
|
||||
|
||||
count, err := repo.CountByOwner(owner.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(3), count)
|
||||
}
|
||||
|
||||
func TestResidenceRepository_CreateShareCode(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
||||
|
||||
shareCode, err := repo.CreateShareCode(residence.ID, owner.ID, 24*time.Hour)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, shareCode.Code)
|
||||
assert.Len(t, shareCode.Code, 6)
|
||||
assert.True(t, shareCode.IsActive)
|
||||
assert.NotNil(t, shareCode.ExpiresAt)
|
||||
}
|
||||
|
||||
func TestResidenceRepository_FindShareCodeByCode(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
||||
|
||||
created, err := repo.CreateShareCode(residence.ID, owner.ID, 24*time.Hour)
|
||||
require.NoError(t, err)
|
||||
|
||||
found, err := repo.FindShareCodeByCode(created.Code)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, created.ID, found.ID)
|
||||
assert.Equal(t, residence.ID, found.ResidenceID)
|
||||
}
|
||||
|
||||
func TestResidenceRepository_FindShareCodeByCode_Expired(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
||||
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
||||
|
||||
// Create expired code
|
||||
created, err := repo.CreateShareCode(residence.ID, owner.ID, -1*time.Hour) // Already expired
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = repo.FindShareCodeByCode(created.Code)
|
||||
assert.Error(t, err) // Should fail for expired code
|
||||
}
|
||||
|
||||
func TestResidenceRepository_GetAllResidenceTypes(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewResidenceRepository(db)
|
||||
|
||||
// Create residence types
|
||||
types := []models.ResidenceType{
|
||||
{Name: "House"},
|
||||
{Name: "Apartment"},
|
||||
{Name: "Condo"},
|
||||
}
|
||||
for _, rt := range types {
|
||||
db.Create(&rt)
|
||||
}
|
||||
|
||||
result, err := repo.GetAllResidenceTypes()
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, result, 3)
|
||||
}
|
||||
Reference in New Issue
Block a user