resources/page: Fix shared reader in Source.ValueAsOpenReadSeekCloser

Closes #14684

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Joe Mooring
2026-03-28 10:21:06 -07:00
committed by Bjørn Erik Pedersen
parent b55d452e46
commit df520e3150
3 changed files with 64 additions and 1 deletions
+4 -1
View File
@@ -555,7 +555,10 @@ func (s Source) ValueAsString() string {
}
func (s Source) ValueAsOpenReadSeekCloser() hugio.OpenReadSeekCloser {
return hugio.NewOpenReadSeekCloser(hugio.NewReadSeekerNoOpCloserFromString(s.ValueAsString()))
content := s.ValueAsString()
return func() (hugio.ReadSeekCloser, error) {
return hugio.NewReadSeekerNoOpCloserFromString(content), nil
}
}
// FrontMatterOnlyValues holds values that can only be set via front matter.
@@ -14,6 +14,7 @@
package pagemeta_test
import (
"io"
"strings"
"testing"
"time"
@@ -154,6 +155,39 @@ func TestFrontMatterDatesDefaultKeyword(t *testing.T) {
c.Assert(d.PageConfigLate.Dates.ExpiryDate.IsZero(), qt.Equals, true)
}
// Issue 14684
// Each call to the opener must return an independent reader. With the old
// implementation, opener() returned the same shared reader seeked to 0, so a
// second open would reset the position of a reader already in use.
func TestSourceValueAsOpenReadSeekCloserIsIndependent(t *testing.T) {
c := qt.New(t)
s := pagemeta.Source{Value: "abcdefgh"}
opener := s.ValueAsOpenReadSeekCloser()
r1, err := opener()
c.Assert(err, qt.IsNil)
defer r1.Close()
// Partially consume r1.
buf := make([]byte, 4)
_, err = io.ReadFull(r1, buf)
c.Assert(err, qt.IsNil)
c.Assert(string(buf), qt.Equals, "abcd")
// Open a second reader and fully consume it.
r2, err := opener()
c.Assert(err, qt.IsNil)
defer r2.Close()
all, err := io.ReadAll(r2)
c.Assert(err, qt.IsNil)
c.Assert(string(all), qt.Equals, "abcdefgh")
// r1's position must be unaffected; it should yield the remaining half.
rest, err := io.ReadAll(r1)
c.Assert(err, qt.IsNil)
c.Assert(string(rest), qt.Equals, "efgh")
}
func TestContentMediaTypeFromMarkup(t *testing.T) {
c := qt.New(t)
logger := loggers.NewDefault()