195 lines
7.0 KiB
Go
195 lines
7.0 KiB
Go
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/fake"
|
|
"gitea.maximumdirect.net/eric/distributor/internal/testutil"
|
|
)
|
|
|
|
func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
prepare func(t *testing.T, backend *fake.Backend, source bundle.Manifest)
|
|
transfer config.TransferPolicy
|
|
wantReason string
|
|
forceAction bool
|
|
}{
|
|
{
|
|
name: "unmanaged content",
|
|
prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) {
|
|
t.Helper()
|
|
testutil.WriteFakeFile(t, backend, "bundle/old.txt", "old")
|
|
},
|
|
transfer: defaultTransfer(),
|
|
wantReason: "fail_unmanaged",
|
|
forceAction: true,
|
|
},
|
|
{
|
|
name: "different source id",
|
|
prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) {
|
|
t.Helper()
|
|
conflict := source
|
|
conflict.ID = "other.source"
|
|
testutil.WriteFakeDestinationState(t, backend, "bundle", conflict, testutil.DestinationStateOptions{})
|
|
},
|
|
transfer: conflictReplaceTransfer(),
|
|
wantReason: "requires --force",
|
|
forceAction: true,
|
|
},
|
|
{
|
|
name: "same created digest conflict",
|
|
prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) {
|
|
t.Helper()
|
|
conflict := testutil.ValidManifest(testutil.BundleOptions{Files: []testutil.SourceFile{{Path: "report.md", Data: "# Different\n"}}})
|
|
testutil.WriteFakeDestinationState(t, backend, "bundle", conflict, testutil.DestinationStateOptions{})
|
|
},
|
|
transfer: conflictReplaceTransfer(),
|
|
wantReason: "requires --force",
|
|
forceAction: true,
|
|
},
|
|
{
|
|
name: "pipeline mismatch",
|
|
prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) {
|
|
t.Helper()
|
|
testutil.WriteFakeDestinationState(t, backend, "bundle", source, testutil.DestinationStateOptions{PipelineID: "other-pipeline"})
|
|
},
|
|
transfer: conflictReplaceTransfer(),
|
|
wantReason: "requires --force",
|
|
forceAction: true,
|
|
},
|
|
{
|
|
name: "destination mismatch",
|
|
prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) {
|
|
t.Helper()
|
|
testutil.WriteFakeDestinationState(t, backend, "bundle", source, testutil.DestinationStateOptions{DestinationID: "other-destination"})
|
|
},
|
|
transfer: conflictReplaceTransfer(),
|
|
wantReason: "requires --force",
|
|
forceAction: true,
|
|
},
|
|
{
|
|
name: "newer destination",
|
|
prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) {
|
|
t.Helper()
|
|
newer := source
|
|
newer.Created = newer.Created.AddDate(0, 0, 1)
|
|
testutil.WriteFakeDestinationState(t, backend, "bundle", newer, testutil.DestinationStateOptions{})
|
|
},
|
|
transfer: newerReplaceTransfer(),
|
|
wantReason: "requires --force",
|
|
forceAction: true,
|
|
},
|
|
}
|
|
|
|
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, sourceBundle.Manifest)
|
|
|
|
req := forceRequest(sourceBackend, destinationBackend, sourceBundle, tt.transfer)
|
|
_, err := Build(context.Background(), req)
|
|
if err == nil || !strings.Contains(err.Error(), tt.wantReason) {
|
|
t.Fatalf("Build() error = %v, want %q", err, tt.wantReason)
|
|
}
|
|
|
|
req.Force = true
|
|
plan, err := Build(context.Background(), req)
|
|
if tt.forceAction {
|
|
if err != nil {
|
|
t.Fatalf("Build() with force error = %v", err)
|
|
}
|
|
if plan.Action != ActionForceReplace || !plan.Force {
|
|
t.Fatalf("forced plan action = %s force=%t", plan.Action, plan.Force)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBuildRequiresConflictPolicyForStateConflicts(t *testing.T) {
|
|
sourceBackend := fake.New()
|
|
sourceBundle := testutil.WriteFakeSourceBundle(t, sourceBackend, "bundle", testutil.BundleOptions{})
|
|
destinationBackend := fake.New()
|
|
conflict := sourceBundle.Manifest
|
|
conflict.ID = "other.source"
|
|
testutil.WriteFakeDestinationState(t, destinationBackend, "bundle", conflict, testutil.DestinationStateOptions{})
|
|
|
|
req := forceRequest(sourceBackend, destinationBackend, sourceBundle, defaultTransfer())
|
|
req.Force = true
|
|
_, err := Build(context.Background(), req)
|
|
if err == nil || !strings.Contains(err.Error(), "destination source id differs") {
|
|
t.Fatalf("Build() error = %v, want conservative conflict", err)
|
|
}
|
|
}
|
|
|
|
func TestExecuteForcedReplacementDeletesOnlyBundlePath(t *testing.T) {
|
|
sourceBackend := fake.New()
|
|
sourceBundle := testutil.WriteFakeSourceBundle(t, sourceBackend, "bundle", testutil.BundleOptions{})
|
|
destinationBackend := fake.New()
|
|
testutil.WriteFakeFile(t, destinationBackend, "bundle/old.txt", "old")
|
|
testutil.WriteFakeFile(t, destinationBackend, "bundle/nested/old.txt", "old")
|
|
testutil.WriteFakeFile(t, destinationBackend, "bundle-sibling/keep.txt", "keep")
|
|
testutil.WriteFakeFile(t, destinationBackend, "outside.txt", "outside")
|
|
|
|
req := forceRequest(sourceBackend, destinationBackend, sourceBundle, defaultTransfer())
|
|
req.Force = true
|
|
plan, err := Build(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
if plan.Action != ActionForceReplace {
|
|
t.Fatalf("plan action = %s, want force_replace", plan.Action)
|
|
}
|
|
if err := Execute(context.Background(), req, plan); err != nil {
|
|
t.Fatalf("Execute() error = %v", err)
|
|
}
|
|
testutil.AssertFakeFile(t, destinationBackend, "bundle/report.md", "# Report\nSunny.\n")
|
|
testutil.AssertFakeMissing(t, destinationBackend, "bundle/old.txt")
|
|
testutil.AssertFakeMissing(t, destinationBackend, "bundle/nested/old.txt")
|
|
testutil.AssertFakeFile(t, destinationBackend, "bundle-sibling/keep.txt", "keep")
|
|
testutil.AssertFakeFile(t, destinationBackend, "outside.txt", "outside")
|
|
}
|
|
|
|
func forceRequest(sourceBackend, destinationBackend *fake.Backend, sourceBundle bundle.Bundle, transfer config.TransferPolicy) Request {
|
|
return Request{
|
|
PipelineID: "reports",
|
|
DestinationID: "archive",
|
|
SourceBundle: sourceBundle,
|
|
SourceBackend: sourceBackend,
|
|
DestinationBackend: destinationBackend,
|
|
DestinationBundlePath: sourceBundle.RootRelativePath,
|
|
Publish: config.PublishPolicy{Source: true},
|
|
Takeover: config.TakeoverPolicy{Mode: config.TakeoverModeNever},
|
|
Transfer: transfer,
|
|
DistributorVersion: "test",
|
|
}
|
|
}
|
|
|
|
func defaultTransfer() config.TransferPolicy {
|
|
return config.TransferPolicy{
|
|
OnDestinationSame: config.TransferActionSkip,
|
|
OnDestinationOlder: config.TransferActionReplace,
|
|
OnDestinationNewer: config.TransferActionSkip,
|
|
OnConflict: config.TransferActionFail,
|
|
}
|
|
}
|
|
|
|
func conflictReplaceTransfer() config.TransferPolicy {
|
|
transfer := defaultTransfer()
|
|
transfer.OnConflict = config.TransferActionReplace
|
|
return transfer
|
|
}
|
|
|
|
func newerReplaceTransfer() config.TransferPolicy {
|
|
transfer := defaultTransfer()
|
|
transfer.OnDestinationNewer = config.TransferActionReplace
|
|
return transfer
|
|
}
|