Move reusable test helpers to testutil

This commit is contained in:
2026-06-02 18:56:08 +00:00
parent eba4d6dd56
commit 9684ffd37f
3 changed files with 290 additions and 315 deletions

View File

@@ -200,8 +200,8 @@ func TestRunPublishesNewLocalBundle(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n")
assertFile(t, filepath.Join(destinationRoot, "summary.txt"), "Summary\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "summary.txt"), "Summary\n")
if _, err := os.Stat(filepath.Join(destinationRoot, "manifest.json")); !os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(destinationRoot, "manifest.json")); !os.IsNotExist(err) {
t.Fatalf("destination manifest stat error = %v, want not exist", err) t.Fatalf("destination manifest stat error = %v, want not exist", err)
} }
@@ -225,11 +225,11 @@ func TestRunExplicitPreserveRelativePathMappingMatchesDefault(t *testing.T) {
destinationRoot := t.TempDir() destinationRoot := t.TempDir()
writeSourceBundle(t, sourceRoot, "daily/report", testBundleOptions{}) writeSourceBundle(t, sourceRoot, "daily/report", testBundleOptions{})
err := Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingPreserveRelative)}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingPreserveRelative)})
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFile(t, filepath.Join(destinationRoot, "daily", "report", "report.md"), "# Report\nSunny.\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "daily", "report", "report.md"), "# Report\nSunny.\n")
if _, err := os.Stat(filepath.Join(destinationRoot, "report.md")); !os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(destinationRoot, "report.md")); !os.IsNotExist(err) {
t.Fatalf("root report.md stat error = %v, want not exist", err) t.Fatalf("root report.md stat error = %v, want not exist", err)
} }
@@ -241,7 +241,7 @@ func TestRunRecordsLinksForNestedBundlePath(t *testing.T) {
writeSourceBundle(t, sourceRoot, "daily/brentwood", testBundleOptions{}) writeSourceBundle(t, sourceRoot, "daily/brentwood", testBundleOptions{})
err := Run(context.Background(), RunOptions{ err := Run(context.Background(), RunOptions{
ConfigPath: writeLocalConfigWithLinks(t, sourceRoot, destinationRoot, config.PathMappingPreserveRelative, "https://reports.example.com/archive", config.LinkPrimaryAuto, true, false, ""), ConfigPath: testutil.WriteLocalConfigWithLinks(t, sourceRoot, destinationRoot, config.PathMappingPreserveRelative, "https://reports.example.com/archive", config.LinkPrimaryAuto, true, false, ""),
}) })
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
@@ -266,7 +266,7 @@ func TestRunRecordsLinksForFixedIndexDestination(t *testing.T) {
writeSourceBundle(t, sourceRoot, "newer", testBundleOptions{ID: "reports.newer", Created: testutil.DefaultCreated.Add(time.Hour)}) writeSourceBundle(t, sourceRoot, "newer", testBundleOptions{ID: "reports.newer", Created: testutil.DefaultCreated.Add(time.Hour)})
err := Run(context.Background(), RunOptions{ err := Run(context.Background(), RunOptions{
ConfigPath: writeLocalConfigWithLinks(t, sourceRoot, destinationRoot, config.PathMappingFixed, "https://reports.example.com/latest", config.LinkPrimaryAuto, false, true, config.TransformModeIndex), ConfigPath: testutil.WriteLocalConfigWithLinks(t, sourceRoot, destinationRoot, config.PathMappingFixed, "https://reports.example.com/latest", config.LinkPrimaryAuto, false, true, config.TransformModeIndex),
}) })
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
@@ -303,11 +303,11 @@ func TestRunFixedPathPublishesNewestBundleAtDestinationRoot(t *testing.T) {
}, },
}) })
err := Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed)}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed)})
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nNew.\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nNew.\n")
if _, err := os.Stat(filepath.Join(destinationRoot, "new", "report.md")); !os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(destinationRoot, "new", "report.md")); !os.IsNotExist(err) {
t.Fatalf("nested new report stat error = %v, want not exist", err) t.Fatalf("nested new report stat error = %v, want not exist", err)
} }
@@ -337,7 +337,7 @@ func TestRunFixedPathTieBreaksByBundlePath(t *testing.T) {
}, },
}) })
err := Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed)}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed)})
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
@@ -355,7 +355,7 @@ func TestRunFixedPathDryRunReportsSelection(t *testing.T) {
var stdout bytes.Buffer var stdout bytes.Buffer
err := Run(context.Background(), RunOptions{ err := Run(context.Background(), RunOptions{
ConfigPath: writeLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed), ConfigPath: testutil.WriteLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed),
DryRun: true, DryRun: true,
Stdout: &stdout, Stdout: &stdout,
}) })
@@ -391,7 +391,7 @@ func TestRunFixedPathDryRunWarnsForReplacement(t *testing.T) {
{Path: "summary.txt", Data: "Old summary\n"}, {Path: "summary.txt", Data: "Old summary\n"},
}, },
}) })
configPath := writeLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed) configPath := testutil.WriteLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed)
if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil { if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil {
t.Fatalf("first Run() error = %v", err) t.Fatalf("first Run() error = %v", err)
} }
@@ -422,7 +422,7 @@ func TestRunFixedPathDryRunWarnsForReplacement(t *testing.T) {
t.Fatalf("stdout = %q, want substring %q", output, want) t.Fatalf("stdout = %q, want substring %q", output, want)
} }
} }
assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nOld.\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nOld.\n")
} }
func TestRunFixedPathReplacesOlderManagedState(t *testing.T) { func TestRunFixedPathReplacesOlderManagedState(t *testing.T) {
@@ -436,11 +436,11 @@ func TestRunFixedPathReplacesOlderManagedState(t *testing.T) {
{Path: "summary.txt", Data: "Old summary\n"}, {Path: "summary.txt", Data: "Old summary\n"},
}, },
}) })
configPath := writeLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed) configPath := testutil.WriteLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed)
if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil { if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil {
t.Fatalf("first Run() error = %v", err) t.Fatalf("first Run() error = %v", err)
} }
assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nOld.\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nOld.\n")
writeSourceBundle(t, sourceRoot, "new", testBundleOptions{ writeSourceBundle(t, sourceRoot, "new", testBundleOptions{
ID: "reports.new", ID: "reports.new",
@@ -454,7 +454,7 @@ func TestRunFixedPathReplacesOlderManagedState(t *testing.T) {
if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil { if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil {
t.Fatalf("second Run() error = %v", err) t.Fatalf("second Run() error = %v", err)
} }
assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nNew.\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nNew.\n")
destinationState := readStateFile(t, filepath.Join(destinationRoot, storage.StateFileName)) destinationState := readStateFile(t, filepath.Join(destinationRoot, storage.StateFileName))
if destinationState.Source.Manifest.ID != "reports.new" { if destinationState.Source.Manifest.ID != "reports.new" {
t.Fatalf("state source id = %q, want reports.new", destinationState.Source.Manifest.ID) t.Fatalf("state source id = %q, want reports.new", destinationState.Source.Manifest.ID)
@@ -479,7 +479,7 @@ func TestRunFixedPathSkipsWhenDestinationStateIsNewer(t *testing.T) {
var stdout bytes.Buffer var stdout bytes.Buffer
err := Run(context.Background(), RunOptions{ err := Run(context.Background(), RunOptions{
ConfigPath: writeLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed), ConfigPath: testutil.WriteLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed),
Stdout: &stdout, Stdout: &stdout,
}) })
if err != nil { if err != nil {
@@ -488,7 +488,7 @@ func TestRunFixedPathSkipsWhenDestinationStateIsNewer(t *testing.T) {
if !strings.Contains(stdout.String(), "action=skip_destination_newer") { if !strings.Contains(stdout.String(), "action=skip_destination_newer") {
t.Fatalf("stdout = %q, want skip_destination_newer", stdout.String()) t.Fatalf("stdout = %q, want skip_destination_newer", stdout.String())
} }
assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nExisting.\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nExisting.\n")
} }
func TestRunFixedPathFailsUnmanagedWithoutForce(t *testing.T) { func TestRunFixedPathFailsUnmanagedWithoutForce(t *testing.T) {
@@ -499,7 +499,7 @@ func TestRunFixedPathFailsUnmanagedWithoutForce(t *testing.T) {
t.Fatalf("write unmanaged file: %v", err) t.Fatalf("write unmanaged file: %v", err)
} }
err := Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed)}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed)})
if err == nil || !strings.Contains(err.Error(), "fail_unmanaged") { if err == nil || !strings.Contains(err.Error(), "fail_unmanaged") {
t.Fatalf("Run() error = %v, want unmanaged failure", err) t.Fatalf("Run() error = %v, want unmanaged failure", err)
} }
@@ -521,14 +521,14 @@ func TestRunFixedPathForceReplacementStaysWithinDestinationRoot(t *testing.T) {
writeSourceBundle(t, sourceRoot, "bundle", testBundleOptions{}) writeSourceBundle(t, sourceRoot, "bundle", testBundleOptions{})
err := Run(context.Background(), RunOptions{ err := Run(context.Background(), RunOptions{
ConfigPath: writeLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed), ConfigPath: testutil.WriteLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed),
Force: true, Force: true,
}) })
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n")
assertFile(t, filepath.Join(parent, "keep.txt"), "keep") testutil.AssertFile(t, filepath.Join(parent, "keep.txt"), "keep")
if _, err := os.Stat(filepath.Join(destinationRoot, "unmanaged.txt")); !os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(destinationRoot, "unmanaged.txt")); !os.IsNotExist(err) {
t.Fatalf("unmanaged stat error = %v, want removed", err) t.Fatalf("unmanaged stat error = %v, want removed", err)
} }
@@ -583,12 +583,12 @@ func TestRunFixedPathRemoteBackendsUseBackendRoots(t *testing.T) {
if err := runConfigWithBackendFactory(context.Background(), cfg, RunOptions{}, provider); err != nil { if err := runConfigWithBackendFactory(context.Background(), cfg, RunOptions{}, provider); err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFakeFile(t, s3Destination, "report.md", "# Report\nNew.\n") testutil.AssertFakeFile(t, s3Destination, "report.md", "# Report\nNew.\n")
assertFakeFile(t, s3Destination, "summary.txt", "New summary\n") testutil.AssertFakeFile(t, s3Destination, "summary.txt", "New summary\n")
assertFakeMissing(t, s3Destination, "new/report.md") testutil.AssertFakeMissing(t, s3Destination, "new/report.md")
assertFakeFile(t, sshDestination, "report.md", "# Report\nNew.\n") testutil.AssertFakeFile(t, sshDestination, "report.md", "# Report\nNew.\n")
assertFakeFile(t, sshDestination, "summary.txt", "New summary\n") testutil.AssertFakeFile(t, sshDestination, "summary.txt", "New summary\n")
assertFakeMissing(t, sshDestination, "new/report.md") testutil.AssertFakeMissing(t, sshDestination, "new/report.md")
} }
func TestRunNotifiesAfterPublication(t *testing.T) { func TestRunNotifiesAfterPublication(t *testing.T) {
@@ -629,7 +629,7 @@ func TestRunNotifiesGeneratedOutputMetadata(t *testing.T) {
notifier := &recordingNotifier{} notifier := &recordingNotifier{}
err := Run(context.Background(), RunOptions{ err := Run(context.Background(), RunOptions{
ConfigPath: writeLocalConfigWithPolicy(t, sourceRoot, destinationRoot, false, true), ConfigPath: testutil.WriteLocalConfigWithPublishPolicy(t, sourceRoot, destinationRoot, false, true),
Notifier: notifier, Notifier: notifier,
}) })
if err != nil { if err != nil {
@@ -682,7 +682,7 @@ func TestRunJSONIncludesGeneratedOutputMetadata(t *testing.T) {
var stdout bytes.Buffer var stdout bytes.Buffer
err := Run(context.Background(), RunOptions{ err := Run(context.Background(), RunOptions{
ConfigPath: writeLocalConfigWithLinks(t, sourceRoot, destinationRoot, config.PathMappingFixed, "https://reports.example.com/latest", config.LinkPrimaryAuto, false, true, config.TransformModeIndex), ConfigPath: testutil.WriteLocalConfigWithLinks(t, sourceRoot, destinationRoot, config.PathMappingFixed, "https://reports.example.com/latest", config.LinkPrimaryAuto, false, true, config.TransformModeIndex),
DryRun: true, DryRun: true,
Stdout: &stdout, Stdout: &stdout,
OutputFormat: OutputFormatJSON, OutputFormat: OutputFormatJSON,
@@ -789,7 +789,7 @@ func TestRunContinuesAfterDestinationFailure(t *testing.T) {
t.Fatalf("stdout = %q, want substring %q", output, want) t.Fatalf("stdout = %q, want substring %q", output, want)
} }
} }
assertFile(t, filepath.Join(secondDestination, "report.md"), "# Report\nSunny.\n") testutil.AssertFile(t, filepath.Join(secondDestination, "report.md"), "# Report\nSunny.\n")
} }
func TestRunPublishesHTMLOnly(t *testing.T) { func TestRunPublishesHTMLOnly(t *testing.T) {
@@ -797,11 +797,11 @@ func TestRunPublishesHTMLOnly(t *testing.T) {
destinationRoot := t.TempDir() destinationRoot := t.TempDir()
writeSourceBundle(t, sourceRoot, "", testBundleOptions{}) writeSourceBundle(t, sourceRoot, "", testBundleOptions{})
err := Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithPolicy(t, sourceRoot, destinationRoot, false, true)}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithPublishPolicy(t, sourceRoot, destinationRoot, false, true)})
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFileContains(t, filepath.Join(destinationRoot, "report.html"), "<h1>Report</h1>") testutil.AssertFileContains(t, filepath.Join(destinationRoot, "report.html"), "<h1>Report</h1>")
if _, err := os.Stat(filepath.Join(destinationRoot, "report.md")); !os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(destinationRoot, "report.md")); !os.IsNotExist(err) {
t.Fatalf("report.md stat error = %v, want not exist", err) t.Fatalf("report.md stat error = %v, want not exist", err)
} }
@@ -822,11 +822,11 @@ func TestRunPublishesHTMLIndexWithExplicitInput(t *testing.T) {
ExtraFiles: []testFile{{Path: "notes.md", Data: "# Notes\nHidden.\n"}}, ExtraFiles: []testFile{{Path: "notes.md", Data: "# Notes\nHidden.\n"}},
}) })
err := Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, false, true, config.TransformModeIndex, "report.md")}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, false, true, config.TransformModeIndex, "report.md")})
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFileContains(t, filepath.Join(destinationRoot, "index.html"), "<h1>Report</h1>") testutil.AssertFileContains(t, filepath.Join(destinationRoot, "index.html"), "<h1>Report</h1>")
if _, err := os.Stat(filepath.Join(destinationRoot, "report.html")); !os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(destinationRoot, "report.html")); !os.IsNotExist(err) {
t.Fatalf("report.html stat error = %v, want not exist", err) t.Fatalf("report.html stat error = %v, want not exist", err)
} }
@@ -845,11 +845,11 @@ func TestRunPublishesHTMLIndexWithSingleMarkdownFallback(t *testing.T) {
destinationRoot := t.TempDir() destinationRoot := t.TempDir()
writeSourceBundle(t, sourceRoot, "", testBundleOptions{}) writeSourceBundle(t, sourceRoot, "", testBundleOptions{})
err := Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, false, true, config.TransformModeIndex, "")}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, false, true, config.TransformModeIndex, "")})
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFileContains(t, filepath.Join(destinationRoot, "index.html"), "<h1>Report</h1>") testutil.AssertFileContains(t, filepath.Join(destinationRoot, "index.html"), "<h1>Report</h1>")
} }
func TestRunFailsIndexModeWithAmbiguousMarkdownInput(t *testing.T) { func TestRunFailsIndexModeWithAmbiguousMarkdownInput(t *testing.T) {
@@ -859,7 +859,7 @@ func TestRunFailsIndexModeWithAmbiguousMarkdownInput(t *testing.T) {
ExtraFiles: []testFile{{Path: "notes.md", Data: "# Notes\n"}}, ExtraFiles: []testFile{{Path: "notes.md", Data: "# Notes\n"}},
}) })
err := Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, false, true, config.TransformModeIndex, "")}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, false, true, config.TransformModeIndex, "")})
if err == nil || !strings.Contains(err.Error(), "multiple markdown source files") { if err == nil || !strings.Contains(err.Error(), "multiple markdown source files") {
t.Fatalf("Run() error = %v, want ambiguous input error", err) t.Fatalf("Run() error = %v, want ambiguous input error", err)
} }
@@ -873,13 +873,13 @@ func TestRunPublishesSourceAndHTML(t *testing.T) {
destinationRoot := t.TempDir() destinationRoot := t.TempDir()
writeSourceBundle(t, sourceRoot, "", testBundleOptions{}) writeSourceBundle(t, sourceRoot, "", testBundleOptions{})
err := Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithPolicy(t, sourceRoot, destinationRoot, true, true)}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithPublishPolicy(t, sourceRoot, destinationRoot, true, true)})
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n")
assertFileContains(t, filepath.Join(destinationRoot, "report.html"), "<p>Sunny.</p>") testutil.AssertFileContains(t, filepath.Join(destinationRoot, "report.html"), "<p>Sunny.</p>")
assertFile(t, filepath.Join(destinationRoot, "summary.txt"), "Summary\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "summary.txt"), "Summary\n")
destinationState := readStateFile(t, filepath.Join(destinationRoot, storage.StateFileName)) destinationState := readStateFile(t, filepath.Join(destinationRoot, storage.StateFileName))
if got, want := len(destinationState.Outputs), 3; got != want { if got, want := len(destinationState.Outputs), 3; got != want {
t.Fatalf("state output count = %d, want %d", got, want) t.Fatalf("state output count = %d, want %d", got, want)
@@ -896,7 +896,7 @@ func TestRunDoesNotMutateSourceBundle(t *testing.T) {
t.Fatalf("read source before: %v", err) t.Fatalf("read source before: %v", err)
} }
err = Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithPolicy(t, sourceRoot, destinationRoot, true, true)}) err = Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithPublishPolicy(t, sourceRoot, destinationRoot, true, true)})
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
@@ -914,7 +914,7 @@ func TestRunFailsOnOutputPathCollision(t *testing.T) {
destinationRoot := t.TempDir() destinationRoot := t.TempDir()
writeSourceBundle(t, sourceRoot, "", testBundleOptions{ExtraFiles: []testFile{{Path: "report.html", Data: "<p>source html</p>\n"}}}) writeSourceBundle(t, sourceRoot, "", testBundleOptions{ExtraFiles: []testFile{{Path: "report.html", Data: "<p>source html</p>\n"}}})
err := Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithPolicy(t, sourceRoot, destinationRoot, true, true)}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithPublishPolicy(t, sourceRoot, destinationRoot, true, true)})
if err == nil || !strings.Contains(err.Error(), "destination output path collision") { if err == nil || !strings.Contains(err.Error(), "destination output path collision") {
t.Fatalf("Run() error = %v, want collision", err) t.Fatalf("Run() error = %v, want collision", err)
} }
@@ -930,7 +930,7 @@ func TestRunFailsOnIndexOutputPathCollision(t *testing.T) {
ExtraFiles: []testFile{{Path: "index.html", Data: "<p>source index</p>\n"}}, ExtraFiles: []testFile{{Path: "index.html", Data: "<p>source index</p>\n"}},
}) })
err := Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, true, true, config.TransformModeIndex, "report.md")}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, true, true, config.TransformModeIndex, "report.md")})
if err == nil || !strings.Contains(err.Error(), "destination output path collision") { if err == nil || !strings.Contains(err.Error(), "destination output path collision") {
t.Fatalf("Run() error = %v, want collision", err) t.Fatalf("Run() error = %v, want collision", err)
} }
@@ -946,7 +946,7 @@ func TestRunDryRunReportsGeneratedOutputs(t *testing.T) {
var stdout bytes.Buffer var stdout bytes.Buffer
err := Run(context.Background(), RunOptions{ err := Run(context.Background(), RunOptions{
ConfigPath: writeLocalConfigWithPolicy(t, sourceRoot, destinationRoot, false, true), ConfigPath: testutil.WriteLocalConfigWithPublishPolicy(t, sourceRoot, destinationRoot, false, true),
DryRun: true, DryRun: true,
Stdout: &stdout, Stdout: &stdout,
}) })
@@ -965,7 +965,7 @@ func TestRunDryRunReportsIndexOutputWithoutWriting(t *testing.T) {
var stdout bytes.Buffer var stdout bytes.Buffer
err := Run(context.Background(), RunOptions{ err := Run(context.Background(), RunOptions{
ConfigPath: writeLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, false, true, config.TransformModeIndex, ""), ConfigPath: testutil.WriteLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, false, true, config.TransformModeIndex, ""),
DryRun: true, DryRun: true,
Stdout: &stdout, Stdout: &stdout,
}) })
@@ -985,11 +985,11 @@ func TestRunSourceOnlyDoesNotWriteIndexOutput(t *testing.T) {
destinationRoot := t.TempDir() destinationRoot := t.TempDir()
writeSourceBundle(t, sourceRoot, "", testBundleOptions{}) writeSourceBundle(t, sourceRoot, "", testBundleOptions{})
err := Run(context.Background(), RunOptions{ConfigPath: writeLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, true, false, config.TransformModeIndex, "")}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, true, false, config.TransformModeIndex, "")})
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n")
if _, err := os.Stat(filepath.Join(destinationRoot, "index.html")); !os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(destinationRoot, "index.html")); !os.IsNotExist(err) {
t.Fatalf("index.html stat error = %v, want not exist", err) t.Fatalf("index.html stat error = %v, want not exist", err)
} }
@@ -1005,11 +1005,11 @@ func TestRunReplacesHTMLIndexOutput(t *testing.T) {
{Path: "summary.txt", Data: "Summary\n"}, {Path: "summary.txt", Data: "Summary\n"},
}, },
}) })
configPath := writeLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, false, true, config.TransformModeIndex, "") configPath := testutil.WriteLocalConfigWithMarkdownTransform(t, sourceRoot, destinationRoot, false, true, config.TransformModeIndex, "")
if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil { if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil {
t.Fatalf("first Run() error = %v", err) t.Fatalf("first Run() error = %v", err)
} }
assertFileContains(t, filepath.Join(destinationRoot, "index.html"), "<p>Old.</p>") testutil.AssertFileContains(t, filepath.Join(destinationRoot, "index.html"), "<p>Old.</p>")
writeSourceBundle(t, sourceRoot, "", testBundleOptions{ writeSourceBundle(t, sourceRoot, "", testBundleOptions{
Created: testutil.DefaultCreated.Add(time.Hour), Created: testutil.DefaultCreated.Add(time.Hour),
@@ -1022,7 +1022,7 @@ func TestRunReplacesHTMLIndexOutput(t *testing.T) {
if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil { if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil {
t.Fatalf("second Run() error = %v", err) t.Fatalf("second Run() error = %v", err)
} }
assertFileContains(t, filepath.Join(destinationRoot, "index.html"), "<p>New.</p>") testutil.AssertFileContains(t, filepath.Join(destinationRoot, "index.html"), "<p>New.</p>")
} }
func TestRunSkipsWhenDestinationStateMatches(t *testing.T) { func TestRunSkipsWhenDestinationStateMatches(t *testing.T) {
@@ -1063,7 +1063,7 @@ func TestRunReplacesOlderDestination(t *testing.T) {
if !strings.Contains(stdout.String(), "action=replace_older") { if !strings.Contains(stdout.String(), "action=replace_older") {
t.Fatalf("stdout = %q, want replace_older", stdout.String()) t.Fatalf("stdout = %q, want replace_older", stdout.String())
} }
assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n")
} }
func TestRunSkipsNewerDestination(t *testing.T) { func TestRunSkipsNewerDestination(t *testing.T) {
@@ -1085,7 +1085,7 @@ func TestRunSkipsNewerDestination(t *testing.T) {
if !strings.Contains(stdout.String(), "action=skip_destination_newer") { if !strings.Contains(stdout.String(), "action=skip_destination_newer") {
t.Fatalf("stdout = %q, want skip_destination_newer", stdout.String()) t.Fatalf("stdout = %q, want skip_destination_newer", stdout.String())
} }
assertFile(t, filepath.Join(destinationRoot, "report.md"), "newer\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "newer\n")
} }
func TestRunFailsOnConflict(t *testing.T) { func TestRunFailsOnConflict(t *testing.T) {
@@ -1138,7 +1138,7 @@ func TestRunForceReplacesUnmanagedDestination(t *testing.T) {
if _, err := os.Stat(filepath.Join(destinationRoot, "unmanaged.txt")); !os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(destinationRoot, "unmanaged.txt")); !os.IsNotExist(err) {
t.Fatalf("unmanaged file stat error = %v, want not exist", err) t.Fatalf("unmanaged file stat error = %v, want not exist", err)
} }
assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n") testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n")
} }
func TestRunFansOutToLocalDestinations(t *testing.T) { func TestRunFansOutToLocalDestinations(t *testing.T) {
@@ -1151,8 +1151,8 @@ func TestRunFansOutToLocalDestinations(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFile(t, filepath.Join(firstDestination, "daily", "report.md"), "# Report\nSunny.\n") testutil.AssertFile(t, filepath.Join(firstDestination, "daily", "report.md"), "# Report\nSunny.\n")
assertFile(t, filepath.Join(secondDestination, "daily", "summary.txt"), "Summary\n") testutil.AssertFile(t, filepath.Join(secondDestination, "daily", "summary.txt"), "Summary\n")
} }
func TestRunFansOutWithDifferentPublishPolicies(t *testing.T) { func TestRunFansOutWithDifferentPublishPolicies(t *testing.T) {
@@ -1161,16 +1161,16 @@ func TestRunFansOutWithDifferentPublishPolicies(t *testing.T) {
htmlDestination := t.TempDir() htmlDestination := t.TempDir()
writeSourceBundle(t, sourceRoot, "", testBundleOptions{}) writeSourceBundle(t, sourceRoot, "", testBundleOptions{})
err := Run(context.Background(), RunOptions{ConfigPath: writeMixedPolicyFanoutConfig(t, sourceRoot, archiveDestination, htmlDestination)}) err := Run(context.Background(), RunOptions{ConfigPath: testutil.WriteMixedPolicyFanoutLocalConfig(t, sourceRoot, archiveDestination, htmlDestination)})
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFile(t, filepath.Join(archiveDestination, "report.md"), "# Report\nSunny.\n") testutil.AssertFile(t, filepath.Join(archiveDestination, "report.md"), "# Report\nSunny.\n")
assertFile(t, filepath.Join(archiveDestination, "summary.txt"), "Summary\n") testutil.AssertFile(t, filepath.Join(archiveDestination, "summary.txt"), "Summary\n")
if _, err := os.Stat(filepath.Join(archiveDestination, "report.html")); !os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(archiveDestination, "report.html")); !os.IsNotExist(err) {
t.Fatalf("archive report.html stat error = %v, want not exist", err) t.Fatalf("archive report.html stat error = %v, want not exist", err)
} }
assertFileContains(t, filepath.Join(htmlDestination, "report.html"), "<h1>Report</h1>") testutil.AssertFileContains(t, filepath.Join(htmlDestination, "report.html"), "<h1>Report</h1>")
if _, err := os.Stat(filepath.Join(htmlDestination, "report.md")); !os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(htmlDestination, "report.md")); !os.IsNotExist(err) {
t.Fatalf("html report.md stat error = %v, want not exist", err) t.Fatalf("html report.md stat error = %v, want not exist", err)
} }
@@ -1228,10 +1228,10 @@ func TestRunExercisesRemoteBackendShapesThroughCommonPath(t *testing.T) {
if err := runConfigWithBackendFactory(context.Background(), cfg, RunOptions{Stdout: &publishOutput}, provider); err != nil { if err := runConfigWithBackendFactory(context.Background(), cfg, RunOptions{Stdout: &publishOutput}, provider); err != nil {
t.Fatalf("publish error = %v", err) t.Fatalf("publish error = %v", err)
} }
assertFakeFile(t, s3Destination, "report.md", "# Report\nSunny.\n") testutil.AssertFakeFile(t, s3Destination, "report.md", "# Report\nSunny.\n")
assertFakeFile(t, sshDestination, "summary.txt", "Summary\n") testutil.AssertFakeFile(t, sshDestination, "summary.txt", "Summary\n")
assertFile(t, filepath.Join(s3ToLocalDestination, "report.md"), "# Report\nSunny.\n") testutil.AssertFile(t, filepath.Join(s3ToLocalDestination, "report.md"), "# Report\nSunny.\n")
assertFile(t, filepath.Join(sshToLocalDestination, "summary.txt"), "Summary\n") testutil.AssertFile(t, filepath.Join(sshToLocalDestination, "summary.txt"), "Summary\n")
var repeatOutput bytes.Buffer var repeatOutput bytes.Buffer
if err := runConfigWithBackendFactory(context.Background(), cfg, RunOptions{Stdout: &repeatOutput}, provider); err != nil { if err := runConfigWithBackendFactory(context.Background(), cfg, RunOptions{Stdout: &repeatOutput}, provider); err != nil {
@@ -1247,10 +1247,10 @@ func TestRunForceReplacementStaysWithinRemoteBundlePaths(t *testing.T) {
writeSourceBundle(t, localSourceRoot, "bundle", testBundleOptions{}) writeSourceBundle(t, localSourceRoot, "bundle", testBundleOptions{})
s3Destination := fake.New() s3Destination := fake.New()
sshDestination := fake.New() sshDestination := fake.New()
mustWriteFake(t, s3Destination, "bundle/old.txt", "old") testutil.WriteFakeFile(t, s3Destination, "bundle/old.txt", "old")
mustWriteFake(t, s3Destination, "bundle-sibling/keep.txt", "keep") testutil.WriteFakeFile(t, s3Destination, "bundle-sibling/keep.txt", "keep")
mustWriteFake(t, sshDestination, "bundle/old.txt", "old") testutil.WriteFakeFile(t, sshDestination, "bundle/old.txt", "old")
mustWriteFake(t, sshDestination, "bundle-sibling/keep.txt", "keep") testutil.WriteFakeFile(t, sshDestination, "bundle-sibling/keep.txt", "keep")
cfg := config.Config{Pipelines: []config.Pipeline{{ cfg := config.Config{Pipelines: []config.Pipeline{{
ID: "reports", ID: "reports",
Source: config.Backend{Backend: config.BackendLocal, Path: localSourceRoot}, Source: config.Backend{Backend: config.BackendLocal, Path: localSourceRoot},
@@ -1278,12 +1278,12 @@ func TestRunForceReplacementStaysWithinRemoteBundlePaths(t *testing.T) {
if err := runConfigWithBackendFactory(context.Background(), cfg, RunOptions{Force: true}, provider); err != nil { if err := runConfigWithBackendFactory(context.Background(), cfg, RunOptions{Force: true}, provider); err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
assertFakeFile(t, s3Destination, "bundle/report.md", "# Report\nSunny.\n") testutil.AssertFakeFile(t, s3Destination, "bundle/report.md", "# Report\nSunny.\n")
assertFakeMissing(t, s3Destination, "bundle/old.txt") testutil.AssertFakeMissing(t, s3Destination, "bundle/old.txt")
assertFakeFile(t, s3Destination, "bundle-sibling/keep.txt", "keep") testutil.AssertFakeFile(t, s3Destination, "bundle-sibling/keep.txt", "keep")
assertFakeFile(t, sshDestination, "bundle/report.md", "# Report\nSunny.\n") testutil.AssertFakeFile(t, sshDestination, "bundle/report.md", "# Report\nSunny.\n")
assertFakeMissing(t, sshDestination, "bundle/old.txt") testutil.AssertFakeMissing(t, sshDestination, "bundle/old.txt")
assertFakeFile(t, sshDestination, "bundle-sibling/keep.txt", "keep") testutil.AssertFakeFile(t, sshDestination, "bundle-sibling/keep.txt", "keep")
} }
func TestRunDryRunDoesNotWrite(t *testing.T) { func TestRunDryRunDoesNotWrite(t *testing.T) {
@@ -1341,141 +1341,11 @@ func writeLocalConfig(t *testing.T, sourceRoot, destinationRoot string) string {
return testutil.WriteMinimalLocalConfig(t, sourceRoot, destinationRoot) return testutil.WriteMinimalLocalConfig(t, sourceRoot, destinationRoot)
} }
func writeLocalConfigWithPolicy(t *testing.T, sourceRoot, destinationRoot string, publishSource, publishHTML bool) string {
t.Helper()
transformConfig := ""
if publishHTML {
transformConfig = `
transform:
markdown_to_html:
enabled: true
mode: sidecar`
}
return writeConfigFile(t, `
pipelines:
- id: reports
source:
backend: local
path: `+sourceRoot+`
destinations:
- id: archive
backend: local
path: `+destinationRoot+`
publish:
source: `+fmt.Sprintf("%t", publishSource)+`
html: `+fmt.Sprintf("%t", publishHTML)+transformConfig+`
`)
}
func writeLocalConfigWithPathMapping(t *testing.T, sourceRoot, destinationRoot, mode string) string {
t.Helper()
return writeConfigFile(t, `
pipelines:
- id: reports
source:
backend: local
path: `+sourceRoot+`
destinations:
- id: archive
backend: local
path: `+destinationRoot+`
path_mapping:
mode: `+mode+`
`)
}
func writeLocalConfigWithLinks(t *testing.T, sourceRoot, destinationRoot, pathMapping, baseURL, primary string, publishSource, publishHTML bool, transformMode string) string {
t.Helper()
transformConfig := ""
if publishHTML {
transformConfig = `
transform:
markdown_to_html:
enabled: true
mode: ` + transformMode
}
return writeConfigFile(t, `
pipelines:
- id: reports
source:
backend: local
path: `+sourceRoot+`
destinations:
- id: archive
backend: local
path: `+destinationRoot+`
path_mapping:
mode: `+pathMapping+`
links:
base_url: `+baseURL+`
primary: `+primary+`
publish:
source: `+fmt.Sprintf("%t", publishSource)+`
html: `+fmt.Sprintf("%t", publishHTML)+transformConfig+`
`)
}
func writeLocalConfigWithMarkdownTransform(t *testing.T, sourceRoot, destinationRoot string, publishSource, publishHTML bool, mode, input string) string {
t.Helper()
enabled := publishHTML
inputConfig := ""
if input != "" {
inputConfig = `
input: ` + input
}
return writeConfigFile(t, `
pipelines:
- id: reports
source:
backend: local
path: `+sourceRoot+`
destinations:
- id: archive
backend: local
path: `+destinationRoot+`
publish:
source: `+fmt.Sprintf("%t", publishSource)+`
html: `+fmt.Sprintf("%t", publishHTML)+`
transform:
markdown_to_html:
enabled: `+fmt.Sprintf("%t", enabled)+`
mode: `+mode+inputConfig+`
`)
}
func writeFanoutConfig(t *testing.T, sourceRoot, firstDestination, secondDestination string) string { func writeFanoutConfig(t *testing.T, sourceRoot, firstDestination, secondDestination string) string {
t.Helper() t.Helper()
return testutil.WriteFanoutLocalConfig(t, sourceRoot, firstDestination, secondDestination) return testutil.WriteFanoutLocalConfig(t, sourceRoot, firstDestination, secondDestination)
} }
func writeMixedPolicyFanoutConfig(t *testing.T, sourceRoot, archiveDestination, htmlDestination string) string {
t.Helper()
return writeConfigFile(t, `
pipelines:
- id: reports
source:
backend: local
path: `+sourceRoot+`
destinations:
- id: archive
backend: local
path: `+archiveDestination+`
publish:
source: true
html: false
- id: html
backend: local
path: `+htmlDestination+`
publish:
source: false
html: true
transform:
markdown_to_html:
enabled: true
mode: sidecar
`)
}
func writeConfigFile(t *testing.T, body string) string { func writeConfigFile(t *testing.T, body string) string {
t.Helper() t.Helper()
path := filepath.Join(t.TempDir(), "config.yml") path := filepath.Join(t.TempDir(), "config.yml")
@@ -1503,53 +1373,6 @@ func outputsByPath(outputs []state.OutputFile) map[string]state.OutputFile {
return byPath return byPath
} }
func assertFile(t *testing.T, path, want string) {
t.Helper()
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read file %s: %v", path, err)
}
if got := string(data); got != want {
t.Fatalf("%s = %q, want %q", path, got, want)
}
}
func assertFileContains(t *testing.T, path, want string) {
t.Helper()
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read file %s: %v", path, err)
}
if !strings.Contains(string(data), want) {
t.Fatalf("%s = %q, want substring %q", path, data, want)
}
}
func assertFakeFile(t *testing.T, backend *fake.Backend, path, want string) {
t.Helper()
data, err := backend.ReadFile(context.Background(), path)
if err != nil {
t.Fatalf("read fake file %s: %v", path, err)
}
if got := string(data); got != want {
t.Fatalf("%s = %q, want %q", path, got, want)
}
}
func assertFakeMissing(t *testing.T, backend *fake.Backend, path string) {
t.Helper()
if _, err := backend.Stat(context.Background(), path); !storage.IsNotFound(err) {
t.Fatalf("fake file %s stat error = %v, want not found", path, err)
}
}
func mustWriteFake(t *testing.T, backend *fake.Backend, path, data string) {
t.Helper()
if _, err := backend.WriteFile(context.Background(), path, []byte(data), storage.WriteOptions{}); err != nil {
t.Fatalf("write fake file %s: %v", path, err)
}
}
func crossBackendConfig(localSourceRoot, s3ToLocalDestination, sshToLocalDestination string) config.Config { func crossBackendConfig(localSourceRoot, s3ToLocalDestination, sshToLocalDestination string) config.Config {
cfg := config.Config{ cfg := config.Config{
Pipelines: []config.Pipeline{ Pipelines: []config.Pipeline{

View File

@@ -2,13 +2,11 @@ package publish
import ( import (
"context" "context"
"encoding/json"
"strings" "strings"
"testing" "testing"
"gitea.maximumdirect.net/eric/distributor/internal/bundle" "gitea.maximumdirect.net/eric/distributor/internal/bundle"
"gitea.maximumdirect.net/eric/distributor/internal/config" "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/storage/fake"
"gitea.maximumdirect.net/eric/distributor/internal/testutil" "gitea.maximumdirect.net/eric/distributor/internal/testutil"
) )
@@ -25,7 +23,7 @@ func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) {
name: "unmanaged content", name: "unmanaged content",
prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) { prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) {
t.Helper() t.Helper()
writeFakeFile(t, backend, "bundle/old.txt", "old") testutil.WriteFakeFile(t, backend, "bundle/old.txt", "old")
}, },
transfer: defaultTransfer(), transfer: defaultTransfer(),
wantReason: "fail_unmanaged", wantReason: "fail_unmanaged",
@@ -37,7 +35,7 @@ func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) {
t.Helper() t.Helper()
conflict := source conflict := source
conflict.ID = "other.source" conflict.ID = "other.source"
writeFakeDestinationState(t, backend, "bundle", conflict, testutil.DestinationStateOptions{}) testutil.WriteFakeDestinationState(t, backend, "bundle", conflict, testutil.DestinationStateOptions{})
}, },
transfer: conflictReplaceTransfer(), transfer: conflictReplaceTransfer(),
wantReason: "requires --force", wantReason: "requires --force",
@@ -48,7 +46,7 @@ func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) {
prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) { prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) {
t.Helper() t.Helper()
conflict := testutil.ValidManifest(testutil.BundleOptions{Files: []testutil.SourceFile{{Path: "report.md", Data: "# Different\n"}}}) conflict := testutil.ValidManifest(testutil.BundleOptions{Files: []testutil.SourceFile{{Path: "report.md", Data: "# Different\n"}}})
writeFakeDestinationState(t, backend, "bundle", conflict, testutil.DestinationStateOptions{}) testutil.WriteFakeDestinationState(t, backend, "bundle", conflict, testutil.DestinationStateOptions{})
}, },
transfer: conflictReplaceTransfer(), transfer: conflictReplaceTransfer(),
wantReason: "requires --force", wantReason: "requires --force",
@@ -58,7 +56,7 @@ func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) {
name: "pipeline mismatch", name: "pipeline mismatch",
prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) { prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) {
t.Helper() t.Helper()
writeFakeDestinationState(t, backend, "bundle", source, testutil.DestinationStateOptions{PipelineID: "other-pipeline"}) testutil.WriteFakeDestinationState(t, backend, "bundle", source, testutil.DestinationStateOptions{PipelineID: "other-pipeline"})
}, },
transfer: conflictReplaceTransfer(), transfer: conflictReplaceTransfer(),
wantReason: "requires --force", wantReason: "requires --force",
@@ -68,7 +66,7 @@ func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) {
name: "destination mismatch", name: "destination mismatch",
prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) { prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) {
t.Helper() t.Helper()
writeFakeDestinationState(t, backend, "bundle", source, testutil.DestinationStateOptions{DestinationID: "other-destination"}) testutil.WriteFakeDestinationState(t, backend, "bundle", source, testutil.DestinationStateOptions{DestinationID: "other-destination"})
}, },
transfer: conflictReplaceTransfer(), transfer: conflictReplaceTransfer(),
wantReason: "requires --force", wantReason: "requires --force",
@@ -80,7 +78,7 @@ func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) {
t.Helper() t.Helper()
newer := source newer := source
newer.Created = newer.Created.AddDate(0, 0, 1) newer.Created = newer.Created.AddDate(0, 0, 1)
writeFakeDestinationState(t, backend, "bundle", newer, testutil.DestinationStateOptions{}) testutil.WriteFakeDestinationState(t, backend, "bundle", newer, testutil.DestinationStateOptions{})
}, },
transfer: newerReplaceTransfer(), transfer: newerReplaceTransfer(),
wantReason: "requires --force", wantReason: "requires --force",
@@ -121,7 +119,7 @@ func TestBuildRequiresConflictPolicyForStateConflicts(t *testing.T) {
destinationBackend := fake.New() destinationBackend := fake.New()
conflict := sourceBundle.Manifest conflict := sourceBundle.Manifest
conflict.ID = "other.source" conflict.ID = "other.source"
writeFakeDestinationState(t, destinationBackend, "bundle", conflict, testutil.DestinationStateOptions{}) testutil.WriteFakeDestinationState(t, destinationBackend, "bundle", conflict, testutil.DestinationStateOptions{})
req := forceRequest(sourceBackend, destinationBackend, sourceBundle, defaultTransfer()) req := forceRequest(sourceBackend, destinationBackend, sourceBundle, defaultTransfer())
req.Force = true req.Force = true
@@ -135,10 +133,10 @@ func TestExecuteForcedReplacementDeletesOnlyBundlePath(t *testing.T) {
sourceBackend := fake.New() sourceBackend := fake.New()
sourceBundle := testutil.WriteFakeSourceBundle(t, sourceBackend, "bundle", testutil.BundleOptions{}) sourceBundle := testutil.WriteFakeSourceBundle(t, sourceBackend, "bundle", testutil.BundleOptions{})
destinationBackend := fake.New() destinationBackend := fake.New()
writeFakeFile(t, destinationBackend, "bundle/old.txt", "old") testutil.WriteFakeFile(t, destinationBackend, "bundle/old.txt", "old")
writeFakeFile(t, destinationBackend, "bundle/nested/old.txt", "old") testutil.WriteFakeFile(t, destinationBackend, "bundle/nested/old.txt", "old")
writeFakeFile(t, destinationBackend, "bundle-sibling/keep.txt", "keep") testutil.WriteFakeFile(t, destinationBackend, "bundle-sibling/keep.txt", "keep")
writeFakeFile(t, destinationBackend, "outside.txt", "outside") testutil.WriteFakeFile(t, destinationBackend, "outside.txt", "outside")
req := forceRequest(sourceBackend, destinationBackend, sourceBundle, defaultTransfer()) req := forceRequest(sourceBackend, destinationBackend, sourceBundle, defaultTransfer())
req.Force = true req.Force = true
@@ -152,11 +150,11 @@ func TestExecuteForcedReplacementDeletesOnlyBundlePath(t *testing.T) {
if err := Execute(context.Background(), req, plan); err != nil { if err := Execute(context.Background(), req, plan); err != nil {
t.Fatalf("Execute() error = %v", err) t.Fatalf("Execute() error = %v", err)
} }
assertFakeFile(t, destinationBackend, "bundle/report.md", "# Report\nSunny.\n") testutil.AssertFakeFile(t, destinationBackend, "bundle/report.md", "# Report\nSunny.\n")
assertFakeMissing(t, destinationBackend, "bundle/old.txt") testutil.AssertFakeMissing(t, destinationBackend, "bundle/old.txt")
assertFakeMissing(t, destinationBackend, "bundle/nested/old.txt") testutil.AssertFakeMissing(t, destinationBackend, "bundle/nested/old.txt")
assertFakeFile(t, destinationBackend, "bundle-sibling/keep.txt", "keep") testutil.AssertFakeFile(t, destinationBackend, "bundle-sibling/keep.txt", "keep")
assertFakeFile(t, destinationBackend, "outside.txt", "outside") testutil.AssertFakeFile(t, destinationBackend, "outside.txt", "outside")
} }
func forceRequest(sourceBackend, destinationBackend *fake.Backend, sourceBundle bundle.Bundle, transfer config.TransferPolicy) Request { func forceRequest(sourceBackend, destinationBackend *fake.Backend, sourceBundle bundle.Bundle, transfer config.TransferPolicy) Request {
@@ -193,49 +191,3 @@ func newerReplaceTransfer() config.TransferPolicy {
transfer.OnDestinationNewer = config.TransferActionReplace transfer.OnDestinationNewer = config.TransferActionReplace
return transfer return transfer
} }
func writeFakeDestinationState(t *testing.T, backend *fake.Backend, relative string, manifest bundle.Manifest, opts testutil.DestinationStateOptions) {
t.Helper()
destinationState := testutil.DestinationState(manifest, opts)
data, err := json.MarshalIndent(destinationState, "", " ")
if err != nil {
t.Fatalf("marshal destination state: %v", err)
}
statePath, err := storage.StatePath(relative)
if err != nil {
t.Fatalf("state path: %v", err)
}
writeFakeFile(t, backend, statePath, string(append(data, '\n')))
for _, output := range destinationState.Outputs {
path, err := storage.Join(relative, output.Path)
if err != nil {
t.Fatalf("join output path: %v", err)
}
writeFakeFile(t, backend, path, "old")
}
}
func writeFakeFile(t *testing.T, backend *fake.Backend, path, data string) {
t.Helper()
if _, err := backend.WriteFile(context.Background(), path, []byte(data), storage.WriteOptions{}); err != nil {
t.Fatalf("write fake file %s: %v", path, err)
}
}
func assertFakeFile(t *testing.T, backend *fake.Backend, path, want string) {
t.Helper()
data, err := backend.ReadFile(context.Background(), path)
if err != nil {
t.Fatalf("read fake file %s: %v", path, err)
}
if got := string(data); got != want {
t.Fatalf("fake file %s = %q, want %q", path, got, want)
}
}
func assertFakeMissing(t *testing.T, backend *fake.Backend, path string) {
t.Helper()
if _, err := backend.Stat(context.Background(), path); !storage.IsNotFound(err) {
t.Fatalf("fake file %s stat error = %v, want not found", path, err)
}
}

View File

@@ -3,6 +3,7 @@ package testutil
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -106,6 +107,53 @@ func WriteFakeSourceBundle(t testing.TB, backend *fake.Backend, relative string,
return bundle.Bundle{RootRelativePath: relative, Manifest: manifest} return bundle.Bundle{RootRelativePath: relative, Manifest: manifest}
} }
func WriteFakeFile(t testing.TB, backend *fake.Backend, path, data string) {
t.Helper()
if _, err := backend.WriteFile(context.Background(), path, []byte(data), storage.WriteOptions{}); err != nil {
t.Fatalf("write fake file %s: %v", path, err)
}
}
func AssertFakeFile(t testing.TB, backend *fake.Backend, path, want string) {
t.Helper()
data, err := backend.ReadFile(context.Background(), path)
if err != nil {
t.Fatalf("read fake file %s: %v", path, err)
}
if got := string(data); got != want {
t.Fatalf("fake file %s = %q, want %q", path, got, want)
}
}
func AssertFakeMissing(t testing.TB, backend *fake.Backend, path string) {
t.Helper()
if _, err := backend.Stat(context.Background(), path); !storage.IsNotFound(err) {
t.Fatalf("fake file %s stat error = %v, want not found", path, err)
}
}
func WriteFakeDestinationState(t testing.TB, backend *fake.Backend, relative string, manifest bundle.Manifest, opts DestinationStateOptions) state.DistributorState {
t.Helper()
destinationState := DestinationState(manifest, opts)
data, err := json.MarshalIndent(destinationState, "", " ")
if err != nil {
t.Fatalf("marshal destination state: %v", err)
}
statePath, err := storage.StatePath(relative)
if err != nil {
t.Fatalf("state path: %v", err)
}
WriteFakeFile(t, backend, statePath, string(append(data, '\n')))
for _, output := range destinationState.Outputs {
path, err := storage.Join(relative, output.Path)
if err != nil {
t.Fatalf("join output path: %v", err)
}
WriteFakeFile(t, backend, path, "old")
}
return destinationState
}
func WriteMinimalLocalConfig(t testing.TB, sourceRoot, destinationRoot string) string { func WriteMinimalLocalConfig(t testing.TB, sourceRoot, destinationRoot string) string {
t.Helper() t.Helper()
return writeConfigFile(t, ` return writeConfigFile(t, `
@@ -139,6 +187,136 @@ pipelines:
`) `)
} }
func WriteLocalConfigWithPublishPolicy(t testing.TB, sourceRoot, destinationRoot string, publishSource, publishHTML bool) string {
t.Helper()
transformConfig := ""
if publishHTML {
transformConfig = `
transform:
markdown_to_html:
enabled: true
mode: sidecar`
}
return writeConfigFile(t, `
pipelines:
- id: reports
source:
backend: local
path: `+sourceRoot+`
destinations:
- id: archive
backend: local
path: `+destinationRoot+`
publish:
source: `+fmt.Sprintf("%t", publishSource)+`
html: `+fmt.Sprintf("%t", publishHTML)+transformConfig+`
`)
}
func WriteLocalConfigWithPathMapping(t testing.TB, sourceRoot, destinationRoot, mode string) string {
t.Helper()
return writeConfigFile(t, `
pipelines:
- id: reports
source:
backend: local
path: `+sourceRoot+`
destinations:
- id: archive
backend: local
path: `+destinationRoot+`
path_mapping:
mode: `+mode+`
`)
}
func WriteLocalConfigWithLinks(t testing.TB, sourceRoot, destinationRoot, pathMapping, baseURL, primary string, publishSource, publishHTML bool, transformMode string) string {
t.Helper()
transformConfig := ""
if publishHTML {
transformConfig = `
transform:
markdown_to_html:
enabled: true
mode: ` + transformMode
}
return writeConfigFile(t, `
pipelines:
- id: reports
source:
backend: local
path: `+sourceRoot+`
destinations:
- id: archive
backend: local
path: `+destinationRoot+`
path_mapping:
mode: `+pathMapping+`
links:
base_url: `+baseURL+`
primary: `+primary+`
publish:
source: `+fmt.Sprintf("%t", publishSource)+`
html: `+fmt.Sprintf("%t", publishHTML)+transformConfig+`
`)
}
func WriteLocalConfigWithMarkdownTransform(t testing.TB, sourceRoot, destinationRoot string, publishSource, publishHTML bool, mode, input string) string {
t.Helper()
enabled := publishHTML
inputConfig := ""
if input != "" {
inputConfig = `
input: ` + input
}
return writeConfigFile(t, `
pipelines:
- id: reports
source:
backend: local
path: `+sourceRoot+`
destinations:
- id: archive
backend: local
path: `+destinationRoot+`
publish:
source: `+fmt.Sprintf("%t", publishSource)+`
html: `+fmt.Sprintf("%t", publishHTML)+`
transform:
markdown_to_html:
enabled: `+fmt.Sprintf("%t", enabled)+`
mode: `+mode+inputConfig+`
`)
}
func WriteMixedPolicyFanoutLocalConfig(t testing.TB, sourceRoot, archiveDestination, htmlDestination string) string {
t.Helper()
return writeConfigFile(t, `
pipelines:
- id: reports
source:
backend: local
path: `+sourceRoot+`
destinations:
- id: archive
backend: local
path: `+archiveDestination+`
publish:
source: true
html: false
- id: html
backend: local
path: `+htmlDestination+`
publish:
source: false
html: true
transform:
markdown_to_html:
enabled: true
mode: sidecar
`)
}
func WriteDestinationState(t testing.TB, root, relative string, manifest bundle.Manifest, opts DestinationStateOptions) state.DistributorState { func WriteDestinationState(t testing.TB, root, relative string, manifest bundle.Manifest, opts DestinationStateOptions) state.DistributorState {
t.Helper() t.Helper()
bundleRoot := filepath.Join(root, filepath.FromSlash(relative)) bundleRoot := filepath.Join(root, filepath.FromSlash(relative))
@@ -175,6 +353,28 @@ func ReadDestinationState(t testing.TB, path string) state.DistributorState {
return destinationState return destinationState
} }
func AssertFile(t testing.TB, path, want string) {
t.Helper()
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read file %s: %v", path, err)
}
if got := string(data); got != want {
t.Fatalf("%s = %q, want %q", path, got, want)
}
}
func AssertFileContains(t testing.TB, path, want string) {
t.Helper()
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read file %s: %v", path, err)
}
if !strings.Contains(string(data), want) {
t.Fatalf("%s = %q, want substring %q", path, data, want)
}
}
func sourceFiles(opts BundleOptions) []SourceFile { func sourceFiles(opts BundleOptions) []SourceFile {
files := opts.Files files := opts.Files
if files == nil { if files == nil {