package storage_test import ( "context" "errors" "net/http/httptest" "testing" "github.com/johannesboyne/gofakes3" "github.com/johannesboyne/gofakes3/backend/s3mem" "sneak.berlin/go/vaultik/internal/s3" "sneak.berlin/go/vaultik/internal/storage" ) // s3TestBucket is the bucket created for each in-process S3 server. const s3TestBucket = "test-bucket" // newS3Storer builds an s3:// backend backed by a fresh in-process // S3 server. It reuses the same in-memory S3 harness (gofakes3 + s3mem // over httptest) that internal/s3 and the not-found regression test use, // so no new mock or dependency is introduced. Each call gets its own // server, bucket, and client, so the conformance suite's per-section // instances stay isolated. // //nolint:ireturn // conformance runs against the Storer interface by design func newS3Storer(t *testing.T) storage.Storer { t.Helper() backend := s3mem.New() err := backend.CreateBucket(s3TestBucket) if err != nil { t.Fatalf("create bucket: %v", err) } srv := httptest.NewServer(gofakes3.New(backend).Server()) t.Cleanup(srv.Close) client, err := s3.NewClient(context.Background(), s3.Config{ Endpoint: srv.URL, Bucket: s3TestBucket, AccessKeyID: "test", SecretAccessKey: "test", Region: "us-east-1", }) if err != nil { t.Fatalf("new client: %v", err) } return storage.NewS3Storer(client) } // TestS3Storer runs the shared Storer contract against the s3:// backend, // so it is held to the same round-trip, list, delete, and not-found // behaviour as the file:// backend. func TestS3Storer(t *testing.T) { t.Parallel() runStorerConformance(t, newS3Storer) } // TestS3StorerMissingKeyMapsToErrNotFound pins the specific contract that a // missing object surfaces as storage.ErrNotFound rather than the raw AWS SDK // error. Without the mapping, errors.Is(err, storage.ErrNotFound) is false on // s3 and callers would branch differently per backend. func TestS3StorerMissingKeyMapsToErrNotFound(t *testing.T) { t.Parallel() storer := newS3Storer(t) ctx := context.Background() _, err := storer.Get(ctx, "does-not-exist") if !errors.Is(err, storage.ErrNotFound) { t.Errorf("Get on missing key: got %v, want ErrNotFound", err) } _, err = storer.Stat(ctx, "does-not-exist") if !errors.Is(err, storage.ErrNotFound) { t.Errorf("Stat on missing key: got %v, want ErrNotFound", err) } }