142 lines
5.3 KiB
Go
142 lines
5.3 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestFakeBackendListPrefixFiltering(t *testing.T) {
|
|
fake := &FakeBackend{}
|
|
fake.SeedObject(FakeObject{Key: "dnd/campaigns/forsaken/audio/a.flac", Data: []byte("a")})
|
|
fake.SeedObject(FakeObject{Key: "dnd/campaigns/forsaken/audio/b.flac", Data: []byte("b")})
|
|
fake.SeedObject(FakeObject{Key: "dnd/campaigns/other/audio/c.flac", Data: []byte("c")})
|
|
|
|
items, err := fake.List(context.Background(), "dnd/campaigns/forsaken/audio/")
|
|
if err != nil {
|
|
t.Fatalf("List() error = %v", err)
|
|
}
|
|
if len(items) != 2 {
|
|
t.Fatalf("List() len = %d, want 2", len(items))
|
|
}
|
|
if items[0].Key != "dnd/campaigns/forsaken/audio/a.flac" || items[1].Key != "dnd/campaigns/forsaken/audio/b.flac" {
|
|
t.Fatalf("List() keys = %#v", items)
|
|
}
|
|
}
|
|
|
|
func TestFakeBackendDownload(t *testing.T) {
|
|
fake := &FakeBackend{}
|
|
fake.SeedObject(FakeObject{Key: "audio/a.flac", Data: []byte("audio-a")})
|
|
|
|
dst := filepath.Join(t.TempDir(), "nested", "a.flac")
|
|
if err := fake.Download(context.Background(), `audio\a.flac`, dst); err != nil {
|
|
t.Fatalf("Download() error = %v", err)
|
|
}
|
|
data, err := os.ReadFile(dst)
|
|
if err != nil {
|
|
t.Fatalf("ReadFile() error = %v", err)
|
|
}
|
|
if string(data) != "audio-a" {
|
|
t.Fatalf("downloaded content = %q, want %q", string(data), "audio-a")
|
|
}
|
|
}
|
|
|
|
func TestFakeBackendUploadAndExists(t *testing.T) {
|
|
fake := &FakeBackend{}
|
|
|
|
local := filepath.Join(t.TempDir(), "upload.txt")
|
|
if err := os.WriteFile(local, []byte("payload"), 0o644); err != nil {
|
|
t.Fatalf("WriteFile() error = %v", err)
|
|
}
|
|
|
|
info, err := fake.Upload(context.Background(), local, `runs\id\artifact.txt`, UploadOptions{
|
|
Metadata: map[string]string{"kind": "artifact"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Upload() error = %v", err)
|
|
}
|
|
if info.Key != "runs/id/artifact.txt" {
|
|
t.Fatalf("Upload() key = %q, want normalized key", info.Key)
|
|
}
|
|
|
|
ok, err := fake.Exists(context.Background(), "runs/id/artifact.txt")
|
|
if err != nil {
|
|
t.Fatalf("Exists() error = %v", err)
|
|
}
|
|
if !ok {
|
|
t.Fatal("Exists() = false, want true")
|
|
}
|
|
}
|
|
|
|
func TestFakeBackendDeleteIsIdempotent(t *testing.T) {
|
|
fake := &FakeBackend{}
|
|
fake.SeedObject(FakeObject{Key: "locks/source.lock", Data: []byte("lock")})
|
|
|
|
for range 2 {
|
|
if err := fake.Delete(context.Background(), `locks\source.lock`); err != nil {
|
|
t.Fatalf("Delete() error = %v", err)
|
|
}
|
|
}
|
|
if _, ok := fake.Objects["locks/source.lock"]; ok {
|
|
t.Fatal("Delete() left the object present")
|
|
}
|
|
if len(fake.Deletes) != 2 || fake.Deletes[0] != "locks/source.lock" || fake.Deletes[1] != "locks/source.lock" {
|
|
t.Fatalf("Delete() calls = %#v, want two normalized keys", fake.Deletes)
|
|
}
|
|
}
|
|
|
|
func TestFakeBackendDeleteHonorsContextAndInjectedError(t *testing.T) {
|
|
canceled, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
fake := &FakeBackend{}
|
|
if err := fake.Delete(canceled, "locks/source.lock"); !errors.Is(err, context.Canceled) {
|
|
t.Fatalf("Delete() canceled error = %v, want context.Canceled", err)
|
|
}
|
|
|
|
wantErr := errors.New("delete fail")
|
|
fake.DeleteErr = wantErr
|
|
if err := fake.Delete(context.Background(), "locks/source.lock"); !errors.Is(err, wantErr) {
|
|
t.Fatalf("Delete() injected error = %v, want %v", err, wantErr)
|
|
}
|
|
}
|
|
|
|
func TestFakeBackendConditionalUploadRejectsStaleGeneration(t *testing.T) {
|
|
fake := &FakeBackend{}
|
|
fake.SeedObject(FakeObject{Key: "locks.yml", Data: []byte("old")})
|
|
old := fake.Objects["locks.yml"].ETag
|
|
if _, err := fake.UploadConditional(context.Background(), strings.NewReader("new"), "locks.yml", UploadOptions{}, WriteCondition{MatchETag: old}); err != nil {
|
|
t.Fatalf("UploadConditional() error = %v", err)
|
|
}
|
|
if _, err := fake.UploadConditional(context.Background(), strings.NewReader("lost"), "locks.yml", UploadOptions{}, WriteCondition{MatchETag: old}); !errors.Is(err, ErrConditionNotMet) {
|
|
t.Fatalf("UploadConditional() error = %v, want ErrConditionNotMet", err)
|
|
}
|
|
if got := string(fake.Objects["locks.yml"].Data); got != "new" {
|
|
t.Fatalf("locks object = %q, want successful replacement preserved", got)
|
|
}
|
|
}
|
|
|
|
func TestFakeBackendObjectErrors(t *testing.T) {
|
|
fake := &FakeBackend{DownloadErr: errors.New("download fail"), UploadErr: errors.New("upload fail"), ListErr: errors.New("list fail"), DeleteErr: errors.New("delete fail"), ExistsErr: errors.New("exists fail")}
|
|
|
|
if _, err := fake.List(context.Background(), "x"); err == nil || !strings.Contains(err.Error(), "list fail") {
|
|
t.Fatalf("List() error = %v, want list fail", err)
|
|
}
|
|
if err := fake.Download(context.Background(), "x", filepath.Join(t.TempDir(), "x")); err == nil || !strings.Contains(err.Error(), "download fail") {
|
|
t.Fatalf("Download() error = %v, want download fail", err)
|
|
}
|
|
local := filepath.Join(t.TempDir(), "x.txt")
|
|
_ = os.WriteFile(local, []byte("x"), 0o644)
|
|
if _, err := fake.Upload(context.Background(), local, "x", UploadOptions{}); err == nil || !strings.Contains(err.Error(), "upload fail") {
|
|
t.Fatalf("Upload() error = %v, want upload fail", err)
|
|
}
|
|
if err := fake.Delete(context.Background(), "x"); err == nil || !strings.Contains(err.Error(), "delete fail") {
|
|
t.Fatalf("Delete() error = %v, want delete fail", err)
|
|
}
|
|
if _, err := fake.Exists(context.Background(), "x"); err == nil || !strings.Contains(err.Error(), "exists fail") {
|
|
t.Fatalf("Exists() error = %v, want exists fail", err)
|
|
}
|
|
}
|