mirror of
https://github.com/minio/minio-rs.git
synced 2025-12-06 15:26:51 +08:00
Added several tests to test_append_object (#158)
* Added several tests to test_append_object * moved file creation to create_object_helper
This commit is contained in:
parent
24a1321673
commit
75602b2673
@ -46,6 +46,7 @@ pub enum ErrorCode {
|
||||
NotSupported,
|
||||
BucketNotEmpty,
|
||||
BucketAlreadyOwnedByYou,
|
||||
InvalidWriteOffset,
|
||||
|
||||
OtherError(String),
|
||||
}
|
||||
@ -76,6 +77,7 @@ impl ErrorCode {
|
||||
"notsupported" => ErrorCode::NotSupported,
|
||||
"bucketnotempty" => ErrorCode::BucketNotEmpty,
|
||||
"bucketalreadyownedbyyou" => ErrorCode::BucketAlreadyOwnedByYou,
|
||||
"invalidwriteoffset" => ErrorCode::InvalidWriteOffset,
|
||||
|
||||
v => ErrorCode::OtherError(v.to_owned()),
|
||||
}
|
||||
|
||||
@ -65,8 +65,8 @@ impl FromS3Response for GetBucketLifecycleResponse {
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let config: LifecycleConfig = {
|
||||
let body = resp.bytes().await?;
|
||||
let mut root = Element::parse(body.reader())?;
|
||||
LifecycleConfig::from_xml(&mut root)?
|
||||
let root = Element::parse(body.reader())?;
|
||||
LifecycleConfig::from_xml(&root)?
|
||||
};
|
||||
let updated_at: Option<DateTime<Utc>> = headers
|
||||
.get("x-minio-lifecycleconfig-updatedat")
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use minio::s3::builders::ObjectContent;
|
||||
use minio::s3::error::{Error, ErrorCode};
|
||||
use minio::s3::response::{
|
||||
AppendObjectResponse, GetObjectResponse, PutObjectContentResponse, PutObjectResponse,
|
||||
StatObjectResponse,
|
||||
@ -25,8 +26,51 @@ use minio_common::test_context::TestContext;
|
||||
use minio_common::utils::rand_object_name;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
/// create an object with the given content and check that it is created correctly
|
||||
async fn create_object_helper(
|
||||
content: &str,
|
||||
bucket_name: &str,
|
||||
object_name: &str,
|
||||
ctx: &TestContext,
|
||||
) {
|
||||
let data: SegmentedBytes = SegmentedBytes::from(content.to_string());
|
||||
let size = content.len() as u64;
|
||||
// create an object (with put) that contains "aaaa"
|
||||
let resp: PutObjectResponse = ctx
|
||||
.client
|
||||
.put_object(bucket_name, object_name, data)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.bucket, bucket_name);
|
||||
assert_eq!(resp.object, object_name);
|
||||
|
||||
let resp: GetObjectResponse = ctx
|
||||
.client
|
||||
.get_object(bucket_name, object_name)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.bucket, bucket_name);
|
||||
assert_eq!(resp.object, object_name);
|
||||
assert_eq!(resp.object_size, size);
|
||||
|
||||
// double check that the content we just have put is "aaaa"
|
||||
let content1: String = String::from_utf8(
|
||||
resp.content
|
||||
.to_segmented_bytes()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_bytes()
|
||||
.to_vec(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(content, content1);
|
||||
}
|
||||
|
||||
/// Append to the end of an existing object (happy flow)
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
|
||||
async fn append_object() {
|
||||
async fn append_object_0() {
|
||||
let ctx = TestContext::new_from_env();
|
||||
if !ctx.client.is_minio_express() {
|
||||
println!("Skipping test because it is NOT running in MinIO Express mode");
|
||||
@ -38,44 +82,16 @@ async fn append_object() {
|
||||
|
||||
let content1 = "aaaa";
|
||||
let content2 = "bbbb";
|
||||
|
||||
let size = content1.len() as u64;
|
||||
let data1: SegmentedBytes = SegmentedBytes::from(content1.to_string());
|
||||
|
||||
create_object_helper(content1, &bucket_name, &object_name, &ctx).await;
|
||||
|
||||
// now append "bbbb" to the end of the object
|
||||
let data2: SegmentedBytes = SegmentedBytes::from(content2.to_string());
|
||||
|
||||
let resp: PutObjectResponse = ctx
|
||||
.client
|
||||
.put_object(&bucket_name, &object_name, data1)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.bucket, bucket_name);
|
||||
assert_eq!(resp.object, object_name);
|
||||
|
||||
let resp: GetObjectResponse = ctx
|
||||
.client
|
||||
.get_object(&bucket_name, &object_name)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.bucket, bucket_name);
|
||||
assert_eq!(resp.object, object_name);
|
||||
assert_eq!(resp.object_size, size);
|
||||
|
||||
let content: String = String::from_utf8(
|
||||
resp.content
|
||||
.to_segmented_bytes()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_bytes()
|
||||
.to_vec(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(content, content1);
|
||||
|
||||
let offset_bytes = size;
|
||||
let resp: AppendObjectResponse = ctx
|
||||
.client
|
||||
.append_object(&bucket_name, &object_name, data2, size)
|
||||
.append_object(&bucket_name, &object_name, data2, offset_bytes)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
@ -90,6 +106,7 @@ async fn append_object() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// retrieve the content of the object and check that it is "aaaabbbb"
|
||||
let content: String = String::from_utf8(
|
||||
resp.content
|
||||
.to_segmented_bytes()
|
||||
@ -106,6 +123,214 @@ async fn append_object() {
|
||||
assert_eq!(resp.object_size, size * 2);
|
||||
}
|
||||
|
||||
/// Append to the beginning of an existing object (happy flow)
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
|
||||
async fn append_object_1() {
|
||||
let ctx = TestContext::new_from_env();
|
||||
if !ctx.client.is_minio_express() {
|
||||
println!("Skipping test because it is NOT running in MinIO Express mode");
|
||||
return;
|
||||
}
|
||||
|
||||
let (bucket_name, _cleanup) = ctx.create_bucket_helper().await;
|
||||
let object_name = rand_object_name();
|
||||
|
||||
let content1 = "aaaa";
|
||||
let content2 = "bbbb";
|
||||
let size = content1.len() as u64;
|
||||
|
||||
create_object_helper(content1, &bucket_name, &object_name, &ctx).await;
|
||||
|
||||
// now append "bbbb" to the beginning of the object
|
||||
let data2: SegmentedBytes = SegmentedBytes::from(content2.to_string());
|
||||
let offset_bytes = 0; // byte 0, thus the beginning of the file
|
||||
let resp: AppendObjectResponse = ctx
|
||||
.client
|
||||
.append_object(&bucket_name, &object_name, data2, offset_bytes)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.bucket, bucket_name);
|
||||
assert_eq!(resp.object, object_name);
|
||||
assert_eq!(resp.object_size, size);
|
||||
|
||||
let resp: GetObjectResponse = ctx
|
||||
.client
|
||||
.get_object(&bucket_name, &object_name)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// retrieve the content of the object and check that it is "bbbb"
|
||||
let content: String = String::from_utf8(
|
||||
resp.content
|
||||
.to_segmented_bytes()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_bytes()
|
||||
.to_vec(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(content, content2);
|
||||
assert_eq!(resp.bucket, bucket_name);
|
||||
assert_eq!(resp.object, object_name);
|
||||
assert_eq!(resp.object_size, size);
|
||||
}
|
||||
|
||||
/// Append to the middle of an existing object (error InvalidWriteOffset)
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
|
||||
async fn append_object_2() {
|
||||
let ctx = TestContext::new_from_env();
|
||||
if !ctx.client.is_minio_express() {
|
||||
println!("Skipping test because it is NOT running in MinIO Express mode");
|
||||
return;
|
||||
}
|
||||
|
||||
let (bucket_name, _cleanup) = ctx.create_bucket_helper().await;
|
||||
let object_name = rand_object_name();
|
||||
|
||||
let content1 = "aaaa";
|
||||
let content2 = "bbbb";
|
||||
let size = content1.len() as u64;
|
||||
|
||||
create_object_helper(content1, &bucket_name, &object_name, &ctx).await;
|
||||
|
||||
// now try to append "bbbb" to the object at an invalid offset
|
||||
let offset_bytes = size - 1;
|
||||
let data2: SegmentedBytes = SegmentedBytes::from(content2.to_string());
|
||||
let resp: Result<AppendObjectResponse, Error> = ctx
|
||||
.client
|
||||
.append_object(&bucket_name, &object_name, data2, offset_bytes)
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match resp {
|
||||
Ok(v) => panic!("append object should have failed; got value: {:?}", v),
|
||||
Err(Error::S3Error(e)) => {
|
||||
assert_eq!(e.code, ErrorCode::InvalidWriteOffset);
|
||||
}
|
||||
Err(e) => panic!("append object should have failed; got error: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Append beyond the size of an existing object (error InvalidWriteOffset)
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
|
||||
async fn append_object_3() {
|
||||
let ctx = TestContext::new_from_env();
|
||||
if !ctx.client.is_minio_express() {
|
||||
println!("Skipping test because it is NOT running in MinIO Express mode");
|
||||
return;
|
||||
}
|
||||
|
||||
let (bucket_name, _cleanup) = ctx.create_bucket_helper().await;
|
||||
let object_name = rand_object_name();
|
||||
|
||||
let content1 = "aaaa";
|
||||
let content2 = "bbbb";
|
||||
let size = content1.len() as u64;
|
||||
|
||||
create_object_helper(content1, &bucket_name, &object_name, &ctx).await;
|
||||
|
||||
// now try to append "bbbb" to the object at an invalid offset
|
||||
let data2: SegmentedBytes = SegmentedBytes::from(content2.to_string());
|
||||
let offset_bytes = size + 1;
|
||||
let resp: Result<AppendObjectResponse, Error> = ctx
|
||||
.client
|
||||
.append_object(&bucket_name, &object_name, data2, offset_bytes)
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match resp {
|
||||
Ok(v) => panic!("append object should have failed; got value: {:?}", v),
|
||||
Err(Error::S3Error(e)) => {
|
||||
assert_eq!(e.code, ErrorCode::InvalidWriteOffset);
|
||||
}
|
||||
Err(e) => panic!("append object should have failed; got error: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Append to the beginning/end of a non-existing object (happy flow)
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
|
||||
async fn append_object_4() {
|
||||
let ctx = TestContext::new_from_env();
|
||||
if !ctx.client.is_minio_express() {
|
||||
println!("Skipping test because it is NOT running in MinIO Express mode");
|
||||
return;
|
||||
}
|
||||
|
||||
let (bucket_name, _cleanup) = ctx.create_bucket_helper().await;
|
||||
let object_name = rand_object_name();
|
||||
|
||||
let content1 = "aaaa";
|
||||
let size = content1.len() as u64;
|
||||
let data1: SegmentedBytes = SegmentedBytes::from(content1.to_string());
|
||||
|
||||
// now append "bbbb" to the beginning of the object
|
||||
let offset_bytes = 0; // byte 0, thus the beginning of the file
|
||||
let resp: AppendObjectResponse = ctx
|
||||
.client
|
||||
.append_object(&bucket_name, &object_name, data1, offset_bytes)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.bucket, bucket_name);
|
||||
assert_eq!(resp.object, object_name);
|
||||
assert_eq!(resp.object_size, size);
|
||||
|
||||
let resp: GetObjectResponse = ctx
|
||||
.client
|
||||
.get_object(&bucket_name, &object_name)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// retrieve the content of the object and check that it is "aaaa"
|
||||
let content: String = String::from_utf8(
|
||||
resp.content
|
||||
.to_segmented_bytes()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_bytes()
|
||||
.to_vec(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(content, content1);
|
||||
assert_eq!(resp.bucket, bucket_name);
|
||||
assert_eq!(resp.object, object_name);
|
||||
assert_eq!(resp.object_size, size);
|
||||
}
|
||||
|
||||
/// Append beyond the size of a non-existing object (error NoSuchKey)
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
|
||||
async fn append_object_5() {
|
||||
let ctx = TestContext::new_from_env();
|
||||
if !ctx.client.is_minio_express() {
|
||||
println!("Skipping test because it is NOT running in MinIO Express mode");
|
||||
return;
|
||||
}
|
||||
|
||||
let (bucket_name, _cleanup) = ctx.create_bucket_helper().await;
|
||||
let object_name = rand_object_name();
|
||||
|
||||
let content1 = "aaaa";
|
||||
let data1: SegmentedBytes = SegmentedBytes::from(content1.to_string());
|
||||
|
||||
let offset_bytes = 1; // byte 1, thus beyond the current length of the (non-existing) file
|
||||
let resp: Result<AppendObjectResponse, Error> = ctx
|
||||
.client
|
||||
.append_object(&bucket_name, &object_name, data1, offset_bytes)
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match resp {
|
||||
Ok(v) => panic!("append object should have failed; got value: {:?}", v),
|
||||
Err(Error::S3Error(e)) => {
|
||||
assert_eq!(e.code, ErrorCode::NoSuchKey);
|
||||
}
|
||||
Err(e) => panic!("append object should have failed; got error: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
|
||||
async fn append_object_content_0() {
|
||||
let ctx = TestContext::new_from_env();
|
||||
@ -119,39 +344,9 @@ async fn append_object_content_0() {
|
||||
|
||||
let content1 = "aaaaa";
|
||||
let content2 = "bbbbb";
|
||||
|
||||
let size = content1.len() as u64;
|
||||
let data1: SegmentedBytes = SegmentedBytes::from(content1.to_string());
|
||||
//let data2: SegmentedBytes = SegmentedBytes::from(content2.to_string());
|
||||
|
||||
let resp: PutObjectResponse = ctx
|
||||
.client
|
||||
.put_object(&bucket_name, &object_name, data1)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.bucket, bucket_name);
|
||||
assert_eq!(resp.object, object_name);
|
||||
|
||||
let resp: GetObjectResponse = ctx
|
||||
.client
|
||||
.get_object(&bucket_name, &object_name)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.bucket, bucket_name);
|
||||
assert_eq!(resp.object, object_name);
|
||||
|
||||
let content: String = String::from_utf8(
|
||||
resp.content
|
||||
.to_segmented_bytes()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_bytes()
|
||||
.to_vec(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(content, content1);
|
||||
create_object_helper(content1, &bucket_name, &object_name, &ctx).await;
|
||||
|
||||
let resp: AppendObjectResponse = ctx
|
||||
.client
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user