228 lines
9.6 KiB
Go
228 lines
9.6 KiB
Go
package publish
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/eric/distributor/internal/config"
|
|
"gitea.maximumdirect.net/eric/distributor/internal/state"
|
|
"gitea.maximumdirect.net/eric/distributor/internal/storage"
|
|
"gitea.maximumdirect.net/eric/distributor/internal/storage/fake"
|
|
"gitea.maximumdirect.net/eric/distributor/internal/testutil"
|
|
)
|
|
|
|
func TestExecuteAdditiveWritesOutputsAndCatalog(t *testing.T) {
|
|
_, destinationBackend, req := catalogPlanRequest(t, config.WorkflowAdditive)
|
|
existing := baseCatalog(req)
|
|
existing.Outputs = []state.CatalogOutputFile{
|
|
catalogOutput(req, "reports", "archive", "report.md", state.OutputKindSource, planCreatedAt),
|
|
catalogOutput(req, "reports", "web", "old.txt", state.OutputKindSource, planCreatedAt),
|
|
}
|
|
writeCatalogState(t, destinationBackend, "", existing)
|
|
testutil.WriteFakeFile(t, destinationBackend, "report.md", "old report")
|
|
testutil.WriteFakeFile(t, destinationBackend, "old.txt", "retained")
|
|
|
|
plan, err := Build(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
if err := Execute(context.Background(), req, plan); err != nil {
|
|
t.Fatalf("Execute() error = %v", err)
|
|
}
|
|
|
|
testutil.AssertFakeFile(t, destinationBackend, "report.md", "# Report\nSunny.\n")
|
|
testutil.AssertFakeFile(t, destinationBackend, "summary.txt", "Summary\n")
|
|
testutil.AssertFakeFile(t, destinationBackend, "old.txt", "retained")
|
|
catalog := readCatalogState(t, destinationBackend, "")
|
|
if catalog.SchemaVersion != state.CatalogSchemaVersion || catalog.State.Mode != state.StateModeCatalog {
|
|
t.Fatalf("catalog identity = schema %d mode %s", catalog.SchemaVersion, catalog.State.Mode)
|
|
}
|
|
if len(catalog.Outputs) != 3 {
|
|
t.Fatalf("catalog outputs = %#v, want three outputs", catalog.Outputs)
|
|
}
|
|
report, ok := state.FindCatalogOutputByPath(catalog.Outputs, "report.md")
|
|
if !ok {
|
|
t.Fatalf("catalog outputs = %#v, want report.md", catalog.Outputs)
|
|
}
|
|
if !report.CreatedAt.Equal(planCreatedAt) || !report.UpdatedAt.Equal(planUpdatedAt) {
|
|
t.Fatalf("report times = %s/%s, want created preserved and updated now", report.CreatedAt, report.UpdatedAt)
|
|
}
|
|
if report.SourcePath != "" {
|
|
t.Fatalf("source catalog output source_path = %q, want empty", report.SourcePath)
|
|
}
|
|
}
|
|
|
|
func TestExecuteReplacementDeletesCurrentOwnerAndPreservesOtherOwners(t *testing.T) {
|
|
_, destinationBackend, req := catalogPlanRequest(t, config.WorkflowReplacement)
|
|
existing := baseCatalog(req)
|
|
existing.Outputs = []state.CatalogOutputFile{
|
|
catalogOutput(req, "reports", "archive", "report.md", state.OutputKindSource, planCreatedAt),
|
|
catalogOutput(req, "reports", "archive", "stale.txt", state.OutputKindSource, planCreatedAt),
|
|
catalogOutput(req, "reports", "web", "shared.txt", state.OutputKindSource, planCreatedAt),
|
|
}
|
|
writeCatalogState(t, destinationBackend, "", existing)
|
|
testutil.WriteFakeFile(t, destinationBackend, "report.md", "old report")
|
|
testutil.WriteFakeFile(t, destinationBackend, "stale.txt", "delete me")
|
|
testutil.WriteFakeFile(t, destinationBackend, "shared.txt", "keep me")
|
|
|
|
plan, err := Build(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
if err := Execute(context.Background(), req, plan); err != nil {
|
|
t.Fatalf("Execute() error = %v", err)
|
|
}
|
|
|
|
testutil.AssertFakeFile(t, destinationBackend, "report.md", "# Report\nSunny.\n")
|
|
testutil.AssertFakeFile(t, destinationBackend, "summary.txt", "Summary\n")
|
|
testutil.AssertFakeMissing(t, destinationBackend, "stale.txt")
|
|
testutil.AssertFakeFile(t, destinationBackend, "shared.txt", "keep me")
|
|
catalog := readCatalogState(t, destinationBackend, "")
|
|
if _, ok := state.FindCatalogOutputByPath(catalog.Outputs, "stale.txt"); ok {
|
|
t.Fatalf("catalog outputs = %#v, want stale.txt removed", catalog.Outputs)
|
|
}
|
|
if _, ok := state.FindCatalogOutputByPath(catalog.Outputs, "shared.txt"); !ok {
|
|
t.Fatalf("catalog outputs = %#v, want shared.txt retained", catalog.Outputs)
|
|
}
|
|
}
|
|
|
|
func TestExecuteSupersededReplacementClearsDestinationRootOnly(t *testing.T) {
|
|
_, destinationBackend, req := catalogPlanRequest(t, config.WorkflowReplacement)
|
|
req.DestinationBundlePath = "bundle"
|
|
legacyState := legacyStateDocument(2)
|
|
writeJSONState(t, destinationBackend, req.DestinationBundlePath, legacyState)
|
|
testutil.WriteFakeFile(t, destinationBackend, "bundle/unplanned.txt", "remove")
|
|
testutil.WriteFakeFile(t, destinationBackend, "outside.txt", "keep")
|
|
|
|
plan, err := Build(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
if err := Execute(context.Background(), req, plan); err != nil {
|
|
t.Fatalf("Execute() error = %v", err)
|
|
}
|
|
|
|
testutil.AssertFakeMissing(t, destinationBackend, "bundle/unplanned.txt")
|
|
testutil.AssertFakeFile(t, destinationBackend, "bundle/report.md", "# Report\nSunny.\n")
|
|
testutil.AssertFakeFile(t, destinationBackend, "outside.txt", "keep")
|
|
readCatalogState(t, destinationBackend, "bundle")
|
|
}
|
|
|
|
func TestExecuteSupersededAdditiveLeavesUnplannedFilesUnmanaged(t *testing.T) {
|
|
_, destinationBackend, req := catalogPlanRequest(t, config.WorkflowAdditive)
|
|
legacyState := legacyStateDocument(2)
|
|
writeJSONState(t, destinationBackend, "", legacyState)
|
|
testutil.WriteFakeFile(t, destinationBackend, "report.md", "legacy report")
|
|
testutil.WriteFakeFile(t, destinationBackend, "unplanned.txt", "leave me")
|
|
|
|
plan, err := Build(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
if err := Execute(context.Background(), req, plan); err != nil {
|
|
t.Fatalf("Execute() error = %v", err)
|
|
}
|
|
|
|
testutil.AssertFakeFile(t, destinationBackend, "report.md", "# Report\nSunny.\n")
|
|
testutil.AssertFakeFile(t, destinationBackend, "unplanned.txt", "leave me")
|
|
catalog := readCatalogState(t, destinationBackend, "")
|
|
if _, ok := state.FindCatalogOutputByPath(catalog.Outputs, "unplanned.txt"); ok {
|
|
t.Fatalf("catalog outputs = %#v, want unplanned file omitted", catalog.Outputs)
|
|
}
|
|
}
|
|
|
|
func TestExecuteForceReplaceClearsDestinationBundlePathOnly(t *testing.T) {
|
|
_, destinationBackend, req := catalogPlanRequest(t, config.WorkflowAdditive)
|
|
req.DestinationBundlePath = "bundle"
|
|
req.Force = true
|
|
testutil.WriteFakeFile(t, destinationBackend, "bundle/report.md", "old report")
|
|
testutil.WriteFakeFile(t, destinationBackend, "bundle/unplanned.txt", "remove")
|
|
testutil.WriteFakeFile(t, destinationBackend, "bundle-child/keep.txt", "keep")
|
|
testutil.WriteFakeFile(t, destinationBackend, "outside.txt", "keep")
|
|
|
|
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 %s", plan.Action, ActionForceReplace)
|
|
}
|
|
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.AssertFakeFile(t, destinationBackend, "bundle/summary.txt", "Summary\n")
|
|
testutil.AssertFakeMissing(t, destinationBackend, "bundle/unplanned.txt")
|
|
testutil.AssertFakeFile(t, destinationBackend, "bundle-child/keep.txt", "keep")
|
|
testutil.AssertFakeFile(t, destinationBackend, "outside.txt", "keep")
|
|
catalog := readCatalogState(t, destinationBackend, "bundle")
|
|
if catalog.SchemaVersion != state.CatalogSchemaVersion || catalog.State.Mode != state.StateModeCatalog {
|
|
t.Fatalf("catalog identity = schema %d mode %s", catalog.SchemaVersion, catalog.State.Mode)
|
|
}
|
|
if len(catalog.Outputs) != 2 {
|
|
t.Fatalf("catalog outputs = %#v, want planned outputs only", catalog.Outputs)
|
|
}
|
|
}
|
|
|
|
func TestExecuteForceReplaceClearsFixedDestinationRoot(t *testing.T) {
|
|
_, destinationBackend, req := catalogPlanRequest(t, config.WorkflowAdditive)
|
|
req.DestinationBundlePath = ""
|
|
req.PathMapping = config.PathMappingFixed
|
|
req.Force = true
|
|
testutil.WriteFakeFile(t, destinationBackend, "report.md", "old report")
|
|
testutil.WriteFakeFile(t, destinationBackend, "unplanned.txt", "remove")
|
|
|
|
plan, err := Build(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
if plan.Action != ActionForceReplace || storage.DisplayPath(plan.DestinationBundlePath) != "." {
|
|
t.Fatalf("plan action=%s destination=%s, want force_replace at root", plan.Action, storage.DisplayPath(plan.DestinationBundlePath))
|
|
}
|
|
if err := Execute(context.Background(), req, plan); err != nil {
|
|
t.Fatalf("Execute() error = %v", err)
|
|
}
|
|
|
|
testutil.AssertFakeFile(t, destinationBackend, "report.md", "# Report\nSunny.\n")
|
|
testutil.AssertFakeFile(t, destinationBackend, "summary.txt", "Summary\n")
|
|
testutil.AssertFakeMissing(t, destinationBackend, "unplanned.txt")
|
|
readCatalogState(t, destinationBackend, "")
|
|
}
|
|
|
|
func TestExecuteFailedWriteDoesNotWriteCatalogState(t *testing.T) {
|
|
_, destinationBackend, req := catalogPlanRequest(t, config.WorkflowAdditive)
|
|
plan, err := Build(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
if err := destinationBackend.AddDirectory("report.md"); err != nil {
|
|
t.Fatalf("add conflicting directory: %v", err)
|
|
}
|
|
|
|
err = Execute(context.Background(), req, plan)
|
|
if err == nil {
|
|
t.Fatal("Execute() error = nil, want write failure")
|
|
}
|
|
if _, statErr := destinationBackend.Stat(context.Background(), storage.StateFileName); !storage.IsNotFound(statErr) {
|
|
t.Fatalf("state stat error = %v, want missing state", statErr)
|
|
}
|
|
}
|
|
|
|
func readCatalogState(t *testing.T, backend *fake.Backend, relative string) state.CatalogState {
|
|
t.Helper()
|
|
statePath, err := storage.StatePath(relative)
|
|
if err != nil {
|
|
t.Fatalf("state path: %v", err)
|
|
}
|
|
data, err := backend.ReadFile(context.Background(), statePath)
|
|
if err != nil {
|
|
t.Fatalf("read catalog state: %v", err)
|
|
}
|
|
catalog, err := state.ParseCatalog(data)
|
|
if err != nil {
|
|
t.Fatalf("parse catalog state: %v", err)
|
|
}
|
|
return catalog
|
|
}
|