Files
distributor/internal/storage/path_test.go

213 lines
5.2 KiB
Go

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 TestStatePath(t *testing.T) {
tests := map[string]string{
"": StateFileName,
"bundle": "bundle/" + StateFileName,
}
for bundlePath, want := range tests {
t.Run(bundlePath, func(t *testing.T) {
got, err := StatePath(bundlePath)
if err != nil {
t.Fatalf("StatePath(%q) error = %v", bundlePath, err)
}
if got != want {
t.Fatalf("StatePath(%q) = %q, want %q", bundlePath, got, want)
}
})
}
}
func TestDisplayPath(t *testing.T) {
tests := map[string]string{
"": ".",
"bundle": "bundle",
}
for path, want := range tests {
t.Run(path, func(t *testing.T) {
if got := DisplayPath(path); got != want {
t.Fatalf("DisplayPath(%q) = %q, want %q", path, got, want)
}
})
}
}
func TestManagedBundleTargets(t *testing.T) {
tests := []struct {
name string
bundlePath string
outputs []string
want []string
}{
{
name: "root",
outputs: []string{"report.html", "assets/style.css"},
want: []string{"report.html", "assets/style.css", StateFileName},
},
{
name: "nested",
bundlePath: "daily",
outputs: []string{"report.html"},
want: []string{"daily/report.html", "daily/" + StateFileName},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ManagedBundleTargets(tt.bundlePath, tt.outputs)
if err != nil {
t.Fatalf("ManagedBundleTargets() error = %v", err)
}
if len(got) != len(tt.want) {
t.Fatalf("targets = %v, want %v", got, tt.want)
}
for i := range got {
if got[i] != tt.want[i] {
t.Fatalf("targets = %v, want %v", got, tt.want)
}
}
})
}
}
func TestManagedBundleTargetsRejectsInvalidOutputPath(t *testing.T) {
if _, err := ManagedBundleTargets("bundle", []string{"../outside"}); !IsInvalidPath(err) {
t.Fatalf("ManagedBundleTargets() 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
}
func (b walkBackend) DeletePrefix(context.Context, string, DeleteOptions) error {
return nil
}