Files
query-orchestration/internal/folder/delete_test.go
T
Jay Brown ad7b21f3a2 Merged in bugfix/delete-client-cascade (pull request #220)
fix cascading delete issue

* test passing
2026-04-08 10:54:32 +00:00

224 lines
6.7 KiB
Go

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, &notFoundErr))
})
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("deletes folder with batch-backed documents and outcomes", func(t *testing.T) {
// Use a unique client to avoid collisions
batchClientID := "test-folder-batch-del"
test.CreateTestClient(t, cfg, batchClientID, "Test Folder Batch Del")
batchRootFolder, err := q.GetFolderByPath(ctx, &repository.GetFolderByPathParams{
Clientid: batchClientID,
Path: "/",
})
require.NoError(t, err)
batchSvc := folder.New(cfg)
// Create a subfolder under root
subFolder, err := batchSvc.CreateFolder(ctx, "/batch-del-sub", &batchRootFolder.ID, batchClientID, "testuser")
require.NoError(t, err)
// Create a batch upload for the client
batchID, err := q.CreateBatchUpload(ctx, &repository.CreateBatchUploadParams{
ClientID: batchClientID,
OriginalFilename: "test.zip",
TotalDocuments: 1,
})
require.NoError(t, err)
// Create a document in the subfolder with batch_id
filename := "batch-folder-doc.pdf"
docID, err := q.CreateDocument(ctx, &repository.CreateDocumentParams{
Clientid: batchClientID,
Hash: "batch-folder-doc-hash",
Filename: &filename,
Folderid: &subFolder.ID,
BatchID: &batchID,
})
require.NoError(t, err)
// Add a document entry (S3 ref)
err = q.AddDocumentEntry(ctx, &repository.AddDocumentEntryParams{
Documentid: docID,
Bucket: "test-bucket",
Key: "test-key/batch-folder-doc-hash",
})
require.NoError(t, err)
// Insert a resolved outcome with document_id set
err = q.InsertBatchDocumentOutcome(ctx, &repository.InsertBatchDocumentOutcomeParams{
BatchID: batchID,
Filename: "batch-folder-doc.pdf",
Column3: repository.BatchOutcomeStatusInitComplete,
DocumentID: &docID,
})
require.NoError(t, err)
// Hard delete the folder with includeDocuments=true -- should fail before FK fix
_, err = batchSvc.HardDelete(ctx, subFolder.ID, true)
require.NoError(t, err)
// Verify folder is gone
_, err = q.GetFolderByID(ctx, subFolder.ID)
require.Error(t, err)
// Verify document is gone
_, err = q.GetDocumentSummary(ctx, docID)
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))
})
}