userver: /data/code/userver/libraries/s3api/src/s3api/models/multipart_upload/responses_test.cpp Source File
Loading...
Searching...
No Matches
responses_test.cpp
1#include <userver/s3api/models/multipart_upload/responses.hpp>
2
3#include <userver/utest/utest.hpp>
4
5USERVER_NAMESPACE_BEGIN
6
7namespace s3api::multipart_upload {
8
9TEST(S3ApiInitiateMultipartUploadResult, ParseResponseBody) {
10 const auto result = InitiateMultipartUploadResult::Parse(
11 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
12 "<InitiateMultipartUploadResult>"
13 "<Bucket>some-bucket</Bucket><Key>some/key</Key><UploadId>123456ABCDEF</UploadId>"
14 "</InitiateMultipartUploadResult>"
15 );
16 EXPECT_EQ(result.bucket, "some-bucket");
17 EXPECT_EQ(result.key, "some/key");
18 EXPECT_EQ(result.upload_id, "123456ABCDEF");
19}
20
21TEST(S3ApiCompleteMultipartUploadeResult, ParseResponseBody) {
22 const auto result = CompleteMultipartUploadResult::Parse(
23 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
24 "<CompleteMultipartUploadResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">"
25 "<Location>https://some-bucket.some-s3.net/some/key</Location>"
26 "<Bucket>some-bucket</Bucket><Key>some/key</Key>"
27 "<UploadId>123456ABCDEF</UploadId>"
28 "<ETag>\"123456\"</ETag>"
29 "</CompleteMultipartUploadResult>"
30 );
31 EXPECT_EQ(result.bucket, "some-bucket");
32 EXPECT_EQ(result.key, "some/key");
33 EXPECT_EQ(result.location, "https://some-bucket.some-s3.net/some/key");
34}
35
36TEST(S3ApiListMultipartUploadsResult, ParseResponseBody) {
37 using std::chrono::milliseconds;
38 using TimePoint = std::chrono::system_clock::time_point;
39
40 // no multipart uploads
41 {
42 const auto result = ListMultipartUploadsResult::Parse(
43 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
44 "<ListMultipartUploadsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">"
45 "<Bucket>some-bucket</Bucket>"
46 "<KeyMarker></KeyMarker><UploadIdMarker/><NextKeyMarker/><NextUploadIdMarker/>"
47 "<IsTruncated>false</IsTruncated>"
48 "<MaxUploads>1000</MaxUploads>"
49 "</ListMultipartUploadsResult>"
50 );
51 EXPECT_EQ(result.bucket, "some-bucket");
52 EXPECT_EQ(result.key_marker, std::string());
53 EXPECT_EQ(result.upload_id_marker, std::string());
54 EXPECT_EQ(result.next_key_marker, std::string());
55 EXPECT_EQ(result.next_upload_id_marker, std::string());
56 EXPECT_FALSE(result.is_truncated);
57 EXPECT_EQ(result.max_uploads, 1000);
58 EXPECT_TRUE(result.uploads.empty());
59 EXPECT_TRUE(result.common_prefixes.empty());
60 }
61 // 2 active multipart uploads
62 {
63 const auto result = ListMultipartUploadsResult::Parse(
64 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
65 "<ListMultipartUploadsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">"
66 "<Bucket>some-bucket</Bucket>"
67 "<KeyMarker/><UploadIdMarker/><NextKeyMarker/><NextUploadIdMarker/>"
68 "<IsTruncated>false</IsTruncated>"
69 "<MaxUploads>10</MaxUploads>"
70 "<Upload>"
71 "<Initiated>2025-08-29T09:29:46.506Z</Initiated><Key>a1/b1/c1</Key>"
72 "<StorageClass>STANDARD</StorageClass><UploadId>00063D7DA9A30F29</UploadId>"
73 "</Upload><Upload>"
74 "<Initiated>2025-08-29T09:29:46Z</Initiated><Key>a1/b1/c2</Key>"
75 "<StorageClass>STANDARD</StorageClass><UploadId>00063D7DA9A77808</UploadId>"
76 "</Upload></ListMultipartUploadsResult>"
77 );
78 EXPECT_EQ(result.bucket, "some-bucket");
79 EXPECT_EQ(result.key_marker, std::string());
80 EXPECT_EQ(result.upload_id_marker, std::string());
81 EXPECT_EQ(result.next_key_marker, std::string());
82 EXPECT_EQ(result.next_upload_id_marker, std::string());
83 EXPECT_TRUE(!result.delimiter);
84 EXPECT_FALSE(result.is_truncated);
85 EXPECT_EQ(result.max_uploads, 10);
86 ASSERT_EQ(result.uploads.size(), 2);
87 EXPECT_EQ(result.uploads[0].key, "a1/b1/c1");
88 EXPECT_EQ(result.uploads[0].upload_id, "00063D7DA9A30F29");
89 EXPECT_EQ(result.uploads[0].initiated_ts, TimePoint(milliseconds(1756459786506)));
90 EXPECT_EQ(result.uploads[1].key, "a1/b1/c2");
91 EXPECT_EQ(result.uploads[1].upload_id, "00063D7DA9A77808");
92 EXPECT_EQ(result.uploads[1].initiated_ts, TimePoint(milliseconds(1756459786000)));
93 EXPECT_TRUE(result.common_prefixes.empty());
94 }
95 // with one delimiter, with max uploads 2
96 {
97 const auto result = ListMultipartUploadsResult::Parse(
98 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
99 "<ListMultipartUploadsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">"
100 "<Bucket>some-bucket</Bucket><KeyMarker>a_1</KeyMarker>"
101 "<UploadIdMarker>00063D7E07565264</UploadIdMarker>"
102 "<Delimiter>/</Delimiter><IsTruncated>true</IsTruncated>"
103 "<NextKeyMarker>a_4</NextKeyMarker>"
104 "<NextUploadIdMarker>00063D7E075A93B9</NextUploadIdMarker>"
105 "<MaxUploads>2</MaxUploads>"
106 "<Upload>"
107 "<Initiated>2025-08-29T09:55:58.535Z</Initiated><Key>a_3</Key>"
108 "<StorageClass>STANDARD</StorageClass><UploadId>00063D7E07565265</UploadId>"
109 "</Upload><Upload><Key>a_4</Key>"
110 "<StorageClass>STANDARD</StorageClass><UploadId>00063D7E075A93B9</UploadId>"
111 "</Upload>"
112 "<CommonPrefixes><Prefix>a_1/</Prefix></CommonPrefixes>"
113 "<CommonPrefixes><Prefix>a_2/</Prefix></CommonPrefixes>"
114 "</ListMultipartUploadsResult>"
115 );
116 EXPECT_EQ(result.bucket, "some-bucket");
117 EXPECT_EQ(result.key_marker, "a_1");
118 EXPECT_EQ(result.upload_id_marker, "00063D7E07565264");
119 EXPECT_EQ(result.next_key_marker, "a_4");
120 EXPECT_EQ(result.next_upload_id_marker, "00063D7E075A93B9");
121 EXPECT_EQ(result.delimiter, "/");
122 EXPECT_TRUE(result.is_truncated);
123 EXPECT_EQ(result.max_uploads, 2);
124 ASSERT_EQ(result.uploads.size(), 2);
125 EXPECT_EQ(result.uploads[0].key, "a_3");
126 EXPECT_EQ(result.uploads[0].upload_id, "00063D7E07565265");
127 EXPECT_EQ(result.uploads[0].initiated_ts, TimePoint(milliseconds(1756461358535)));
128 EXPECT_EQ(result.uploads[1].key, "a_4");
129 EXPECT_EQ(result.uploads[1].upload_id, "00063D7E075A93B9");
130 ASSERT_FALSE(result.uploads[1].initiated_ts.has_value());
131 ASSERT_EQ(result.common_prefixes.size(), 2);
132 EXPECT_EQ(result.common_prefixes[0], "a_1/");
133 EXPECT_EQ(result.common_prefixes[1], "a_2/");
134 }
135}
136
137TEST(S3ApiListPartsResult, ParseResponseBody) {
138 using std::chrono::milliseconds;
139 using TimePoint = std::chrono::system_clock::time_point;
140
141 // no parts in multipart upload
142 {
143 const auto result = ListPartsResult::Parse(
144 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
145 "<ListPartsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">"
146 "<Bucket>some-bucket</Bucket>"
147 "<Key>a_1/b_1/c_1</Key><UploadId>00063DD078D0EFFC</UploadId>"
148 "<StorageClass>STANDARD</StorageClass><PartNumberMarker>0</PartNumberMarker>"
149 "<MaxParts>1000</MaxParts>"
150 "<IsTruncated>false</IsTruncated>"
151 "</ListPartsResult>"
152 );
153 EXPECT_EQ(result.bucket, "some-bucket");
154 EXPECT_EQ(result.key, "a_1/b_1/c_1");
155 EXPECT_EQ(result.upload_id, "00063DD078D0EFFC");
156 EXPECT_EQ(result.max_parts, 1000);
157 EXPECT_EQ(result.part_number_marker, 0);
158 EXPECT_FALSE(result.next_part_number_marker.has_value());
159 EXPECT_FALSE(result.is_truncated);
160 EXPECT_TRUE(result.parts.empty());
161 }
162 // 3 parts; not truncated
163 {
164 const auto result = ListPartsResult::Parse(
165 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
166 "<ListPartsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">"
167 "<Bucket>some-bucket</Bucket>"
168 "<Key>a_1/b_1/c_1</Key><UploadId>00063DD078D0EFFC</UploadId>"
169 "<StorageClass>STANDARD</StorageClass><PartNumberMarker>0</PartNumberMarker>"
170 "<MaxParts>10</MaxParts>"
171 "<IsTruncated>false</IsTruncated>"
172 "<Part><PartNumber>1</PartNumber>"
173 "<LastModified>2025-09-02T12:15:47.363Z</LastModified>"
174 "<ETag>&#34;975cd7fe7fd02f5556d0410161f9243e&#34;</ETag>"
175 "<Size>104857600</Size></Part>"
176 "<Part><PartNumber>2</PartNumber>"
177 "<LastModified>2025-09-02T12:15:50.482Z</LastModified>"
178 "<ETag>&#34;865db48e2a10f7fc8e2d06a5cc390f17&#34;</ETag>"
179 "<Size>104857600</Size></Part>"
180 "<Part><PartNumber>3</PartNumber>"
181 "<LastModified>2025-09-02T12:15:53.717Z</LastModified>"
182 "<ETag>&#34;44f2b11f5bf7f61283eab82bbe0998ca&#34;</ETag>"
183 "<Size>104857600</Size></Part>"
184 "</ListPartsResult>"
185 );
186 EXPECT_EQ(result.bucket, "some-bucket");
187 EXPECT_EQ(result.key, "a_1/b_1/c_1");
188 EXPECT_EQ(result.upload_id, "00063DD078D0EFFC");
189 EXPECT_EQ(result.max_parts, 10);
190 EXPECT_EQ(result.part_number_marker, 0);
191 EXPECT_FALSE(result.next_part_number_marker.has_value());
192 EXPECT_FALSE(result.is_truncated);
193 ASSERT_EQ(result.parts.size(), 3u);
194 EXPECT_EQ(result.parts[0].part_number, 1);
195 EXPECT_EQ(result.parts[0].byte_size, 104857600);
196 EXPECT_EQ(result.parts[0].last_modified_ts, TimePoint(milliseconds(1756815347363)));
197 EXPECT_EQ(result.parts[0].etag, "\"975cd7fe7fd02f5556d0410161f9243e\"");
198 EXPECT_EQ(result.parts[1].part_number, 2);
199 EXPECT_EQ(result.parts[1].byte_size, 104857600);
200 EXPECT_EQ(result.parts[1].last_modified_ts, TimePoint(milliseconds(1756815350482)));
201 EXPECT_EQ(result.parts[1].etag, "\"865db48e2a10f7fc8e2d06a5cc390f17\"");
202 EXPECT_EQ(result.parts[2].part_number, 3);
203 EXPECT_EQ(result.parts[2].byte_size, 104857600);
204 EXPECT_EQ(result.parts[2].last_modified_ts, TimePoint(milliseconds(1756815353717)));
205 EXPECT_EQ(result.parts[2].etag, "\"44f2b11f5bf7f61283eab82bbe0998ca\"");
206 }
207 // 1 part and truncated
208 {
209 const auto result = ListPartsResult::Parse(
210 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
211 "<ListPartsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">"
212 "<Bucket>some-bucket</Bucket>"
213 "<Key>a_1/b_1/c_1</Key><UploadId>00063DD078D0EFFC</UploadId>"
214 "<StorageClass>STANDARD</StorageClass><PartNumberMarker>1</PartNumberMarker>"
215 "<NextPartNumberMarker>2</NextPartNumberMarker><MaxParts>1</MaxParts>"
216 "<IsTruncated>true</IsTruncated>"
217 "<Part><PartNumber>2</PartNumber>"
218 "<LastModified>2025-09-02T12:15:50.482Z</LastModified>"
219 "<ETag>&#34;865db48e2a10f7fc8e2d06a5cc390f17&#34;</ETag>"
220 "<Size>104857600</Size></Part>"
221 "</ListPartsResult>"
222 );
223 EXPECT_EQ(result.bucket, "some-bucket");
224 EXPECT_EQ(result.key, "a_1/b_1/c_1");
225 EXPECT_EQ(result.upload_id, "00063DD078D0EFFC");
226 EXPECT_EQ(result.max_parts, 1);
227 EXPECT_EQ(result.part_number_marker, 1);
228 EXPECT_EQ(result.next_part_number_marker, 2);
229 EXPECT_TRUE(result.is_truncated);
230 ASSERT_EQ(result.parts.size(), 1u);
231 EXPECT_EQ(result.parts[0].part_number, 2);
232 EXPECT_EQ(result.parts[0].byte_size, 104857600);
233 EXPECT_EQ(result.parts[0].last_modified_ts, TimePoint(milliseconds(1756815350482)));
234 EXPECT_EQ(result.parts[0].etag, "\"865db48e2a10f7fc8e2d06a5cc390f17\"");
235 }
236}
237
238} // namespace s3api::multipart_upload
239
240USERVER_NAMESPACE_END