09c61ea9b4
support delete for client, document and folder * support delete for client, document and folder * remove batch cancel conflict not used Approved-by: Jacob Mathison
364 lines
10 KiB
Go
364 lines
10 KiB
Go
package queryapi_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
queryapi "queryorchestration/api/queryAPI"
|
|
"queryorchestration/internal/cognitoauth"
|
|
"queryorchestration/internal/database/repository"
|
|
"queryorchestration/internal/test"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/labstack/echo/v4"
|
|
openapi_types "github.com/oapi-codegen/runtime/types"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// createDeleteContext creates an Echo context with DELETE method and admin user info set.
|
|
// This simulates an authenticated admin making a delete request.
|
|
func createDeleteContext(t testing.TB) (echo.Context, *httptest.ResponseRecorder) {
|
|
t.Helper()
|
|
e := echo.New()
|
|
req := httptest.NewRequest(http.MethodDelete, "/", nil)
|
|
rec := httptest.NewRecorder()
|
|
ctx := e.NewContext(req, rec)
|
|
ctx.Set("user_info", cognitoauth.UserInfo{
|
|
Email: "admin@test.local",
|
|
Username: "admin",
|
|
Groups: []string{"admin"},
|
|
})
|
|
return ctx, rec
|
|
}
|
|
|
|
// seedClientWithDocument creates a client, a root folder (via CreateTestClient), a sub-folder,
|
|
// and a document with an S3 entry. Returns the document ID and sub-folder ID.
|
|
func seedClientWithDocument(t *testing.T, cfg *ControllerConfig, clientID string) (uuid.UUID, uuid.UUID) {
|
|
t.Helper()
|
|
ctx := t.Context()
|
|
q := cfg.GetDBQueries()
|
|
|
|
test.CreateTestClient(t, cfg, clientID, "Test Client "+clientID)
|
|
|
|
// Get root folder created by CreateTestClient
|
|
rootFolder, err := q.GetFolderByPath(ctx, &repository.GetFolderByPathParams{
|
|
Clientid: clientID,
|
|
Path: "/",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Create a sub-folder
|
|
subFolder, err := q.CreateFolder(ctx, &repository.CreateFolderParams{
|
|
Path: "/test-folder",
|
|
Clientid: clientID,
|
|
Createdby: "admin@test.local",
|
|
Parentid: &rootFolder.ID,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Create a document in the sub-folder
|
|
filename := "test-doc.pdf"
|
|
docID, err := q.CreateDocument(ctx, &repository.CreateDocumentParams{
|
|
Clientid: clientID,
|
|
Hash: "hash-" + clientID,
|
|
Filename: &filename,
|
|
Folderid: &subFolder.ID,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Add an S3 entry for the document
|
|
err = q.AddDocumentEntry(ctx, &repository.AddDocumentEntryParams{
|
|
Documentid: docID,
|
|
Bucket: "test-bucket",
|
|
Key: "test-key/" + clientID + "/doc.pdf",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return docID, subFolder.ID
|
|
}
|
|
|
|
func TestDeleteDocument_Success(t *testing.T) {
|
|
cfg := &ControllerConfig{}
|
|
test.CreateDB(t, cfg)
|
|
initializeTestConfig(t, cfg)
|
|
|
|
svc := createControllerServices(cfg)
|
|
cons := queryapi.NewControllers(svc, cfg)
|
|
|
|
clientID := "del-doc-success"
|
|
docID, _ := seedClientWithDocument(t, cfg, clientID)
|
|
|
|
ctx, rec := createDeleteContext(t)
|
|
|
|
err := cons.DeleteDocument(ctx, openapi_types.UUID(docID), queryapi.DeleteDocumentParams{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusNoContent, rec.Code)
|
|
assert.Empty(t, rec.Body.String())
|
|
|
|
// Verify document is actually gone
|
|
_, dbErr := cfg.GetDBQueries().GetDocumentSummary(t.Context(), docID)
|
|
require.Error(t, dbErr)
|
|
}
|
|
|
|
// ptrBool returns a pointer to a bool value.
|
|
func ptrBool(b bool) *bool {
|
|
return &b
|
|
}
|
|
|
|
func TestDeleteDocument_Verbose(t *testing.T) {
|
|
cfg := &ControllerConfig{}
|
|
test.CreateDB(t, cfg)
|
|
initializeTestConfig(t, cfg)
|
|
|
|
svc := createControllerServices(cfg)
|
|
cons := queryapi.NewControllers(svc, cfg)
|
|
|
|
clientID := "del-doc-verbose"
|
|
docID, _ := seedClientWithDocument(t, cfg, clientID)
|
|
|
|
ctx, rec := createDeleteContext(t)
|
|
|
|
err := cons.DeleteDocument(ctx, openapi_types.UUID(docID), queryapi.DeleteDocumentParams{
|
|
Verbose: ptrBool(true),
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, rec.Code)
|
|
|
|
var resp queryapi.DeleteResponse
|
|
err = json.Unmarshal(rec.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, resp.DeletedDocuments, 1)
|
|
assert.Equal(t, openapi_types.UUID(docID), resp.DeletedDocuments[0].DocumentId)
|
|
assert.Contains(t, resp.DeletedDocuments[0].S3Path, "s3://test-bucket/test-key/")
|
|
}
|
|
|
|
func TestDeleteDocument_NotFound(t *testing.T) {
|
|
cfg := &ControllerConfig{}
|
|
test.CreateDB(t, cfg)
|
|
initializeTestConfig(t, cfg)
|
|
|
|
svc := createControllerServices(cfg)
|
|
cons := queryapi.NewControllers(svc, cfg)
|
|
|
|
ctx, rec := createDeleteContext(t)
|
|
|
|
fakeID := uuid.New()
|
|
err := cons.DeleteDocument(ctx, openapi_types.UUID(fakeID), queryapi.DeleteDocumentParams{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusNotFound, rec.Code)
|
|
|
|
var resp queryapi.ErrorMessage
|
|
err = json.Unmarshal(rec.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, resp.Message, "not found")
|
|
}
|
|
|
|
func TestDeleteClient_MissingConfirm(t *testing.T) {
|
|
cfg := &ControllerConfig{}
|
|
test.CreateDB(t, cfg)
|
|
initializeTestConfig(t, cfg)
|
|
|
|
svc := createControllerServices(cfg)
|
|
cons := queryapi.NewControllers(svc, cfg)
|
|
|
|
clientID := "del-client-noconfirm"
|
|
test.CreateTestClient(t, cfg, clientID, "Test Client")
|
|
|
|
ctx, rec := createDeleteContext(t)
|
|
|
|
err := cons.DeleteClient(ctx, queryapi.ClientID(clientID), queryapi.DeleteClientParams{
|
|
Confirm: false,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusBadRequest, rec.Code)
|
|
|
|
var resp queryapi.ErrorMessage
|
|
err = json.Unmarshal(rec.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, resp.Message, "confirm=true")
|
|
}
|
|
|
|
func TestDeleteClient_Success(t *testing.T) {
|
|
cfg := &ControllerConfig{}
|
|
test.CreateDB(t, cfg)
|
|
initializeTestConfig(t, cfg)
|
|
|
|
svc := createControllerServices(cfg)
|
|
cons := queryapi.NewControllers(svc, cfg)
|
|
|
|
clientID := "del-client-ok"
|
|
seedClientWithDocument(t, cfg, clientID)
|
|
|
|
ctx, rec := createDeleteContext(t)
|
|
|
|
err := cons.DeleteClient(ctx, queryapi.ClientID(clientID), queryapi.DeleteClientParams{
|
|
Confirm: true,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusNoContent, rec.Code)
|
|
assert.Empty(t, rec.Body.String())
|
|
|
|
// Verify client is gone
|
|
_, dbErr := cfg.GetDBQueries().GetClient(t.Context(), clientID)
|
|
require.Error(t, dbErr)
|
|
}
|
|
|
|
func TestDeleteClient_Verbose(t *testing.T) {
|
|
cfg := &ControllerConfig{}
|
|
test.CreateDB(t, cfg)
|
|
initializeTestConfig(t, cfg)
|
|
|
|
svc := createControllerServices(cfg)
|
|
cons := queryapi.NewControllers(svc, cfg)
|
|
|
|
clientID := "del-client-verbose"
|
|
docID, _ := seedClientWithDocument(t, cfg, clientID)
|
|
|
|
ctx, rec := createDeleteContext(t)
|
|
|
|
err := cons.DeleteClient(ctx, queryapi.ClientID(clientID), queryapi.DeleteClientParams{
|
|
Confirm: true,
|
|
Verbose: ptrBool(true),
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, rec.Code)
|
|
|
|
var resp queryapi.DeleteResponse
|
|
err = json.Unmarshal(rec.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, resp.DeletedDocuments, 1)
|
|
assert.Equal(t, openapi_types.UUID(docID), resp.DeletedDocuments[0].DocumentId)
|
|
assert.Contains(t, resp.DeletedDocuments[0].S3Path, "s3://test-bucket/")
|
|
}
|
|
|
|
func TestDeleteFolder_Success(t *testing.T) {
|
|
cfg := &ControllerConfig{}
|
|
test.CreateDB(t, cfg)
|
|
initializeTestConfig(t, cfg)
|
|
|
|
svc := createControllerServices(cfg)
|
|
cons := queryapi.NewControllers(svc, cfg)
|
|
|
|
clientID := "del-folder-ok"
|
|
test.CreateTestClient(t, cfg, clientID, "Test Client")
|
|
|
|
q := cfg.GetDBQueries()
|
|
rootFolder, err := q.GetFolderByPath(t.Context(), &repository.GetFolderByPathParams{
|
|
Clientid: clientID,
|
|
Path: "/",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
subFolder, err := q.CreateFolder(t.Context(), &repository.CreateFolderParams{
|
|
Path: "/empty-folder",
|
|
Clientid: clientID,
|
|
Createdby: "admin@test.local",
|
|
Parentid: &rootFolder.ID,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
ctx, rec := createDeleteContext(t)
|
|
|
|
err = cons.DeleteFolder(ctx, openapi_types.UUID(subFolder.ID), queryapi.DeleteFolderParams{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusNoContent, rec.Code)
|
|
assert.Empty(t, rec.Body.String())
|
|
|
|
// Verify folder is gone
|
|
_, dbErr := q.GetFolderByID(t.Context(), subFolder.ID)
|
|
require.Error(t, dbErr)
|
|
}
|
|
|
|
func TestDeleteFolder_RootFolderConflict(t *testing.T) {
|
|
cfg := &ControllerConfig{}
|
|
test.CreateDB(t, cfg)
|
|
initializeTestConfig(t, cfg)
|
|
|
|
svc := createControllerServices(cfg)
|
|
cons := queryapi.NewControllers(svc, cfg)
|
|
|
|
clientID := "del-folder-root"
|
|
test.CreateTestClient(t, cfg, clientID, "Test Client")
|
|
|
|
q := cfg.GetDBQueries()
|
|
rootFolder, err := q.GetFolderByPath(t.Context(), &repository.GetFolderByPathParams{
|
|
Clientid: clientID,
|
|
Path: "/",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
ctx, rec := createDeleteContext(t)
|
|
|
|
err = cons.DeleteFolder(ctx, openapi_types.UUID(rootFolder.ID), queryapi.DeleteFolderParams{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusConflict, rec.Code)
|
|
|
|
var resp queryapi.ErrorMessage
|
|
err = json.Unmarshal(rec.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, resp.Message, "root folder")
|
|
}
|
|
|
|
func TestDeleteFolder_DocumentsWithoutInclude(t *testing.T) {
|
|
cfg := &ControllerConfig{}
|
|
test.CreateDB(t, cfg)
|
|
initializeTestConfig(t, cfg)
|
|
|
|
svc := createControllerServices(cfg)
|
|
cons := queryapi.NewControllers(svc, cfg)
|
|
|
|
clientID := "del-folder-nodocs"
|
|
_, folderID := seedClientWithDocument(t, cfg, clientID)
|
|
|
|
ctx, rec := createDeleteContext(t)
|
|
|
|
err := cons.DeleteFolder(ctx, openapi_types.UUID(folderID), queryapi.DeleteFolderParams{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusConflict, rec.Code)
|
|
|
|
var resp queryapi.ErrorMessage
|
|
err = json.Unmarshal(rec.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, resp.Message, "documents")
|
|
}
|
|
|
|
func TestDeleteFolder_IncludeDocumentsCascade(t *testing.T) {
|
|
cfg := &ControllerConfig{}
|
|
test.CreateDB(t, cfg)
|
|
initializeTestConfig(t, cfg)
|
|
|
|
svc := createControllerServices(cfg)
|
|
cons := queryapi.NewControllers(svc, cfg)
|
|
|
|
clientID := "del-folder-cascade"
|
|
docID, folderID := seedClientWithDocument(t, cfg, clientID)
|
|
|
|
ctx, rec := createDeleteContext(t)
|
|
|
|
err := cons.DeleteFolder(ctx, openapi_types.UUID(folderID), queryapi.DeleteFolderParams{
|
|
IncludeDocuments: ptrBool(true),
|
|
Verbose: ptrBool(true),
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, rec.Code)
|
|
|
|
var resp queryapi.DeleteResponse
|
|
err = json.Unmarshal(rec.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, resp.DeletedDocuments, 1)
|
|
assert.Equal(t, openapi_types.UUID(docID), resp.DeletedDocuments[0].DocumentId)
|
|
|
|
// Verify both folder and document are gone
|
|
q := cfg.GetDBQueries()
|
|
_, dbErr := q.GetFolderByID(t.Context(), folderID)
|
|
require.Error(t, dbErr)
|
|
_, dbErr = q.GetDocumentSummary(t.Context(), docID)
|
|
require.Error(t, dbErr)
|
|
}
|