Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub mod backoff_policy;
pub mod read_object;
pub mod read_resume_policy;
pub mod retry_policy;
pub use crate::storage::request_options;
pub use crate::storage::streaming_source;

mod control;
Expand Down Expand Up @@ -69,7 +70,10 @@ pub mod error;
/// The messages and enums that are part of this client library.
pub use crate::control::model;
pub mod model_ext;
pub use crate::control::stub;
pub mod stub {
pub use crate::control::stub::*;
pub use crate::storage::stub::*;
}

#[allow(dead_code)]
pub(crate) mod generated;
Expand Down
5 changes: 2 additions & 3 deletions src/storage/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ pub(crate) mod checksum;
pub(crate) mod client;
pub(crate) mod perform_upload;
pub(crate) mod read_object;
pub(crate) mod request_options;
pub mod request_options;
pub mod streaming_source;
// TODO(#2041) - make the stub public
pub(crate) mod stub;
pub mod stub;
pub(crate) mod transport;
pub(crate) mod v1;
pub(crate) mod write_object;
Expand Down
14 changes: 14 additions & 0 deletions src/storage/src/storage/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,20 @@ impl<S> Storage<S>
where
S: crate::storage::stub::Storage + 'static,
{
/// Creates a new client from the provided stub.
///
/// The most common case for calling this function is in tests mocking the
/// client's behavior.
pub fn from_stub(stub: S) -> Self
where
S: super::stub::Storage + 'static,
{
Self {
stub: std::sync::Arc::new(stub),
options: RequestOptions::new(),
}
}

/// Write an object using a local buffer.
///
/// If the data source does **not** implement [Seek] the client library must
Expand Down
106 changes: 105 additions & 1 deletion src/storage/tests/mocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,114 @@

#[cfg(test)]
mod tests {
use gax::error::{
Error,
rpc::{Code, Status},
};
use gcs::Result;
use gcs::model::{Object, ReadObjectRequest};
use gcs::model_ext::{ObjectHighlights, WriteObjectRequest};
use gcs::read_object::ReadObjectResponse;
use gcs::request_options::RequestOptions;
use gcs::streaming_source::{BytesSource, Payload, Seek, StreamingSource};
use google_cloud_storage as gcs;
use paste::paste;

mockall::mock! {
#[derive(Debug)]
Storage {}
impl gcs::stub::Storage for Storage {
async fn read_object(&self, _req: ReadObjectRequest, _options: RequestOptions) -> Result<ReadObjectResponse>;
async fn write_object_buffered<P: StreamingSource + Send + Sync + 'static>(
&self,
_payload: P,
_req: WriteObjectRequest,
_options: RequestOptions,
) -> Result<Object>;
async fn write_object_unbuffered<P: StreamingSource + Seek + Send + Sync + 'static>(
&self,
_payload: P,
_req: WriteObjectRequest,
_options: RequestOptions,
) -> Result<Object>;
}
}

#[tokio::test]
async fn mock_read_object_fail() {
let mut mock = MockStorage::new();
mock.expect_read_object()
.return_once(|_, _| Err(Error::service(Status::default().set_code(Code::Aborted))));
let client = gcs::client::Storage::from_stub(mock);
let _ = client
.read_object("projects/_/buckets/my-bucket", "my-object")
.send()
.await
.unwrap_err();
}

#[tokio::test]
async fn mock_read_object_success() -> anyhow::Result<()> {
const LAZY: &str = "the quick brown fox jumps over the lazy dog";
let object = {
let mut o = ObjectHighlights::default();
o.etag = "custom-etag".to_string();
o
};

let mut mock = MockStorage::new();
mock.expect_read_object().return_once({
let o = object.clone();
move |_, _| Ok(ReadObjectResponse::from_source(o, LAZY))
});

let client = gcs::client::Storage::from_stub(mock);
let mut reader = client
.read_object("projects/_/buckets/my-bucket", "my-object")
.send()
.await?;
assert_eq!(&object, &reader.object());

let mut contents = Vec::new();
while let Some(chunk) = reader.next().await.transpose()? {
contents.extend_from_slice(&chunk);
}
let contents = bytes::Bytes::from_owner(contents);
assert_eq!(contents, LAZY);
Ok(())
}

#[tokio::test]
async fn mock_write_object_buffered() {
let mut mock = MockStorage::new();
mock.expect_write_object_buffered()
.return_once(|_payload: Payload<BytesSource>, _, _| {
Err(Error::service(Status::default().set_code(Code::Aborted)))
});
let client = gcs::client::Storage::from_stub(mock);
let _ = client
.write_object("projects/_/buckets/my-bucket", "my-object", "hello")
.send_buffered()
.await
.unwrap_err();
}

#[tokio::test]
async fn mock_write_object_unbuffered() {
let mut mock = MockStorage::new();
mock.expect_write_object_unbuffered().return_once(
|_payload: Payload<BytesSource>, _, _| {
Err(Error::service(Status::default().set_code(Code::Aborted)))
},
);
let client = gcs::client::Storage::from_stub(mock);
let _ = client
.write_object("projects/_/buckets/my-bucket", "my-object", "hello")
.send_unbuffered()
.await
.unwrap_err();
}

#[derive(Debug)]
struct DefaultStorageControl;
impl gcs::stub::StorageControl for DefaultStorageControl {}
Expand Down Expand Up @@ -75,7 +180,6 @@ mod tests {
$( paste! {
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn [<mock_stub_$method>]() {
use gax::error::{Error, rpc::{Code, Status}};
let mut mock = MockStorageControl::new();
mock.[<expect_$method>]()
.times(1)
Expand Down
Loading