e881d37de0
Every public method on these five services now takes ctx context.Context as the first arg and routes its repo calls through .WithContext(ctx). With TaskService and ResidenceService already migrated, this means every in-process service that touches Postgres now produces a flame graph in Jaeger where the SQL spans nest under the parent HTTP request span. Endpoints now fully traced (HTTP → service → SQL): - /api/auth/login, /register, /logout, /me, /verify-email, /resend-verification - /api/auth/forgot-password, /verify-reset, /reset-password, /update-profile - /api/contractors/* (CRUD + favorite + by-residence + tasks) - /api/documents/* (CRUD + activate/deactivate + image upload/delete) - /api/notifications/* (list, count, mark-read, prefs, devices) - /api/subscription/* (status, purchase, cancel, triggers, promotions) - All previously-migrated /api/tasks/* and /api/residences/* paths Internal helpers also threaded: - TaskService.sendTaskCompletedNotification → forwards ctx - TaskService.UpdateUserTimezone → forwards ctx to NotificationService - ResidenceService.CreateResidence → forwards ctx to SubscriptionService.CheckLimit - NotificationService.registerAPNSDevice / registerGCMDevice → both take ctx ~75 method signatures, ~120 handler/test call sites updated. Tests pass green; the only failure is the pre-existing flaky TaskHandler_QuickComplete SQLite race that fails ~60% of runs on master. Step 3 of the observability plan is now genuinely complete: every API endpoint backed by a Go service emits a per-request flame graph with HTTP → service → SQL spans, plus B2/APNs/FCM/asynq spans where applicable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
766 lines
30 KiB
Go
766 lines
30 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/treytartt/honeydue-api/internal/dto/requests"
|
|
"github.com/treytartt/honeydue-api/internal/models"
|
|
"github.com/treytartt/honeydue-api/internal/repositories"
|
|
"github.com/treytartt/honeydue-api/internal/testutil"
|
|
)
|
|
|
|
func setupDocumentService(t *testing.T) (*DocumentService, *repositories.DocumentRepository, *repositories.ResidenceRepository) {
|
|
db := testutil.SetupTestDB(t)
|
|
testutil.SeedLookupData(t, db)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
return service, documentRepo, residenceRepo
|
|
}
|
|
|
|
// === CreateDocument ===
|
|
|
|
func TestDocumentService_CreateDocument(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
|
|
req := &requests.CreateDocumentRequest{
|
|
ResidenceID: residence.ID,
|
|
Title: "Furnace Manual",
|
|
Description: "Installation manual for the furnace",
|
|
DocumentType: models.DocumentTypeManual,
|
|
FileURL: "https://example.com/manual.pdf",
|
|
FileName: "manual.pdf",
|
|
}
|
|
|
|
resp, err := service.CreateDocument(context.Background(), req, user.ID)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
assert.Equal(t, "Furnace Manual", resp.Title)
|
|
assert.Equal(t, models.DocumentTypeManual, resp.DocumentType)
|
|
}
|
|
|
|
func TestDocumentService_CreateDocument_DefaultType(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
|
|
req := &requests.CreateDocumentRequest{
|
|
ResidenceID: residence.ID,
|
|
Title: "Some Document",
|
|
// DocumentType not set — should default to "general"
|
|
}
|
|
|
|
resp, err := service.CreateDocument(context.Background(), req, user.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, models.DocumentTypeGeneral, resp.DocumentType)
|
|
}
|
|
|
|
func TestDocumentService_CreateDocument_WithImages(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
|
|
req := &requests.CreateDocumentRequest{
|
|
ResidenceID: residence.ID,
|
|
Title: "Receipt with photos",
|
|
ImageURLs: []string{"https://example.com/img1.jpg", "https://example.com/img2.jpg"},
|
|
}
|
|
|
|
resp, err := service.CreateDocument(context.Background(), req, user.ID)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
assert.Equal(t, "Receipt with photos", resp.Title)
|
|
}
|
|
|
|
func TestDocumentService_CreateDocument_AccessDenied(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
|
|
req := &requests.CreateDocumentRequest{
|
|
ResidenceID: residence.ID,
|
|
Title: "Unauthorized Doc",
|
|
}
|
|
|
|
_, err := service.CreateDocument(context.Background(), req, other.ID)
|
|
testutil.AssertAppError(t, err, http.StatusForbidden, "error.residence_access_denied")
|
|
}
|
|
|
|
// === GetDocument ===
|
|
|
|
func TestDocumentService_GetDocument(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, user.ID, "Test Doc")
|
|
|
|
resp, err := service.GetDocument(context.Background(), doc.ID, user.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, doc.ID, resp.ID)
|
|
assert.Equal(t, "Test Doc", resp.Title)
|
|
}
|
|
|
|
func TestDocumentService_GetDocument_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
|
|
_, err := service.GetDocument(context.Background(), 9999, user.ID)
|
|
testutil.AssertAppError(t, err, http.StatusNotFound, "error.document_not_found")
|
|
}
|
|
|
|
func TestDocumentService_GetDocument_AccessDenied(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, owner.ID, "Private Doc")
|
|
|
|
_, err := service.GetDocument(context.Background(), doc.ID, other.ID)
|
|
testutil.AssertAppError(t, err, http.StatusForbidden, "error.document_access_denied")
|
|
}
|
|
|
|
// === UpdateDocument ===
|
|
|
|
func TestDocumentService_UpdateDocument(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, user.ID, "Original Title")
|
|
|
|
newTitle := "Updated Title"
|
|
newDesc := "Updated description"
|
|
req := &requests.UpdateDocumentRequest{
|
|
Title: &newTitle,
|
|
Description: &newDesc,
|
|
}
|
|
|
|
resp, err := service.UpdateDocument(context.Background(), doc.ID, user.ID, req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Updated Title", resp.Title)
|
|
assert.Equal(t, "Updated description", resp.Description)
|
|
}
|
|
|
|
func TestDocumentService_UpdateDocument_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
|
|
newTitle := "Won't Work"
|
|
req := &requests.UpdateDocumentRequest{Title: &newTitle}
|
|
|
|
_, err := service.UpdateDocument(context.Background(), 9999, user.ID, req)
|
|
testutil.AssertAppError(t, err, http.StatusNotFound, "error.document_not_found")
|
|
}
|
|
|
|
func TestDocumentService_UpdateDocument_AccessDenied(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, owner.ID, "Private Doc")
|
|
|
|
newTitle := "Hacked"
|
|
req := &requests.UpdateDocumentRequest{Title: &newTitle}
|
|
|
|
_, err := service.UpdateDocument(context.Background(), doc.ID, other.ID, req)
|
|
testutil.AssertAppError(t, err, http.StatusForbidden, "error.document_access_denied")
|
|
}
|
|
|
|
func TestDocumentService_UpdateDocument_ChangeType(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, user.ID, "My Receipt")
|
|
|
|
newType := models.DocumentTypeWarranty
|
|
req := &requests.UpdateDocumentRequest{DocumentType: &newType}
|
|
|
|
resp, err := service.UpdateDocument(context.Background(), doc.ID, user.ID, req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, models.DocumentTypeWarranty, resp.DocumentType)
|
|
}
|
|
|
|
// === DeleteDocument ===
|
|
|
|
func TestDocumentService_DeleteDocument(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, user.ID, "To Delete")
|
|
|
|
err := service.DeleteDocument(context.Background(), doc.ID, user.ID)
|
|
require.NoError(t, err)
|
|
|
|
// Should not be found after deletion
|
|
_, err = service.GetDocument(context.Background(), doc.ID, user.ID)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestDocumentService_DeleteDocument_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
|
|
err := service.DeleteDocument(context.Background(), 9999, user.ID)
|
|
testutil.AssertAppError(t, err, http.StatusNotFound, "error.document_not_found")
|
|
}
|
|
|
|
func TestDocumentService_DeleteDocument_AccessDenied(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, owner.ID, "Private Doc")
|
|
|
|
err := service.DeleteDocument(context.Background(), doc.ID, other.ID)
|
|
testutil.AssertAppError(t, err, http.StatusForbidden, "error.document_access_denied")
|
|
}
|
|
|
|
// === ListDocuments ===
|
|
|
|
func TestDocumentService_ListDocuments(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
testutil.CreateTestDocument(t, db, residence.ID, user.ID, "Doc 1")
|
|
testutil.CreateTestDocument(t, db, residence.ID, user.ID, "Doc 2")
|
|
|
|
resp, err := service.ListDocuments(context.Background(), user.ID, nil)
|
|
require.NoError(t, err)
|
|
assert.Len(t, resp, 2)
|
|
}
|
|
|
|
func TestDocumentService_ListDocuments_NoResidences(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "loner", "loner@test.com", "Password123")
|
|
|
|
resp, err := service.ListDocuments(context.Background(), user.ID, nil)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, resp)
|
|
}
|
|
|
|
func TestDocumentService_ListDocuments_FilterByResidence(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence1 := testutil.CreateTestResidence(t, db, user.ID, "House 1")
|
|
residence2 := testutil.CreateTestResidence(t, db, user.ID, "House 2")
|
|
testutil.CreateTestDocument(t, db, residence1.ID, user.ID, "Doc A")
|
|
testutil.CreateTestDocument(t, db, residence2.ID, user.ID, "Doc B")
|
|
|
|
filter := &repositories.DocumentFilter{ResidenceID: &residence1.ID}
|
|
resp, err := service.ListDocuments(context.Background(), user.ID, filter)
|
|
require.NoError(t, err)
|
|
assert.Len(t, resp, 1)
|
|
assert.Equal(t, "Doc A", resp[0].Title)
|
|
}
|
|
|
|
func TestDocumentService_ListDocuments_FilterByResidence_AccessDenied(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Owner House")
|
|
// other has their own residence so they have at least one
|
|
testutil.CreateTestResidence(t, db, other.ID, "Other House")
|
|
|
|
filter := &repositories.DocumentFilter{ResidenceID: &residence.ID}
|
|
_, err := service.ListDocuments(context.Background(), other.ID, filter)
|
|
testutil.AssertAppError(t, err, http.StatusForbidden, "error.residence_access_denied")
|
|
}
|
|
|
|
// === ListWarranties ===
|
|
|
|
func TestDocumentService_ListWarranties(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
|
|
// Create a warranty doc directly
|
|
warrantyDoc := &models.Document{
|
|
ResidenceID: residence.ID,
|
|
CreatedByID: user.ID,
|
|
Title: "HVAC Warranty",
|
|
DocumentType: "warranty",
|
|
FileURL: "https://example.com/warranty.pdf",
|
|
}
|
|
err := db.Create(warrantyDoc).Error
|
|
require.NoError(t, err)
|
|
|
|
// Create a general doc
|
|
testutil.CreateTestDocument(t, db, residence.ID, user.ID, "General Doc")
|
|
|
|
resp, err := service.ListWarranties(context.Background(), user.ID)
|
|
require.NoError(t, err)
|
|
assert.Len(t, resp, 1)
|
|
assert.Equal(t, "HVAC Warranty", resp[0].Title)
|
|
}
|
|
|
|
func TestDocumentService_ListWarranties_NoResidences(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "loner", "loner@test.com", "Password123")
|
|
|
|
resp, err := service.ListWarranties(context.Background(), user.ID)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, resp)
|
|
}
|
|
|
|
// === DeactivateDocument ===
|
|
|
|
func TestDocumentService_DeactivateDocument(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, user.ID, "To Deactivate")
|
|
|
|
resp, err := service.DeactivateDocument(context.Background(), doc.ID, user.ID)
|
|
require.NoError(t, err)
|
|
assert.False(t, resp.IsActive)
|
|
}
|
|
|
|
func TestDocumentService_DeactivateDocument_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
|
|
_, err := service.DeactivateDocument(context.Background(), 9999, user.ID)
|
|
testutil.AssertAppError(t, err, http.StatusNotFound, "error.document_not_found")
|
|
}
|
|
|
|
func TestDocumentService_DeactivateDocument_AccessDenied(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, owner.ID, "Private Doc")
|
|
|
|
_, err := service.DeactivateDocument(context.Background(), doc.ID, other.ID)
|
|
testutil.AssertAppError(t, err, http.StatusForbidden, "error.document_access_denied")
|
|
}
|
|
|
|
// === UploadDocumentImage ===
|
|
|
|
func TestDocumentService_UploadDocumentImage(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, user.ID, "Test Doc")
|
|
|
|
resp, err := service.UploadDocumentImage(context.Background(), doc.ID, user.ID, "https://example.com/photo.jpg", "Front view")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
}
|
|
|
|
func TestDocumentService_UploadDocumentImage_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
|
|
_, err := service.UploadDocumentImage(context.Background(), 9999, user.ID, "https://example.com/photo.jpg", "")
|
|
testutil.AssertAppError(t, err, http.StatusNotFound, "error.document_not_found")
|
|
}
|
|
|
|
func TestDocumentService_UploadDocumentImage_AccessDenied(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, owner.ID, "Private Doc")
|
|
|
|
_, err := service.UploadDocumentImage(context.Background(), doc.ID, other.ID, "https://example.com/photo.jpg", "")
|
|
testutil.AssertAppError(t, err, http.StatusForbidden, "error.document_access_denied")
|
|
}
|
|
|
|
// === DeleteDocumentImage ===
|
|
|
|
func TestDocumentService_DeleteDocumentImage(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, user.ID, "Test Doc")
|
|
|
|
// Create an image
|
|
img := &models.DocumentImage{
|
|
DocumentID: doc.ID,
|
|
ImageURL: "https://example.com/photo.jpg",
|
|
}
|
|
err := db.Create(img).Error
|
|
require.NoError(t, err)
|
|
|
|
resp, err := service.DeleteDocumentImage(context.Background(), doc.ID, img.ID, user.ID)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
}
|
|
|
|
func TestDocumentService_DeleteDocumentImage_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, user.ID, "Test Doc")
|
|
|
|
_, err := service.DeleteDocumentImage(context.Background(), doc.ID, 9999, user.ID)
|
|
testutil.AssertAppError(t, err, http.StatusNotFound, "error.document_image_not_found")
|
|
}
|
|
|
|
func TestDocumentService_DeleteDocumentImage_WrongDocument(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
doc1 := testutil.CreateTestDocument(t, db, residence.ID, user.ID, "Doc 1")
|
|
doc2 := testutil.CreateTestDocument(t, db, residence.ID, user.ID, "Doc 2")
|
|
|
|
// Create an image on doc1
|
|
img := &models.DocumentImage{
|
|
DocumentID: doc1.ID,
|
|
ImageURL: "https://example.com/photo.jpg",
|
|
}
|
|
err := db.Create(img).Error
|
|
require.NoError(t, err)
|
|
|
|
// Try to delete the image specifying doc2
|
|
_, err = service.DeleteDocumentImage(context.Background(), doc2.ID, img.ID, user.ID)
|
|
testutil.AssertAppError(t, err, http.StatusNotFound, "error.document_image_not_found")
|
|
}
|
|
|
|
func TestDocumentService_DeleteDocumentImage_AccessDenied(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, owner.ID, "Private Doc")
|
|
|
|
img := &models.DocumentImage{
|
|
DocumentID: doc.ID,
|
|
ImageURL: "https://example.com/photo.jpg",
|
|
}
|
|
err := db.Create(img).Error
|
|
require.NoError(t, err)
|
|
|
|
_, err = service.DeleteDocumentImage(context.Background(), doc.ID, img.ID, other.ID)
|
|
testutil.AssertAppError(t, err, http.StatusForbidden, "error.document_access_denied")
|
|
}
|
|
|
|
// === SharedUser access ===
|
|
|
|
func TestDocumentService_GetDocument_SharedUserHasAccess(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
shared := testutil.CreateTestUser(t, db, "shared", "shared@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
residenceRepo.AddUser(residence.ID, shared.ID)
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, owner.ID, "Shared Doc")
|
|
|
|
resp, err := service.GetDocument(context.Background(), doc.ID, shared.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Shared Doc", resp.Title)
|
|
}
|
|
|
|
// === ActivateDocument ===
|
|
|
|
func TestDocumentService_ActivateDocument(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, user.ID, "To Activate")
|
|
|
|
// Deactivate first
|
|
_, err := service.DeactivateDocument(context.Background(), doc.ID, user.ID)
|
|
require.NoError(t, err)
|
|
|
|
// Now activate
|
|
resp, err := service.ActivateDocument(context.Background(), doc.ID, user.ID)
|
|
require.NoError(t, err)
|
|
assert.True(t, resp.IsActive)
|
|
}
|
|
|
|
func TestDocumentService_ActivateDocument_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
|
|
_, err := service.ActivateDocument(context.Background(), 9999, user.ID)
|
|
testutil.AssertAppError(t, err, http.StatusNotFound, "error.document_not_found")
|
|
}
|
|
|
|
func TestDocumentService_ActivateDocument_AccessDenied(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, owner.ID, "Private Doc")
|
|
|
|
_, err := service.ActivateDocument(context.Background(), doc.ID, other.ID)
|
|
testutil.AssertAppError(t, err, http.StatusForbidden, "error.document_access_denied")
|
|
}
|
|
|
|
// === CreateDocument — with empty image URL in array (should skip) ===
|
|
|
|
func TestDocumentService_CreateDocument_WithEmptyImageURL(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
|
|
req := &requests.CreateDocumentRequest{
|
|
ResidenceID: residence.ID,
|
|
Title: "Doc with empty images",
|
|
ImageURLs: []string{"", "https://example.com/img.jpg", ""},
|
|
}
|
|
|
|
resp, err := service.CreateDocument(context.Background(), req, user.ID)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
}
|
|
|
|
// === UpdateDocument — all optional fields ===
|
|
|
|
func TestDocumentService_UpdateDocument_AllFields(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, user.ID, "Original")
|
|
|
|
newTitle := "Updated"
|
|
newDesc := "New description"
|
|
newType := models.DocumentTypeWarranty
|
|
newFileURL := "https://example.com/new.pdf"
|
|
newFileName := "new.pdf"
|
|
newMimeType := "application/pdf"
|
|
newVendor := "HVAC Corp"
|
|
newSerial := "SN12345"
|
|
newModel := "Model X"
|
|
size := int64(1024)
|
|
|
|
req := &requests.UpdateDocumentRequest{
|
|
Title: &newTitle,
|
|
Description: &newDesc,
|
|
DocumentType: &newType,
|
|
FileURL: &newFileURL,
|
|
FileName: &newFileName,
|
|
FileSize: &size,
|
|
MimeType: &newMimeType,
|
|
Vendor: &newVendor,
|
|
SerialNumber: &newSerial,
|
|
ModelNumber: &newModel,
|
|
}
|
|
|
|
resp, err := service.UpdateDocument(context.Background(), doc.ID, user.ID, req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Updated", resp.Title)
|
|
assert.Equal(t, "New description", resp.Description)
|
|
assert.Equal(t, models.DocumentTypeWarranty, resp.DocumentType)
|
|
}
|
|
|
|
// === ListDocuments — filter by document type ===
|
|
|
|
func TestDocumentService_ListDocuments_FilterByType(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
|
|
|
|
// Create a warranty doc
|
|
warrantyDoc := &models.Document{
|
|
ResidenceID: residence.ID,
|
|
CreatedByID: user.ID,
|
|
Title: "Warranty Doc",
|
|
DocumentType: models.DocumentTypeWarranty,
|
|
FileURL: "https://example.com/w.pdf",
|
|
}
|
|
err := db.Create(warrantyDoc).Error
|
|
require.NoError(t, err)
|
|
|
|
// Create a general doc
|
|
testutil.CreateTestDocument(t, db, residence.ID, user.ID, "General Doc")
|
|
|
|
filter := &repositories.DocumentFilter{DocumentType: string(models.DocumentTypeWarranty)}
|
|
resp, err := service.ListDocuments(context.Background(), user.ID, filter)
|
|
require.NoError(t, err)
|
|
assert.Len(t, resp, 1)
|
|
assert.Equal(t, "Warranty Doc", resp[0].Title)
|
|
}
|
|
|
|
// === Shared user can update/delete documents ===
|
|
|
|
func TestDocumentService_SharedUser_CanUpdate(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
shared := testutil.CreateTestUser(t, db, "shared", "shared@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
residenceRepo.AddUser(residence.ID, shared.ID)
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, owner.ID, "Shared Doc")
|
|
|
|
newTitle := "Updated by shared user"
|
|
req := &requests.UpdateDocumentRequest{Title: &newTitle}
|
|
resp, err := service.UpdateDocument(context.Background(), doc.ID, shared.ID, req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Updated by shared user", resp.Title)
|
|
}
|
|
|
|
func TestDocumentService_SharedUser_CanDelete(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
documentRepo := repositories.NewDocumentRepository(db)
|
|
residenceRepo := repositories.NewResidenceRepository(db)
|
|
service := NewDocumentService(documentRepo, residenceRepo)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
shared := testutil.CreateTestUser(t, db, "shared", "shared@test.com", "Password123")
|
|
residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
|
|
residenceRepo.AddUser(residence.ID, shared.ID)
|
|
doc := testutil.CreateTestDocument(t, db, residence.ID, owner.ID, "Shared Doc")
|
|
|
|
err := service.DeleteDocument(context.Background(), doc.ID, shared.ID)
|
|
require.NoError(t, err)
|
|
}
|