package storage import ( "context" "io" "os" "strings" "testing" ) func TestSaveAndOpen(t *testing.T) { dir := t.TempDir() store, err := NewLocalStorage(dir) if err != nil { t.Fatalf("NewLocalStorage: %v", err) } ctx := context.Background() content := "hello, mygo storage" written, err := store.SaveStaged(ctx, "staging/test/hello.txt", strings.NewReader(content)) if err != nil { t.Fatalf("SaveStaged: %v", err) } if written != int64(len(content)) { t.Errorf("written = %d, want %d", written, len(content)) } if err := store.PromoteStaged(ctx, "staging/test/hello.txt", "data/test/hello.txt"); err != nil { t.Fatalf("PromoteStaged: %v", err) } reader, err := store.Open(ctx, "data/test/hello.txt") if err != nil { t.Fatalf("Open: %v", err) } defer reader.Close() got, err := io.ReadAll(reader) if err != nil { t.Fatalf("ReadAll: %v", err) } if string(got) != content { t.Errorf("content = %q, want %q", string(got), content) } // Verify the file exists on disk under the base path. expectedPath := dir + "/data/test/hello.txt" if _, err := os.Stat(expectedPath); err != nil { t.Errorf("file not found on disk at %s: %v", expectedPath, err) } } func TestDelete(t *testing.T) { dir := t.TempDir() store, err := NewLocalStorage(dir) if err != nil { t.Fatalf("NewLocalStorage: %v", err) } ctx := context.Background() _, err = store.SaveStaged(ctx, "staging/to_delete.txt", strings.NewReader("delete me")) if err != nil { t.Fatalf("SaveStaged: %v", err) } if err := store.PromoteStaged(ctx, "staging/to_delete.txt", "data/to_delete.txt"); err != nil { t.Fatalf("PromoteStaged: %v", err) } if err := store.Delete(ctx, "data/to_delete.txt"); err != nil { t.Fatalf("Delete: %v", err) } _, err = store.Open(ctx, "data/to_delete.txt") if err == nil { t.Error("Open should fail after delete") } } func TestDeleteIdempotent(t *testing.T) { dir := t.TempDir() store, err := NewLocalStorage(dir) if err != nil { t.Fatalf("NewLocalStorage: %v", err) } ctx := context.Background() // Deleting a non-existent file should not error. if err := store.Delete(ctx, "nonexistent.txt"); err != nil { t.Errorf("Delete nonexistent should be idempotent, got: %v", err) } } func TestPathTraversalRejected(t *testing.T) { dir := t.TempDir() store, err := NewLocalStorage(dir) if err != nil { t.Fatalf("NewLocalStorage: %v", err) } ctx := context.Background() _, err = store.SaveStaged(ctx, "../escape.txt", strings.NewReader("malicious")) if err == nil { t.Error("SaveStaged should reject path traversal") } else if !strings.Contains(err.Error(), "path traversal") { t.Errorf("expected path traversal error, got: %v", err) } err = store.PromoteStaged(ctx, "../escape.txt", "data/escape.txt") if err == nil { t.Error("PromoteStaged should reject staged path traversal") } else if !strings.Contains(err.Error(), "path traversal") { t.Errorf("expected path traversal error, got: %v", err) } err = store.PromoteStaged(ctx, "staging/escape.txt", "../escape.txt") if err == nil { t.Error("PromoteStaged should reject final path traversal") } else if !strings.Contains(err.Error(), "path traversal") { t.Errorf("expected path traversal error, got: %v", err) } _, err = store.Open(ctx, "../escape.txt") if err == nil { t.Error("Open should reject path traversal") } else if !strings.Contains(err.Error(), "path traversal") { t.Errorf("expected path traversal error, got: %v", err) } err = store.Delete(ctx, "../escape.txt") if err == nil { t.Error("Delete should reject path traversal") } else if !strings.Contains(err.Error(), "path traversal") { t.Errorf("expected path traversal error, got: %v", err) } err = store.DeleteStaged(ctx, "../escape.txt") if err == nil { t.Error("DeleteStaged should reject path traversal") } else if !strings.Contains(err.Error(), "path traversal") { t.Errorf("expected path traversal error, got: %v", err) } } func TestStagedPathsRequireStagingPrefix(t *testing.T) { dir := t.TempDir() store, err := NewLocalStorage(dir) if err != nil { t.Fatalf("NewLocalStorage: %v", err) } ctx := context.Background() _, err = store.SaveStaged(ctx, "data/not-staged.txt", strings.NewReader("bad")) if err == nil { t.Fatal("SaveStaged should reject non-staging path") } err = store.DeleteStaged(ctx, "data/not-staged.txt") if err == nil { t.Fatal("DeleteStaged should reject non-staging path") } } func TestPromoteRequiresDataFinalPrefix(t *testing.T) { dir := t.TempDir() store, err := NewLocalStorage(dir) if err != nil { t.Fatalf("NewLocalStorage: %v", err) } ctx := context.Background() _, err = store.SaveStaged(ctx, "staging/promote.txt", strings.NewReader("content")) if err != nil { t.Fatalf("SaveStaged: %v", err) } err = store.PromoteStaged(ctx, "staging/promote.txt", "other/promote.txt") if err == nil { t.Fatal("PromoteStaged should reject non-data final path") } } func TestOpenMissingFile(t *testing.T) { dir := t.TempDir() store, err := NewLocalStorage(dir) if err != nil { t.Fatalf("NewLocalStorage: %v", err) } ctx := context.Background() _, err = store.Open(ctx, "no/such/file.txt") if err == nil { t.Error("Open should error for missing file") } else if !strings.Contains(err.Error(), "not found on disk") { t.Errorf("expected 'not found on disk' error, got: %v", err) } } func TestIsSubPath_SameDirectory(t *testing.T) { if !isSubPath("/base", "/base") { t.Error("isSubPath should return true when base == target") } } func TestIsSubPath_ShortChildName(t *testing.T) { if !isSubPath("/base", "/base/a") { t.Error("isSubPath should return true for single-char child path") } } func TestIsSubPath_DotDotPrefixFilename(t *testing.T) { if !isSubPath("/base", "/base/..foo") { t.Error("isSubPath should return true for filename starting with ..") } }