diff --git a/lib/ex_aws/s3.ex b/lib/ex_aws/s3.ex
index e6c1d38..98154b6 100644
--- a/lib/ex_aws/s3.ex
+++ b/lib/ex_aws/s3.ex
@@ -229,12 +229,39 @@ defmodule ExAws.S3 do
)
end
- @doc "List metadata about all versions of the objects in a bucket."
+ @type list_object_versions_opts :: [
+ {:delimiter, binary}
+ | {:key_marker, binary}
+ | {:version_id_marker, binary}
+ | {:max_keys, 0..1000}
+ | {:prefix, binary}
+ | {:encoding_type, binary}
+ ]
+
+ @doc """
+ List metadata about all versions of the objects in a bucket.
+
+ Can be streamed.
+
+ ## Examples
+ ```
+ S3.list_object_versions("my-bucket") |> ExAws.request
+
+ S3.list_object_versions("my-bucket") |> ExAws.stream!
+ S3.list_object_versions("my-bucket", prefix: "backup/") |> ExAws.stream!
+ ```
+ """
@spec list_object_versions(bucket :: binary) :: ExAws.Operation.S3.t()
- @spec list_object_versions(bucket :: binary, opts :: Keyword.t()) ::
+ @spec list_object_versions(bucket :: binary, opts :: list_object_versions_opts) ::
ExAws.Operation.S3.t()
+ @params [:delimiter, :key_marker, :version_id_marker, :max_keys, :prefix, :encoding_type]
def list_object_versions(bucket, opts \\ []) do
- request(:get, bucket, "/", [resource: "versions", params: opts],
+ params =
+ opts
+ |> format_and_take(@params)
+
+ request(:get, bucket, "/", [resource: "versions", params: params],
+ stream_builder: &ExAws.S3.Lazy.stream_object_versions!(bucket, opts, &1),
parser: &ExAws.S3.Parsers.parse_object_versions/1
)
end
diff --git a/lib/ex_aws/s3/lazy.ex b/lib/ex_aws/s3/lazy.ex
index 8770dd8..8e32a2c 100644
--- a/lib/ex_aws/s3/lazy.ex
+++ b/lib/ex_aws/s3/lazy.ex
@@ -54,6 +54,37 @@ defmodule ExAws.S3.Lazy do
)
end
+ def stream_object_versions!(bucket, opts, config) do
+ request_fun = fn fun_opts ->
+ ExAws.S3.list_object_versions(bucket, Keyword.merge(opts, fun_opts))
+ |> ExAws.request!(config)
+ |> Map.get(:body)
+ end
+
+ Stream.resource(
+ fn -> {request_fun, []} end,
+ fn
+ :quit ->
+ {:halt, nil}
+
+ {fun, args} ->
+ case fun.(args) do
+ results = %{is_truncated: "true"} ->
+ {add_version_results(results),
+ {fun,
+ [
+ key_marker: results[:next_key_marker],
+ version_id_marker: results[:next_version_id_marker]
+ ]}}
+
+ results ->
+ {add_version_results(results), :quit}
+ end
+ end,
+ & &1
+ )
+ end
+
def add_results(results, opts) do
case Keyword.get(opts, :stream_prefixes, nil) do
nil -> results.contents
@@ -68,4 +99,8 @@ defmodule ExAws.S3.Lazy do
end
def next_marker(%{next_marker: marker}), do: marker
+
+ def add_version_results(results) do
+ (results[:versions] || []) ++ (results[:delete_markers] || [])
+ end
end
diff --git a/test/lib/s3_minio_test.exs b/test/lib/s3_minio_test.exs
index 2bad7fc..9e9e045 100644
--- a/test/lib/s3_minio_test.exs
+++ b/test/lib/s3_minio_test.exs
@@ -1,110 +1,128 @@
defmodule ExAws.S3MinioTest do
- use ExUnit.Case, async: false
+ use ExUnit.Case
alias ExAws.S3
@moduletag :minio
- @test_bucket "ex-aws-s3-test-#{:rand.uniform(1_000_000)}"
@test_object "test-object.txt"
@test_content "Hello MinIO from ExAws.S3"
@test_webhook_target "testhook"
setup do
- # Ensure test bucket is clean
- S3.delete_object(@test_bucket, @test_object) |> ExAws.request()
- S3.delete_bucket(@test_bucket) |> ExAws.request()
+ # Generate unique bucket name for this test
+ bucket = "ex-aws-s3-test-#{System.unique_integer([:positive])}"
on_exit(fn ->
- # Cleanup after all tests
- S3.delete_object(@test_bucket, @test_object) |> ExAws.request()
- S3.delete_bucket(@test_bucket) |> ExAws.request()
+ # Cleanup: delete all object versions
+ # Note: list_object_versions works for both versioned and non-versioned buckets.
+ # For non-versioned buckets, objects have a version ID of "null"
+ case S3.list_object_versions(bucket) |> ExAws.request() do
+ {:ok, %{body: body}} ->
+ # Delete all versions
+ for version <- body[:versions] || [] do
+ S3.delete_object(bucket, version.key, version_id: version.version_id)
+ |> ExAws.request!()
+ end
+
+ # Delete all delete markers
+ for marker <- body[:delete_markers] || [] do
+ S3.delete_object(bucket, marker.key, version_id: marker.version_id)
+ |> ExAws.request!()
+ end
+
+ _ ->
+ :ok
+ end
+
+ # Delete the bucket itself
+ S3.delete_bucket(bucket) |> ExAws.request!()
end)
- :ok
+ {:ok, bucket: bucket}
end
describe "Bucket operations" do
- test "put_bucket creates bucket in MinIO" do
- result = S3.put_bucket(@test_bucket, "us-east-1") |> ExAws.request()
+ test "put_bucket creates bucket in MinIO", %{bucket: bucket} do
+ result = S3.put_bucket(bucket, "us-east-1") |> ExAws.request()
assert {:ok, _} = result
end
- test "list_objects returns empty list for new bucket" do
- S3.put_bucket(@test_bucket, "us-east-1") |> ExAws.request()
+ test "list_objects returns empty list for new bucket", %{bucket: bucket} do
+ S3.put_bucket(bucket, "us-east-1") |> ExAws.request()
- {:ok, result} = S3.list_objects(@test_bucket) |> ExAws.request()
+ {:ok, result} = S3.list_objects(bucket) |> ExAws.request()
assert result.body.contents == []
end
- test "list_objects_v2 returns empty list for new bucket" do
- S3.put_bucket(@test_bucket, "us-east-1") |> ExAws.request()
+ test "list_objects_v2 returns empty list for new bucket", %{bucket: bucket} do
+ S3.put_bucket(bucket, "us-east-1") |> ExAws.request()
- {:ok, result} = S3.list_objects_v2(@test_bucket) |> ExAws.request()
+ {:ok, result} = S3.list_objects_v2(bucket) |> ExAws.request()
assert result.body.contents == []
end
end
describe "Object operations" do
- setup do
- S3.put_bucket(@test_bucket, "us-east-1") |> ExAws.request()
+ setup %{bucket: bucket} do
+ S3.put_bucket(bucket, "us-east-1") |> ExAws.request!()
on_exit(fn ->
# Clean up test object from this describe block
- S3.delete_object(@test_bucket, @test_object) |> ExAws.request()
+ S3.delete_object(bucket, @test_object) |> ExAws.request!()
end)
:ok
end
- test "put_object uploads content to MinIO" do
- result = S3.put_object(@test_bucket, @test_object, @test_content) |> ExAws.request()
+ test "put_object uploads content to MinIO", %{bucket: bucket} do
+ result = S3.put_object(bucket, @test_object, @test_content) |> ExAws.request()
assert {:ok, %{status_code: 200}} = result
end
- test "get_object retrieves content from MinIO" do
- S3.put_object(@test_bucket, @test_object, @test_content) |> ExAws.request()
+ test "get_object retrieves content from MinIO", %{bucket: bucket} do
+ S3.put_object(bucket, @test_object, @test_content) |> ExAws.request()
- {:ok, result} = S3.get_object(@test_bucket, @test_object) |> ExAws.request()
+ {:ok, result} = S3.get_object(bucket, @test_object) |> ExAws.request()
assert result.body == @test_content
end
- test "head_object checks object existence in MinIO" do
- S3.put_object(@test_bucket, @test_object, @test_content) |> ExAws.request()
+ test "head_object checks object existence in MinIO", %{bucket: bucket} do
+ S3.put_object(bucket, @test_object, @test_content) |> ExAws.request()
- result = S3.head_object(@test_bucket, @test_object) |> ExAws.request()
+ result = S3.head_object(bucket, @test_object) |> ExAws.request()
assert {:ok, %{status_code: 200}} = result
end
- test "delete_object removes object from MinIO" do
- S3.put_object(@test_bucket, @test_object, @test_content) |> ExAws.request()
+ test "delete_object removes object from MinIO", %{bucket: bucket} do
+ S3.put_object(bucket, @test_object, @test_content) |> ExAws.request()
- result = S3.delete_object(@test_bucket, @test_object) |> ExAws.request()
+ result = S3.delete_object(bucket, @test_object) |> ExAws.request()
assert {:ok, %{status_code: 204}} = result
# Verify object is gone
- result = S3.get_object(@test_bucket, @test_object) |> ExAws.request()
+ result = S3.get_object(bucket, @test_object) |> ExAws.request()
assert {:error, {:http_error, 404, _}} = result
end
- test "list_objects shows uploaded objects" do
- S3.put_object(@test_bucket, @test_object, @test_content) |> ExAws.request()
+ test "list_objects shows uploaded objects", %{bucket: bucket} do
+ S3.put_object(bucket, @test_object, @test_content) |> ExAws.request()
- {:ok, result} = S3.list_objects(@test_bucket) |> ExAws.request()
+ {:ok, result} = S3.list_objects(bucket) |> ExAws.request()
assert length(result.body.contents) == 1
assert List.first(result.body.contents).key == @test_object
end
- test "list_objects_v2 shows uploaded objects" do
- S3.put_object(@test_bucket, @test_object, @test_content) |> ExAws.request()
+ test "list_objects_v2 shows uploaded objects", %{bucket: bucket} do
+ S3.put_object(bucket, @test_object, @test_content) |> ExAws.request()
- {:ok, result} = S3.list_objects_v2(@test_bucket) |> ExAws.request()
+ {:ok, result} = S3.list_objects_v2(bucket) |> ExAws.request()
assert length(result.body.contents) == 1
assert List.first(result.body.contents).key == @test_object
end
- test "put_object with metadata and headers" do
+ test "put_object with metadata and headers", %{bucket: bucket} do
result =
- S3.put_object(@test_bucket, @test_object, @test_content,
+ S3.put_object(bucket, @test_object, @test_content,
content_type: "text/plain",
meta: [foo: "bar", baz: "qux"]
)
@@ -113,7 +131,7 @@ defmodule ExAws.S3MinioTest do
assert {:ok, %{status_code: 200}} = result
# Verify metadata was set
- {:ok, head_result} = S3.head_object(@test_bucket, @test_object) |> ExAws.request()
+ {:ok, head_result} = S3.head_object(bucket, @test_object) |> ExAws.request()
headers = head_result.headers
assert Enum.any?(headers, fn {k, v} ->
@@ -131,43 +149,43 @@ defmodule ExAws.S3MinioTest do
end
describe "Object copy operations" do
- setup do
- S3.put_bucket(@test_bucket, "us-east-1") |> ExAws.request()
- S3.put_object(@test_bucket, @test_object, @test_content) |> ExAws.request()
+ setup %{bucket: bucket} do
+ S3.put_bucket(bucket, "us-east-1") |> ExAws.request!()
+ S3.put_object(bucket, @test_object, @test_content) |> ExAws.request!()
on_exit(fn ->
# Clean up any copied objects that might have been created
- S3.delete_object(@test_bucket, "copied-#{@test_object}") |> ExAws.request()
+ S3.delete_object(bucket, "copied-#{@test_object}") |> ExAws.request!()
end)
:ok
end
- test "put_object_copy duplicates object in MinIO" do
+ test "put_object_copy duplicates object in MinIO", %{bucket: bucket} do
dest_object = "copied-#{@test_object}"
result =
- S3.put_object_copy(@test_bucket, dest_object, @test_bucket, @test_object)
+ S3.put_object_copy(bucket, dest_object, bucket, @test_object)
|> ExAws.request()
assert {:ok, %{status_code: 200}} = result
# Verify copy exists and has same content
- {:ok, get_result} = S3.get_object(@test_bucket, dest_object) |> ExAws.request()
+ {:ok, get_result} = S3.get_object(bucket, dest_object) |> ExAws.request()
assert get_result.body == @test_content
end
end
describe "Multiple object operations" do
- setup do
- S3.put_bucket(@test_bucket, "us-east-1") |> ExAws.request()
+ setup %{bucket: bucket} do
+ S3.put_bucket(bucket, "us-east-1") |> ExAws.request!()
on_exit(fn ->
# Clean up any objects that might have been created
- case S3.list_objects(@test_bucket) |> ExAws.request() do
+ case S3.list_objects(bucket) |> ExAws.request() do
{:ok, %{body: %{contents: objects}}} ->
for object <- objects do
- S3.delete_object(@test_bucket, object.key) |> ExAws.request()
+ S3.delete_object(bucket, object.key) |> ExAws.request!()
end
_ ->
@@ -178,50 +196,50 @@ defmodule ExAws.S3MinioTest do
:ok
end
- test "delete_multiple_objects removes multiple objects" do
+ test "delete_multiple_objects removes multiple objects", %{bucket: bucket} do
objects = ["obj1.txt", "obj2.txt", "obj3.txt"]
# Upload multiple objects
for obj <- objects do
- S3.put_object(@test_bucket, obj, "content for #{obj}") |> ExAws.request()
+ S3.put_object(bucket, obj, "content for #{obj}") |> ExAws.request()
end
# Verify they exist
- {:ok, list_result} = S3.list_objects(@test_bucket) |> ExAws.request()
+ {:ok, list_result} = S3.list_objects(bucket) |> ExAws.request()
assert length(list_result.body.contents) == 3
# Delete multiple objects
- result = S3.delete_multiple_objects(@test_bucket, objects) |> ExAws.request()
+ result = S3.delete_multiple_objects(bucket, objects) |> ExAws.request()
assert {:ok, %{status_code: 200}} = result
# Verify they're gone
- {:ok, list_result} = S3.list_objects(@test_bucket) |> ExAws.request()
+ {:ok, list_result} = S3.list_objects(bucket) |> ExAws.request()
assert list_result.body.contents == []
end
end
describe "Object tagging operations" do
- setup do
- S3.put_bucket(@test_bucket, "us-east-1") |> ExAws.request()
- S3.put_object(@test_bucket, @test_object, @test_content) |> ExAws.request()
+ setup %{bucket: bucket} do
+ S3.put_bucket(bucket, "us-east-1") |> ExAws.request!()
+ S3.put_object(bucket, @test_object, @test_content) |> ExAws.request!()
on_exit(fn ->
# Clean up test object and any tags from this describe block
- S3.delete_object(@test_bucket, @test_object) |> ExAws.request()
+ S3.delete_object(bucket, @test_object) |> ExAws.request!()
end)
:ok
end
- test "put_object_tagging and get_object_tagging work with MinIO" do
+ test "put_object_tagging and get_object_tagging work with MinIO", %{bucket: bucket} do
tags = [environment: "test", team: "engineering"]
# Set tags
- result = S3.put_object_tagging(@test_bucket, @test_object, tags) |> ExAws.request()
+ result = S3.put_object_tagging(bucket, @test_object, tags) |> ExAws.request()
assert {:ok, %{status_code: 200}} = result
# Get tags
- {:ok, get_result} = S3.get_object_tagging(@test_bucket, @test_object) |> ExAws.request()
+ {:ok, get_result} = S3.get_object_tagging(bucket, @test_object) |> ExAws.request()
returned_tags = get_result.body
assert %{tags: tags} = returned_tags
@@ -229,33 +247,33 @@ defmodule ExAws.S3MinioTest do
assert %{"environment" => "test", "team" => "engineering"} = tag_map
end
- test "delete_object_tagging removes tags from object" do
+ test "delete_object_tagging removes tags from object", %{bucket: bucket} do
tags = [environment: "test"]
# Set tags first
- S3.put_object_tagging(@test_bucket, @test_object, tags) |> ExAws.request()
+ S3.put_object_tagging(bucket, @test_object, tags) |> ExAws.request()
# Delete tags
- result = S3.delete_object_tagging(@test_bucket, @test_object) |> ExAws.request()
+ result = S3.delete_object_tagging(bucket, @test_object) |> ExAws.request()
assert {:ok, %{status_code: 204}} = result
# Verify tags are gone
- {:ok, get_result} = S3.get_object_tagging(@test_bucket, @test_object) |> ExAws.request()
+ {:ok, get_result} = S3.get_object_tagging(bucket, @test_object) |> ExAws.request()
assert get_result.body == %{tags: []}
end
end
describe "Bucket versioning operations" do
- setup do
- S3.put_bucket(@test_bucket, "us-east-1") |> ExAws.request()
+ setup %{bucket: bucket} do
+ S3.put_bucket(bucket, "us-east-1") |> ExAws.request!()
on_exit(fn ->
# Clean up all versions of objects when versioning is enabled
- case S3.list_object_versions(@test_bucket) |> ExAws.request() do
+ case S3.list_object_versions(bucket) |> ExAws.request() do
{:ok, %{body: %{versions: versions}}} ->
for version <- versions do
- S3.delete_object(@test_bucket, version.key, version_id: version.version_id)
- |> ExAws.request()
+ S3.delete_object(bucket, version.key, version_id: version.version_id)
+ |> ExAws.request!()
end
_ ->
@@ -266,67 +284,171 @@ defmodule ExAws.S3MinioTest do
:ok
end
- test "put_bucket_versioning enables versioning in MinIO" do
+ test "put_bucket_versioning enables versioning in MinIO", %{bucket: bucket} do
# Enable versioning with proper XML
version_config =
"Enabled"
- result = S3.put_bucket_versioning(@test_bucket, version_config) |> ExAws.request()
+ result = S3.put_bucket_versioning(bucket, version_config) |> ExAws.request()
assert {:ok, %{status_code: 200}} = result
# Get versioning status
- {:ok, get_result} = S3.get_bucket_versioning(@test_bucket) |> ExAws.request()
+ {:ok, get_result} = S3.get_bucket_versioning(bucket) |> ExAws.request()
versioning_config = get_result.body
# MinIO returns raw XML, so check the string content
assert versioning_config =~ "Enabled"
# Upload same object twice to test versioning
- S3.put_object(@test_bucket, @test_object, "version 1") |> ExAws.request()
- S3.put_object(@test_bucket, @test_object, "version 2") |> ExAws.request()
+ S3.put_object(bucket, @test_object, "version 1") |> ExAws.request()
+ S3.put_object(bucket, @test_object, "version 2") |> ExAws.request()
# List object versions
- {:ok, versions_result} = S3.list_object_versions(@test_bucket) |> ExAws.request()
+ {:ok, versions_result} = S3.list_object_versions(bucket) |> ExAws.request()
versions = versions_result.body.versions
# Should have 2 versions of the same object
assert length(versions) == 2
assert Enum.all?(versions, fn version -> version.key == @test_object end)
end
+
+ test "list_object_versions can be streamed", %{bucket: bucket} do
+ # Enable versioning
+ version_config =
+ "Enabled"
+
+ S3.put_bucket_versioning(bucket, version_config) |> ExAws.request()
+
+ # Upload multiple versions of multiple objects
+ for obj <- ["obj1.txt", "obj2.txt", "obj3.txt"] do
+ S3.put_object(bucket, obj, "#{obj} version 1") |> ExAws.request()
+ S3.put_object(bucket, obj, "#{obj} version 2") |> ExAws.request()
+ end
+
+ # Stream all versions
+ versions =
+ S3.list_object_versions(bucket)
+ |> ExAws.stream!()
+ |> Enum.to_list()
+
+ # Should have 6 versions total (3 objects × 2 versions each)
+ assert length(versions) == 6
+
+ assert Enum.all?(versions, fn version ->
+ version.key in ["obj1.txt", "obj2.txt", "obj3.txt"]
+ end)
+ end
+
+ test "list_object_versions streams with pagination when max_keys is set", %{bucket: bucket} do
+ # Enable versioning
+ version_config =
+ "Enabled"
+
+ S3.put_bucket_versioning(bucket, version_config) |> ExAws.request()
+
+ # Upload 11 objects with 3 versions each = 33 total versions
+ # With max_keys=5, this gives: 5+5+5+5+5+5+3 (7 pages, last page has only 3 items)
+ object_names = for i <- 1..11, do: "obj#{i}.txt"
+
+ for obj <- object_names do
+ S3.put_object(bucket, obj, "#{obj} version 1") |> ExAws.request()
+ S3.put_object(bucket, obj, "#{obj} version 2") |> ExAws.request()
+ S3.put_object(bucket, obj, "#{obj} version 3") |> ExAws.request()
+ end
+
+ # Stream all versions with max_keys set to 5 to force multiple requests with uneven last page
+ versions =
+ S3.list_object_versions(bucket, max_keys: 5)
+ |> ExAws.stream!()
+ |> Enum.to_list()
+
+ # Should have 33 versions total (11 objects × 3 versions each)
+ assert length(versions) == 33
+
+ # Verify all objects are present
+ unique_keys = versions |> Enum.map(& &1.key) |> Enum.uniq() |> Enum.sort()
+ assert length(unique_keys) == 11
+ assert unique_keys == Enum.sort(object_names)
+
+ # Verify each object has 3 versions
+ for obj <- object_names do
+ obj_versions = Enum.filter(versions, fn v -> v.key == obj end)
+ assert length(obj_versions) == 3
+ end
+ end
+
+ test "list_object_versions includes delete markers in stream", %{bucket: bucket} do
+ # Enable versioning
+ version_config =
+ "Enabled"
+
+ S3.put_bucket_versioning(bucket, version_config) |> ExAws.request()
+
+ # Upload multiple objects
+ for obj <- ["obj1.txt", "obj2.txt", "obj3.txt", "obj4.txt"] do
+ S3.put_object(bucket, obj, "#{obj} version 1") |> ExAws.request()
+ S3.put_object(bucket, obj, "#{obj} version 2") |> ExAws.request()
+ end
+
+ # Delete some objects (creates delete markers)
+ S3.delete_object(bucket, "obj2.txt") |> ExAws.request()
+ S3.delete_object(bucket, "obj4.txt") |> ExAws.request()
+
+ # Stream all versions and delete markers
+ all_entries =
+ S3.list_object_versions(bucket)
+ |> ExAws.stream!()
+ |> Enum.to_list()
+
+ # Should have 8 versions (4 objects × 2 versions) + 2 delete markers = 10 total
+ assert length(all_entries) == 10
+
+ # Separate versions from delete markers using split_with
+ {versions, delete_markers} =
+ Enum.split_with(all_entries, fn entry -> Map.has_key?(entry, :size) end)
+
+ # Verify we have the expected counts
+ assert length(versions) == 8
+ assert length(delete_markers) == 2
+
+ # Verify delete markers are for the deleted objects
+ delete_marker_keys = Enum.map(delete_markers, & &1.key) |> Enum.sort()
+ assert delete_marker_keys == ["obj2.txt", "obj4.txt"]
+ end
end
describe "Multipart upload operations" do
- setup do
- S3.put_bucket(@test_bucket, "us-east-1") |> ExAws.request()
+ setup %{bucket: bucket} do
+ S3.put_bucket(bucket, "us-east-1") |> ExAws.request!()
on_exit(fn ->
# Clean up any completed multipart uploads
- S3.delete_object(@test_bucket, @test_object) |> ExAws.request()
+ S3.delete_object(bucket, @test_object) |> ExAws.request!()
end)
:ok
end
- test "initiate and abort multipart upload" do
+ test "initiate and abort multipart upload", %{bucket: bucket} do
# Initiate multipart upload
{:ok, init_result} =
- S3.initiate_multipart_upload(@test_bucket, @test_object) |> ExAws.request()
+ S3.initiate_multipart_upload(bucket, @test_object) |> ExAws.request()
upload_id = init_result.body.upload_id
assert is_binary(upload_id)
# Abort multipart upload
- result = S3.abort_multipart_upload(@test_bucket, @test_object, upload_id) |> ExAws.request()
+ result = S3.abort_multipart_upload(bucket, @test_object, upload_id) |> ExAws.request()
assert {:ok, %{status_code: 204}} = result
end
- test "complete multipart upload workflow" do
+ test "complete multipart upload workflow", %{bucket: bucket} do
# >5MB to ensure multipart
large_content = String.duplicate("A", 5 * 1024 * 1024 + 1000)
# Initiate multipart upload
{:ok, init_result} =
- S3.initiate_multipart_upload(@test_bucket, @test_object) |> ExAws.request()
+ S3.initiate_multipart_upload(bucket, @test_object) |> ExAws.request()
upload_id = init_result.body.upload_id
@@ -334,7 +456,7 @@ defmodule ExAws.S3MinioTest do
part_content = String.slice(large_content, 0, 5 * 1024 * 1024)
{:ok, part_result} =
- S3.upload_part(@test_bucket, @test_object, upload_id, 1, part_content) |> ExAws.request()
+ S3.upload_part(bucket, @test_object, upload_id, 1, part_content) |> ExAws.request()
etag =
part_result.headers |> Enum.find(fn {k, _} -> String.downcase(k) == "etag" end) |> elem(1)
@@ -343,37 +465,37 @@ defmodule ExAws.S3MinioTest do
parts = %{1 => etag}
result =
- S3.complete_multipart_upload(@test_bucket, @test_object, upload_id, parts)
+ S3.complete_multipart_upload(bucket, @test_object, upload_id, parts)
|> ExAws.request()
assert {:ok, %{status_code: 200}} = result
# Verify object exists
- {:ok, _get_result} = S3.head_object(@test_bucket, @test_object) |> ExAws.request()
+ {:ok, _get_result} = S3.head_object(bucket, @test_object) |> ExAws.request()
end
end
describe "Bucket notification operations" do
- setup do
- S3.put_bucket(@test_bucket, "us-east-1") |> ExAws.request()
+ setup %{bucket: bucket} do
+ S3.put_bucket(bucket, "us-east-1") |> ExAws.request!()
on_exit(fn ->
# Clean up notification configuration
empty_config = %{}
- S3.put_bucket_notification(@test_bucket, empty_config) |> ExAws.request()
+ S3.put_bucket_notification(bucket, empty_config) |> ExAws.request!()
end)
:ok
end
- test "get_bucket_notification returns empty configuration initially" do
- {:ok, result} = S3.get_bucket_notification(@test_bucket) |> ExAws.request()
+ test "get_bucket_notification returns empty configuration initially", %{bucket: bucket} do
+ {:ok, result} = S3.get_bucket_notification(bucket) |> ExAws.request()
# MinIO returns empty notification configuration as empty XML
assert result.body =~ "NotificationConfiguration"
end
- test "put_bucket_notification configures webhook notifications" do
+ test "put_bucket_notification configures webhook notifications", %{bucket: bucket} do
# Note: This test verifies the API works, but actual webhook delivery
# requires MinIO to have webhook endpoints configured via mc admin config
webhook_config = %{
@@ -396,11 +518,11 @@ defmodule ExAws.S3MinioTest do
# This should succeed even if webhook endpoint isn't configured
# (MinIO accepts the configuration but won't deliver events)
- result = S3.put_bucket_notification(@test_bucket, webhook_config) |> ExAws.request()
+ result = S3.put_bucket_notification(bucket, webhook_config) |> ExAws.request()
assert {:ok, %{status_code: 200}} = result
# Verify configuration was set
- {:ok, get_result} = S3.get_bucket_notification(@test_bucket) |> ExAws.request()
+ {:ok, get_result} = S3.get_bucket_notification(bucket) |> ExAws.request()
config_body = get_result.body
# Should contain the webhook configuration
@@ -409,22 +531,22 @@ defmodule ExAws.S3MinioTest do
assert config_body =~ "s3:ObjectCreated"
end
- test "delete_bucket_notification clears configuration" do
+ test "delete_bucket_notification clears configuration", %{bucket: bucket} do
# First set a configuration
config = %{
topic_arn: "arn:aws:sns:us-east-1:123456789012:test-topic",
events: ["s3:ObjectCreated:Put"]
}
- S3.put_bucket_notification(@test_bucket, config) |> ExAws.request()
+ S3.put_bucket_notification(bucket, config) |> ExAws.request()
# Clear the configuration
empty_config = %{}
- result = S3.put_bucket_notification(@test_bucket, empty_config) |> ExAws.request()
+ result = S3.put_bucket_notification(bucket, empty_config) |> ExAws.request()
assert {:ok, %{status_code: 200}} = result
# Verify configuration is empty
- {:ok, get_result} = S3.get_bucket_notification(@test_bucket) |> ExAws.request()
+ {:ok, get_result} = S3.get_bucket_notification(bucket) |> ExAws.request()
config_body = get_result.body
# Should be empty notification configuration