Files
query-orchestration/api/queryAPI/folders_test.go
T
Jay Brown a94637ab13 Merged in feature/fix_test_timeouts (pull request #194)
text extractions tests

* increase timeout

for slow ci/cd systems

* add tests
2025-12-04 22:45:15 +00:00

661 lines
19 KiB
Go

package queryapi_test
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
queryapi "queryorchestration/api/queryAPI"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/folder"
"queryorchestration/internal/serviceconfig"
"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"
)
type FoldersTestConfig struct {
serviceconfig.BaseConfig
}
func (c *FoldersTestConfig) GetBackgroundRunner() any { return nil }
func setupFoldersTestController(t *testing.T) (*queryapi.Controllers, *FoldersTestConfig, string) {
t.Helper()
cfg := &FoldersTestConfig{}
test.CreateDB(t, cfg)
// Create services
folderSvc := folder.New(cfg)
services := &queryapi.Services{
Folder: folderSvc,
}
// Create test client with unique ID
ctx := t.Context()
uniqueID := uuid.New().String()[:8]
clientID := fmt.Sprintf("test-client-%s", uniqueID)
err := cfg.GetDBQueries().CreateClient(ctx, &repository.CreateClientParams{
Clientid: clientID,
Name: fmt.Sprintf("Test Client %s", uniqueID),
})
require.NoError(t, err)
ctrl := queryapi.NewControllers(services, nil)
return ctrl, cfg, clientID
}
func TestCreateFolder_Success(t *testing.T) {
ctrl, _, clientID := setupFoldersTestController(t)
reqBody := queryapi.FolderCreate{
Path: "/documents/contracts",
ClientId: queryapi.ClientID(clientID),
CreatedBy: openapi_types.Email("user@example.com"),
}
body, err := json.Marshal(reqBody)
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/folders", bytes.NewReader(body))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
err = ctrl.CreateFolder(c)
require.NoError(t, err)
assert.Equal(t, http.StatusCreated, rec.Code)
// Parse response
var resp queryapi.Folder
err = json.Unmarshal(rec.Body.Bytes(), &resp)
require.NoError(t, err)
assert.Equal(t, "/documents/contracts", resp.Path)
assert.Equal(t, clientID, string(resp.ClientId))
assert.Equal(t, "user@example.com", string(resp.CreatedBy))
assert.NotEqual(t, uuid.Nil, uuid.UUID(resp.Id))
}
func TestCreateFolder_WithParent(t *testing.T) {
ctrl, cfg, clientID := setupFoldersTestController(t)
ctx := t.Context()
// Create parent folder first
parentFolder, err := cfg.GetDBQueries().CreateFolder(ctx, &repository.CreateFolderParams{
Path: "/documents",
Clientid: clientID,
Createdby: "admin@example.com",
})
require.NoError(t, err)
reqBody := queryapi.FolderCreate{
Path: "/documents/contracts",
ClientId: queryapi.ClientID(clientID),
CreatedBy: openapi_types.Email("user@example.com"),
ParentId: (*openapi_types.UUID)(&parentFolder.ID),
}
body, err := json.Marshal(reqBody)
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/folders", bytes.NewReader(body))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
err = ctrl.CreateFolder(c)
require.NoError(t, err)
assert.Equal(t, http.StatusCreated, rec.Code)
var resp queryapi.Folder
err = json.Unmarshal(rec.Body.Bytes(), &resp)
require.NoError(t, err)
// Verify parent ID is set
assert.True(t, resp.ParentId.IsSpecified())
parentID, err := resp.ParentId.Get()
require.NoError(t, err)
assert.Equal(t, parentFolder.ID, uuid.UUID(parentID))
}
func TestCreateFolder_MissingPath(t *testing.T) {
ctrl, _, clientID := setupFoldersTestController(t)
reqBody := queryapi.FolderCreate{
Path: "",
ClientId: queryapi.ClientID(clientID),
CreatedBy: openapi_types.Email("user@example.com"),
}
body, err := json.Marshal(reqBody)
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/folders", bytes.NewReader(body))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
err = ctrl.CreateFolder(c)
require.Error(t, err)
httpErr, ok := err.(*echo.HTTPError)
require.True(t, ok)
assert.Equal(t, http.StatusBadRequest, httpErr.Code)
}
func TestCreateFolder_MissingClientId(t *testing.T) {
ctrl, _, _ := setupFoldersTestController(t)
reqBody := queryapi.FolderCreate{
Path: "/documents",
ClientId: "",
CreatedBy: openapi_types.Email("user@example.com"),
}
body, err := json.Marshal(reqBody)
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/folders", bytes.NewReader(body))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
err = ctrl.CreateFolder(c)
require.Error(t, err)
httpErr, ok := err.(*echo.HTTPError)
require.True(t, ok)
assert.Equal(t, http.StatusBadRequest, httpErr.Code)
}
func TestCreateFolder_MissingCreatedBy(t *testing.T) {
ctrl, _, clientID := setupFoldersTestController(t)
// Send raw JSON with empty createdBy field
body := []byte(fmt.Sprintf(`{"path":"/documents","clientId":"%s","createdBy":""}`, clientID))
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/folders", bytes.NewReader(body))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
err := ctrl.CreateFolder(c)
require.Error(t, err)
httpErr, ok := err.(*echo.HTTPError)
require.True(t, ok)
assert.Equal(t, http.StatusBadRequest, httpErr.Code)
}
func TestRenameFolder_Success(t *testing.T) {
ctrl, cfg, clientID := setupFoldersTestController(t)
ctx := t.Context()
// Create folder first
folderRec, err := cfg.GetDBQueries().CreateFolder(ctx, &repository.CreateFolderParams{
Path: "/old-path",
Clientid: clientID,
Createdby: "admin@example.com",
})
require.NoError(t, err)
reqBody := queryapi.FolderRename{
Path: "/new-path",
}
body, err := json.Marshal(reqBody)
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/folders/%s", folderRec.ID), bytes.NewReader(body))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
err = ctrl.RenameFolder(c, openapi_types.UUID(folderRec.ID))
require.NoError(t, err)
assert.Equal(t, http.StatusNoContent, rec.Code)
// Verify the folder was renamed
updatedFolder, err := cfg.GetDBQueries().GetFolderByID(ctx, folderRec.ID)
require.NoError(t, err)
assert.Equal(t, "/new-path", updatedFolder.Path)
}
func TestRenameFolder_MissingPath(t *testing.T) {
ctrl, cfg, clientID := setupFoldersTestController(t)
ctx := t.Context()
// Create folder first
folderRec, err := cfg.GetDBQueries().CreateFolder(ctx, &repository.CreateFolderParams{
Path: "/old-path",
Clientid: clientID,
Createdby: "admin@example.com",
})
require.NoError(t, err)
reqBody := queryapi.FolderRename{
Path: "",
}
body, err := json.Marshal(reqBody)
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/folders/%s", folderRec.ID), bytes.NewReader(body))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
err = ctrl.RenameFolder(c, openapi_types.UUID(folderRec.ID))
require.Error(t, err)
httpErr, ok := err.(*echo.HTTPError)
require.True(t, ok)
assert.Equal(t, http.StatusBadRequest, httpErr.Code)
}
func TestGetFolderDocuments_Success(t *testing.T) {
ctrl, cfg, clientID := setupFoldersTestController(t)
ctx := t.Context()
// Create folder
folderRec, err := cfg.GetDBQueries().CreateFolder(ctx, &repository.CreateFolderParams{
Path: "/documents",
Clientid: clientID,
Createdby: "admin@example.com",
})
require.NoError(t, err)
// Create documents in the folder
filename1 := "doc1.pdf"
docID1, err := cfg.GetDBQueries().CreateDocument(ctx, &repository.CreateDocumentParams{
Clientid: clientID,
Hash: fmt.Sprintf("hash1-%s", uuid.New().String()[:8]),
Filename: &filename1,
Folderid: &folderRec.ID,
})
require.NoError(t, err)
filename2 := "doc2.pdf"
docID2, err := cfg.GetDBQueries().CreateDocument(ctx, &repository.CreateDocumentParams{
Clientid: clientID,
Hash: fmt.Sprintf("hash2-%s", uuid.New().String()[:8]),
Filename: &filename2,
Folderid: &folderRec.ID,
})
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/folders/%s/documents", folderRec.ID), nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
err = ctrl.GetFolderDocuments(c, openapi_types.UUID(folderRec.ID))
require.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var resp struct {
Documents []queryapi.DocumentSummary `json:"documents"`
}
err = json.Unmarshal(rec.Body.Bytes(), &resp)
require.NoError(t, err)
assert.Len(t, resp.Documents, 2)
// Verify both documents are returned
docIDs := make(map[uuid.UUID]bool)
for _, doc := range resp.Documents {
docIDs[uuid.UUID(doc.Id)] = true
}
assert.True(t, docIDs[docID1])
assert.True(t, docIDs[docID2])
}
func TestGetFolderDocuments_EmptyFolder(t *testing.T) {
ctrl, cfg, clientID := setupFoldersTestController(t)
ctx := t.Context()
// Create folder with no documents
folderRec, err := cfg.GetDBQueries().CreateFolder(ctx, &repository.CreateFolderParams{
Path: "/empty-folder",
Clientid: clientID,
Createdby: "admin@example.com",
})
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/folders/%s/documents", folderRec.ID), nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
err = ctrl.GetFolderDocuments(c, openapi_types.UUID(folderRec.ID))
require.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var resp struct {
Documents []queryapi.DocumentSummary `json:"documents"`
}
err = json.Unmarshal(rec.Body.Bytes(), &resp)
require.NoError(t, err)
assert.Len(t, resp.Documents, 0)
}
func TestGetFolderMetrics_Success(t *testing.T) {
ctrl, cfg, clientID := setupFoldersTestController(t)
ctx := t.Context()
// Create folder
folderRec, err := cfg.GetDBQueries().CreateFolder(ctx, &repository.CreateFolderParams{
Path: "/documents",
Clientid: clientID,
Createdby: "admin@example.com",
})
require.NoError(t, err)
// Create documents in the folder
filename1 := "doc1.pdf"
docID1, err := cfg.GetDBQueries().CreateDocument(ctx, &repository.CreateDocumentParams{
Clientid: clientID,
Hash: fmt.Sprintf("metricshash1-%s", uuid.New().String()[:8]),
Filename: &filename1,
Folderid: &folderRec.ID,
})
require.NoError(t, err)
filename2 := "doc2.pdf"
docID2, err := cfg.GetDBQueries().CreateDocument(ctx, &repository.CreateDocumentParams{
Clientid: clientID,
Hash: fmt.Sprintf("metricshash2-%s", uuid.New().String()[:8]),
Filename: &filename2,
Folderid: &folderRec.ID,
})
require.NoError(t, err)
filename3 := "doc3.pdf"
_, err = cfg.GetDBQueries().CreateDocument(ctx, &repository.CreateDocumentParams{
Clientid: clientID,
Hash: fmt.Sprintf("metricshash3-%s", uuid.New().String()[:8]),
Filename: &filename3,
Folderid: &folderRec.ID,
})
require.NoError(t, err)
// Apply labels to documents (using labels from the lookup table)
_, err = cfg.GetDBQueries().ApplyLabel(ctx, &repository.ApplyLabelParams{
Documentid: docID1,
Label: "Ingested",
Appliedby: "user@example.com",
})
require.NoError(t, err)
_, err = cfg.GetDBQueries().ApplyLabel(ctx, &repository.ApplyLabelParams{
Documentid: docID2,
Label: "Ingested",
Appliedby: "user@example.com",
})
require.NoError(t, err)
_, err = cfg.GetDBQueries().ApplyLabel(ctx, &repository.ApplyLabelParams{
Documentid: docID2,
Label: "OCR_Processed",
Appliedby: "user@example.com",
})
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/folders/%s/metrics", folderRec.ID), nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
err = ctrl.GetFolderMetrics(c, openapi_types.UUID(folderRec.ID))
require.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var resp queryapi.FolderMetrics
err = json.Unmarshal(rec.Body.Bytes(), &resp)
require.NoError(t, err)
assert.Equal(t, folderRec.ID, uuid.UUID(resp.FolderId))
assert.Equal(t, int32(3), resp.TotalDocuments)
assert.Equal(t, int32(2), resp.ByLabel["Ingested"])
assert.Equal(t, int32(1), resp.ByLabel["OCR_Processed"])
}
func TestGetFolderMetrics_EmptyFolder(t *testing.T) {
ctrl, cfg, clientID := setupFoldersTestController(t)
ctx := t.Context()
// Create folder with no documents
folderRec, err := cfg.GetDBQueries().CreateFolder(ctx, &repository.CreateFolderParams{
Path: "/empty-metrics",
Clientid: clientID,
Createdby: "admin@example.com",
})
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/folders/%s/metrics", folderRec.ID), nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
err = ctrl.GetFolderMetrics(c, openapi_types.UUID(folderRec.ID))
require.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var resp queryapi.FolderMetrics
err = json.Unmarshal(rec.Body.Bytes(), &resp)
require.NoError(t, err)
assert.Equal(t, int32(0), resp.TotalDocuments)
assert.Len(t, resp.ByLabel, 0)
}
func TestListClientFolders_Success(t *testing.T) {
ctrl, cfg, clientID := setupFoldersTestController(t)
ctx := t.Context()
// Create some folders for the client
folder1, err := cfg.GetDBQueries().CreateFolder(ctx, &repository.CreateFolderParams{
Path: "/folder1",
Clientid: clientID,
Createdby: "admin@example.com",
})
require.NoError(t, err)
_, err = cfg.GetDBQueries().CreateFolder(ctx, &repository.CreateFolderParams{
Path: "/folder1/subfolder",
Clientid: clientID,
Parentid: &folder1.ID,
Createdby: "admin@example.com",
})
require.NoError(t, err)
_, err = cfg.GetDBQueries().CreateFolder(ctx, &repository.CreateFolderParams{
Path: "/folder2",
Clientid: clientID,
Createdby: "admin@example.com",
})
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/client/%s/folders", clientID), nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
// Call with empty params (metrics=false by default)
err = ctrl.ListClientFolders(c, queryapi.ClientID(clientID), queryapi.ListClientFoldersParams{})
require.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var resp queryapi.FolderList
err = json.Unmarshal(rec.Body.Bytes(), &resp)
require.NoError(t, err)
// Should have 3 folders
assert.Len(t, resp.Folders, 3)
// Folders should be ordered by path
assert.Equal(t, "/folder1", resp.Folders[0].Path)
assert.Equal(t, "/folder1/subfolder", resp.Folders[1].Path)
assert.Equal(t, "/folder2", resp.Folders[2].Path)
// Metrics should not be specified when metrics=false
for _, f := range resp.Folders {
assert.False(t, f.Metrics.IsSpecified(), "metrics should not be specified when metrics param is false/omitted")
}
}
func TestListClientFolders_EmptyResult(t *testing.T) {
ctrl, _, clientID := setupFoldersTestController(t)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/client/%s/folders", clientID), nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
err := ctrl.ListClientFolders(c, queryapi.ClientID(clientID), queryapi.ListClientFoldersParams{})
require.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var resp queryapi.FolderList
err = json.Unmarshal(rec.Body.Bytes(), &resp)
require.NoError(t, err)
// Should have empty folders array
assert.Len(t, resp.Folders, 0)
}
func TestListClientFolders_WithMetrics(t *testing.T) {
ctrl, cfg, clientID := setupFoldersTestController(t)
ctx := t.Context()
// Create a folder
folderRec, err := cfg.GetDBQueries().CreateFolder(ctx, &repository.CreateFolderParams{
Path: "/metrics-folder",
Clientid: clientID,
Createdby: "admin@example.com",
})
require.NoError(t, err)
// Create documents in the folder
filename1 := "doc1.pdf"
docID1, err := cfg.GetDBQueries().CreateDocument(ctx, &repository.CreateDocumentParams{
Clientid: clientID,
Hash: fmt.Sprintf("listmetricshash1-%s", uuid.New().String()[:8]),
Filename: &filename1,
Folderid: &folderRec.ID,
})
require.NoError(t, err)
filename2 := "doc2.pdf"
docID2, err := cfg.GetDBQueries().CreateDocument(ctx, &repository.CreateDocumentParams{
Clientid: clientID,
Hash: fmt.Sprintf("listmetricshash2-%s", uuid.New().String()[:8]),
Filename: &filename2,
Folderid: &folderRec.ID,
})
require.NoError(t, err)
// Apply labels
_, err = cfg.GetDBQueries().ApplyLabel(ctx, &repository.ApplyLabelParams{
Documentid: docID1,
Label: "Ingested",
Appliedby: "user@example.com",
})
require.NoError(t, err)
_, err = cfg.GetDBQueries().ApplyLabel(ctx, &repository.ApplyLabelParams{
Documentid: docID2,
Label: "Ingested",
Appliedby: "user@example.com",
})
require.NoError(t, err)
_, err = cfg.GetDBQueries().ApplyLabel(ctx, &repository.ApplyLabelParams{
Documentid: docID2,
Label: "OCR_Processed",
Appliedby: "user@example.com",
})
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/client/%s/folders?metrics=true", clientID), nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
// Call with metrics=true
metricsTrue := true
err = ctrl.ListClientFolders(c, queryapi.ClientID(clientID), queryapi.ListClientFoldersParams{
Metrics: &metricsTrue,
})
require.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var resp queryapi.FolderList
err = json.Unmarshal(rec.Body.Bytes(), &resp)
require.NoError(t, err)
// Should have 1 folder
require.Len(t, resp.Folders, 1)
// Metrics should be populated
folder := resp.Folders[0]
assert.True(t, folder.Metrics.IsSpecified(), "metrics should be specified when metrics=true")
metricsVal, err := folder.Metrics.Get()
require.NoError(t, err)
assert.Equal(t, int32(2), metricsVal.TotalDocuments)
assert.Equal(t, int32(2), metricsVal.ByLabel["Ingested"])
assert.Equal(t, int32(1), metricsVal.ByLabel["OCR_Processed"])
}
func TestListClientFolders_MetricsFalseExplicit(t *testing.T) {
ctrl, cfg, clientID := setupFoldersTestController(t)
ctx := t.Context()
// Create a folder
_, err := cfg.GetDBQueries().CreateFolder(ctx, &repository.CreateFolderParams{
Path: "/no-metrics-folder",
Clientid: clientID,
Createdby: "admin@example.com",
})
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/client/%s/folders?metrics=false", clientID), nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
// Call with metrics=false explicitly
metricsFalse := false
err = ctrl.ListClientFolders(c, queryapi.ClientID(clientID), queryapi.ListClientFoldersParams{
Metrics: &metricsFalse,
})
require.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var resp queryapi.FolderList
err = json.Unmarshal(rec.Body.Bytes(), &resp)
require.NoError(t, err)
// Should have 1 folder
require.Len(t, resp.Folders, 1)
// Metrics should NOT be specified
folder := resp.Folders[0]
assert.False(t, folder.Metrics.IsSpecified(), "metrics should not be specified when metrics=false")
}