github.com/anthropics/anthropic-sdk-go@v1.70.1/internal/filestore/filestore_test.go (about)

     1  //go:build !windows
     2  
     3  package filestore
     4  
     5  // Tests for FileStore — the confined-folder filesystem layer.
     6  
     7  import (
     8  	"archive/zip"
     9  	"bytes"
    10  	"crypto/sha256"
    11  	"encoding/hex"
    12  	"errors"
    13  	"io/fs"
    14  	"maps"
    15  	"os"
    16  	"path/filepath"
    17  	"slices"
    18  	"testing"
    19  	"time"
    20  )
    21  
    22  func sha(s string) string {
    23  	sum := sha256.Sum256([]byte(s))
    24  	return hex.EncodeToString(sum[:])
    25  }
    26  
    27  func mustOpen(t *testing.T, root string, opts ...Option) *FileStore {
    28  	t.Helper()
    29  	store, err := Open(root, opts...)
    30  	if err != nil {
    31  		t.Fatalf("Open: %v", err)
    32  	}
    33  	return store
    34  }
    35  
    36  func createdStore(t *testing.T, root string, opts ...Option) *FileStore {
    37  	t.Helper()
    38  	store := mustOpen(t, root, opts...)
    39  	if err := store.CreateRoot(); err != nil {
    40  		t.Fatalf("CreateRoot: %v", err)
    41  	}
    42  	return store
    43  }
    44  
    45  // newStore opens a FileStore on a fresh root, creates the root, and returns
    46  // the store with the root path.
    47  func newStore(t *testing.T) (*FileStore, string) {
    48  	t.Helper()
    49  	root := filepath.Join(t.TempDir(), "store")
    50  	return createdStore(t, root), root
    51  }
    52  
    53  func put(t *testing.T, store *FileStore, rel, data string) {
    54  	t.Helper()
    55  	if err := store.Put(rel, []byte(data)); err != nil {
    56  		t.Fatalf("Put(%q): %v", rel, err)
    57  	}
    58  }
    59  
    60  func requireRefused(t *testing.T, err error) {
    61  	t.Helper()
    62  	var fse *FileStoreError
    63  	if !errors.As(err, &fse) {
    64  		t.Fatalf("expected *FileStoreError, got %T: %v", err, err)
    65  	}
    66  }
    67  
    68  // requireNotCreated fails the test unless err matches fs.ErrNotExist and dir
    69  // is still absent: the write failed instead of creating dir.
    70  func requireNotCreated(t *testing.T, err error, dir string) {
    71  	t.Helper()
    72  	if !errors.Is(err, fs.ErrNotExist) {
    73  		t.Fatalf("err = %v; want one matching fs.ErrNotExist", err)
    74  	}
    75  	if _, statErr := os.Lstat(dir); !errors.Is(statErr, fs.ErrNotExist) {
    76  		t.Fatalf("%s exists after the failed write (lstat: %v)", dir, statErr)
    77  	}
    78  }
    79  
    80  func TestFileStorePutWritesTextAndCreatesOwnerOnlyParents(t *testing.T) {
    81  	store, root := newStore(t)
    82  	put(t, store, "projects/foo/notes.md", "héllo")
    83  
    84  	dest := filepath.Join(root, "projects", "foo", "notes.md")
    85  	got, err := os.ReadFile(dest)
    86  	if err != nil || string(got) != "héllo" {
    87  		t.Fatalf("read back %q, %v", got, err)
    88  	}
    89  	if st, _ := os.Stat(filepath.Dir(dest)); st.Mode().Perm() != 0o700 {
    90  		t.Fatalf("parent mode = %v, want 0o700", st.Mode().Perm())
    91  	}
    92  	if st, _ := os.Stat(dest); st.Mode().Perm() != 0o600 {
    93  		t.Fatalf("file mode = %v, want 0o600", st.Mode().Perm())
    94  	}
    95  }
    96  
    97  func TestFileStorePutExecutableSetsOwnerExec(t *testing.T) {
    98  	store, root := newStore(t)
    99  	if err := store.Put("run.sh", []byte("#!/bin/sh\necho hi\n"), Executable()); err != nil {
   100  		t.Fatalf("Put: %v", err)
   101  	}
   102  	if st, _ := os.Stat(filepath.Join(root, "run.sh")); st.Mode().Perm() != 0o700 {
   103  		t.Fatalf("mode = %v, want 0o700", st.Mode().Perm())
   104  	}
   105  }
   106  
   107  func TestFileStorePutIsAtomicAndLeavesNoTempOnSuccess(t *testing.T) {
   108  	store, root := newStore(t)
   109  	put(t, store, "a.md", "first")
   110  	put(t, store, "a.md", "second")
   111  
   112  	if got, _ := os.ReadFile(filepath.Join(root, "a.md")); string(got) != "second" {
   113  		t.Fatalf("got %q, want second", got)
   114  	}
   115  	if entries, _ := os.ReadDir(root); len(entries) != 1 || entries[0].Name() != "a.md" {
   116  		t.Fatalf("dir entries = %v", entries)
   117  	}
   118  }
   119  
   120  func TestFileStoreEveryVerbRefusesAnEscapingPath(t *testing.T) {
   121  	store, root := newStore(t)
   122  	secret := filepath.Join(filepath.Dir(root), "secret.md")
   123  	if err := os.WriteFile(secret, []byte("top secret"), 0o644); err != nil {
   124  		t.Fatal(err)
   125  	}
   126  	put(t, store, "ok.md", "ok")
   127  
   128  	for _, bad := range []string{"../secret.md", "a/../../secret.md"} {
   129  		requireRefused(t, store.Put(bad, []byte("nope")))
   130  		_, err := store.Get(bad)
   131  		requireRefused(t, err)
   132  		_, err = store.Ls(bad)
   133  		requireRefused(t, err)
   134  		_, err = store.HashTree(bad)
   135  		requireRefused(t, err)
   136  		_, err = store.HashFile(bad)
   137  		requireRefused(t, err)
   138  		_, err = store.FindSymlinks(bad)
   139  		requireRefused(t, err)
   140  		requireRefused(t, store.Move("ok.md", bad))
   141  		requireRefused(t, store.Move(bad, "landed.md"))
   142  		requireRefused(t, store.Remove(bad))
   143  	}
   144  
   145  	if got, _ := os.ReadFile(secret); string(got) != "top secret" {
   146  		t.Fatalf("secret was modified: %q", got)
   147  	}
   148  	// Nothing materialised outside the root either — not even a temp file.
   149  	entries, _ := os.ReadDir(filepath.Dir(root))
   150  	names := make([]string, len(entries))
   151  	for i, e := range entries {
   152  		names[i] = e.Name()
   153  	}
   154  	if !slices.Equal(names, []string{"secret.md", "store"}) {
   155  		t.Fatalf("outside the root: %v", names)
   156  	}
   157  }
   158  
   159  func TestFileStoreGetDoesNotFollowASymlinkLeaf(t *testing.T) {
   160  	store, root := newStore(t)
   161  	put(t, store, "real.md", "original")
   162  	if err := os.Symlink(filepath.Join(root, "real.md"), filepath.Join(root, "link.md")); err != nil {
   163  		t.Fatal(err)
   164  	}
   165  
   166  	_, err := store.Get("link.md")
   167  	requireRefused(t, err)
   168  	// Put replaces the symlink atomically rather than following it.
   169  	put(t, store, "link.md", "new")
   170  	if got, _ := os.ReadFile(filepath.Join(root, "real.md")); string(got) != "original" {
   171  		t.Fatalf("real.md was overwritten: %q", got)
   172  	}
   173  	if st, _ := os.Lstat(filepath.Join(root, "link.md")); st.Mode()&fs.ModeSymlink != 0 {
   174  		t.Fatal("link.md is still a symlink")
   175  	}
   176  	if got, _ := os.ReadFile(filepath.Join(root, "link.md")); string(got) != "new" {
   177  		t.Fatalf("link.md = %q", got)
   178  	}
   179  }
   180  
   181  func TestFileStoreGetReturnsNilWhenAbsent(t *testing.T) {
   182  	store, _ := newStore(t)
   183  	got, err := store.Get("absent.md")
   184  	if got != nil || err != nil {
   185  		t.Fatalf("absent: got %v, %v", got, err)
   186  	}
   187  	put(t, store, "present.md", "here")
   188  	got, err = store.Get("present.md")
   189  	if err != nil || string(got) != "here" {
   190  		t.Fatalf("present: got %q, %v", got, err)
   191  	}
   192  }
   193  
   194  func TestFileStoreLsListsRecursivelyAndNeverDescendsSymlinks(t *testing.T) {
   195  	store, root := newStore(t)
   196  	put(t, store, "a.md", "alpha")
   197  	put(t, store, "sub/b.md", "bravo")
   198  	outside := filepath.Join(filepath.Dir(root), "outside")
   199  	if err := os.Mkdir(outside, 0o755); err != nil {
   200  		t.Fatal(err)
   201  	}
   202  	if err := os.WriteFile(filepath.Join(outside, "s.md"), []byte("secret"), 0o644); err != nil {
   203  		t.Fatal(err)
   204  	}
   205  	if err := os.Symlink(outside, filepath.Join(root, "lnkdir")); err != nil {
   206  		t.Fatal(err)
   207  	}
   208  	if err := os.Symlink(filepath.Join(outside, "s.md"), filepath.Join(root, "lnk.md")); err != nil {
   209  		t.Fatal(err)
   210  	}
   211  
   212  	want := map[string]bool{"a.md": true, "sub/b.md": true}
   213  	if got, err := store.Ls("/"); err != nil || !maps.Equal(got, want) {
   214  		t.Fatalf("Ls(/) = %v, %v; want %v", got, err, want)
   215  	}
   216  	if got, err := store.Ls("sub"); err != nil || !maps.Equal(got, map[string]bool{"sub/b.md": true}) {
   217  		t.Fatalf("Ls(sub) = %v, %v", got, err)
   218  	}
   219  	if got, err := store.Ls("absent"); err != nil || len(got) != 0 {
   220  		t.Fatalf("Ls(absent) = %v, %v; want empty map", got, err)
   221  	}
   222  	if got, err := store.Ls("a.md/under"); err != nil || len(got) != 0 {
   223  		t.Fatalf("Ls(a.md/under) = %v, %v; want empty map", got, err)
   224  	}
   225  	_, err := store.Ls("a.md")
   226  	requireRefused(t, err)
   227  }
   228  
   229  func TestFileStoreFindSymlinksReportsEverySymlink(t *testing.T) {
   230  	store, root := newStore(t)
   231  	put(t, store, "a.md", "alpha")
   232  	put(t, store, "sub/b.md", "bravo")
   233  	if got, err := store.FindSymlinks("/"); err != nil || len(got) != 0 {
   234  		t.Fatalf("FindSymlinks on clean store = %v, %v; want empty map", got, err)
   235  	}
   236  
   237  	outside := filepath.Join(filepath.Dir(root), "outside")
   238  	if err := os.Mkdir(outside, 0o755); err != nil {
   239  		t.Fatal(err)
   240  	}
   241  	if err := os.WriteFile(filepath.Join(outside, "s.md"), []byte("secret"), 0o644); err != nil {
   242  		t.Fatal(err)
   243  	}
   244  	if err := os.Symlink(outside, filepath.Join(root, "lnkdir")); err != nil {
   245  		t.Fatal(err)
   246  	}
   247  	if err := os.Symlink(filepath.Join(outside, "s.md"), filepath.Join(root, "sub", "lnk.md")); err != nil {
   248  		t.Fatal(err)
   249  	}
   250  	if err := os.Symlink(filepath.Join(root, "gone.md"), filepath.Join(root, "dangling.md")); err != nil {
   251  		t.Fatal(err)
   252  	}
   253  
   254  	want := map[string]bool{"lnkdir": true, "sub/lnk.md": true, "dangling.md": true}
   255  	if got, err := store.FindSymlinks("/"); err != nil || !maps.Equal(got, want) {
   256  		t.Fatalf("FindSymlinks(/) = %v, %v; want %v", got, err, want)
   257  	}
   258  	if got, err := store.FindSymlinks("sub"); err != nil || !maps.Equal(got, map[string]bool{"sub/lnk.md": true}) {
   259  		t.Fatalf("FindSymlinks(sub) = %v, %v", got, err)
   260  	}
   261  	if got, err := store.FindSymlinks("absent"); err != nil || len(got) != 0 {
   262  		t.Fatalf("FindSymlinks(absent) = %v, %v; want empty map", got, err)
   263  	}
   264  	// The base itself being a symlink is reported, not descended.
   265  	if got, err := store.FindSymlinks("lnkdir"); err != nil || !maps.Equal(got, map[string]bool{"lnkdir": true}) {
   266  		t.Fatalf("FindSymlinks(lnkdir) = %v, %v", got, err)
   267  	}
   268  	// A regular-file base is refused like any non-directory.
   269  	_, err := store.FindSymlinks("a.md")
   270  	requireRefused(t, err)
   271  }
   272  
   273  func TestFileStoreHashTreeHashesRegularFilesAndSkipsSymlinks(t *testing.T) {
   274  	store, root := newStore(t)
   275  	put(t, store, "a.md", "alpha")
   276  	put(t, store, "sub/b.md", "bravo")
   277  	outside := filepath.Join(filepath.Dir(root), "secret.txt")
   278  	_ = os.WriteFile(outside, []byte("secret"), 0o644)
   279  	_ = os.Symlink(outside, filepath.Join(root, "link.md"))
   280  
   281  	want := map[string]string{"a.md": sha("alpha"), "sub/b.md": sha("bravo")}
   282  	if got, err := store.HashTree("/"); err != nil || !maps.Equal(got, want) {
   283  		t.Fatalf("HashTree(/) = %v, %v; want %v", got, err, want)
   284  	}
   285  	wantSub := map[string]string{"sub/b.md": sha("bravo")}
   286  	if got, err := store.HashTree("sub/"); err != nil || !maps.Equal(got, wantSub) {
   287  		t.Fatalf("HashTree(sub/) = %v, %v; want %v", got, err, wantSub)
   288  	}
   289  }
   290  
   291  func TestFileStoreListingsErrorOnAnUnreadableSubdirectory(t *testing.T) {
   292  	// An unreadable subdirectory fails the listing rather than dropping out
   293  	// of it — a caller diffing listings would otherwise read its files as gone.
   294  	if os.Geteuid() == 0 {
   295  		t.Skip("permission checks do not bind root")
   296  	}
   297  	store, root := newStore(t)
   298  	put(t, store, "a.md", "alpha")
   299  	put(t, store, "locked/b.md", "bravo")
   300  	locked := filepath.Join(root, "locked")
   301  	if err := os.Chmod(locked, 0o000); err != nil {
   302  		t.Fatal(err)
   303  	}
   304  	defer os.Chmod(locked, 0o700)
   305  
   306  	if _, err := store.HashTree("/"); !errors.Is(err, fs.ErrPermission) {
   307  		t.Fatalf("HashTree = %v; want a permission error", err)
   308  	}
   309  	if _, err := store.Ls("/"); !errors.Is(err, fs.ErrPermission) {
   310  		t.Fatalf("Ls = %v; want a permission error", err)
   311  	}
   312  	if _, err := store.FindSymlinks("/"); !errors.Is(err, fs.ErrPermission) {
   313  		t.Fatalf("FindSymlinks = %v; want a permission error", err)
   314  	}
   315  
   316  	if err := os.Chmod(locked, 0o700); err != nil {
   317  		t.Fatal(err)
   318  	}
   319  	want := map[string]string{"a.md": sha("alpha"), "locked/b.md": sha("bravo")}
   320  	if got, err := store.HashTree("/"); err != nil || !maps.Equal(got, want) {
   321  		t.Fatalf("HashTree(/) after chmod = %v, %v; want %v", got, err, want)
   322  	}
   323  }
   324  
   325  func TestFileStoreHashTreeRefusesASymlinkedPrefix(t *testing.T) {
   326  	// A prefix that exists but is a symlink is refused rather than listed
   327  	// (or silently empty); a plain file prefix is refused too.
   328  	store, root := newStore(t)
   329  	put(t, store, "real/a.md", "alpha")
   330  	if err := os.Symlink(filepath.Join(root, "real"), filepath.Join(root, "alias")); err != nil {
   331  		t.Fatal(err)
   332  	}
   333  
   334  	_, err := store.HashTree("alias")
   335  	requireRefused(t, err)
   336  	_, err = store.HashTree("real/a.md")
   337  	requireRefused(t, err)
   338  	if got, err := store.HashTree("absent"); err != nil || len(got) != 0 {
   339  		t.Fatalf("HashTree(absent) = %v, %v; want empty map", got, err)
   340  	}
   341  }
   342  
   343  func TestFileStoreHashFileHashesOneFileAndSharesTheCache(t *testing.T) {
   344  	store, root := newStore(t)
   345  	realMargin := timestampTrustMarginNs
   346  	timestampTrustMarginNs = -1_000_000_000_000_000
   347  	t.Cleanup(func() { timestampTrustMarginNs = realMargin })
   348  	hashed := countHashes(t)
   349  
   350  	put(t, store, "a.md", "alpha")
   351  	if got, err := store.HashFile("a.md"); err != nil || got != sha("alpha") {
   352  		t.Fatalf("HashFile(a.md) = %q, %v", got, err)
   353  	}
   354  	if got, err := store.HashFile("absent.md"); err != nil || got != "" {
   355  		t.Fatalf("HashFile(absent.md) = %q, %v; want empty", got, err)
   356  	}
   357  	// HashTree reuses the entry HashFile just cached.
   358  	want := map[string]string{"a.md": sha("alpha")}
   359  	if got, err := store.HashTree("/"); err != nil || !maps.Equal(got, want) {
   360  		t.Fatalf("HashTree = %v, %v; want %v", got, err, want)
   361  	}
   362  	if !slices.Equal(*hashed, []string{"a.md"}) {
   363  		t.Fatalf("hashed %v, want one hash shared across both verbs", *hashed)
   364  	}
   365  
   366  	if err := os.Symlink(filepath.Join(root, "a.md"), filepath.Join(root, "lnk.md")); err != nil {
   367  		t.Fatal(err)
   368  	}
   369  	_, err := store.HashFile("lnk.md")
   370  	requireRefused(t, err)
   371  	var fse *FileStoreError
   372  	if !errors.As(err, &fse) || fse.Reason != ReasonIsASymlink {
   373  		t.Fatalf("HashFile(lnk.md) = %v; want an is-a-symlink refusal", err)
   374  	}
   375  }
   376  
   377  func TestFileStoreMoveRenamesWithinRootAndRefusesExistingDest(t *testing.T) {
   378  	store, _ := newStore(t)
   379  	put(t, store, "a.md", "alpha")
   380  	if err := store.Move("a.md", "nested/b.md"); err != nil {
   381  		t.Fatalf("Move: %v", err)
   382  	}
   383  	if got, _ := store.Get("nested/b.md"); string(got) != "alpha" {
   384  		t.Fatalf("Get nested/b.md = %q", got)
   385  	}
   386  	if got, _ := store.Get("a.md"); got != nil {
   387  		t.Fatal("a.md still present")
   388  	}
   389  	put(t, store, "c.md", "charlie")
   390  	requireRefused(t, store.Move("nested/b.md", "c.md"))
   391  }
   392  
   393  func TestFileStoreRootPathIsBannedFromTheInterface(t *testing.T) {
   394  	// A rel resolving to the root: Put/Get refuse, Move/Remove do nothing.
   395  	store, _ := newStore(t)
   396  	put(t, store, "x.md", "x")
   397  
   398  	if err := store.Remove("/"); err != nil {
   399  		t.Fatalf("Remove(/): %v", err)
   400  	}
   401  	if got, err := store.Get("x.md"); err != nil || string(got) != "x" {
   402  		t.Fatalf("x.md after Remove(/) = %q, %v", got, err)
   403  	}
   404  	if err := store.Move("/", "elsewhere"); err != nil {
   405  		t.Fatalf("Move(/, elsewhere): %v", err)
   406  	}
   407  	if err := store.Move("x.md", "/"); err != nil {
   408  		t.Fatalf("Move(x.md, /): %v", err)
   409  	}
   410  	if got, err := store.Ls("/"); err != nil || !maps.Equal(got, map[string]bool{"x.md": true}) {
   411  		t.Fatalf("Ls = %v, %v; want only x.md", got, err)
   412  	}
   413  
   414  	requireRefused(t, store.Put("/", []byte("data")))
   415  	_, err := store.Get("/")
   416  	requireRefused(t, err)
   417  	var fse *FileStoreError
   418  	if !errors.As(err, &fse) || fse.Reason != ReasonNotAFile {
   419  		t.Fatalf("Get(/) = %v; want a not-a-regular-file refusal", err)
   420  	}
   421  }
   422  
   423  func TestFileStoreRemoveDeletesFileAndSubtree(t *testing.T) {
   424  	store, root := newStore(t)
   425  	put(t, store, "a.md", "a")
   426  	put(t, store, "sub/b.md", "b")
   427  
   428  	if err := store.Remove("a.md"); err != nil {
   429  		t.Fatal(err)
   430  	}
   431  	if _, err := os.Stat(filepath.Join(root, "a.md")); !errors.Is(err, fs.ErrNotExist) {
   432  		t.Fatal("a.md still exists")
   433  	}
   434  	if err := store.Remove("sub"); err != nil {
   435  		t.Fatal(err)
   436  	}
   437  	if _, err := os.Stat(filepath.Join(root, "sub")); !errors.Is(err, fs.ErrNotExist) {
   438  		t.Fatal("sub/ still exists")
   439  	}
   440  	if err := store.Remove("absent.md"); err != nil {
   441  		t.Fatalf("Remove(absent.md): %v", err)
   442  	}
   443  }
   444  
   445  func TestFileStoreRemoveUnlinksADanglingSymlink(t *testing.T) {
   446  	store, root := newStore(t)
   447  	if err := os.Symlink(filepath.Join(root, "nowhere"), filepath.Join(root, "dangle")); err != nil {
   448  		t.Fatal(err)
   449  	}
   450  
   451  	if err := store.Remove("dangle"); err != nil {
   452  		t.Fatalf("Remove: %v", err)
   453  	}
   454  	if _, err := os.Lstat(filepath.Join(root, "dangle")); !errors.Is(err, fs.ErrNotExist) {
   455  		t.Fatalf("dangling symlink still present: %v", err)
   456  	}
   457  }
   458  
   459  func TestFileStoreOpenErrorsOnAnUnreadableMountPath(t *testing.T) {
   460  	// A mount path the process cannot stat errors at Open — it is never read
   461  	// as absent, which would mark a real directory ours to remove on Dispose.
   462  	if os.Geteuid() == 0 {
   463  		t.Skip("permission checks do not bind root")
   464  	}
   465  	locked := filepath.Join(t.TempDir(), "locked")
   466  	if err := os.MkdirAll(filepath.Join(locked, "store"), 0o755); err != nil {
   467  		t.Fatal(err)
   468  	}
   469  	if err := os.Chmod(locked, 0o000); err != nil {
   470  		t.Fatal(err)
   471  	}
   472  	defer os.Chmod(locked, 0o700)
   473  
   474  	_, err := Open(filepath.Join(locked, "store"))
   475  	if !errors.Is(err, fs.ErrPermission) {
   476  		t.Fatalf("Open = %v; want a permission error", err)
   477  	}
   478  	var fse *FileStoreError
   479  	if errors.As(err, &fse) {
   480  		t.Fatalf("Open = %v; a deployment failure must not be a *FileStoreError", err)
   481  	}
   482  }
   483  
   484  func TestFileStoreDisposeRemovesRootOnlyIfCreated(t *testing.T) {
   485  	store, fresh := newStore(t)
   486  	put(t, store, "x.md", "x")
   487  	if r := store.Root(); !r.RemovedOnDispose || r.Path != fresh {
   488  		t.Fatalf("Root = %+v", r)
   489  	}
   490  	if err := store.Dispose(); err != nil {
   491  		t.Fatal(err)
   492  	}
   493  	if _, err := os.Stat(fresh); !errors.Is(err, fs.ErrNotExist) {
   494  		t.Fatalf("root %s not removed", fresh)
   495  	}
   496  
   497  	old := filepath.Join(t.TempDir(), "old")
   498  	if err := os.Mkdir(old, 0o755); err != nil {
   499  		t.Fatal(err)
   500  	}
   501  	store = mustOpen(t, old)
   502  	put(t, store, "y.md", "y")
   503  	if r := store.Root(); r.RemovedOnDispose {
   504  		t.Fatal("Root marked a pre-existing root for removal")
   505  	}
   506  	if err := store.Dispose(); err != nil {
   507  		t.Fatal(err)
   508  	}
   509  	if got, err := os.ReadFile(filepath.Join(old, "y.md")); err != nil || string(got) != "y" {
   510  		t.Fatalf("y.md = %q, %v", got, err)
   511  	}
   512  }
   513  
   514  func TestFileStorePutWritesArchiveBytesVerbatim(t *testing.T) {
   515  	// Archive-shaped bytes are just bytes — never extracted.
   516  	store, _ := newStore(t)
   517  	var buf bytes.Buffer
   518  	zw := zip.NewWriter(&buf)
   519  	w, _ := zw.Create("pdf/SKILL.md")
   520  	_, _ = w.Write([]byte("# pdf"))
   521  	_ = zw.Close()
   522  	blob := buf.Bytes()
   523  
   524  	if err := store.Put("bundle.zip", blob); err != nil {
   525  		t.Fatalf("Put: %v", err)
   526  	}
   527  	got, err := store.Get("bundle.zip")
   528  	if err != nil || !bytes.Equal(got, blob) {
   529  		t.Fatalf("bundle.zip is not byte-identical to the input: %v", err)
   530  	}
   531  	want := map[string]string{"bundle.zip": sha(string(blob))}
   532  	if tree, _ := store.HashTree("/"); !maps.Equal(tree, want) {
   533  		t.Fatalf("HashTree = %v, want %v", tree, want)
   534  	}
   535  }
   536  
   537  func TestFileStorePutRefusesDirectoryShapedPaths(t *testing.T) {
   538  	store, _ := newStore(t)
   539  	for _, dir := range []string{"bundles/v1/", "dir/.", ".", "/", ""} {
   540  		requireRefused(t, store.Put(dir, []byte("data")))
   541  	}
   542  	if got, err := store.HashTree("/"); err != nil || len(got) != 0 {
   543  		t.Fatalf("HashTree = %v, %v; want nothing written", got, err)
   544  	}
   545  }
   546  
   547  func TestFileStoreUTF8RestrictionRefusesBinaryContent(t *testing.T) {
   548  	root := filepath.Join(t.TempDir(), "store")
   549  	store := createdStore(t, root, WithUTF8())
   550  	binary := []byte{0xff, 0xfe, 0x00, ' ', 'n', 'o', 't', ' ', 'u', 't', 'f', '8'}
   551  
   552  	// A Put of invalid bytes is refused and writes nothing.
   553  	requireRefused(t, store.Put("blob.bin", binary))
   554  	if _, err := os.Stat(filepath.Join(root, "blob.bin")); !errors.Is(err, fs.ErrNotExist) {
   555  		t.Fatal("blob.bin materialised despite the refusal")
   556  	}
   557  
   558  	// Conforming content is unaffected.
   559  	put(t, store, "note.md", "héllo")
   560  	if got, err := store.Get("note.md"); err != nil || string(got) != "héllo" {
   561  		t.Fatalf("Get note.md = %q, %v", got, err)
   562  	}
   563  
   564  	// A file smuggled in behind the store's back (written directly to disk)
   565  	// is refused at Get.
   566  	if err := os.WriteFile(filepath.Join(root, "smuggled.bin"), binary, 0o600); err != nil {
   567  		t.Fatal(err)
   568  	}
   569  	_, err := store.Get("smuggled.bin")
   570  	requireRefused(t, err)
   571  
   572  	// An unrestricted store keeps returning raw bytes.
   573  	loose := createdStore(t, filepath.Join(t.TempDir(), "store2"))
   574  	if err := loose.Put("blob.bin", binary); err != nil {
   575  		t.Fatalf("Put on unrestricted store: %v", err)
   576  	}
   577  	if got, err := loose.Get("blob.bin"); err != nil || !bytes.Equal(got, binary) {
   578  		t.Fatalf("Get blob.bin = %q, %v", got, err)
   579  	}
   580  }
   581  
   582  func TestFileStoreIsPathLegal(t *testing.T) {
   583  	for path, want := range map[string]bool{
   584  		"/mnt/memory/notes":     true,
   585  		"mnt/relative":          false,
   586  		"/mnt/../../etc/cron.d": false,
   587  		"":                      false,
   588  	} {
   589  		if got := IsPathLegal(path); got != want {
   590  			t.Errorf("IsPathLegal(%q) = %v, want %v", path, got, want)
   591  		}
   592  	}
   593  }
   594  
   595  // countHashes replaces hashFile with a counting wrapper for the duration of
   596  // the test and returns the slice of hashed base names.
   597  func countHashes(t *testing.T) *[]string {
   598  	t.Helper()
   599  	var hashed []string
   600  	realHash := hashFile
   601  	hashFile = func(path string) (string, error) {
   602  		hashed = append(hashed, filepath.Base(path))
   603  		return realHash(path)
   604  	}
   605  	t.Cleanup(func() { hashFile = realHash })
   606  	return &hashed
   607  }
   608  
   609  func TestFileStoreHashTreeRehashesOnlyWhatChanged(t *testing.T) {
   610  	// A file whose stat identity (mtime, ctime, size) is unchanged since the
   611  	// last walk is served from the cache; a changed file is re-read. The
   612  	// trust margin is disabled here so fresh files cache immediately; a
   613  	// writer that restores mtime and size (rsync -t style) is still
   614  	// re-hashed, because userspace cannot restore ctime.
   615  	store, root := newStore(t)
   616  	realMargin := timestampTrustMarginNs
   617  	timestampTrustMarginNs = -1_000_000_000_000_000
   618  	t.Cleanup(func() { timestampTrustMarginNs = realMargin })
   619  	hashed := countHashes(t)
   620  
   621  	put(t, store, "a.md", "alpha")
   622  	put(t, store, "b.md", "bravo")
   623  	want := map[string]string{"a.md": sha("alpha"), "b.md": sha("bravo")}
   624  	if got, err := store.HashTree("/"); err != nil || !maps.Equal(got, want) {
   625  		t.Fatalf("HashTree = %v, %v; want %v", got, err, want)
   626  	}
   627  	slices.Sort(*hashed)
   628  	if !slices.Equal(*hashed, []string{"a.md", "b.md"}) {
   629  		t.Fatalf("first walk hashed %v, want both files", *hashed)
   630  	}
   631  
   632  	*hashed = nil
   633  	if got, err := store.HashTree("/"); err != nil || !maps.Equal(got, want) {
   634  		t.Fatalf("HashTree = %v, %v; want %v", got, err, want)
   635  	}
   636  	if len(*hashed) != 0 {
   637  		t.Fatalf("unchanged walk hashed %v, want nothing", *hashed)
   638  	}
   639  
   640  	// An mtime-and-size-preserving rewrite: same length, mtime restored. The
   641  	// sleep guarantees the rewrite's ctime lands on a later coarse-clock tick
   642  	// than the original's — ctime is the one stamp the rewrite cannot restore.
   643  	time.Sleep(50 * time.Millisecond)
   644  	b := filepath.Join(root, "b.md")
   645  	st, err := os.Stat(b)
   646  	if err != nil {
   647  		t.Fatal(err)
   648  	}
   649  	if err := os.WriteFile(b, []byte("BRAVO"), 0o600); err != nil {
   650  		t.Fatal(err)
   651  	}
   652  	if err := os.Chtimes(b, st.ModTime(), st.ModTime()); err != nil {
   653  		t.Fatal(err)
   654  	}
   655  	got, err := store.HashTree("/")
   656  	if err != nil || got["b.md"] != sha("BRAVO") {
   657  		t.Fatalf("HashTree after rewrite = %v, %v; want b.md re-read", got, err)
   658  	}
   659  	if !slices.Equal(*hashed, []string{"b.md"}) {
   660  		t.Fatalf("rewrite walk hashed %v, want only b.md", *hashed)
   661  	}
   662  }
   663  
   664  func TestFileStoreHashTreeDistrustsFreshStamps(t *testing.T) {
   665  	// Filesystems stamp times with coarse clocks, so a same-tick rewrite can
   666  	// reuse the exact stat identity of the version just hashed. A file whose
   667  	// stamps are within the trust margin of the walk is never cached —
   668  	// pinned by freezing the walk clock at the file's own stamp time.
   669  	store, root := newStore(t)
   670  	hashed := countHashes(t)
   671  
   672  	put(t, store, "racy.md", "AAAAAA")
   673  	racy := filepath.Join(root, "racy.md")
   674  	st, err := os.Lstat(racy)
   675  	if err != nil {
   676  		t.Fatal(err)
   677  	}
   678  	stamp := ctimeNs(st)
   679  	realNow := nowNs
   680  	nowNs = func() int64 { return stamp }
   681  	t.Cleanup(func() { nowNs = realNow })
   682  
   683  	if got, err := store.HashTree("/"); err != nil || got["racy.md"] != sha("AAAAAA") {
   684  		t.Fatalf("HashTree = %v, %v", got, err)
   685  	}
   686  	if got, err := store.HashTree("/"); err != nil || got["racy.md"] != sha("AAAAAA") {
   687  		t.Fatalf("HashTree = %v, %v", got, err)
   688  	}
   689  	// Both walks hashed: nothing this fresh is ever cached.
   690  	if !slices.Equal(*hashed, []string{"racy.md", "racy.md"}) {
   691  		t.Fatalf("hashed = %v, want both walks to hash", *hashed)
   692  	}
   693  
   694  	// What the margin defends: a same-tick rewrite that preserves the whole
   695  	// stat identity must be seen by the next walk.
   696  	if err := os.WriteFile(racy, []byte("BBBBBB"), 0o600); err != nil {
   697  		t.Fatal(err)
   698  	}
   699  	if got, err := store.HashTree("/"); err != nil || got["racy.md"] != sha("BBBBBB") {
   700  		t.Fatalf("HashTree after rewrite = %v, %v; want the new bytes", got, err)
   701  	}
   702  
   703  	// The margin's width is part of the contract: stamps 1.5s old are still
   704  	// distrusted (coarse filesystems stamp in whole seconds); 10s-old stamps
   705  	// are safely past it.
   706  	*hashed = nil
   707  	st, err = os.Lstat(racy)
   708  	if err != nil {
   709  		t.Fatal(err)
   710  	}
   711  	now := ctimeNs(st)
   712  	nowNs = func() int64 { return now + 1_500_000_000 }
   713  	if _, err := store.HashTree("/"); err != nil {
   714  		t.Fatal(err)
   715  	}
   716  	if _, err := store.HashTree("/"); err != nil {
   717  		t.Fatal(err)
   718  	}
   719  	if !slices.Equal(*hashed, []string{"racy.md", "racy.md"}) {
   720  		t.Fatalf("hashed inside the margin = %v, want both walks to hash", *hashed)
   721  	}
   722  	nowNs = func() int64 { return now + 10_000_000_000 }
   723  	if _, err := store.HashTree("/"); err != nil {
   724  		t.Fatal(err)
   725  	}
   726  	if _, err := store.HashTree("/"); err != nil {
   727  		t.Fatal(err)
   728  	}
   729  	if !slices.Equal(*hashed, []string{"racy.md", "racy.md", "racy.md"}) {
   730  		t.Fatalf("hashed past the margin = %v, want exactly one more hash", *hashed)
   731  	}
   732  }
   733  
   734  func TestCreateRootMakesTheFolderOwnerOnly(t *testing.T) {
   735  	root := filepath.Join(t.TempDir(), "store")
   736  	store := mustOpen(t, root)
   737  	if err := store.CreateRoot(); err != nil {
   738  		t.Fatalf("CreateRoot: %v", err)
   739  	}
   740  	st, err := os.Stat(root)
   741  	if err != nil {
   742  		t.Fatal(err)
   743  	}
   744  	if got := st.Mode().Perm(); got != 0o700 {
   745  		t.Fatalf("root mode = %v, want 0700", got)
   746  	}
   747  	if err := store.CreateRoot(); err != nil { // already existing is fine
   748  		t.Fatalf("second CreateRoot: %v", err)
   749  	}
   750  	if !store.Root().RemovedOnDispose {
   751  		t.Fatal("RemovedOnDispose = false, want true: Open found no root, so this store created it")
   752  	}
   753  }
   754  
   755  func TestFileStorePutNeverCreatesAMissingRoot(t *testing.T) {
   756  	// Only CreateRoot makes the folder: a Put into a store that was never
   757  	// created, or whose folder was removed, fails and re-creates nothing —
   758  	// not the root and nothing above it.
   759  	mnt := filepath.Join(t.TempDir(), "mnt")
   760  	root := filepath.Join(mnt, "store")
   761  	store := mustOpen(t, root)
   762  
   763  	requireNotCreated(t, store.Put("a.md", []byte("a")), mnt)
   764  	requireNotCreated(t, store.Put("sub/a.md", []byte("a")), mnt)
   765  
   766  	if err := store.CreateRoot(); err != nil {
   767  		t.Fatalf("CreateRoot: %v", err)
   768  	}
   769  	put(t, store, "a.md", "a")
   770  	if err := os.RemoveAll(root); err != nil {
   771  		t.Fatal(err)
   772  	}
   773  	requireNotCreated(t, store.Put("b.md", []byte("b")), root)
   774  	requireNotCreated(t, store.Put("sub/b.md", []byte("b")), root)
   775  
   776  	if err := os.RemoveAll(mnt); err != nil {
   777  		t.Fatal(err)
   778  	}
   779  	requireNotCreated(t, store.Put("z.md", []byte("z")), mnt)
   780  
   781  	if err := store.CreateRoot(); err != nil {
   782  		t.Fatalf("CreateRoot: %v", err)
   783  	}
   784  	put(t, store, "z.md", "z")
   785  	if got, err := store.Get("z.md"); err != nil || string(got) != "z" {
   786  		t.Fatalf("Get z.md = %q, %v", got, err)
   787  	}
   788  }
   789  
   790  func TestFileStoreMoveNeverCreatesAMissingRoot(t *testing.T) {
   791  	store, root := newStore(t)
   792  	put(t, store, "a.md", "a")
   793  	if err := os.RemoveAll(root); err != nil {
   794  		t.Fatal(err)
   795  	}
   796  
   797  	requireNotCreated(t, store.Move("a.md", "sub/b.md"), root)
   798  }