Files
honeyDueAPI/internal/services/document_service_test.go
T
Trey t b7f83293b8
Backend CI / Test (push) Has been cancelled
Backend CI / Contract Tests (push) Has been cancelled
Backend CI / Build (push) Has been cancelled
Backend CI / Lint (push) Has been cancelled
Backend CI / Secret Scanning (push) Has been cancelled
refactor(uploads): drop legacy multipart code paths
The presigned-URL upload flow (POST /api/uploads/presign + direct B2 POST
+ upload_ids[] in entity creation) is now the only image upload path. The
legacy multipart routes and DTO fields used by older clients are removed:

Removed:
  - POST /api/uploads/image/        (legacy multipart upload → URL)
  - POST /api/uploads/document/     (legacy multipart upload → URL)
  - POST /api/uploads/completion/   (legacy multipart upload → URL)
  - Multipart branch in POST /api/task-completions/ (now JSON-only)
  - CreateTaskCompletionRequest.ImageURLs DTO field
  - UpdateTaskCompletionRequest.ImageURLs DTO field
  - CreateDocumentRequest.ImageURLs DTO field
  - Service-layer ImageURLs loops in task_service.CreateCompletion,
    task_service.UpdateCompletion, document_service.CreateDocument
  - Tests exercising the removed paths
  - Now-unused imports (strings/time/decimal) in task_handler.go

Kept:
  - DELETE /api/uploads/  (orphan-cleanup endpoint, still useful)
  - POST /api/uploads/presign/  (the new path)
  - POST /api/documents/:id/images/  (uses storage_service.Upload directly,
    same multipart pattern but separate code path; deferred for now)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:19:21 -07:00

731 lines
29 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)
}
// TestDocumentService_CreateDocument_WithImages was removed alongside the
// legacy ImageURLs field. The presigned-URL flow is exercised end-to-end
// in the integration tests; mocking B2 + a pending_uploads fixture for a
// unit test was deemed not worth the complexity.
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")
}
// TestDocumentService_CreateDocument_WithEmptyImageURL was removed alongside
// the legacy ImageURLs field — empty-URL filtering is no longer a code path.
// === 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)
}