Implement single-owner managed takeover
This commit is contained in:
215
internal/publish/takeover_test.go
Normal file
215
internal/publish/takeover_test.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package publish
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.maximumdirect.net/eric/distributor/internal/bundle"
|
||||
"gitea.maximumdirect.net/eric/distributor/internal/config"
|
||||
"gitea.maximumdirect.net/eric/distributor/internal/storage"
|
||||
"gitea.maximumdirect.net/eric/distributor/internal/storage/fake"
|
||||
"gitea.maximumdirect.net/eric/distributor/internal/testutil"
|
||||
)
|
||||
|
||||
func TestBuildPlansSingleOwnerTakeoverByPolicy(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
takeover config.TakeoverPolicy
|
||||
mutateState func(*bundle.Manifest, *testutil.DestinationStateOptions)
|
||||
wantAction Action
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "default same pipeline different source",
|
||||
takeover: config.TakeoverPolicy{},
|
||||
mutateState: func(manifest *bundle.Manifest, opts *testutil.DestinationStateOptions) {
|
||||
manifest.ID = "other.source"
|
||||
},
|
||||
wantAction: ActionReplaceTakeover,
|
||||
},
|
||||
{
|
||||
name: "same pipeline different newer source",
|
||||
takeover: config.TakeoverPolicy{Mode: config.TakeoverModeSamePipeline},
|
||||
mutateState: func(manifest *bundle.Manifest, opts *testutil.DestinationStateOptions) {
|
||||
manifest.ID = "other.source"
|
||||
manifest.Created = manifest.Created.AddDate(0, 0, 1)
|
||||
},
|
||||
wantAction: ActionReplaceTakeover,
|
||||
},
|
||||
{
|
||||
name: "default same pipeline different destination",
|
||||
takeover: config.TakeoverPolicy{},
|
||||
mutateState: func(manifest *bundle.Manifest, opts *testutil.DestinationStateOptions) {
|
||||
opts.DestinationID = "web"
|
||||
},
|
||||
wantAction: ActionReplaceTakeover,
|
||||
},
|
||||
{
|
||||
name: "same pipeline refuses different pipeline",
|
||||
takeover: config.TakeoverPolicy{Mode: config.TakeoverModeSamePipeline},
|
||||
mutateState: func(manifest *bundle.Manifest, opts *testutil.DestinationStateOptions) {
|
||||
opts.PipelineID = "other"
|
||||
},
|
||||
wantErr: "fail_conflict",
|
||||
},
|
||||
{
|
||||
name: "same source allows different pipeline",
|
||||
takeover: config.TakeoverPolicy{Mode: config.TakeoverModeSameSource},
|
||||
mutateState: func(manifest *bundle.Manifest, opts *testutil.DestinationStateOptions) {
|
||||
opts.PipelineID = "other"
|
||||
},
|
||||
wantAction: ActionReplaceTakeover,
|
||||
},
|
||||
{
|
||||
name: "same source refuses different source",
|
||||
takeover: config.TakeoverPolicy{Mode: config.TakeoverModeSameSource},
|
||||
mutateState: func(manifest *bundle.Manifest, opts *testutil.DestinationStateOptions) {
|
||||
manifest.ID = "other.source"
|
||||
},
|
||||
wantErr: "fail_conflict",
|
||||
},
|
||||
{
|
||||
name: "any managed allows different pipeline",
|
||||
takeover: config.TakeoverPolicy{Mode: config.TakeoverModeAnyManaged},
|
||||
mutateState: func(manifest *bundle.Manifest, opts *testutil.DestinationStateOptions) {
|
||||
manifest.ID = "other.source"
|
||||
opts.PipelineID = "other"
|
||||
},
|
||||
wantAction: ActionReplaceTakeover,
|
||||
},
|
||||
{
|
||||
name: "never refuses different source",
|
||||
takeover: config.TakeoverPolicy{Mode: config.TakeoverModeNever},
|
||||
mutateState: func(manifest *bundle.Manifest, opts *testutil.DestinationStateOptions) {
|
||||
manifest.ID = "other.source"
|
||||
},
|
||||
wantErr: "fail_conflict",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sourceBackend := fake.New()
|
||||
sourceBundle := testutil.WriteFakeSourceBundle(t, sourceBackend, "bundle", testutil.BundleOptions{})
|
||||
destinationBackend := fake.New()
|
||||
destinationManifest := sourceBundle.Manifest
|
||||
destinationManifest.Files = append([]bundle.ManifestFile(nil), sourceBundle.Manifest.Files...)
|
||||
opts := testutil.DestinationStateOptions{}
|
||||
tt.mutateState(&destinationManifest, &opts)
|
||||
testutil.WriteFakeDestinationState(t, destinationBackend, "bundle", destinationManifest, opts)
|
||||
|
||||
req := takeoverRequest(sourceBackend, destinationBackend, sourceBundle, tt.takeover, config.ReconciliationModeReplace)
|
||||
plan, err := Build(context.Background(), req)
|
||||
if tt.wantErr != "" {
|
||||
if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
|
||||
t.Fatalf("Build() error = %v, want %q", err, tt.wantErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Build() error = %v", err)
|
||||
}
|
||||
if plan.Action != tt.wantAction {
|
||||
t.Fatalf("plan action = %s, want %s", plan.Action, tt.wantAction)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDoesNotTakeOverInvalidOrUnmanagedDestination(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
prepare func(t *testing.T, backend *fake.Backend)
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "invalid state",
|
||||
prepare: func(t *testing.T, backend *fake.Backend) {
|
||||
t.Helper()
|
||||
statePath, err := storage.StatePath("bundle")
|
||||
if err != nil {
|
||||
t.Fatalf("state path: %v", err)
|
||||
}
|
||||
testutil.WriteFakeFile(t, backend, statePath, "{invalid")
|
||||
},
|
||||
wantErr: "fail_conflict",
|
||||
},
|
||||
{
|
||||
name: "unmanaged content",
|
||||
prepare: func(t *testing.T, backend *fake.Backend) {
|
||||
t.Helper()
|
||||
testutil.WriteFakeFile(t, backend, "bundle/old.txt", "old")
|
||||
},
|
||||
wantErr: "fail_unmanaged",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sourceBackend := fake.New()
|
||||
sourceBundle := testutil.WriteFakeSourceBundle(t, sourceBackend, "bundle", testutil.BundleOptions{})
|
||||
destinationBackend := fake.New()
|
||||
tt.prepare(t, destinationBackend)
|
||||
|
||||
req := takeoverRequest(sourceBackend, destinationBackend, sourceBundle, config.TakeoverPolicy{Mode: config.TakeoverModeAnyManaged}, config.ReconciliationModeReplace)
|
||||
_, err := Build(context.Background(), req)
|
||||
if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
|
||||
t.Fatalf("Build() error = %v, want %q", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteTakeoverMergeDoesNotRetainOldSourceOutputs(t *testing.T) {
|
||||
sourceBackend := fake.New()
|
||||
sourceBundle := testutil.WriteFakeSourceBundle(t, sourceBackend, "bundle", testutil.BundleOptions{
|
||||
Files: []testutil.SourceFile{{Path: "report.md", Data: "# Report\nNew.\n"}},
|
||||
})
|
||||
destinationBackend := fake.New()
|
||||
oldManifest := sourceBundle.Manifest
|
||||
oldManifest.ID = "old.source"
|
||||
oldManifest.Files = []bundle.ManifestFile{
|
||||
{Path: "report.md", SHA256: bundle.FileDigest([]byte("old\n")), Size: int64(len("old\n"))},
|
||||
{Path: "summary.txt", SHA256: bundle.FileDigest([]byte("old summary\n")), Size: int64(len("old summary\n"))},
|
||||
}
|
||||
oldManifest.Digest = bundle.BundleDigest(oldManifest.Files)
|
||||
testutil.WriteFakeDestinationState(t, destinationBackend, "bundle", oldManifest, testutil.DestinationStateOptions{})
|
||||
|
||||
req := takeoverRequest(sourceBackend, destinationBackend, sourceBundle, config.TakeoverPolicy{Mode: config.TakeoverModeSamePipeline}, config.ReconciliationModeMerge)
|
||||
plan, err := Build(context.Background(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("Build() error = %v", err)
|
||||
}
|
||||
if plan.Action != ActionReplaceTakeover {
|
||||
t.Fatalf("plan action = %s, want %s", plan.Action, ActionReplaceTakeover)
|
||||
}
|
||||
if err := Execute(context.Background(), req, plan); err != nil {
|
||||
t.Fatalf("Execute() error = %v", err)
|
||||
}
|
||||
|
||||
testutil.AssertFakeFile(t, destinationBackend, "bundle/report.md", "# Report\nNew.\n")
|
||||
testutil.AssertFakeMissing(t, destinationBackend, "bundle/summary.txt")
|
||||
destinationState := readFakeState(t, destinationBackend, "bundle")
|
||||
if got, want := len(destinationState.Outputs), 1; got != want {
|
||||
t.Fatalf("state output count = %d, want %d", got, want)
|
||||
}
|
||||
if got, want := destinationState.Source.Manifest.ID, sourceBundle.Manifest.ID; got != want {
|
||||
t.Fatalf("state source id = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func takeoverRequest(sourceBackend, destinationBackend *fake.Backend, sourceBundle bundle.Bundle, takeover config.TakeoverPolicy, reconciliationMode string) Request {
|
||||
return Request{
|
||||
PipelineID: "reports",
|
||||
DestinationID: "archive",
|
||||
SourceBundle: sourceBundle,
|
||||
SourceBackend: sourceBackend,
|
||||
DestinationBackend: destinationBackend,
|
||||
DestinationBundlePath: sourceBundle.RootRelativePath,
|
||||
Publish: config.PublishPolicy{Source: true},
|
||||
Reconciliation: config.ReconciliationPolicy{Mode: reconciliationMode},
|
||||
Takeover: takeover,
|
||||
Transfer: defaultTransfer(),
|
||||
DistributorVersion: "test",
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user