Listings now filter both '.' and '_' prefixes: - '.' entries: excluded from listings AND 404 on direct HTTP access (existing behavior). For invisible side-state like .devshell. - '_' entries: excluded from listings only — direct URL access still works. For operator scaffolding like install.zip's _template/ directory of bootstrap stubs that should be reachable but should not appear in the project picker. Filter applied at both listing entry points: ServeProjectList (the project picker JSON at GET / Accept: application/json) and the generic listing/FromDirEntries (used by ServeDirectory for sub-directory browse listings). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
60 lines
1.4 KiB
Go
60 lines
1.4 KiB
Go
package listing
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// TestFromDirEntriesFiltersHidden asserts that both '.' and '_' prefixed
|
|
// entries are excluded from listings — the '.' branch is the long-standing
|
|
// rule (matches dispatch dot-prefix guard); '_' is for operator-managed
|
|
// scaffolding like install.zip's _template/ that should be reachable by
|
|
// direct URL but invisible to browse.
|
|
func TestFromDirEntriesFiltersHidden(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
for _, name := range []string{
|
|
"Project-A",
|
|
"Project-B",
|
|
".zddc", // hidden file
|
|
".devshell", // hidden dir
|
|
"_template", // scaffolding dir
|
|
"_archive", // scaffolding dir
|
|
"_notes.txt", // scaffolding file
|
|
"normal.txt",
|
|
} {
|
|
path := filepath.Join(dir, name)
|
|
if err := os.WriteFile(path, []byte("x"), 0o644); err != nil {
|
|
t.Fatalf("write %s: %v", name, err)
|
|
}
|
|
}
|
|
|
|
entries, err := os.ReadDir(dir)
|
|
if err != nil {
|
|
t.Fatalf("ReadDir: %v", err)
|
|
}
|
|
|
|
got, err := FromDirEntries(entries, "/")
|
|
if err != nil {
|
|
t.Fatalf("FromDirEntries: %v", err)
|
|
}
|
|
|
|
want := map[string]bool{
|
|
"Project-A": true,
|
|
"Project-B": true,
|
|
"normal.txt": true,
|
|
}
|
|
if len(got) != len(want) {
|
|
var names []string
|
|
for _, e := range got {
|
|
names = append(names, e.Name)
|
|
}
|
|
t.Fatalf("got %d entries (%v), want %d (%v)", len(got), names, len(want), want)
|
|
}
|
|
for _, e := range got {
|
|
if !want[e.Name] {
|
|
t.Errorf("unexpected entry in listing: %q", e.Name)
|
|
}
|
|
}
|
|
}
|