Coverage priorities 1-5: test pure functions, extract interfaces, mock-based handler tests
- Priority 1: Test NewSendEmailTask + NewSendPushTask (5 tests) - Priority 2: Test customHTTPErrorHandler — all 15+ branches (21 tests) - Priority 3: Extract Enqueuer interface + payload builders in worker pkg (5 tests) - Priority 4: Extract ClassifyFile/ComputeRelPath in migrate-encrypt (6 tests) - Priority 5: Define Handler interfaces, refactor to accept them, mock-based tests (14 tests) - Fix .gitignore: /worker instead of worker to stop ignoring internal/worker/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
356
internal/repositories/contractor_repo_coverage_test.go
Normal file
356
internal/repositories/contractor_repo_coverage_test.go
Normal file
@@ -0,0 +1,356 @@
|
||||
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 TestContractorRepository_Create(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
|
||||
contractor := &models.Contractor{
|
||||
ResidenceID: &residence.ID,
|
||||
CreatedByID: user.ID,
|
||||
Name: "Mike's Plumbing",
|
||||
Company: "Mike's Plumbing Co.",
|
||||
Phone: "+1-555-1234",
|
||||
Email: "mike@plumbing.com",
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
err := repo.Create(contractor)
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, contractor.ID)
|
||||
}
|
||||
|
||||
func TestContractorRepository_FindByID(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Test Contractor")
|
||||
|
||||
found, err := repo.FindByID(contractor.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, contractor.ID, found.ID)
|
||||
assert.Equal(t, "Test Contractor", found.Name)
|
||||
}
|
||||
|
||||
func TestContractorRepository_FindByID_NotFound(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
_, err := repo.FindByID(9999)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestContractorRepository_FindByID_InactiveNotFound(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Inactive Contractor")
|
||||
|
||||
// Soft-delete
|
||||
db.Model(&models.Contractor{}).Where("id = ?", contractor.ID).Update("is_active", false)
|
||||
|
||||
_, err := repo.FindByID(contractor.ID)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestContractorRepository_FindByResidence(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
otherResidence := testutil.CreateTestResidence(t, db, user.ID, "Other House")
|
||||
|
||||
testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Contractor A")
|
||||
testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Contractor B")
|
||||
testutil.CreateTestContractor(t, db, otherResidence.ID, user.ID, "Other Contractor")
|
||||
|
||||
contractors, err := repo.FindByResidence(residence.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, contractors, 2)
|
||||
}
|
||||
|
||||
func TestContractorRepository_FindByResidence_ExcludesInactive(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
|
||||
active := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Active Contractor")
|
||||
inactive := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Inactive Contractor")
|
||||
db.Model(&models.Contractor{}).Where("id = ?", inactive.ID).Update("is_active", false)
|
||||
|
||||
contractors, err := repo.FindByResidence(residence.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, contractors, 1)
|
||||
assert.Equal(t, active.ID, contractors[0].ID)
|
||||
}
|
||||
|
||||
func TestContractorRepository_FindByUser_WithResidences(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
|
||||
testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Residence Contractor")
|
||||
|
||||
contractors, err := repo.FindByUser(user.ID, []uint{residence.ID})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, contractors, 1)
|
||||
assert.Equal(t, "Residence Contractor", contractors[0].Name)
|
||||
}
|
||||
|
||||
func TestContractorRepository_FindByUser_NoResidences(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
|
||||
// Personal contractor with no residence
|
||||
personal := &models.Contractor{
|
||||
ResidenceID: nil,
|
||||
CreatedByID: user.ID,
|
||||
Name: "Personal Contractor",
|
||||
IsActive: true,
|
||||
}
|
||||
err := db.Create(personal).Error
|
||||
require.NoError(t, err)
|
||||
|
||||
contractors, err := repo.FindByUser(user.ID, []uint{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, contractors, 1)
|
||||
assert.Equal(t, "Personal Contractor", contractors[0].Name)
|
||||
}
|
||||
|
||||
func TestContractorRepository_Update(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Original Name")
|
||||
|
||||
contractor.Name = "Updated Name"
|
||||
contractor.Phone = "+1-555-9999"
|
||||
err := repo.Update(contractor)
|
||||
require.NoError(t, err)
|
||||
|
||||
found, err := repo.FindByID(contractor.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Updated Name", found.Name)
|
||||
assert.Equal(t, "+1-555-9999", found.Phone)
|
||||
}
|
||||
|
||||
func TestContractorRepository_Delete(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "To Delete")
|
||||
|
||||
err := repo.Delete(contractor.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should not be found (soft delete)
|
||||
_, err = repo.FindByID(contractor.ID)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestContractorRepository_CountByResidence(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
|
||||
testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Contractor 1")
|
||||
testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Contractor 2")
|
||||
|
||||
count, err := repo.CountByResidence(residence.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(2), count)
|
||||
}
|
||||
|
||||
func TestContractorRepository_CountByResidenceIDs(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
r1 := testutil.CreateTestResidence(t, db, user.ID, "House 1")
|
||||
r2 := testutil.CreateTestResidence(t, db, user.ID, "House 2")
|
||||
|
||||
testutil.CreateTestContractor(t, db, r1.ID, user.ID, "Contractor A")
|
||||
testutil.CreateTestContractor(t, db, r2.ID, user.ID, "Contractor B")
|
||||
testutil.CreateTestContractor(t, db, r2.ID, user.ID, "Contractor C")
|
||||
|
||||
count, err := repo.CountByResidenceIDs([]uint{r1.ID, r2.ID})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(3), count)
|
||||
}
|
||||
|
||||
func TestContractorRepository_CountByResidenceIDs_Empty(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
count, err := repo.CountByResidenceIDs([]uint{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(0), count)
|
||||
}
|
||||
|
||||
func TestContractorRepository_SetSpecialties(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
testutil.SeedLookupData(t, db)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Skilled Contractor")
|
||||
|
||||
// Get seeded specialties
|
||||
var specialties []models.ContractorSpecialty
|
||||
err := db.Find(&specialties).Error
|
||||
require.NoError(t, err)
|
||||
require.GreaterOrEqual(t, len(specialties), 2)
|
||||
|
||||
// Set specialties
|
||||
err = repo.SetSpecialties(contractor.ID, []uint{specialties[0].ID, specialties[1].ID})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify via FindByID
|
||||
found, err := repo.FindByID(contractor.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, found.Specialties, 2)
|
||||
}
|
||||
|
||||
func TestContractorRepository_SetSpecialties_ClearsExisting(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
testutil.SeedLookupData(t, db)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Skilled Contractor")
|
||||
|
||||
var specialties []models.ContractorSpecialty
|
||||
err := db.Find(&specialties).Error
|
||||
require.NoError(t, err)
|
||||
require.GreaterOrEqual(t, len(specialties), 2)
|
||||
|
||||
// Set initial specialties
|
||||
err = repo.SetSpecialties(contractor.ID, []uint{specialties[0].ID, specialties[1].ID})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Clear all specialties
|
||||
err = repo.SetSpecialties(contractor.ID, []uint{})
|
||||
require.NoError(t, err)
|
||||
|
||||
found, err := repo.FindByID(contractor.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, found.Specialties, 0)
|
||||
}
|
||||
|
||||
func TestContractorRepository_GetAllSpecialties(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
testutil.SeedLookupData(t, db)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
specialties, err := repo.GetAllSpecialties()
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, len(specialties), 4) // Seeded 4 specialties
|
||||
}
|
||||
|
||||
func TestContractorRepository_FindSpecialtyByID(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
testutil.SeedLookupData(t, db)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
var seeded models.ContractorSpecialty
|
||||
err := db.First(&seeded).Error
|
||||
require.NoError(t, err)
|
||||
|
||||
found, err := repo.FindSpecialtyByID(seeded.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, seeded.Name, found.Name)
|
||||
}
|
||||
|
||||
func TestContractorRepository_FindSpecialtyByID_NotFound(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
_, err := repo.FindSpecialtyByID(9999)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestContractorRepository_GetTasksForContractor(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Task Contractor")
|
||||
|
||||
// Create tasks linked to contractor
|
||||
task1 := &models.Task{
|
||||
ResidenceID: residence.ID,
|
||||
CreatedByID: user.ID,
|
||||
Title: "Task 1",
|
||||
ContractorID: &contractor.ID,
|
||||
Version: 1,
|
||||
}
|
||||
err := db.Create(task1).Error
|
||||
require.NoError(t, err)
|
||||
|
||||
task2 := &models.Task{
|
||||
ResidenceID: residence.ID,
|
||||
CreatedByID: user.ID,
|
||||
Title: "Task 2",
|
||||
ContractorID: &contractor.ID,
|
||||
Version: 1,
|
||||
}
|
||||
err = db.Create(task2).Error
|
||||
require.NoError(t, err)
|
||||
|
||||
tasks, err := repo.GetTasksForContractor(contractor.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, tasks, 2)
|
||||
}
|
||||
|
||||
func TestContractorRepository_FindByResidence_FavoritesFirst(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
repo := NewContractorRepository(db)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
||||
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
||||
|
||||
// Create non-favorite first (alphabetically first)
|
||||
testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Alpha Contractor")
|
||||
|
||||
// Create favorite
|
||||
fav := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Zeta Contractor")
|
||||
db.Model(&models.Contractor{}).Where("id = ?", fav.ID).Update("is_favorite", true)
|
||||
|
||||
contractors, err := repo.FindByResidence(residence.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, contractors, 2)
|
||||
// Favorite should be first
|
||||
assert.Equal(t, fav.ID, contractors[0].ID)
|
||||
}
|
||||
Reference in New Issue
Block a user