From 9684ffd37f23e69b40fc8fc7403873908cb3f0b1 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 2 Jun 2026 18:56:08 +0000 Subject: [PATCH] Move reusable test helpers to testutil --- internal/app/run_test.go | 325 ++++++++------------------------- internal/publish/force_test.go | 80 ++------ internal/testutil/fixtures.go | 200 ++++++++++++++++++++ 3 files changed, 290 insertions(+), 315 deletions(-) diff --git a/internal/app/run_test.go b/internal/app/run_test.go index d7ce9f0..0de1525 100644 --- a/internal/app/run_test.go +++ b/internal/app/run_test.go @@ -200,8 +200,8 @@ func TestRunPublishesNewLocalBundle(t *testing.T) { if err != nil { t.Fatalf("Run() error = %v", err) } - 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, "report.md"), "# Report\nSunny.\n") + testutil.AssertFile(t, filepath.Join(destinationRoot, "summary.txt"), "Summary\n") if _, err := os.Stat(filepath.Join(destinationRoot, "manifest.json")); !os.IsNotExist(err) { t.Fatalf("destination manifest stat error = %v, want not exist", err) } @@ -225,11 +225,11 @@ func TestRunExplicitPreserveRelativePathMappingMatchesDefault(t *testing.T) { destinationRoot := t.TempDir() 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 { 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) { 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{}) 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 { 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)}) 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 { 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 { 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) { 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 { t.Fatalf("Run() error = %v", err) } @@ -355,7 +355,7 @@ func TestRunFixedPathDryRunReportsSelection(t *testing.T) { var stdout bytes.Buffer err := Run(context.Background(), RunOptions{ - ConfigPath: writeLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed), + ConfigPath: testutil.WriteLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed), DryRun: true, Stdout: &stdout, }) @@ -391,7 +391,7 @@ func TestRunFixedPathDryRunWarnsForReplacement(t *testing.T) { {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 { 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) } } - 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) { @@ -436,11 +436,11 @@ func TestRunFixedPathReplacesOlderManagedState(t *testing.T) { {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 { 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{ ID: "reports.new", @@ -454,7 +454,7 @@ func TestRunFixedPathReplacesOlderManagedState(t *testing.T) { if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil { 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)) if destinationState.Source.Manifest.ID != "reports.new" { 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 err := Run(context.Background(), RunOptions{ - ConfigPath: writeLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed), + ConfigPath: testutil.WriteLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed), Stdout: &stdout, }) if err != nil { @@ -488,7 +488,7 @@ func TestRunFixedPathSkipsWhenDestinationStateIsNewer(t *testing.T) { if !strings.Contains(stdout.String(), "action=skip_destination_newer") { 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) { @@ -499,7 +499,7 @@ func TestRunFixedPathFailsUnmanagedWithoutForce(t *testing.T) { 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") { t.Fatalf("Run() error = %v, want unmanaged failure", err) } @@ -521,14 +521,14 @@ func TestRunFixedPathForceReplacementStaysWithinDestinationRoot(t *testing.T) { writeSourceBundle(t, sourceRoot, "bundle", testBundleOptions{}) err := Run(context.Background(), RunOptions{ - ConfigPath: writeLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed), + ConfigPath: testutil.WriteLocalConfigWithPathMapping(t, sourceRoot, destinationRoot, config.PathMappingFixed), Force: true, }) if err != nil { t.Fatalf("Run() error = %v", err) } - assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n") - assertFile(t, filepath.Join(parent, "keep.txt"), "keep") + testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n") + testutil.AssertFile(t, filepath.Join(parent, "keep.txt"), "keep") if _, err := os.Stat(filepath.Join(destinationRoot, "unmanaged.txt")); !os.IsNotExist(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 { t.Fatalf("Run() error = %v", err) } - assertFakeFile(t, s3Destination, "report.md", "# Report\nNew.\n") - assertFakeFile(t, s3Destination, "summary.txt", "New summary\n") - assertFakeMissing(t, s3Destination, "new/report.md") - assertFakeFile(t, sshDestination, "report.md", "# Report\nNew.\n") - assertFakeFile(t, sshDestination, "summary.txt", "New summary\n") - assertFakeMissing(t, sshDestination, "new/report.md") + testutil.AssertFakeFile(t, s3Destination, "report.md", "# Report\nNew.\n") + testutil.AssertFakeFile(t, s3Destination, "summary.txt", "New summary\n") + testutil.AssertFakeMissing(t, s3Destination, "new/report.md") + testutil.AssertFakeFile(t, sshDestination, "report.md", "# Report\nNew.\n") + testutil.AssertFakeFile(t, sshDestination, "summary.txt", "New summary\n") + testutil.AssertFakeMissing(t, sshDestination, "new/report.md") } func TestRunNotifiesAfterPublication(t *testing.T) { @@ -629,7 +629,7 @@ func TestRunNotifiesGeneratedOutputMetadata(t *testing.T) { notifier := &recordingNotifier{} err := Run(context.Background(), RunOptions{ - ConfigPath: writeLocalConfigWithPolicy(t, sourceRoot, destinationRoot, false, true), + ConfigPath: testutil.WriteLocalConfigWithPublishPolicy(t, sourceRoot, destinationRoot, false, true), Notifier: notifier, }) if err != nil { @@ -682,7 +682,7 @@ func TestRunJSONIncludesGeneratedOutputMetadata(t *testing.T) { var stdout bytes.Buffer 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, Stdout: &stdout, OutputFormat: OutputFormatJSON, @@ -789,7 +789,7 @@ func TestRunContinuesAfterDestinationFailure(t *testing.T) { 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) { @@ -797,11 +797,11 @@ func TestRunPublishesHTMLOnly(t *testing.T) { destinationRoot := t.TempDir() 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 { t.Fatalf("Run() error = %v", err) } - assertFileContains(t, filepath.Join(destinationRoot, "report.html"), "

Report

") + testutil.AssertFileContains(t, filepath.Join(destinationRoot, "report.html"), "

Report

") if _, err := os.Stat(filepath.Join(destinationRoot, "report.md")); !os.IsNotExist(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"}}, }) - 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 { t.Fatalf("Run() error = %v", err) } - assertFileContains(t, filepath.Join(destinationRoot, "index.html"), "

Report

") + testutil.AssertFileContains(t, filepath.Join(destinationRoot, "index.html"), "

Report

") if _, err := os.Stat(filepath.Join(destinationRoot, "report.html")); !os.IsNotExist(err) { t.Fatalf("report.html stat error = %v, want not exist", err) } @@ -845,11 +845,11 @@ func TestRunPublishesHTMLIndexWithSingleMarkdownFallback(t *testing.T) { destinationRoot := t.TempDir() 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 { t.Fatalf("Run() error = %v", err) } - assertFileContains(t, filepath.Join(destinationRoot, "index.html"), "

Report

") + testutil.AssertFileContains(t, filepath.Join(destinationRoot, "index.html"), "

Report

") } func TestRunFailsIndexModeWithAmbiguousMarkdownInput(t *testing.T) { @@ -859,7 +859,7 @@ func TestRunFailsIndexModeWithAmbiguousMarkdownInput(t *testing.T) { 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") { t.Fatalf("Run() error = %v, want ambiguous input error", err) } @@ -873,13 +873,13 @@ func TestRunPublishesSourceAndHTML(t *testing.T) { destinationRoot := t.TempDir() 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 { t.Fatalf("Run() error = %v", err) } - assertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n") - assertFileContains(t, filepath.Join(destinationRoot, "report.html"), "

Sunny.

") - assertFile(t, filepath.Join(destinationRoot, "summary.txt"), "Summary\n") + testutil.AssertFile(t, filepath.Join(destinationRoot, "report.md"), "# Report\nSunny.\n") + testutil.AssertFileContains(t, filepath.Join(destinationRoot, "report.html"), "

Sunny.

") + testutil.AssertFile(t, filepath.Join(destinationRoot, "summary.txt"), "Summary\n") destinationState := readStateFile(t, filepath.Join(destinationRoot, storage.StateFileName)) if got, want := len(destinationState.Outputs), 3; 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) } - 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 { t.Fatalf("Run() error = %v", err) } @@ -914,7 +914,7 @@ func TestRunFailsOnOutputPathCollision(t *testing.T) { destinationRoot := t.TempDir() writeSourceBundle(t, sourceRoot, "", testBundleOptions{ExtraFiles: []testFile{{Path: "report.html", Data: "

source html

\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") { t.Fatalf("Run() error = %v, want collision", err) } @@ -930,7 +930,7 @@ func TestRunFailsOnIndexOutputPathCollision(t *testing.T) { ExtraFiles: []testFile{{Path: "index.html", Data: "

source index

\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") { t.Fatalf("Run() error = %v, want collision", err) } @@ -946,7 +946,7 @@ func TestRunDryRunReportsGeneratedOutputs(t *testing.T) { var stdout bytes.Buffer err := Run(context.Background(), RunOptions{ - ConfigPath: writeLocalConfigWithPolicy(t, sourceRoot, destinationRoot, false, true), + ConfigPath: testutil.WriteLocalConfigWithPublishPolicy(t, sourceRoot, destinationRoot, false, true), DryRun: true, Stdout: &stdout, }) @@ -965,7 +965,7 @@ func TestRunDryRunReportsIndexOutputWithoutWriting(t *testing.T) { var stdout bytes.Buffer 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, Stdout: &stdout, }) @@ -985,11 +985,11 @@ func TestRunSourceOnlyDoesNotWriteIndexOutput(t *testing.T) { destinationRoot := t.TempDir() 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 { 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) { 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"}, }, }) - 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 { t.Fatalf("first Run() error = %v", err) } - assertFileContains(t, filepath.Join(destinationRoot, "index.html"), "

Old.

") + testutil.AssertFileContains(t, filepath.Join(destinationRoot, "index.html"), "

Old.

") writeSourceBundle(t, sourceRoot, "", testBundleOptions{ 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 { t.Fatalf("second Run() error = %v", err) } - assertFileContains(t, filepath.Join(destinationRoot, "index.html"), "

New.

") + testutil.AssertFileContains(t, filepath.Join(destinationRoot, "index.html"), "

New.

") } func TestRunSkipsWhenDestinationStateMatches(t *testing.T) { @@ -1063,7 +1063,7 @@ func TestRunReplacesOlderDestination(t *testing.T) { if !strings.Contains(stdout.String(), "action=replace_older") { 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) { @@ -1085,7 +1085,7 @@ func TestRunSkipsNewerDestination(t *testing.T) { if !strings.Contains(stdout.String(), "action=skip_destination_newer") { 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) { @@ -1138,7 +1138,7 @@ func TestRunForceReplacesUnmanagedDestination(t *testing.T) { if _, err := os.Stat(filepath.Join(destinationRoot, "unmanaged.txt")); !os.IsNotExist(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) { @@ -1151,8 +1151,8 @@ func TestRunFansOutToLocalDestinations(t *testing.T) { if err != nil { t.Fatalf("Run() error = %v", err) } - 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(firstDestination, "daily", "report.md"), "# Report\nSunny.\n") + testutil.AssertFile(t, filepath.Join(secondDestination, "daily", "summary.txt"), "Summary\n") } func TestRunFansOutWithDifferentPublishPolicies(t *testing.T) { @@ -1161,16 +1161,16 @@ func TestRunFansOutWithDifferentPublishPolicies(t *testing.T) { htmlDestination := t.TempDir() 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 { t.Fatalf("Run() error = %v", err) } - 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, "report.md"), "# Report\nSunny.\n") + testutil.AssertFile(t, filepath.Join(archiveDestination, "summary.txt"), "Summary\n") if _, err := os.Stat(filepath.Join(archiveDestination, "report.html")); !os.IsNotExist(err) { t.Fatalf("archive report.html stat error = %v, want not exist", err) } - assertFileContains(t, filepath.Join(htmlDestination, "report.html"), "

Report

") + testutil.AssertFileContains(t, filepath.Join(htmlDestination, "report.html"), "

Report

") if _, err := os.Stat(filepath.Join(htmlDestination, "report.md")); !os.IsNotExist(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 { t.Fatalf("publish error = %v", err) } - assertFakeFile(t, s3Destination, "report.md", "# Report\nSunny.\n") - assertFakeFile(t, sshDestination, "summary.txt", "Summary\n") - assertFile(t, filepath.Join(s3ToLocalDestination, "report.md"), "# Report\nSunny.\n") - assertFile(t, filepath.Join(sshToLocalDestination, "summary.txt"), "Summary\n") + testutil.AssertFakeFile(t, s3Destination, "report.md", "# Report\nSunny.\n") + testutil.AssertFakeFile(t, sshDestination, "summary.txt", "Summary\n") + testutil.AssertFile(t, filepath.Join(s3ToLocalDestination, "report.md"), "# Report\nSunny.\n") + testutil.AssertFile(t, filepath.Join(sshToLocalDestination, "summary.txt"), "Summary\n") var repeatOutput bytes.Buffer 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{}) s3Destination := fake.New() sshDestination := fake.New() - mustWriteFake(t, s3Destination, "bundle/old.txt", "old") - mustWriteFake(t, s3Destination, "bundle-sibling/keep.txt", "keep") - mustWriteFake(t, sshDestination, "bundle/old.txt", "old") - mustWriteFake(t, sshDestination, "bundle-sibling/keep.txt", "keep") + testutil.WriteFakeFile(t, s3Destination, "bundle/old.txt", "old") + testutil.WriteFakeFile(t, s3Destination, "bundle-sibling/keep.txt", "keep") + testutil.WriteFakeFile(t, sshDestination, "bundle/old.txt", "old") + testutil.WriteFakeFile(t, sshDestination, "bundle-sibling/keep.txt", "keep") cfg := config.Config{Pipelines: []config.Pipeline{{ ID: "reports", 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 { t.Fatalf("Run() error = %v", err) } - assertFakeFile(t, s3Destination, "bundle/report.md", "# Report\nSunny.\n") - assertFakeMissing(t, s3Destination, "bundle/old.txt") - assertFakeFile(t, s3Destination, "bundle-sibling/keep.txt", "keep") - assertFakeFile(t, sshDestination, "bundle/report.md", "# Report\nSunny.\n") - assertFakeMissing(t, sshDestination, "bundle/old.txt") - assertFakeFile(t, sshDestination, "bundle-sibling/keep.txt", "keep") + testutil.AssertFakeFile(t, s3Destination, "bundle/report.md", "# Report\nSunny.\n") + testutil.AssertFakeMissing(t, s3Destination, "bundle/old.txt") + testutil.AssertFakeFile(t, s3Destination, "bundle-sibling/keep.txt", "keep") + testutil.AssertFakeFile(t, sshDestination, "bundle/report.md", "# Report\nSunny.\n") + testutil.AssertFakeMissing(t, sshDestination, "bundle/old.txt") + testutil.AssertFakeFile(t, sshDestination, "bundle-sibling/keep.txt", "keep") } func TestRunDryRunDoesNotWrite(t *testing.T) { @@ -1341,141 +1341,11 @@ func writeLocalConfig(t *testing.T, sourceRoot, destinationRoot string) string { 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 { t.Helper() 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 { t.Helper() path := filepath.Join(t.TempDir(), "config.yml") @@ -1503,53 +1373,6 @@ func outputsByPath(outputs []state.OutputFile) map[string]state.OutputFile { 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 { cfg := config.Config{ Pipelines: []config.Pipeline{ diff --git a/internal/publish/force_test.go b/internal/publish/force_test.go index eaca0e7..7e94813 100644 --- a/internal/publish/force_test.go +++ b/internal/publish/force_test.go @@ -2,13 +2,11 @@ package publish import ( "context" - "encoding/json" "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" ) @@ -25,7 +23,7 @@ func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) { name: "unmanaged content", prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) { t.Helper() - writeFakeFile(t, backend, "bundle/old.txt", "old") + testutil.WriteFakeFile(t, backend, "bundle/old.txt", "old") }, transfer: defaultTransfer(), wantReason: "fail_unmanaged", @@ -37,7 +35,7 @@ func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) { t.Helper() conflict := source conflict.ID = "other.source" - writeFakeDestinationState(t, backend, "bundle", conflict, testutil.DestinationStateOptions{}) + testutil.WriteFakeDestinationState(t, backend, "bundle", conflict, testutil.DestinationStateOptions{}) }, transfer: conflictReplaceTransfer(), wantReason: "requires --force", @@ -48,7 +46,7 @@ func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) { 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"}}}) - writeFakeDestinationState(t, backend, "bundle", conflict, testutil.DestinationStateOptions{}) + testutil.WriteFakeDestinationState(t, backend, "bundle", conflict, testutil.DestinationStateOptions{}) }, transfer: conflictReplaceTransfer(), wantReason: "requires --force", @@ -58,7 +56,7 @@ func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) { name: "pipeline mismatch", prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) { 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(), wantReason: "requires --force", @@ -68,7 +66,7 @@ func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) { name: "destination mismatch", prepare: func(t *testing.T, backend *fake.Backend, source bundle.Manifest) { 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(), wantReason: "requires --force", @@ -80,7 +78,7 @@ func TestBuildPlansForcedReplacementOnlyWhenExplicit(t *testing.T) { t.Helper() newer := source 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(), wantReason: "requires --force", @@ -121,7 +119,7 @@ func TestBuildRequiresConflictPolicyForStateConflicts(t *testing.T) { destinationBackend := fake.New() conflict := sourceBundle.Manifest 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.Force = true @@ -135,10 +133,10 @@ func TestExecuteForcedReplacementDeletesOnlyBundlePath(t *testing.T) { sourceBackend := fake.New() sourceBundle := testutil.WriteFakeSourceBundle(t, sourceBackend, "bundle", testutil.BundleOptions{}) destinationBackend := fake.New() - writeFakeFile(t, destinationBackend, "bundle/old.txt", "old") - writeFakeFile(t, destinationBackend, "bundle/nested/old.txt", "old") - writeFakeFile(t, destinationBackend, "bundle-sibling/keep.txt", "keep") - writeFakeFile(t, destinationBackend, "outside.txt", "outside") + 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 @@ -152,11 +150,11 @@ func TestExecuteForcedReplacementDeletesOnlyBundlePath(t *testing.T) { if err := Execute(context.Background(), req, plan); err != nil { t.Fatalf("Execute() error = %v", err) } - assertFakeFile(t, destinationBackend, "bundle/report.md", "# Report\nSunny.\n") - assertFakeMissing(t, destinationBackend, "bundle/old.txt") - assertFakeMissing(t, destinationBackend, "bundle/nested/old.txt") - assertFakeFile(t, destinationBackend, "bundle-sibling/keep.txt", "keep") - assertFakeFile(t, destinationBackend, "outside.txt", "outside") + 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 { @@ -193,49 +191,3 @@ func newerReplaceTransfer() config.TransferPolicy { transfer.OnDestinationNewer = config.TransferActionReplace 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) - } -} diff --git a/internal/testutil/fixtures.go b/internal/testutil/fixtures.go index 20e4ca2..deb6448 100644 --- a/internal/testutil/fixtures.go +++ b/internal/testutil/fixtures.go @@ -3,6 +3,7 @@ package testutil import ( "context" "encoding/json" + "fmt" "os" "path/filepath" "strings" @@ -106,6 +107,53 @@ func WriteFakeSourceBundle(t testing.TB, backend *fake.Backend, relative string, 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 { t.Helper() 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 { t.Helper() bundleRoot := filepath.Join(root, filepath.FromSlash(relative)) @@ -175,6 +353,28 @@ func ReadDestinationState(t testing.TB, path string) state.DistributorState { 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 { files := opts.Files if files == nil {