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,
|
NotSupported,
|
||||||
BucketNotEmpty,
|
BucketNotEmpty,
|
||||||
BucketAlreadyOwnedByYou,
|
BucketAlreadyOwnedByYou,
|
||||||
|
InvalidWriteOffset,
|
||||||
|
|
||||||
OtherError(String),
|
OtherError(String),
|
||||||
}
|
}
|
||||||
@ -76,6 +77,7 @@ impl ErrorCode {
|
|||||||
"notsupported" => ErrorCode::NotSupported,
|
"notsupported" => ErrorCode::NotSupported,
|
||||||
"bucketnotempty" => ErrorCode::BucketNotEmpty,
|
"bucketnotempty" => ErrorCode::BucketNotEmpty,
|
||||||
"bucketalreadyownedbyyou" => ErrorCode::BucketAlreadyOwnedByYou,
|
"bucketalreadyownedbyyou" => ErrorCode::BucketAlreadyOwnedByYou,
|
||||||
|
"invalidwriteoffset" => ErrorCode::InvalidWriteOffset,
|
||||||
|
|
||||||
v => ErrorCode::OtherError(v.to_owned()),
|
v => ErrorCode::OtherError(v.to_owned()),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,8 +65,8 @@ impl FromS3Response for GetBucketLifecycleResponse {
|
|||||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||||
let config: LifecycleConfig = {
|
let config: LifecycleConfig = {
|
||||||
let body = resp.bytes().await?;
|
let body = resp.bytes().await?;
|
||||||
let mut root = Element::parse(body.reader())?;
|
let root = Element::parse(body.reader())?;
|
||||||
LifecycleConfig::from_xml(&mut root)?
|
LifecycleConfig::from_xml(&root)?
|
||||||
};
|
};
|
||||||
let updated_at: Option<DateTime<Utc>> = headers
|
let updated_at: Option<DateTime<Utc>> = headers
|
||||||
.get("x-minio-lifecycleconfig-updatedat")
|
.get("x-minio-lifecycleconfig-updatedat")
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use minio::s3::builders::ObjectContent;
|
use minio::s3::builders::ObjectContent;
|
||||||
|
use minio::s3::error::{Error, ErrorCode};
|
||||||
use minio::s3::response::{
|
use minio::s3::response::{
|
||||||
AppendObjectResponse, GetObjectResponse, PutObjectContentResponse, PutObjectResponse,
|
AppendObjectResponse, GetObjectResponse, PutObjectContentResponse, PutObjectResponse,
|
||||||
StatObjectResponse,
|
StatObjectResponse,
|
||||||
@ -25,8 +26,51 @@ use minio_common::test_context::TestContext;
|
|||||||
use minio_common::utils::rand_object_name;
|
use minio_common::utils::rand_object_name;
|
||||||
use tokio::sync::mpsc;
|
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)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
|
||||||
async fn append_object() {
|
async fn append_object_0() {
|
||||||
let ctx = TestContext::new_from_env();
|
let ctx = TestContext::new_from_env();
|
||||||
if !ctx.client.is_minio_express() {
|
if !ctx.client.is_minio_express() {
|
||||||
println!("Skipping test because it is NOT running in MinIO Express mode");
|
println!("Skipping test because it is NOT running in MinIO Express mode");
|
||||||
@ -38,44 +82,16 @@ async fn append_object() {
|
|||||||
|
|
||||||
let content1 = "aaaa";
|
let content1 = "aaaa";
|
||||||
let content2 = "bbbb";
|
let content2 = "bbbb";
|
||||||
|
|
||||||
let size = content1.len() as u64;
|
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 data2: SegmentedBytes = SegmentedBytes::from(content2.to_string());
|
||||||
|
let offset_bytes = size;
|
||||||
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 resp: AppendObjectResponse = ctx
|
let resp: AppendObjectResponse = ctx
|
||||||
.client
|
.client
|
||||||
.append_object(&bucket_name, &object_name, data2, size)
|
.append_object(&bucket_name, &object_name, data2, offset_bytes)
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -90,6 +106,7 @@ async fn append_object() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// retrieve the content of the object and check that it is "aaaabbbb"
|
||||||
let content: String = String::from_utf8(
|
let content: String = String::from_utf8(
|
||||||
resp.content
|
resp.content
|
||||||
.to_segmented_bytes()
|
.to_segmented_bytes()
|
||||||
@ -106,6 +123,214 @@ async fn append_object() {
|
|||||||
assert_eq!(resp.object_size, size * 2);
|
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)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
|
||||||
async fn append_object_content_0() {
|
async fn append_object_content_0() {
|
||||||
let ctx = TestContext::new_from_env();
|
let ctx = TestContext::new_from_env();
|
||||||
@ -119,39 +344,9 @@ async fn append_object_content_0() {
|
|||||||
|
|
||||||
let content1 = "aaaaa";
|
let content1 = "aaaaa";
|
||||||
let content2 = "bbbbb";
|
let content2 = "bbbbb";
|
||||||
|
|
||||||
let size = content1.len() as u64;
|
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
|
create_object_helper(content1, &bucket_name, &object_name, &ctx).await;
|
||||||
.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);
|
|
||||||
|
|
||||||
let resp: AppendObjectResponse = ctx
|
let resp: AppendObjectResponse = ctx
|
||||||
.client
|
.client
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user