Skip to content

Commit 580b4a6

Browse files
committed
Fix #741 Add tests for ListObjectsV2 API in AWS storage module
- Introduced comprehensive tests for the ListObjectsV2 API, covering basic functionality, parameter handling (prefix, max-keys, start-after, delimiter, fetch-owner), and pagination with continuation tokens. - Created separate test files for both model and request layers to ensure thorough validation of the API's behavior and response structure.
1 parent 14fe0de commit 580b4a6

File tree

2 files changed

+309
-0
lines changed

2 files changed

+309
-0
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
Shindo.tests("Storage[:aws] | ListObjectsV2 API", ["aws"]) do
2+
3+
directory_attributes = {
4+
:key => uniq_id('foglistobjectsv2tests')
5+
}
6+
7+
@directory = Fog::Storage[:aws].directories.create(directory_attributes)
8+
9+
tests('Direct ListObjectsV2 API usage') do
10+
11+
# Create some test files
12+
file1 = @directory.files.create(:body => 'test1', :key => 'prefix/file1.txt')
13+
file2 = @directory.files.create(:body => 'test2', :key => 'prefix/file2.txt')
14+
file3 = @directory.files.create(:body => 'test3', :key => 'other/file3.txt')
15+
file4 = @directory.files.create(:body => 'test4', :key => 'file4.txt')
16+
17+
tests('#list_objects_v2 basic functionality') do
18+
response = Fog::Storage[:aws].list_objects_v2(@directory.key)
19+
20+
tests('returns proper response structure').returns(true) do
21+
response.body.has_key?('Contents') &&
22+
response.body.has_key?('KeyCount') &&
23+
response.body.has_key?('IsTruncated')
24+
end
25+
26+
tests('returns all files').returns(4) do
27+
response.body['Contents'].size
28+
end
29+
30+
tests('has V2-specific KeyCount').returns(4) do
31+
response.body['KeyCount']
32+
end
33+
end
34+
35+
tests('#list_objects_v2 with parameters') do
36+
37+
tests('with prefix') do
38+
response = Fog::Storage[:aws].list_objects_v2(@directory.key, 'prefix' => 'prefix/')
39+
40+
tests('filters by prefix').returns(2) do
41+
response.body['Contents'].size
42+
end
43+
44+
tests('KeyCount reflects filtered results').returns(2) do
45+
response.body['KeyCount']
46+
end
47+
end
48+
49+
tests('with max-keys') do
50+
response = Fog::Storage[:aws].list_objects_v2(@directory.key, 'max-keys' => 2)
51+
52+
tests('limits results').returns(2) do
53+
response.body['Contents'].size
54+
end
55+
56+
tests('is truncated').returns(true) do
57+
response.body['IsTruncated']
58+
end
59+
60+
tests('has next continuation token').returns(true) do
61+
!response.body['NextContinuationToken'].nil?
62+
end
63+
end
64+
65+
tests('with start-after') do
66+
response = Fog::Storage[:aws].list_objects_v2(@directory.key, 'start-after' => 'other/file3.txt')
67+
68+
tests('starts after specified key').returns(true) do
69+
keys = response.body['Contents'].map { |obj| obj['Key'] }
70+
keys.none? { |key| key <= 'other/file3.txt' }
71+
end
72+
end
73+
74+
tests('with delimiter') do
75+
response = Fog::Storage[:aws].list_objects_v2(@directory.key, 'delimiter' => '/')
76+
77+
tests('respects delimiter').returns(true) do
78+
# Should have common prefixes and fewer direct contents
79+
response.body.has_key?('CommonPrefixes') && response.body['CommonPrefixes'].size > 0
80+
end
81+
end
82+
83+
tests('with fetch-owner') do
84+
response = Fog::Storage[:aws].list_objects_v2(@directory.key, 'fetch-owner' => true)
85+
86+
tests('request succeeds').returns(true) do
87+
response.body.has_key?('Contents')
88+
end
89+
end unless Fog.mocking?
90+
91+
end
92+
93+
tests('pagination with continuation token') do
94+
first_page = Fog::Storage[:aws].list_objects_v2(@directory.key, 'max-keys' => 2)
95+
96+
if first_page.body['IsTruncated'] && first_page.body['NextContinuationToken']
97+
second_page = Fog::Storage[:aws].list_objects_v2(@directory.key, 'continuation-token' => first_page.body['NextContinuationToken'])
98+
99+
tests('second page has different objects').returns(true) do
100+
first_keys = first_page.body['Contents'].map { |obj| obj['Key'] }
101+
second_keys = second_page.body['Contents'].map { |obj| obj['Key'] }
102+
(first_keys & second_keys).empty?
103+
end
104+
end
105+
end
106+
107+
# Clean up test files
108+
file1.destroy
109+
file2.destroy
110+
file3.destroy
111+
file4.destroy
112+
end
113+
114+
@directory.destroy
115+
116+
end
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
Shindo.tests('Fog::Storage[:aws] | list_objects_v2 requests', ["aws"]) do
2+
@aws_bucket_name = 'foglistobjectsv2tests-' + Time.now.to_i.to_s(32)
3+
4+
tests('success') do
5+
6+
@bucket_v2_format = {
7+
'CommonPrefixes' => [],
8+
'IsTruncated' => Fog::Boolean,
9+
'ContinuationToken' => Fog::Nullable::String,
10+
'NextContinuationToken' => Fog::Nullable::String,
11+
'KeyCount' => Integer,
12+
'MaxKeys' => Integer,
13+
'Name' => String,
14+
'Prefix' => Fog::Nullable::String,
15+
'StartAfter' => Fog::Nullable::String,
16+
'Contents' => [{
17+
'ETag' => String,
18+
'Key' => String,
19+
'LastModified' => Time,
20+
'Owner' => Fog::Nullable::Hash,
21+
'Size' => Integer,
22+
'StorageClass' => String
23+
}]
24+
}
25+
26+
tests("#put_bucket('#{@aws_bucket_name}')").succeeds do
27+
Fog::Storage[:aws].put_bucket(@aws_bucket_name)
28+
end
29+
30+
file = Fog::Storage[:aws].directories.get(@aws_bucket_name).files.create(:body => 'y', :key => 'x')
31+
32+
tests("#list_objects_v2('#{@aws_bucket_name}')").formats(@bucket_v2_format) do
33+
Fog::Storage[:aws].list_objects_v2(@aws_bucket_name).body
34+
end
35+
36+
file.destroy
37+
38+
file1 = Fog::Storage[:aws].directories.get(@aws_bucket_name).files.create(:body => 'a', :key => 'a/a1/file1')
39+
file2 = Fog::Storage[:aws].directories.get(@aws_bucket_name).files.create(:body => 'ab', :key => 'a/file2')
40+
file3 = Fog::Storage[:aws].directories.get(@aws_bucket_name).files.create(:body => 'abc', :key => 'b/file3')
41+
file4 = Fog::Storage[:aws].directories.get(@aws_bucket_name).files.create(:body => 'abcd', :key => 'file4')
42+
43+
tests("#list_objects_v2('#{@aws_bucket_name}')") do
44+
before do
45+
@bucket = Fog::Storage[:aws].list_objects_v2(@aws_bucket_name)
46+
end
47+
48+
tests(".body['Contents'].map{|n| n['Key']}").returns(["a/a1/file1", "a/file2", "b/file3", "file4"]) do
49+
@bucket.body['Contents'].map{|n| n['Key']}
50+
end
51+
52+
tests(".body['Contents'].map{|n| n['Size']}").returns([1, 2, 3, 4]) do
53+
@bucket.body['Contents'].map{|n| n['Size']}
54+
end
55+
56+
tests(".body['CommonPrefixes']").returns([]) do
57+
@bucket.body['CommonPrefixes']
58+
end
59+
60+
tests(".body['KeyCount']").returns(4) do
61+
@bucket.body['KeyCount']
62+
end
63+
64+
tests(".body['IsTruncated']").returns(false) do
65+
@bucket.body['IsTruncated']
66+
end
67+
end
68+
69+
tests("#list_objects_v2('#{@aws_bucket_name}', 'delimiter' => '/')") do
70+
before do
71+
@bucket = Fog::Storage[:aws].list_objects_v2(@aws_bucket_name, 'delimiter' => '/')
72+
end
73+
74+
tests(".body['Contents'].map{|n| n['Key']}").returns(['file4']) do
75+
@bucket.body['Contents'].map{|n| n['Key']}
76+
end
77+
78+
tests(".body['CommonPrefixes']").returns(['a/', 'b/']) do
79+
@bucket.body['CommonPrefixes']
80+
end
81+
82+
tests(".body['KeyCount']").returns(3) do
83+
@bucket.body['KeyCount']
84+
end
85+
end
86+
87+
tests("#list_objects_v2('#{@aws_bucket_name}', 'delimiter' => '/', 'prefix' => 'a/')") do
88+
before do
89+
@bucket = Fog::Storage[:aws].list_objects_v2(@aws_bucket_name, 'delimiter' => '/', 'prefix' => 'a/')
90+
end
91+
92+
tests(".body['Contents'].map{|n| n['Key']}").returns(['a/file2']) do
93+
@bucket.body['Contents'].map{|n| n['Key']}
94+
end
95+
96+
tests(".body['CommonPrefixes']").returns(['a/a1/']) do
97+
@bucket.body['CommonPrefixes']
98+
end
99+
100+
tests(".body['KeyCount']").returns(2) do
101+
@bucket.body['KeyCount']
102+
end
103+
end
104+
105+
tests("#list_objects_v2('#{@aws_bucket_name}', 'start-after' => 'a/file2')") do
106+
before do
107+
@bucket = Fog::Storage[:aws].list_objects_v2(@aws_bucket_name, 'start-after' => 'a/file2')
108+
end
109+
110+
tests(".body['Contents'].map{|n| n['Key']}").returns(['b/file3', 'file4']) do
111+
@bucket.body['Contents'].map{|n| n['Key']}
112+
end
113+
114+
tests(".body['KeyCount']").returns(2) do
115+
@bucket.body['KeyCount']
116+
end
117+
end
118+
119+
tests("#list_objects_v2('#{@aws_bucket_name}', 'max-keys' => 2)") do
120+
before do
121+
@bucket = Fog::Storage[:aws].list_objects_v2(@aws_bucket_name, 'max-keys' => 2)
122+
end
123+
124+
tests(".body['Contents'].size").returns(2) do
125+
@bucket.body['Contents'].size
126+
end
127+
128+
tests(".body['KeyCount']").returns(2) do
129+
@bucket.body['KeyCount']
130+
end
131+
132+
tests(".body['IsTruncated']").returns(true) do
133+
@bucket.body['IsTruncated']
134+
end
135+
136+
tests(".body['NextContinuationToken']").returns(true) do
137+
!@bucket.body['NextContinuationToken'].nil?
138+
end
139+
end
140+
141+
# Test pagination with continuation token
142+
tests("pagination with continuation token") do
143+
first_page = Fog::Storage[:aws].list_objects_v2(@aws_bucket_name, 'max-keys' => 2)
144+
next_token = first_page.body['NextContinuationToken']
145+
146+
tests("#list_objects_v2 with continuation-token").succeeds do
147+
second_page = Fog::Storage[:aws].list_objects_v2(@aws_bucket_name, 'continuation-token' => next_token)
148+
tests("second page has remaining objects").returns(true) do
149+
second_page.body['Contents'].size > 0
150+
end
151+
end if next_token
152+
end
153+
154+
# Test fetch-owner parameter
155+
tests("#list_objects_v2('#{@aws_bucket_name}', 'fetch-owner' => true)") do
156+
before do
157+
@bucket = Fog::Storage[:aws].list_objects_v2(@aws_bucket_name, 'fetch-owner' => true)
158+
end
159+
160+
tests("owner information is included").returns(true) do
161+
@bucket.body['Contents'].first['Owner'] != nil
162+
end
163+
end unless Fog.mocking?
164+
165+
tests("#list_objects_v2('#{@aws_bucket_name}', 'fetch-owner' => false)") do
166+
before do
167+
@bucket = Fog::Storage[:aws].list_objects_v2(@aws_bucket_name, 'fetch-owner' => false)
168+
end
169+
170+
tests("owner information is not included").returns(true) do
171+
@bucket.body['Contents'].first['Owner'].nil?
172+
end
173+
end unless Fog.mocking?
174+
175+
file1.destroy; file2.destroy; file3.destroy; file4.destroy
176+
177+
tests("#delete_bucket('#{@aws_bucket_name}')").succeeds do
178+
Fog::Storage[:aws].delete_bucket(@aws_bucket_name)
179+
end
180+
181+
end
182+
183+
tests('failure') do
184+
tests("#list_objects_v2('fognonbucket')").raises(Excon::Errors::NotFound) do
185+
Fog::Storage[:aws].list_objects_v2('fognonbucket')
186+
end
187+
188+
tests("#list_objects_v2 without bucket name").raises(ArgumentError) do
189+
Fog::Storage[:aws].list_objects_v2(nil)
190+
end
191+
end
192+
193+
end

0 commit comments

Comments
 (0)