Add storage backends and safety checks

This commit is contained in:
2026-05-31 02:00:24 +00:00
parent 29dbad2967
commit 3c2f36a6e5
10 changed files with 1582 additions and 0 deletions

View File

@@ -0,0 +1,133 @@
package storage
import (
"context"
"errors"
"io"
"testing"
)
func TestValidatePath(t *testing.T) {
valid := []string{
"report.md",
"daily/report.md",
"a-b_1.2/report.html",
}
for _, path := range valid {
t.Run("valid "+path, func(t *testing.T) {
if err := ValidatePath(path); err != nil {
t.Fatalf("ValidatePath(%q) error = %v", path, err)
}
})
}
invalid := []string{
"",
"/absolute",
"../outside",
"nested/../outside",
"nested/./file",
"nested//file",
`nested\file`,
}
for _, path := range invalid {
t.Run("invalid "+path, func(t *testing.T) {
err := ValidatePath(path)
if !IsInvalidPath(err) {
t.Fatalf("ValidatePath(%q) error = %v, want invalid path", path, err)
}
})
}
}
func TestValidatePrefixAllowsRoot(t *testing.T) {
if err := ValidatePrefix(""); err != nil {
t.Fatalf("ValidatePrefix(\"\") error = %v", err)
}
if err := ValidatePrefix("a/.."); !IsInvalidPath(err) {
t.Fatalf("ValidatePrefix traversal error = %v, want invalid path", err)
}
}
func TestListSortsEntries(t *testing.T) {
backend := walkBackend{
entries: []Entry{
{Path: "z.txt", Type: EntryTypeFile},
{Path: "a.txt", Type: EntryTypeFile},
},
}
entries, err := List(context.Background(), backend, "", WalkOptions{})
if err != nil {
t.Fatalf("List() error = %v", err)
}
if got, want := []string{entries[0].Path, entries[1].Path}, []string{"a.txt", "z.txt"}; got[0] != want[0] || got[1] != want[1] {
t.Fatalf("paths = %v, want %v", got, want)
}
}
func TestTypedErrorPredicates(t *testing.T) {
err := NewError(OpReadFile, "test", "missing", ErrNotFound, errors.New("missing"))
if !IsNotFound(err) {
t.Fatalf("IsNotFound(%v) = false, want true", err)
}
if IsInvalidPath(err) {
t.Fatalf("IsInvalidPath(%v) = true, want false", err)
}
}
func TestRegistry(t *testing.T) {
registry := NewRegistry()
if err := registry.Register("test", func(context.Context, OpenConfig) (Backend, error) {
return walkBackend{}, nil
}); err != nil {
t.Fatalf("Register() error = %v", err)
}
if _, err := registry.Open(context.Background(), "test", nil); err != nil {
t.Fatalf("Open() error = %v", err)
}
if _, err := registry.Open(context.Background(), "missing", nil); !IsUnsupported(err) {
t.Fatalf("Open() error = %v, want unsupported", err)
}
}
type walkBackend struct {
entries []Entry
}
func (b walkBackend) ReadFile(context.Context, string) ([]byte, error) {
return nil, nil
}
func (b walkBackend) OpenReader(context.Context, string) (io.ReadCloser, error) {
return nil, nil
}
func (b walkBackend) WriteFile(context.Context, string, []byte, WriteOptions) (Entry, error) {
return Entry{}, nil
}
func (b walkBackend) WriteFrom(context.Context, string, io.Reader, WriteOptions) (Entry, error) {
return Entry{}, nil
}
func (b walkBackend) Stat(context.Context, string) (Entry, error) {
return Entry{}, nil
}
func (b walkBackend) Walk(_ context.Context, _ string, _ WalkOptions, fn WalkFunc) error {
for _, entry := range b.entries {
if err := fn(entry); err != nil {
return err
}
}
return nil
}
func (b walkBackend) HasAny(context.Context, string) (bool, error) {
return false, nil
}
func (b walkBackend) DeleteManagedBundle(context.Context, string, []string, DeleteOptions) error {
return nil
}