|
1 | 1 | //! Utilities for chunk upload tests. |
2 | 2 | use std::collections::BTreeMap; |
3 | 3 | use std::error::Error; |
4 | | -use std::io::{self, Read as _}; |
| 4 | +use std::io::{self, Read as _, Write as _}; |
5 | 5 | use std::str; |
6 | 6 | use std::sync::LazyLock; |
7 | 7 |
|
8 | 8 | use flate2::read::GzDecoder; |
| 9 | +use flate2::write::GzEncoder; |
| 10 | +use flate2::Compression; |
9 | 11 | use mockito::Request; |
10 | 12 | use regex::bytes::Regex; |
11 | 13 | use sha1_smol::Sha1; |
@@ -153,75 +155,65 @@ where |
153 | 155 | counts |
154 | 156 | } |
155 | 157 |
|
156 | | -#[cfg(test)] |
157 | | -mod tests { |
158 | | - use std::io::Write as _; |
159 | | - |
160 | | - use flate2::write::GzEncoder; |
161 | | - use flate2::Compression; |
162 | | - |
163 | | - use super::decompress_chunks; |
164 | | - |
165 | | - fn gzip(data: &[u8]) -> Vec<u8> { |
166 | | - let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); |
167 | | - encoder |
168 | | - .write_all(data) |
169 | | - .expect("should write data into gzip encoder"); |
170 | | - encoder.finish().expect("should finish gzip encoding") |
171 | | - } |
172 | | - |
173 | | - fn multipart_body(boundary: &str, parts: &[&[u8]]) -> Vec<u8> { |
174 | | - let mut body = Vec::new(); |
175 | | - |
176 | | - for part in parts { |
177 | | - body.extend_from_slice(format!("--{boundary}\r\n").as_bytes()); |
178 | | - body.extend_from_slice( |
179 | | - b"content-disposition: form-data; name=\"file\"; filename=\"chunk\"\r\n", |
180 | | - ); |
181 | | - body.extend_from_slice(b"content-type: application/octet-stream\r\n\r\n"); |
182 | | - body.extend_from_slice(part); |
183 | | - body.extend_from_slice(b"\r\n"); |
184 | | - } |
185 | | - |
186 | | - body.extend_from_slice(format!("--{boundary}--\r\n").as_bytes()); |
187 | | - body |
188 | | - } |
189 | | - |
190 | | - #[test] |
191 | | - fn decompress_chunks_preserves_duplicate_parts() { |
192 | | - let boundary = "boundary"; |
193 | | - let chunk = gzip(b"duplicate chunk"); |
194 | | - let body = multipart_body(boundary, &[&chunk, &chunk]); |
| 158 | +fn gzip(data: &[u8]) -> Vec<u8> { |
| 159 | + let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); |
| 160 | + encoder |
| 161 | + .write_all(data) |
| 162 | + .expect("should write data into gzip encoder"); |
| 163 | + encoder.finish().expect("should finish gzip encoding") |
| 164 | +} |
195 | 165 |
|
196 | | - let decompressed = |
197 | | - decompress_chunks(&body, boundary).expect("multipart body should decompress"); |
| 166 | +fn multipart_body(boundary: &str, parts: &[&[u8]]) -> Vec<u8> { |
| 167 | + let mut body = Vec::new(); |
198 | 168 |
|
199 | | - assert_eq!( |
200 | | - decompressed, |
201 | | - vec![b"duplicate chunk".to_vec(), b"duplicate chunk".to_vec()] |
202 | | - ); |
203 | | - } |
204 | | - |
205 | | - #[test] |
206 | | - fn decompress_chunks_errors_on_part_missing_header_body_separator() { |
207 | | - let boundary = "boundary"; |
208 | | - let chunk = gzip(b"chunk"); |
209 | | - let mut body = Vec::new(); |
| 169 | + for part in parts { |
210 | 170 | body.extend_from_slice(format!("--{boundary}\r\n").as_bytes()); |
211 | 171 | body.extend_from_slice( |
212 | 172 | b"content-disposition: form-data; name=\"file\"; filename=\"chunk\"\r\n", |
213 | 173 | ); |
214 | | - body.extend_from_slice(b"content-type: application/octet-stream\r\n"); |
215 | | - body.extend_from_slice(&chunk); |
| 174 | + body.extend_from_slice(b"content-type: application/octet-stream\r\n\r\n"); |
| 175 | + body.extend_from_slice(part); |
216 | 176 | body.extend_from_slice(b"\r\n"); |
217 | | - body.extend_from_slice(format!("--{boundary}--\r\n").as_bytes()); |
| 177 | + } |
218 | 178 |
|
219 | | - let error = decompress_chunks(&body, boundary) |
220 | | - .expect_err("multipart part without header/body separator should error"); |
| 179 | + body.extend_from_slice(format!("--{boundary}--\r\n").as_bytes()); |
| 180 | + body |
| 181 | +} |
221 | 182 |
|
222 | | - assert_eq!( |
223 | | - error.to_string(), |
224 | | - r"multipart part 1 of 1 is missing \r\n\r\n header/body separator" |
225 | | - ); |
226 | | - } |
| 183 | +#[test] |
| 184 | +fn decompress_chunks_preserves_duplicate_parts() { |
| 185 | + let boundary = "boundary"; |
| 186 | + let chunk = gzip(b"duplicate chunk"); |
| 187 | + let body = multipart_body(boundary, &[&chunk, &chunk]); |
| 188 | + |
| 189 | + let decompressed = |
| 190 | + decompress_chunks(&body, boundary).expect("multipart body should decompress"); |
| 191 | + |
| 192 | + assert_eq!( |
| 193 | + decompressed, |
| 194 | + vec![b"duplicate chunk".to_vec(), b"duplicate chunk".to_vec()] |
| 195 | + ); |
| 196 | +} |
| 197 | + |
| 198 | +#[test] |
| 199 | +fn decompress_chunks_errors_on_part_missing_header_body_separator() { |
| 200 | + let boundary = "boundary"; |
| 201 | + let chunk = gzip(b"chunk"); |
| 202 | + let mut body = Vec::new(); |
| 203 | + body.extend_from_slice(format!("--{boundary}\r\n").as_bytes()); |
| 204 | + body.extend_from_slice( |
| 205 | + b"content-disposition: form-data; name=\"file\"; filename=\"chunk\"\r\n", |
| 206 | + ); |
| 207 | + body.extend_from_slice(b"content-type: application/octet-stream\r\n"); |
| 208 | + body.extend_from_slice(&chunk); |
| 209 | + body.extend_from_slice(b"\r\n"); |
| 210 | + body.extend_from_slice(format!("--{boundary}--\r\n").as_bytes()); |
| 211 | + |
| 212 | + let error = decompress_chunks(&body, boundary) |
| 213 | + .expect_err("multipart part without header/body separator should error"); |
| 214 | + |
| 215 | + assert_eq!( |
| 216 | + error.to_string(), |
| 217 | + r"multipart part 1 of 1 is missing \r\n\r\n header/body separator" |
| 218 | + ); |
227 | 219 | } |
0 commit comments