check / check (pull_request) Successful in 2m47s
S3Storer.Get and Stat returned the raw AWS SDK error for a missing object, so errors.Is(err, storage.ErrNotFound) was false on the s3 backend while the file and rclone backends honored the Storer contract. Callers that branch on ErrNotFound behaved differently per backend. Both now wrap ErrNotFound when the SDK reports a missing object, leaving every other error intact. The not-found detection is a small exported s3.IsNotFound helper, also used by HeadObject so the two share one definition. A test asserts a missing key maps to ErrNotFound on the s3 backend; it fails without the mapping. model: claude-opus-4-8
60 lines
1.5 KiB
Go
60 lines
1.5 KiB
Go
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"
|
|
)
|
|
|
|
// TestS3StorerMissingKeyMapsToErrNotFound verifies that the s3 backend reports
|
|
// a missing object as storage.ErrNotFound, matching the file and rclone
|
|
// backends and the Storer contract. Without the mapping, Get and Stat leak the
|
|
// raw SDK error and errors.Is(err, storage.ErrNotFound) is false.
|
|
//
|
|
//nolint:paralleltest // shares an in-process S3 server via t.Cleanup
|
|
func TestS3StorerMissingKeyMapsToErrNotFound(t *testing.T) {
|
|
const bucket = "test-bucket"
|
|
|
|
backend := s3mem.New()
|
|
|
|
err := backend.CreateBucket(bucket)
|
|
if err != nil {
|
|
t.Fatalf("create bucket: %v", err)
|
|
}
|
|
|
|
srv := httptest.NewServer(gofakes3.New(backend).Server())
|
|
t.Cleanup(srv.Close)
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := s3.NewClient(ctx, s3.Config{
|
|
Endpoint: srv.URL,
|
|
Bucket: bucket,
|
|
AccessKeyID: "test",
|
|
SecretAccessKey: "test",
|
|
Region: "us-east-1",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("new client: %v", err)
|
|
}
|
|
|
|
storer := storage.NewS3Storer(client)
|
|
|
|
_, 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)
|
|
}
|
|
}
|