diff options
Diffstat (limited to 'internal/processor')
| -rw-r--r-- | internal/processor/processor.go | 8 | ||||
| -rw-r--r-- | internal/processor/processor_test.go | 79 |
2 files changed, 87 insertions, 0 deletions
diff --git a/internal/processor/processor.go b/internal/processor/processor.go index d781a8b..9cbc5a7 100644 --- a/internal/processor/processor.go +++ b/internal/processor/processor.go @@ -77,8 +77,12 @@ func Run(cfg *config.Config) (int, error) { // claimedByMarkdown scans all .md entries in inputDir and returns a set of // image filenames that are referenced within those markdown files. // Those images should be embedded in the markdown post, not processed alone. +// If two different markdown files claim the same image, an error is returned. func claimedByMarkdown(entries []os.DirEntry, inputDir string) (map[string]bool, error) { claimed := make(map[string]bool) + // owners tracks which markdown file first claimed each image so we can + // detect conflicts before processing begins. + owners := make(map[string]string) for _, entry := range entries { if entry.IsDir() || strings.ToLower(filepath.Ext(entry.Name())) != ".md" { @@ -92,6 +96,10 @@ func claimedByMarkdown(entries []os.DirEntry, inputDir string) (map[string]bool, } for _, imgName := range findLocalImages(string(data), inputDir) { + if owner, exists := owners[imgName]; exists && owner != entry.Name() { + return nil, fmt.Errorf("image %q claimed by both %q and %q", imgName, owner, entry.Name()) + } + owners[imgName] = entry.Name() claimed[imgName] = true } } diff --git a/internal/processor/processor_test.go b/internal/processor/processor_test.go index 3b34675..418fb95 100644 --- a/internal/processor/processor_test.go +++ b/internal/processor/processor_test.go @@ -5,6 +5,7 @@ import ( "image/png" "os" "path/filepath" + "strings" "testing" "time" @@ -236,3 +237,81 @@ text` t.Fatal(err) } } + +func TestRun_twoMarkdownsClaimingSameImageFails(t *testing.T) { + t.Parallel() + + in := t.TempDir() + out := t.TempDir() + + // Shared image in the inbox. + pngPath := filepath.Join(in, "pic.png") + f, err := os.Create(pngPath) + if err != nil { + t.Fatal(err) + } + if err := png.Encode(f, image.NewRGBA(image.Rect(0, 0, 2, 2))); err != nil { + f.Close() + t.Fatal(err) + } + f.Close() + + // Two markdown files both reference the same image. + if err := os.WriteFile(filepath.Join(in, "a.md"), []byte("\n"), 0o644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(in, "b.md"), []byte("\n"), 0o644); err != nil { + t.Fatal(err) + } + + _, err = Run(&config.Config{InputDir: in, OutputDir: out, BaseURL: "https://x"}) + if err == nil { + t.Fatal("expected error when two markdowns claim the same image") + } + if !strings.Contains(err.Error(), "pic.png") { + t.Fatalf("error should mention the conflicting image, got: %v", err) + } + + // Verify that no post directories were created and no source files deleted. + entries, _ := os.ReadDir(filepath.Join(out, "posts")) + if len(entries) != 0 { + t.Fatalf("expected no posts created, got %d", len(entries)) + } + for _, name := range []string{"pic.png", "a.md", "b.md"} { + if _, err := os.Stat(filepath.Join(in, name)); err != nil { + t.Fatalf("source %s should still exist: %v", name, err) + } + } +} + +func TestRun_duplicateImageClaimsInSameMarkdownAllowed(t *testing.T) { + t.Parallel() + + in := t.TempDir() + out := t.TempDir() + + pngPath := filepath.Join(in, "pic.png") + f, err := os.Create(pngPath) + if err != nil { + t.Fatal(err) + } + if err := png.Encode(f, image.NewRGBA(image.Rect(0, 0, 2, 2))); err != nil { + f.Close() + t.Fatal(err) + } + f.Close() + + // Same markdown references the same image twice — should not be treated as a conflict. + md := "\n\n" + if err := os.WriteFile(filepath.Join(in, "post.md"), []byte(md), 0o644); err != nil { + t.Fatal(err) + } + + n, err := Run(&config.Config{InputDir: in, OutputDir: out, BaseURL: "https://x"}) + if err != nil { + t.Fatalf("Run: %v", err) + } + if n != 1 { + t.Fatalf("n=%d; want 1", n) + } +} |
