mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-06-04 04:23:00 +00:00
182 lines
5.0 KiB
Go
182 lines
5.0 KiB
Go
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))
|
|
}
|
|
}
|