package folder_test import ( "errors" "testing" "queryorchestration/internal/database/repository" "queryorchestration/internal/folder" "queryorchestration/internal/harddelete" "queryorchestration/internal/test" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // createDocWithEntry creates a document with an S3 entry in the given folder. // Returns the document ID. func createDocWithEntry(t *testing.T, cfg *TestConfig, clientID string, folderID *uuid.UUID, hash string) uuid.UUID { t.Helper() ctx := t.Context() q := cfg.GetDBQueries() filename := hash + ".pdf" docID, err := q.CreateDocument(ctx, &repository.CreateDocumentParams{ Clientid: clientID, Hash: hash, Filename: &filename, Folderid: folderID, }) require.NoError(t, err) err = q.AddDocumentEntry(ctx, &repository.AddDocumentEntryParams{ Documentid: docID, Bucket: "test-bucket", Key: "test-key/" + hash, }) require.NoError(t, err) return docID } func TestHardDeleteFolder(t *testing.T) { ctx := t.Context() cfg := &TestConfig{} test.CreateDB(t, cfg) clientID := "test-folder-delete" test.CreateTestClient(t, cfg, clientID, "Test Folder Delete") svc := folder.New(cfg) q := cfg.GetDBQueries() // Get root folder for this client rootFolder, err := q.GetFolderByPath(ctx, &repository.GetFolderByPathParams{ Clientid: clientID, Path: "/", }) require.NoError(t, err) t.Run("rejects deleting root folder", func(t *testing.T) { _, err := svc.HardDelete(ctx, rootFolder.ID, false) require.Error(t, err) var conflictErr *harddelete.ConflictError assert.True(t, errors.As(err, &conflictErr)) assert.Contains(t, conflictErr.Message, "root folder") }) t.Run("returns NotFoundError for non-existent folder", func(t *testing.T) { fakeID := uuid.New() _, err := svc.HardDelete(ctx, fakeID, false) require.Error(t, err) var notFoundErr *harddelete.NotFoundError assert.True(t, errors.As(err, ¬FoundErr)) }) t.Run("deletes empty folder tree", func(t *testing.T) { parent, err := svc.CreateFolder(ctx, "/del-empty", &rootFolder.ID, clientID, "testuser") require.NoError(t, err) child, err := svc.CreateFolder(ctx, "/del-empty/child", &parent.ID, clientID, "testuser") require.NoError(t, err) s3Paths, err := svc.HardDelete(ctx, parent.ID, false) require.NoError(t, err) assert.Empty(t, s3Paths) // Verify both folders are gone _, err = q.GetFolderByID(ctx, parent.ID) require.Error(t, err) _, err = q.GetFolderByID(ctx, child.ID) require.Error(t, err) }) t.Run("conflicts when folder has documents and include_documents is false", func(t *testing.T) { fdr, err := svc.CreateFolder(ctx, "/del-has-docs", &rootFolder.ID, clientID, "testuser") require.NoError(t, err) createDocWithEntry(t, cfg, clientID, &fdr.ID, "folder-doc-conflict") _, err = svc.HardDelete(ctx, fdr.ID, false) require.Error(t, err) var conflictErr *harddelete.ConflictError assert.True(t, errors.As(err, &conflictErr)) assert.Contains(t, conflictErr.Message, "documents") }) t.Run("deletes folder tree with documents when include_documents is true", func(t *testing.T) { parent, err := svc.CreateFolder(ctx, "/del-with-docs", &rootFolder.ID, clientID, "testuser") require.NoError(t, err) child, err := svc.CreateFolder(ctx, "/del-with-docs/child", &parent.ID, clientID, "testuser") require.NoError(t, err) doc1ID := createDocWithEntry(t, cfg, clientID, &parent.ID, "parent-doc-1") doc2ID := createDocWithEntry(t, cfg, clientID, &child.ID, "child-doc-1") s3Paths, err := svc.HardDelete(ctx, parent.ID, true) require.NoError(t, err) // Verify S3 paths returned for both documents assert.Len(t, s3Paths, 2) // Verify both documents are gone _, err = q.GetDocumentSummary(ctx, doc1ID) require.Error(t, err) _, err = q.GetDocumentSummary(ctx, doc2ID) require.Error(t, err) // Verify folders are gone _, err = q.GetFolderByID(ctx, parent.ID) require.Error(t, err) _, err = q.GetFolderByID(ctx, child.ID) require.Error(t, err) }) t.Run("counts documents in nested folder tree correctly", func(t *testing.T) { parent, err := svc.CreateFolder(ctx, "/del-count", &rootFolder.ID, clientID, "testuser") require.NoError(t, err) child, err := svc.CreateFolder(ctx, "/del-count/sub", &parent.ID, clientID, "testuser") require.NoError(t, err) // Document in child, not parent createDocWithEntry(t, cfg, clientID, &child.ID, "nested-doc-count") // Should conflict because the tree has a document (in the child) _, err = svc.HardDelete(ctx, parent.ID, false) require.Error(t, err) var conflictErr *harddelete.ConflictError assert.True(t, errors.As(err, &conflictErr)) }) }