Merged in feature/batch-status (pull request #215)
Implement and test the batch status feature * working
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"queryorchestration/internal/database/repository"
|
||||
"queryorchestration/internal/document/batch/outcome"
|
||||
documentsync "queryorchestration/internal/document/sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -11,30 +13,56 @@ import (
|
||||
|
||||
const Name = "docSyncRunner"
|
||||
|
||||
// Services holds the dependencies for the docSyncRunner.
|
||||
//
|
||||
// Fields:
|
||||
// - Document: service for syncing documents
|
||||
// - Outcome: reporter for writing batch pipeline outcomes
|
||||
type Services struct {
|
||||
Document *documentsync.Service
|
||||
Outcome *outcome.Reporter
|
||||
}
|
||||
|
||||
// Runner processes document sync messages from the SQS queue.
|
||||
type Runner struct {
|
||||
svc *Services
|
||||
}
|
||||
|
||||
// New creates a new docSyncRunner.
|
||||
//
|
||||
// Parameters:
|
||||
// - svc: the runner's service dependencies
|
||||
//
|
||||
// Returns:
|
||||
// - Runner: the runner instance
|
||||
func New(svc *Services) Runner {
|
||||
return Runner{
|
||||
svc: svc,
|
||||
}
|
||||
}
|
||||
|
||||
// Body is the SQS message body for docSyncRunner.
|
||||
type Body struct {
|
||||
DocumentID uuid.UUID `json:"id" validate:"required,uuid"`
|
||||
}
|
||||
|
||||
// Process handles a single document sync message. Returns true to acknowledge
|
||||
// the message (success), or false to requeue (infrastructure error).
|
||||
func (s Runner) Process(ctx context.Context, body Body) bool {
|
||||
err := s.svc.Document.Sync(ctx, body.DocumentID)
|
||||
result, err := s.svc.Document.Sync(ctx, body.DocumentID)
|
||||
if err != nil {
|
||||
slog.Error("unable to process", "error", err)
|
||||
return false
|
||||
}
|
||||
|
||||
outcomeName := repository.BatchOutcomeStatusSyncComplete
|
||||
if result.Skipped {
|
||||
outcomeName = repository.BatchOutcomeStatusSyncSkipped
|
||||
}
|
||||
if updateErr := s.svc.Outcome.Update(ctx, body.DocumentID, outcomeName, nil); updateErr != nil {
|
||||
slog.Error("failed to update batch outcome",
|
||||
"document_id", body.DocumentID, "outcome", outcomeName, "error", updateErr)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"queryorchestration/internal/client"
|
||||
"queryorchestration/internal/database/repository"
|
||||
"queryorchestration/internal/document"
|
||||
"queryorchestration/internal/document/batch/outcome"
|
||||
documentsync "queryorchestration/internal/document/sync"
|
||||
"queryorchestration/internal/server/runner"
|
||||
"queryorchestration/internal/serviceconfig/objectstore"
|
||||
@@ -42,6 +43,7 @@ func TestDocSyncRunner(t *testing.T) {
|
||||
Document: document.New(cfg),
|
||||
Client: client.New(cfg),
|
||||
}),
|
||||
Outcome: outcome.New(cfg.GetDBQueries()),
|
||||
})
|
||||
|
||||
err := cfg.GetDBQueries().CreateClient(t.Context(), &repository.CreateClientParams{
|
||||
@@ -85,3 +87,131 @@ func TestDocSyncRunner(t *testing.T) {
|
||||
|
||||
test.AssertMessageBody(t, cfg, cfg.GetDocumentCleanURL(), regexp.MustCompile(fmt.Sprintf("{\"id\":\"%s\"}", docId)))
|
||||
}
|
||||
|
||||
// setupDocSyncTest creates shared infrastructure and returns the config.
|
||||
func setupDocSyncTest(t *testing.T) *DocSyncConfig {
|
||||
t.Helper()
|
||||
cfg := &DocSyncConfig{}
|
||||
test.CreateDB(t, cfg)
|
||||
acfg := test.CreateAWSContainer(t, cfg)
|
||||
|
||||
test.SetQueueClient(t, t.Context(), cfg, acfg.ExternalEndpoint)
|
||||
cfg.DocumentCleanURL = test.CreateQueue(t, cfg, test.DocCleanRunnerName)
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// setupBatchSyncTest creates a client, batch, document, outcome row, and
|
||||
// returns all IDs needed for a sync runner test.
|
||||
func setupBatchSyncTest(t *testing.T, cfg *DocSyncConfig, canSync bool) (uuid.UUID, uuid.UUID) {
|
||||
t.Helper()
|
||||
ctx := t.Context()
|
||||
|
||||
clientID := fmt.Sprintf("sync_%s_%s", fmt.Sprintf("%v", canSync), uuid.New().String()[:8])
|
||||
|
||||
// Create client and set sync flag
|
||||
err := cfg.GetDBQueries().CreateClient(ctx, &repository.CreateClientParams{
|
||||
Clientid: clientID,
|
||||
Name: "Test " + clientID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = cfg.GetDBQueries().AddClientCanSync(ctx, &repository.AddClientCanSyncParams{
|
||||
Clientid: clientID,
|
||||
Cansync: canSync,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create batch (client must exist first due to FK)
|
||||
batchID, err := cfg.GetDBQueries().CreateBatchUpload(ctx, &repository.CreateBatchUploadParams{
|
||||
ClientID: clientID,
|
||||
OriginalFilename: "test.zip",
|
||||
TotalDocuments: 1,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create document linked to batch
|
||||
docID, err := cfg.GetDBQueries().CreateDocument(ctx, &repository.CreateDocumentParams{
|
||||
Clientid: clientID,
|
||||
Hash: "hash_" + uuid.New().String()[:8],
|
||||
BatchID: &batchID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
part := uint16(1)
|
||||
filetype := "pdf"
|
||||
key := objectstore.BucketKey{
|
||||
CreatedAt: time.Now().UTC(),
|
||||
ClientID: clientID,
|
||||
EntityID: uuid.New(),
|
||||
Location: objectstore.Import,
|
||||
Part: &part,
|
||||
FileType: &filetype,
|
||||
}
|
||||
err = cfg.GetDBQueries().AddDocumentEntry(ctx, &repository.AddDocumentEntryParams{
|
||||
Documentid: docID,
|
||||
Bucket: "bucket",
|
||||
Key: key.String(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Insert init_complete outcome (the state before sync)
|
||||
err = cfg.GetDBQueries().InsertBatchDocumentOutcome(ctx, &repository.InsertBatchDocumentOutcomeParams{
|
||||
BatchID: batchID,
|
||||
Filename: "test.pdf",
|
||||
Column3: repository.BatchOutcomeStatusInitComplete,
|
||||
DocumentID: &docID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return batchID, docID
|
||||
}
|
||||
|
||||
// TestDocSyncRunner_ReportsSyncComplete verifies that processing a batch document
|
||||
// with can_sync=true updates the outcome to sync_complete.
|
||||
func TestDocSyncRunner_ReportsSyncComplete(t *testing.T) {
|
||||
cfg := setupDocSyncTest(t)
|
||||
ctx := t.Context()
|
||||
|
||||
batchID, docID := setupBatchSyncTest(t, cfg, true)
|
||||
|
||||
syncRunner := docsyncrunner.New(&docsyncrunner.Services{
|
||||
Document: documentsync.New(cfg, &documentsync.Services{
|
||||
Document: document.New(cfg),
|
||||
Client: client.New(cfg),
|
||||
}),
|
||||
Outcome: outcome.New(cfg.GetDBQueries()),
|
||||
})
|
||||
|
||||
assert.True(t, syncRunner.Process(ctx, docsyncrunner.Body{DocumentID: docID}))
|
||||
|
||||
// Verify outcome updated to sync_complete
|
||||
rows, err := cfg.GetDBQueries().ListBatchDocumentOutcomes(ctx, batchID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rows, 1)
|
||||
assert.Equal(t, repository.BatchOutcomeStatusSyncComplete, rows[0].Outcome)
|
||||
}
|
||||
|
||||
// TestDocSyncRunner_ReportsSyncSkipped verifies that processing a batch document
|
||||
// with can_sync=false updates the outcome to sync_skipped.
|
||||
func TestDocSyncRunner_ReportsSyncSkipped(t *testing.T) {
|
||||
cfg := setupDocSyncTest(t)
|
||||
ctx := t.Context()
|
||||
|
||||
batchID, docID := setupBatchSyncTest(t, cfg, false)
|
||||
|
||||
syncRunner := docsyncrunner.New(&docsyncrunner.Services{
|
||||
Document: documentsync.New(cfg, &documentsync.Services{
|
||||
Document: document.New(cfg),
|
||||
Client: client.New(cfg),
|
||||
}),
|
||||
Outcome: outcome.New(cfg.GetDBQueries()),
|
||||
})
|
||||
|
||||
assert.True(t, syncRunner.Process(ctx, docsyncrunner.Body{DocumentID: docID}))
|
||||
|
||||
// Verify outcome updated to sync_skipped
|
||||
rows, err := cfg.GetDBQueries().ListBatchDocumentOutcomes(ctx, batchID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rows, 1)
|
||||
assert.Equal(t, repository.BatchOutcomeStatusSyncSkipped, rows[0].Outcome)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user