initiall commit

This commit is contained in:
Tomas Dvorak
2026-04-10 12:03:31 +02:00
commit 7ddfb1f52b
276 changed files with 37629 additions and 0 deletions
+181
View File
@@ -0,0 +1,181 @@
package storage
import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"io"
"os"
"path/filepath"
"testing"
)
func TestSanitizeObjectKey(t *testing.T) {
t.Parallel()
valid, err := sanitizeObjectKey(" docs/readme.txt ")
if err != nil {
t.Fatalf("expected valid key, got error: %v", err)
}
if valid != "docs/readme.txt" {
t.Fatalf("unexpected sanitized key: %s", valid)
}
invalidKeys := []string{"", ".", "../secret", "/absolute/path", " /../../etc "}
for _, key := range invalidKeys {
if _, err := sanitizeObjectKey(key); err == nil {
t.Fatalf("expected key %q to be invalid", key)
}
}
}
func TestLocalStorePutOpenDeleteRoundTrip(t *testing.T) {
t.Parallel()
root := t.TempDir()
store, err := NewLocalStore(root)
if err != nil {
t.Fatalf("new local store: %v", err)
}
content := []byte("hello primora")
put, err := store.Put(context.Background(), "bucket-a", "docs/hello.txt", bytes.NewReader(content))
if err != nil {
t.Fatalf("put object: %v", err)
}
if put.SizeBytes != int64(len(content)) {
t.Fatalf("unexpected size: %d", put.SizeBytes)
}
expectedDigest := sha256.Sum256(content)
if put.SHA256Digest != hex.EncodeToString(expectedDigest[:]) {
t.Fatalf("unexpected digest: %s", put.SHA256Digest)
}
file, path, err := store.Open("bucket-a", "docs/hello.txt")
if err != nil {
t.Fatalf("open object: %v", err)
}
defer file.Close()
if filepath.Clean(path) != filepath.Clean(put.Path) {
t.Fatalf("path mismatch: got %s, want %s", path, put.Path)
}
data, err := io.ReadAll(file)
if err != nil {
t.Fatalf("read object: %v", err)
}
if !bytes.Equal(data, content) {
t.Fatalf("content mismatch: got %q, want %q", string(data), string(content))
}
if err := store.Delete("bucket-a", "docs/hello.txt"); err != nil {
t.Fatalf("delete object: %v", err)
}
if _, _, err := store.Open("bucket-a", "docs/hello.txt"); err == nil {
t.Fatalf("expected open to fail after delete")
}
}
func TestLocalStoreDeleteBucketRemovesAllFiles(t *testing.T) {
t.Parallel()
root := t.TempDir()
store, err := NewLocalStore(root)
if err != nil {
t.Fatalf("new local store: %v", err)
}
if _, err := store.Put(context.Background(), "bucket-z", "a.txt", bytes.NewReader([]byte("a"))); err != nil {
t.Fatalf("put a.txt: %v", err)
}
if _, err := store.Put(context.Background(), "bucket-z", "nested/b.txt", bytes.NewReader([]byte("b"))); err != nil {
t.Fatalf("put b.txt: %v", err)
}
if err := store.DeleteBucket("bucket-z"); err != nil {
t.Fatalf("delete bucket: %v", err)
}
_, err = os.Stat(filepath.Join(root, "bucket-z"))
if !os.IsNotExist(err) {
t.Fatalf("expected bucket path to be removed, stat err: %v", err)
}
}
func TestLocalStoreMoveObject(t *testing.T) {
t.Parallel()
root := t.TempDir()
store, err := NewLocalStore(root)
if err != nil {
t.Fatalf("new local store: %v", err)
}
content := []byte("rename me")
if _, err := store.Put(context.Background(), "bucket-r", "source/name.txt", bytes.NewReader(content)); err != nil {
t.Fatalf("put source object: %v", err)
}
newPath, err := store.Move("bucket-r", "source/name.txt", "dest/renamed.txt")
if err != nil {
t.Fatalf("move object: %v", err)
}
if filepath.Clean(newPath) != filepath.Clean(filepath.Join(root, "bucket-r", "dest/renamed.txt")) {
t.Fatalf("unexpected moved path: %s", newPath)
}
if _, _, err := store.Open("bucket-r", "source/name.txt"); err == nil {
t.Fatalf("expected old key open to fail after move")
}
file, _, err := store.Open("bucket-r", "dest/renamed.txt")
if err != nil {
t.Fatalf("open moved object: %v", err)
}
defer file.Close()
data, err := io.ReadAll(file)
if err != nil {
t.Fatalf("read moved object: %v", err)
}
if !bytes.Equal(data, content) {
t.Fatalf("moved content mismatch: got %q, want %q", string(data), string(content))
}
}
func TestLocalStoreMoveObjectAcrossBuckets(t *testing.T) {
t.Parallel()
root := t.TempDir()
store, err := NewLocalStore(root)
if err != nil {
t.Fatalf("new local store: %v", err)
}
content := []byte("cross bucket")
if _, err := store.Put(context.Background(), "bucket-src", "folder/source.txt", bytes.NewReader(content)); err != nil {
t.Fatalf("put source object: %v", err)
}
newPath, err := store.MoveBetweenBuckets("bucket-src", "bucket-dst", "folder/source.txt", "archive/destination.txt")
if err != nil {
t.Fatalf("move across buckets: %v", err)
}
if filepath.Clean(newPath) != filepath.Clean(filepath.Join(root, "bucket-dst", "archive/destination.txt")) {
t.Fatalf("unexpected moved path: %s", newPath)
}
if _, _, err := store.Open("bucket-src", "folder/source.txt"); err == nil {
t.Fatalf("expected source key open to fail after move")
}
file, _, err := store.Open("bucket-dst", "archive/destination.txt")
if err != nil {
t.Fatalf("open moved object: %v", err)
}
defer file.Close()
data, err := io.ReadAll(file)
if err != nil {
t.Fatalf("read moved object: %v", err)
}
if !bytes.Equal(data, content) {
t.Fatalf("moved content mismatch: got %q, want %q", string(data), string(content))
}
}