Files
xdrop/apps/api/internal/storage/s3_integration_test.go
T

160 lines
4.6 KiB
Go

package storage
import (
"context"
"fmt"
nethttp "net/http"
"strings"
"testing"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/stretchr/testify/require"
"github.com/xdrop/monorepo/internal/testutil"
)
func TestS3StorageLifecycleAgainstMinIO(t *testing.T) {
skipIfDockerUnavailable(t)
ctx := context.Background()
cfg := startMinIOConfig(t, ctx)
store, err := NewS3Storage(ctx, cfg)
require.NoError(t, err)
require.NoError(t, store.EnsureBucket(ctx))
require.NoError(t, store.EnsureBucket(ctx))
require.NoError(t, store.PutObject(ctx, "transfers/demo/manifest.bin", []byte("manifest"), "application/octet-stream"))
require.NoError(t, store.PutObject(ctx, "transfers/demo/files/file-a/chunks/00000000.bin", []byte("chunk"), "application/octet-stream"))
require.NoError(t, store.PutObject(ctx, "transfers/other/manifest.bin", []byte("other"), "application/octet-stream"))
objectBody := getObjectBody(t, ctx, store, "transfers/demo/manifest.bin")
require.Equal(t, []byte("manifest"), objectBody)
uploadURL, err := store.PresignUpload(ctx, "transfers/demo/files/file-a/chunks/00000001.bin", 5*time.Minute)
require.NoError(t, err)
require.Contains(t, uploadURL, "X-Amz-Algorithm=")
require.Contains(t, uploadURL, "X-Amz-Signature=")
require.Contains(t, uploadURL, cfg.PublicEndpoint)
downloadURL, err := store.PresignDownload(ctx, "transfers/demo/manifest.bin", 5*time.Minute)
require.NoError(t, err)
require.Contains(t, downloadURL, "X-Amz-Algorithm=")
require.Contains(t, downloadURL, cfg.PublicEndpoint)
require.NoError(t, store.DeletePrefix(ctx, "transfers/demo/"))
keys := listObjectKeys(t, ctx, store)
require.NotContains(t, keys, "transfers/demo/manifest.bin")
require.NotContains(t, keys, "transfers/demo/files/file-a/chunks/00000000.bin")
require.Contains(t, keys, "transfers/other/manifest.bin")
}
func TestNormalizeEndpointHandlesSchemeAndWhitespace(t *testing.T) {
t.Parallel()
normalized, err := NormalizeEndpoint(" localhost:9000 ")
require.NoError(t, err)
require.Equal(t, "http://localhost:9000", normalized)
normalized, err = NormalizeEndpoint("https://minio.example.test")
require.NoError(t, err)
require.Equal(t, "https://minio.example.test", normalized)
}
func TestNormalizeEndpointRejectsInvalidURL(t *testing.T) {
t.Parallel()
_, err := NormalizeEndpoint("http://%zz")
require.Error(t, err)
}
func TestReadAllReadsEntireStream(t *testing.T) {
t.Parallel()
body, err := ReadAll(strings.NewReader("hello"))
require.NoError(t, err)
require.Equal(t, []byte("hello"), body)
}
func startMinIOConfig(t *testing.T, ctx context.Context) Config {
t.Helper()
container := testutil.StartDockerContainer(t, ctx, testutil.DockerRunRequest{
NamePrefix: "xdrop-minio",
Image: "minio/minio:latest",
Env: map[string]string{
"MINIO_ROOT_USER": "minioadmin",
"MINIO_ROOT_PASSWORD": "minioadmin",
},
ExposedPorts: []string{"9000/tcp"},
Command: []string{"server", "/data"},
})
port := container.PublishedPort(t, ctx, "9000/tcp")
internalEndpoint := fmt.Sprintf("http://127.0.0.1:%s", port)
require.NoError(t, testutil.WaitForCondition(ctx, 90*time.Second, 500*time.Millisecond, func() error {
response, err := nethttp.Get(internalEndpoint + "/minio/health/live")
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode >= 400 {
return fmt.Errorf("minio returned status %d", response.StatusCode)
}
return nil
}))
return Config{
Endpoint: internalEndpoint,
PublicEndpoint: "http://public.example.test:9000",
Region: "us-east-1",
Bucket: "xdrop",
AccessKey: "minioadmin",
SecretKey: "minioadmin",
}
}
func getObjectBody(t *testing.T, ctx context.Context, store *S3Storage, objectKey string) []byte {
t.Helper()
response, err := store.client.GetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(store.bucket),
Key: aws.String(objectKey),
})
require.NoError(t, err)
defer response.Body.Close()
body, err := ReadAll(response.Body)
require.NoError(t, err)
return body
}
func listObjectKeys(t *testing.T, ctx context.Context, store *S3Storage) []string {
t.Helper()
paginator := s3.NewListObjectsV2Paginator(store.client, &s3.ListObjectsV2Input{
Bucket: aws.String(store.bucket),
})
keys := []string{}
for paginator.HasMorePages() {
page, err := paginator.NextPage(ctx)
require.NoError(t, err)
for _, item := range page.Contents {
if item.Key != nil {
keys = append(keys, *item.Key)
}
}
}
return keys
}
func skipIfDockerUnavailable(t *testing.T) {
t.Helper()
testutil.SkipIfDockerUnavailable(t, true)
}