Memoize completed native link dependencies

This commit is contained in:
2026-10-25 21:43:26 +00:00
parent 8aba43da38
commit 7bf2f0c687
3 changed files with 235 additions and 51 deletions

View File

@@ -2,6 +2,7 @@ package engine
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
@@ -45,3 +46,147 @@ func TestImportLinkResolutionTracksActiveExpansions(t *testing.T) {
})
}
}
func TestNativeLinkResolutionMemoizesCompletedDependencies(t *testing.T) {
// The small graph stays within host symlink-depth limits while exceeding the
// bounded linear operation allowance without completed-target reuse.
const linkCount = 5
root := t.TempDir()
configurationRoot := filepath.Join(root, "workspace", "config")
for _, directory := range []string{
filepath.Join(configurationRoot, "graph"),
filepath.Join(configurationRoot, "selected"),
} {
if err := os.MkdirAll(directory, 0o700); err != nil {
t.Fatal(err)
}
}
for index := 0; index < linkCount; index++ {
name := fmt.Sprintf("s%02d", index)
target := "."
if index > 0 {
previous := fmt.Sprintf("s%02d", index-1)
target = previous + "/" + previous
}
if err := os.Symlink(target, filepath.Join(configurationRoot, "graph", name)); err != nil {
t.Skipf("symlinks unavailable: %v", err)
}
}
if err := os.WriteFile(filepath.Join(configurationRoot, "graph", "item.yml"), []byte("value: linked\n"), 0o600); err != nil {
t.Fatal(err)
}
if err := os.Symlink(fmt.Sprintf("../graph/s%02d", linkCount-1), filepath.Join(configurationRoot, "selected", "entry")); err != nil {
t.Skipf("symlinks unavailable: %v", err)
}
for _, selector := range []string{"directory", "glob"} {
t.Run(selector, func(t *testing.T) {
base := os.DirFS(root)
if _, ok := base.(fs.ReadLinkFS); !ok {
t.Skip("host directory filesystem does not expose link inspection")
}
filesystem := newCountingReadLinkFS(base, linkCount*20+40)
access := newNativeAccess(filesystem, "workspace/config", &namespaceToken{})
if _, err := access.canonical("workspace/config/selected"); err != nil {
t.Fatalf("selected directory canonicalization failed before selection: %v", err)
}
var diagnostics []Diagnostic
if selector == "directory" {
_, diagnostics = selectDirectoryWithDiagnostics(t, access, "workspace/config", " - dir: selected\n follow: true\n")
} else {
_, diagnostics = selectGlobForTestWithDiagnostics(t, access, "workspace/config", " - glob: 'selected/*/*.yml'\n follow: true\n")
}
if len(diagnostics) != 0 {
t.Fatalf("selection diagnostics = %#v after %d link operations", diagnostics, filesystem.operations)
}
if filesystem.operations == 0 || filesystem.operations > filesystem.limit {
t.Fatalf("link-resolution operations = %d, want 1..%d", filesystem.operations, filesystem.limit)
}
if filesystem.readLinks < linkCount {
t.Fatalf("link reads = %d, want the selector to resolve the repeated dependency graph", filesystem.readLinks)
}
})
}
}
func TestNativeLinkResolutionRejectsEscapesAndSkipsWhenFollowingIsDisabled(t *testing.T) {
root := t.TempDir()
configurationRoot := filepath.Join(root, "workspace", "config")
selected := filepath.Join(configurationRoot, "selected")
if err := os.MkdirAll(selected, 0o700); err != nil {
t.Fatal(err)
}
if err := os.Symlink("../../../outside", filepath.Join(selected, "escape")); err != nil {
t.Skipf("symlinks unavailable: %v", err)
}
base := os.DirFS(root)
if _, ok := base.(fs.ReadLinkFS); !ok {
t.Skip("host directory filesystem does not expose link inspection")
}
filesystem := newCountingReadLinkFS(base, 100)
access := newNativeAccess(filesystem, "workspace/config", &namespaceToken{})
if _, err := resolveNativeImportPath(access, filesystem.links, "workspace/config/selected/escape"); !errors.Is(err, errImportPathOutsideRoot) {
t.Fatalf("escaping native link error = %v, want outside-root rejection", err)
}
filesystem.readLinks = 0
_, diagnostics := selectDirectoryWithDiagnostics(t, access, "workspace/config", " - dir: selected\n follow: false\n")
if len(diagnostics) != 0 || filesystem.readLinks != 0 {
t.Fatalf("follow-disabled selection diagnostics=%#v readlink calls=%d", diagnostics, filesystem.readLinks)
}
}
type countingReadLinkFS struct {
filesystem fs.FS
links fs.ReadLinkFS
operations int
readLinks int
limit int
}
func newCountingReadLinkFS(filesystem fs.FS, limit int) *countingReadLinkFS {
links, ok := filesystem.(fs.ReadLinkFS)
if !ok {
panic("test filesystem does not support link inspection")
}
return &countingReadLinkFS{filesystem: filesystem, links: links, limit: limit}
}
func (f *countingReadLinkFS) Open(name string) (fs.File, error) { return f.filesystem.Open(name) }
func (f *countingReadLinkFS) Stat(name string) (fs.FileInfo, error) {
if statter, ok := f.filesystem.(fs.StatFS); ok {
return statter.Stat(name)
}
return fs.Stat(f.filesystem, name)
}
func (f *countingReadLinkFS) ReadDir(name string) ([]fs.DirEntry, error) {
if reader, ok := f.filesystem.(fs.ReadDirFS); ok {
return reader.ReadDir(name)
}
return fs.ReadDir(f.filesystem, name)
}
func (f *countingReadLinkFS) count() error {
f.operations++
if f.operations > f.limit {
return errors.New("native link operation limit exceeded")
}
return nil
}
func (f *countingReadLinkFS) Lstat(name string) (fs.FileInfo, error) {
if err := f.count(); err != nil {
return nil, err
}
return f.links.Lstat(name)
}
func (f *countingReadLinkFS) ReadLink(name string) (string, error) {
if err := f.count(); err != nil {
return "", err
}
f.readLinks++
return f.links.ReadLink(name)
}