From 5a142c9d478857fb0899300d76262203902318a3 Mon Sep 17 00:00:00 2001 From: David Juhasz Date: Wed, 11 Dec 2024 17:51:29 -0800 Subject: [PATCH] Unbag bagged SIPs, fixes #94 Unbag bagged SIPs after validation to avoid issues with identification and double bagging. [skip codecov] --- cmd/worker/workercmd/cmd.go | 4 + go.mod | 2 +- go.sum | 4 +- internal/activities/unbag.go | 75 +++++++++++++++ internal/activities/unbag_test.go | 117 ++++++++++++++++++++++++ internal/localact/is_bag.go | 24 +++++ internal/localact/is_bag_test.go | 52 +++++++++++ internal/workflow/preprocessing.go | 76 ++++++++++++--- internal/workflow/preprocessing_test.go | 59 +++++++++++- 9 files changed, 392 insertions(+), 21 deletions(-) create mode 100644 internal/activities/unbag.go create mode 100644 internal/activities/unbag_test.go create mode 100644 internal/localact/is_bag.go create mode 100644 internal/localact/is_bag_test.go diff --git a/cmd/worker/workercmd/cmd.go b/cmd/worker/workercmd/cmd.go index 908d3a7b..602aaf5a 100644 --- a/cmd/worker/workercmd/cmd.go +++ b/cmd/worker/workercmd/cmd.go @@ -64,6 +64,10 @@ func (m *Main) Run(ctx context.Context) error { temporalsdk_workflow.RegisterOptions{Name: m.cfg.Temporal.WorkflowName}, ) + w.RegisterActivityWithOptions( + activities.NewUnbag().Execute, + temporalsdk_activity.RegisterOptions{Name: activities.UnbagName}, + ) w.RegisterActivityWithOptions( activities.NewIdentifySIP().Execute, temporalsdk_activity.RegisterOptions{Name: activities.IdentifySIPName}, diff --git a/go.mod b/go.mod index 974b4cff..b890468a 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.9.0 - go.artefactual.dev/tools v0.14.0 + go.artefactual.dev/tools v0.17.0 go.temporal.io/sdk v1.26.1 go.uber.org/mock v0.4.0 gocloud.dev v0.39.0 diff --git a/go.sum b/go.sum index 9dc543fd..ebc17211 100644 --- a/go.sum +++ b/go.sum @@ -244,8 +244,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.artefactual.dev/tools v0.14.0 h1:ESLbemsnkdIPmYXtz0uZTcPqVnTUXIEZd9DSTRyTZqY= -go.artefactual.dev/tools v0.14.0/go.mod h1:5RJ7ObocHZv/zQFYFv/zG9cW/UVRGPFywcJx/oQ+TG8= +go.artefactual.dev/tools v0.17.0 h1:7X/qZYKyKT8RxVjBsksqvalQ8F4wcor6jcA0ewjc92M= +go.artefactual.dev/tools v0.17.0/go.mod h1:lsu0JcKFEJanNdrf5/IFjjzxul4pazG1dDHnLX9Nkvs= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= diff --git a/internal/activities/unbag.go b/internal/activities/unbag.go new file mode 100644 index 00000000..8ec674ea --- /dev/null +++ b/internal/activities/unbag.go @@ -0,0 +1,75 @@ +package activities + +import ( + "context" + "errors" + "fmt" + "os" + "path/filepath" + + "go.artefactual.dev/tools/fsutil" +) + +const UnbagName = "unbag" + +type ( + Unbag struct{} + + UnbagParams struct { + Path string + } + + UnbagResult struct { + Path string + } +) + +func NewUnbag() *Unbag { + return &Unbag{} +} + +func (a *Unbag) Execute(ctx context.Context, params *UnbagParams) (*UnbagResult, error) { + if _, err := os.Stat(filepath.Join(params.Path, "bagit.txt")); err != nil { + // Do nothing if not a bag (bagit.txt doesn't exist). + return &UnbagResult{Path: params.Path}, nil + } + if _, err := os.Stat(filepath.Join(params.Path, "data")); err != nil { + return nil, errors.New("missing data directory") + } + + entries, err := os.ReadDir(params.Path) + if err != nil { + return nil, fmt.Errorf("read dir: %v", err) + } + + // Delete everything except the data directory. + for _, e := range entries { + if e.Name() != "data" { + if err := os.RemoveAll(filepath.Join(params.Path, e.Name())); err != nil { + return nil, fmt.Errorf("delete: %v", err) + } + } + } + + // Move the data directory contents to the SIP root. + entries, err = os.ReadDir(filepath.Join(params.Path, "data")) + if err != nil { + return nil, fmt.Errorf("read data dir: %v", err) + } + + for _, e := range entries { + if err = fsutil.Move( + filepath.Join(params.Path, "data", e.Name()), + filepath.Join(params.Path, e.Name()), + ); err != nil { + return nil, fmt.Errorf("move: %v", err) + } + } + + // Delete the empty data directory. + if err := os.Remove(filepath.Join(params.Path, "data")); err != nil { + return nil, fmt.Errorf("remove data dir: %v", err) + } + + return &UnbagResult{Path: params.Path}, nil +} diff --git a/internal/activities/unbag_test.go b/internal/activities/unbag_test.go new file mode 100644 index 00000000..babe48af --- /dev/null +++ b/internal/activities/unbag_test.go @@ -0,0 +1,117 @@ +package activities_test + +import ( + "testing" + + temporalsdk_activity "go.temporal.io/sdk/activity" + temporalsdk_testsuite "go.temporal.io/sdk/testsuite" + "gotest.tools/v3/assert" + "gotest.tools/v3/fs" + + "github.com/artefactual-sdps/preprocessing-sfa/internal/activities" +) + +func TestUnbag(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + path string + params func(string) activities.UnbagParams + result func(string) activities.UnbagResult + wantFS fs.Manifest + wantErr string + }{ + { + name: "Unbags a bag", + path: fs.NewDir(t, "enduro-test", + fs.WithDir("data", + fs.WithDir("d_0000001", + fs.WithFile("Prozess_Digitalisierung_PREMIS.xml", ""), + ), + fs.WithDir("additional"), + ), + fs.WithFile("bagit.txt", ""), + fs.WithFile("manifest-md5.txt", ""), + ).Path(), + params: func(path string) activities.UnbagParams { + return activities.UnbagParams{Path: path} + }, + result: func(path string) activities.UnbagResult { + return activities.UnbagResult{Path: path} + }, + wantFS: fs.Expected(t, + fs.WithDir("d_0000001", + fs.WithFile("Prozess_Digitalisierung_PREMIS.xml", ""), + ), + fs.WithDir("additional"), + ), + }, + { + name: "Does nothing when path is not a bag", + path: fs.NewDir(t, "enduro-test", + fs.WithDir("d_0000001", + fs.WithFile("Prozess_Digitalisierung_PREMIS.xml", ""), + ), + fs.WithDir("additional"), + ).Path(), + params: func(path string) activities.UnbagParams { + return activities.UnbagParams{Path: path} + }, + result: func(path string) activities.UnbagResult { + return activities.UnbagResult{Path: path} + }, + wantFS: fs.Expected(t, + fs.WithDir("d_0000001", + fs.WithFile("Prozess_Digitalisierung_PREMIS.xml", ""), + ), + fs.WithDir("additional"), + ), + }, + { + name: "Errors when bag is missing data dir", + path: fs.NewDir(t, "enduro-test", + fs.WithDir("content", + fs.WithDir("d_0000001", + fs.WithFile("Prozess_Digitalisierung_PREMIS.xml", ""), + ), + fs.WithDir("additional"), + ), + fs.WithFile("bagit.txt", ""), + ).Path(), + params: func(path string) activities.UnbagParams { + return activities.UnbagParams{Path: path} + }, + wantErr: "activity error (type: unbag, scheduledEventID: 0, startedEventID: 0, identity: ): missing data directory", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + ts := &temporalsdk_testsuite.WorkflowTestSuite{} + env := ts.NewTestActivityEnvironment() + env.RegisterActivityWithOptions( + activities.NewUnbag().Execute, + temporalsdk_activity.RegisterOptions{Name: activities.UnbagName}, + ) + + var res activities.UnbagResult + future, err := env.ExecuteActivity(activities.UnbagName, tt.params(tt.path)) + + if tt.wantErr != "" { + if err == nil { + t.Errorf("error is nil, expecting: %q", tt.wantErr) + } else { + assert.ErrorContains(t, err, tt.wantErr) + } + + return + } + assert.NilError(t, err) + + future.Get(&res) + assert.DeepEqual(t, res, tt.result(tt.path)) + }) + } +} diff --git a/internal/localact/is_bag.go b/internal/localact/is_bag.go new file mode 100644 index 00000000..5f87a7e3 --- /dev/null +++ b/internal/localact/is_bag.go @@ -0,0 +1,24 @@ +package localact + +import ( + "context" + "path/filepath" + + "github.com/artefactual-sdps/preprocessing-sfa/internal/fsutil" +) + +type ( + IsBagParams struct { + Path string + } + + IsBagResult struct { + IsBag bool + } +) + +func IsBag(ctx context.Context, params *IsBagParams) (*IsBagResult, error) { + return &IsBagResult{ + IsBag: fsutil.FileExists(filepath.Join(params.Path, "bagit.txt")), + }, nil +} diff --git a/internal/localact/is_bag_test.go b/internal/localact/is_bag_test.go new file mode 100644 index 00000000..23c0355e --- /dev/null +++ b/internal/localact/is_bag_test.go @@ -0,0 +1,52 @@ +package localact_test + +import ( + "testing" + + temporalsdk_testsuite "go.temporal.io/sdk/testsuite" + "gotest.tools/v3/assert" + "gotest.tools/v3/fs" + + "github.com/artefactual-sdps/preprocessing-sfa/internal/localact" +) + +func TestIsBag(t *testing.T) { + t.Parallel() + + bagPath := fs.NewDir(t, "ppsfa-test", + fs.WithFile("bagit.txt", ""), + ).Path() + emptyPath := fs.NewDir(t, "ppsfa-test").Path() + + type test struct { + name string + params localact.IsBagParams + want localact.IsBagResult + } + for _, tt := range []test{ + { + name: "Is a bag", + params: localact.IsBagParams{Path: bagPath}, + want: localact.IsBagResult{IsBag: true}, + }, + { + name: "Is not a bag", + params: localact.IsBagParams{Path: emptyPath}, + want: localact.IsBagResult{IsBag: false}, + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + ts := &temporalsdk_testsuite.WorkflowTestSuite{} + env := ts.NewTestActivityEnvironment() + + var res localact.IsBagResult + enc, err := env.ExecuteLocalActivity(localact.IsBag, &tt.params) + assert.NilError(t, err) + + enc.Get(&res) + assert.DeepEqual(t, res, tt.want) + }) + } +} diff --git a/internal/workflow/preprocessing.go b/internal/workflow/preprocessing.go index 09ee73ae..8828e444 100644 --- a/internal/workflow/preprocessing.go +++ b/internal/workflow/preprocessing.go @@ -17,6 +17,7 @@ import ( "github.com/artefactual-sdps/preprocessing-sfa/internal/activities" "github.com/artefactual-sdps/preprocessing-sfa/internal/enums" "github.com/artefactual-sdps/preprocessing-sfa/internal/eventlog" + "github.com/artefactual-sdps/preprocessing-sfa/internal/localact" "github.com/artefactual-sdps/preprocessing-sfa/internal/premis" "github.com/artefactual-sdps/preprocessing-sfa/internal/sip" ) @@ -110,11 +111,45 @@ func (w *PreprocessingWorkflow) Execute( localPath := filepath.Join(w.sharedPath, filepath.Clean(params.RelativePath)) + // Check if the SIP is a BagIt bag. + var isBag localact.IsBagResult + e = temporalsdk_workflow.ExecuteLocalActivity( + withLocalActOpts(ctx), + localact.IsBag, + &localact.IsBagParams{Path: localPath}, + ).Get(ctx, &isBag) + if e != nil { + return nil, fmt.Errorf("bag check: %v", e) + } + + // Unbag the SIP if it is a bag. + if isBag.IsBag { + ev := result.newEvent(ctx, "Unbag SIP") + var unbagResult activities.UnbagResult + e = temporalsdk_workflow.ExecuteActivity( + withFilesysActOpts(ctx), + activities.UnbagName, + &activities.UnbagParams{Path: localPath}, + ).Get(ctx, &unbagResult) + if e != nil { + result.systemError(ctx, e, ev, "Unbagging the SIP has failed") + return result, nil + } + ev.Succeed(ctx, "SIP unbagged") + + localPath = unbagResult.Path + rp, err := filepath.Rel(w.sharedPath, localPath) + if err != nil { + return nil, fmt.Errorf("Invalid path") + } + result.RelativePath = rp + } + // Identify SIP. ev := result.newEvent(ctx, "Identify SIP structure") var identifySIP activities.IdentifySIPResult e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), activities.IdentifySIPName, &activities.IdentifySIPParams{Path: localPath}, ).Get(ctx, &identifySIP) @@ -128,7 +163,7 @@ func (w *PreprocessingWorkflow) Execute( ev = result.newEvent(ctx, "Validate SIP structure") var validateStructure activities.ValidateStructureResult e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), activities.ValidateStructureName, &activities.ValidateStructureParams{SIP: identifySIP.SIP}, ).Get(ctx, &validateStructure) @@ -147,7 +182,7 @@ func (w *PreprocessingWorkflow) Execute( checksumEv := result.newEvent(ctx, "Verify SIP checksums") var verifyManifest activities.VerifyManifestResult e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), activities.VerifyManifestName, &activities.VerifyManifestParams{SIP: identifySIP.SIP}, ).Get(ctx, &verifyManifest) @@ -184,7 +219,7 @@ func (w *PreprocessingWorkflow) Execute( ev = result.newEvent(ctx, "Validate SIP file formats") var ffvalidateResult ffvalidate.Result e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), ffvalidate.Name, &ffvalidate.Params{Path: identifySIP.SIP.ContentPath}, ).Get(ctx, &ffvalidateResult) @@ -238,7 +273,7 @@ func (w *PreprocessingWorkflow) Execute( ev = result.newEvent(ctx, "Validate SIP metadata") var validateMetadata xmlvalidate.Result e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), xmlvalidate.Name, &xmlvalidate.Params{ XMLPath: identifySIP.SIP.ManifestPath, @@ -276,7 +311,7 @@ func (w *PreprocessingWorkflow) Execute( ev = result.newEvent(ctx, "Restructure SIP") var transformSIP activities.TransformSIPResult e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), activities.TransformSIPName, &activities.TransformSIPParams{SIP: identifySIP.SIP}, ).Get(ctx, &transformSIP) @@ -290,7 +325,7 @@ func (w *PreprocessingWorkflow) Execute( ev = result.newEvent(ctx, "Create identifier.json") var writeIDFile activities.WriteIdentifierFileResult e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), activities.WriteIdentifierFileName, &activities.WriteIdentifierFileParams{PIP: transformSIP.PIP}, ).Get(ctx, &writeIDFile) @@ -304,7 +339,7 @@ func (w *PreprocessingWorkflow) Execute( ev = result.newEvent(ctx, "Bag SIP") var createBag bagcreate.Result e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), bagcreate.Name, &bagcreate.Params{SourcePath: localPath}, ).Get(ctx, &createBag) @@ -318,6 +353,21 @@ func (w *PreprocessingWorkflow) Execute( } func withLocalActOpts(ctx temporalsdk_workflow.Context) temporalsdk_workflow.Context { + return temporalsdk_workflow.WithLocalActivityOptions( + ctx, + temporalsdk_workflow.LocalActivityOptions{ + ScheduleToCloseTimeout: 5 * time.Second, + RetryPolicy: &temporalsdk_temporal.RetryPolicy{ + InitialInterval: time.Second, + BackoffCoefficient: 2, + MaximumInterval: time.Minute, + MaximumAttempts: 3, + }, + }, + ) +} + +func withFilesysActOpts(ctx temporalsdk_workflow.Context) temporalsdk_workflow.Context { return temporalsdk_workflow.WithActivityOptions(ctx, temporalsdk_workflow.ActivityOptions{ ScheduleToCloseTimeout: 5 * time.Minute, RetryPolicy: &temporalsdk_temporal.RetryPolicy{ @@ -333,7 +383,7 @@ func writePREMISFile(ctx temporalsdk_workflow.Context, sip sip.SIP) error { // Add PREMIS objects. var addPREMISObjects activities.AddPREMISObjectsResult e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), activities.AddPREMISObjectsName, &activities.AddPREMISObjectsParams{ SIP: sip, @@ -352,7 +402,7 @@ func writePREMISFile(ctx temporalsdk_workflow.Context, sip sip.SIP) error { var addPREMISEvent activities.AddPREMISEventResult e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), activities.AddPREMISEventName, &activities.AddPREMISEventParams{ PREMISFilePath: path, @@ -369,7 +419,7 @@ func writePREMISFile(ctx temporalsdk_workflow.Context, sip sip.SIP) error { // Add PREMIS events for validate file format activity. e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), activities.AddPREMISEventName, &activities.AddPREMISEventParams{ PREMISFilePath: path, @@ -386,7 +436,7 @@ func writePREMISFile(ctx temporalsdk_workflow.Context, sip sip.SIP) error { // Add PREMIS event noting validate metadata result. e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), activities.AddPREMISEventName, &activities.AddPREMISEventParams{ PREMISFilePath: path, @@ -404,7 +454,7 @@ func writePREMISFile(ctx temporalsdk_workflow.Context, sip sip.SIP) error { // Add Enduro PREMIS agent. var addPREMISAgent activities.AddPREMISAgentResult e = temporalsdk_workflow.ExecuteActivity( - withLocalActOpts(ctx), + withFilesysActOpts(ctx), activities.AddPREMISAgentName, &activities.AddPREMISAgentParams{ PREMISFilePath: path, diff --git a/internal/workflow/preprocessing_test.go b/internal/workflow/preprocessing_test.go index 023c16b7..f3645302 100644 --- a/internal/workflow/preprocessing_test.go +++ b/internal/workflow/preprocessing_test.go @@ -3,6 +3,7 @@ package workflow_test import ( "crypto/rand" "fmt" + "os" "path/filepath" "testing" "time" @@ -21,6 +22,7 @@ import ( "github.com/artefactual-sdps/preprocessing-sfa/internal/config" "github.com/artefactual-sdps/preprocessing-sfa/internal/enums" "github.com/artefactual-sdps/preprocessing-sfa/internal/eventlog" + "github.com/artefactual-sdps/preprocessing-sfa/internal/localact" "github.com/artefactual-sdps/preprocessing-sfa/internal/pips" "github.com/artefactual-sdps/preprocessing-sfa/internal/premis" "github.com/artefactual-sdps/preprocessing-sfa/internal/sip" @@ -116,6 +118,7 @@ type PreprocessingTestSuite struct { env *temporalsdk_testsuite.TestWorkflowEnvironment workflow *workflow.PreprocessingWorkflow testDir string + sipPath string } func (s *PreprocessingTestSuite) SetupTest(cfg *config.Configuration) { @@ -125,7 +128,17 @@ func (s *PreprocessingTestSuite) SetupTest(cfg *config.Configuration) { s.testDir = s.T().TempDir() cfg.SharedPath = s.testDir + sp := filepath.Join(s.testDir, relPath) + if err := os.Mkdir(sp, os.FileMode(0o700)); err != nil { + s.T().Fatalf("create sip dir: %v", err) + } + s.sipPath = sp + // Register activities. + s.env.RegisterActivityWithOptions( + activities.NewUnbag().Execute, + temporalsdk_activity.RegisterOptions{Name: activities.UnbagName}, + ) s.env.RegisterActivityWithOptions( activities.NewIdentifySIP().Execute, temporalsdk_activity.RegisterOptions{Name: activities.IdentifySIPName}, @@ -221,20 +234,49 @@ func TestPreprocessingWorkflow(t *testing.T) { suite.Run(t, new(PreprocessingTestSuite)) } +func (s *PreprocessingTestSuite) writeBagitTxt(path string) { + if err := os.WriteFile( + filepath.Join(path, "bagit.txt"), + []byte(` +BagIt-Version: 0.97 +Tag-File-Character-Encoding: UTF-8 +`), + os.FileMode(0o640), + ); err != nil { + s.T().Fatalf("write bagit.txt: %v", err) + } +} + func (s *PreprocessingTestSuite) TestPreprocessingWorkflowSuccess() { s.SetupTest(&config.Configuration{}) - sipPath := filepath.Join(s.testDir, relPath) - expectedSIP := s.digitizedAIP(sipPath) + s.writeBagitTxt(s.sipPath) + + expectedSIP := s.digitizedAIP(s.sipPath) expectedPIP := pips.NewFromSIP(expectedSIP) // Mock activities. + ctx := mock.AnythingOfType("*context.valueCtx") sessionCtx := mock.AnythingOfType("*context.timerCtx") premisFilePath := filepath.Join(expectedSIP.Path, "metadata", "premis.xml") + s.env.OnActivity( + localact.IsBag, + ctx, + &localact.IsBagParams{Path: s.sipPath}, + ).Return( + &localact.IsBagResult{IsBag: true}, nil, + ) + s.env.OnActivity( + activities.UnbagName, + sessionCtx, + &activities.UnbagParams{Path: s.sipPath}, + ).Return( + &activities.UnbagResult{Path: s.sipPath}, nil, + ) s.env.OnActivity( activities.IdentifySIPName, sessionCtx, - &activities.IdentifySIPParams{Path: sipPath}, + &activities.IdentifySIPParams{Path: s.sipPath}, ).Return( &activities.IdentifySIPResult{SIP: expectedSIP}, nil, ) @@ -356,14 +398,14 @@ func (s *PreprocessingTestSuite) TestPreprocessingWorkflowSuccess() { &activities.WriteIdentifierFileParams{PIP: expectedPIP}, ).Return( &activities.WriteIdentifierFileResult{ - Path: filepath.Join(sipPath, "metadata", "identifiers.json"), + Path: filepath.Join(s.sipPath, "metadata", "identifiers.json"), }, nil, ) s.env.OnActivity( bagcreate.Name, sessionCtx, - &bagcreate.Params{SourcePath: sipPath}, + &bagcreate.Params{SourcePath: s.sipPath}, ).Return( &bagcreate.Result{}, nil, ) @@ -383,6 +425,13 @@ func (s *PreprocessingTestSuite) TestPreprocessingWorkflowSuccess() { Outcome: workflow.OutcomeSuccess, RelativePath: relPath, PreservationTasks: []*eventlog.Event{ + { + Name: "Unbag SIP", + Message: "SIP unbagged", + Outcome: enums.EventOutcomeSuccess, + StartedAt: testTime, + CompletedAt: testTime, + }, { Name: "Identify SIP structure", Message: "SIP structure identified: DigitizedAIP",