Skip to content

Commit

Permalink
Add Unit tests for VssAccessor
Browse files Browse the repository at this point in the history
  • Loading branch information
G8XSU committed May 4, 2023
1 parent 5d70ce6 commit 0d4dd8d
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 1 deletion.
2 changes: 2 additions & 0 deletions vss-accessor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ prost = "0.11.3"
reqwest = { version = "0.11.13", features = ["rustls-tls"] }

[dev-dependencies]
mockito = "0.31.1"
tokio = { version = "1.22.0", features = ["full"]}

[build-dependencies]
prost-build = { version = "0.11.3" }
Expand Down
2 changes: 1 addition & 1 deletion vss-accessor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::vss::{
};
use crate::vss_error::VssError;

mod vss_error;
pub mod vss_error;

pub mod vss {
include!(concat!(env!("OUT_DIR"), "/org.vss.rs"));
Expand Down
12 changes: 12 additions & 0 deletions vss-accessor/src/vss_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,15 @@ impl From<reqwest::Error> for VssError {
VssError::InternalError(err.to_string())
}
}

impl PartialEq for VssError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(VssError::InvalidRequestError(_e1), VssError::InvalidRequestError(_e2)) => true,
(VssError::ConflictError(_e1), VssError::ConflictError(_e2)) => true,
(VssError::InternalServerError(_e1), VssError::InternalServerError(_e2)) => true,
(VssError::InternalError(_m1), VssError::InternalError(_m2)) => true,
_ => false,
}
}
}
282 changes: 282 additions & 0 deletions vss-accessor/tests/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
#[cfg(test)]
mod tests {
use mockito::{self, Matcher};
use prost::Message;
use vss_accessor::vss::{
ErrorCode, ErrorResponse, GetObjectRequest, GetObjectResponse, KeyValue, ListKeyVersionsRequest,
ListKeyVersionsResponse, PutObjectRequest, PutObjectResponse,
};
use vss_accessor::vss_error::VssError;
use vss_accessor::VssAccessor;

#[tokio::test]
async fn test_get() {
// Spin-up mock server with mock response for given request.
let base_url = mockito::server_url().to_string();
let mock_get_endpoint = "/getObject";

// Set up the mock request/response.
let mock_request = GetObjectRequest { store_id: "store".to_string(), key: "k1".to_string() };
let mut mock_response = GetObjectResponse::default();
mock_response.value = Some(KeyValue { key: "k1".to_string(), version: 2, value: b"k1v2".to_vec() });

// Register the mock endpoint with the mockito server.
let mock_server = mockito::mock("POST", mock_get_endpoint)
.match_body(mock_request.encode_to_vec())
.with_status(200)
.with_body(mock_response.encode_to_vec())
.create();

// Create a new VssAccessor with the mock server URL.
let vss_acc = VssAccessor::new(&base_url).unwrap();
let actual_result = vss_acc.get("store", "k1").await.unwrap();

let expected_result = &mock_response;
assert_eq!(&actual_result, expected_result);

// Verify server endpoint was called exactly once.
mock_server.expect(1).assert();
}

#[tokio::test]
async fn test_put() {
// Spin-up mock server with mock response for given request.
let base_url = mockito::server_url().to_string();
let mock_put_endpoint = "/putObjects";

// Set up the mock request/response.
let mock_request = PutObjectRequest {
store_id: "store".to_string(),
global_version: Some(4),
transaction_items: vec![KeyValue { key: "k1".to_string(), version: 2, value: b"k1v3".to_vec() }],
};
let mock_response = PutObjectResponse::default();

// Register the mock endpoint with the mockito server.
let mock_server = mockito::mock("POST", mock_put_endpoint)
.match_body(mock_request.encode_to_vec())
.with_status(200)
.with_body(mock_response.encode_to_vec())
.create();

// Create a new VssAccessor with the mock server URL.
let vss_acc = VssAccessor::new(&base_url).unwrap();
let actual_result = vss_acc.put("store", Some(4), "k1", 2, b"k1v3").await.unwrap();

let expected_result = &mock_response;
assert_eq!(&actual_result, expected_result);

// Verify server endpoint was called exactly once.
mock_server.expect(1).assert();
}

#[tokio::test]
async fn test_put_tx() {
// Spin-up mock server with mock response for given request.
let base_url = mockito::server_url().to_string();
let mock_put_endpoint = "/putObjects";

// Set up the mock request/response.
let mock_request = PutObjectRequest {
store_id: "store".to_string(),
global_version: Some(5),
transaction_items: vec![
KeyValue { key: "k1".to_string(), version: 3, value: b"k1v4".to_vec() },
KeyValue { key: "k2".to_string(), version: 1, value: b"k2v2".to_vec() },
],
};
let mock_response = PutObjectResponse {};

// Register the mock endpoint with the mockito server.
let mock_server = mockito::mock("POST", mock_put_endpoint)
.match_body(mock_request.encode_to_vec())
.with_status(200)
.with_body(mock_response.encode_to_vec())
.create();

// Create a new VssAccessor with the mock server URL.
let vss_acc = VssAccessor::new(&base_url).unwrap();

let actual_result = vss_acc
.put_tx(
"store",
Some(5),
vec![
KeyValue { key: "k1".to_string(), version: 3, value: b"k1v4".to_vec() },
KeyValue { key: "k2".to_string(), version: 1, value: b"k2v2".to_vec() },
],
)
.await
.unwrap();

let expected_result = &mock_response;
assert_eq!(&actual_result, expected_result);

// Verify server endpoint was called exactly once.
mock_server.expect(1).assert();
}

#[tokio::test]
async fn test_list_key_versions() {
// Spin-up mock server with mock response for given request.
let base_url = mockito::server_url().to_string();
let mock_list_endpoint = "/listKeyVersions";

// Set up the mock request/response.
let mock_request = ListKeyVersionsRequest {
store_id: "store".to_string(),
page_size: Some(5),
page_token: None,
key_prefix: Some("k".into()),
};
let key_versions = vec![
KeyValue { key: "k1".to_string(), version: 3, value: b"".to_vec() },
KeyValue { key: "k2".to_string(), version: 1, value: b"".to_vec() },
];

let mock_response =
ListKeyVersionsResponse { key_versions, global_version: Some(4), next_page_token: Some("k2".into()) };

// Register the mock endpoint with the mockito server.
let mock_server = mockito::mock("POST", mock_list_endpoint)
.match_body(mock_request.encode_to_vec())
.with_status(200)
.with_body(mock_response.encode_to_vec())
.create();

// Create a new VssAccessor with the mock server URL.
let vss_acc = VssAccessor::new(&base_url).unwrap();

let actual_result = vss_acc.list_key_versions("store", "k", Some(5), None).await.unwrap();

let expected_result = &mock_response;
assert_eq!(&actual_result, expected_result);

// Verify server endpoint was called exactly once.
mock_server.expect(1).assert();
}

#[tokio::test]
async fn test_invalid_request_err_handling() {
let base_url = mockito::server_url();
let vss_accessor = VssAccessor::new(&base_url).unwrap();

// Invalid Request Error
let error_response = ErrorResponse {
error_code: ErrorCode::InvalidRequestException.into(),
message: "InvalidRequestException".to_string(),
};
let mock_server = mockito::mock("POST", Matcher::Any)
.with_status(400)
.with_body(&error_response.encode_to_vec())
.create();

let get_result = vss_accessor.get("store", "key1").await;
assert_eq!(get_result.unwrap_err(), VssError::InvalidRequestError(ErrorResponse::default()));

let put_result = vss_accessor.put("store", Some(4), "k1", 2, b"k1v3").await;
assert_eq!(put_result.unwrap_err(), VssError::InvalidRequestError(ErrorResponse::default()));

let list_result = vss_accessor.list_key_versions("store", "k", Some(5), None).await;
assert_eq!(list_result.unwrap_err(), VssError::InvalidRequestError(ErrorResponse::default()));

// Verify 3 requests hit the server
mock_server.expect(3).assert();
}

#[tokio::test]
async fn test_conflict_err_handling() {
let base_url = mockito::server_url();
let vss_accessor = VssAccessor::new(&base_url).unwrap();

// Conflict Error
let error_response =
ErrorResponse { error_code: ErrorCode::ConflictException.into(), message: "ConflictException".to_string() };
let mock_server = mockito::mock("POST", Matcher::Any)
.with_status(409)
.with_body(&error_response.encode_to_vec())
.create();

let put_result = vss_accessor.put("store", Some(4), "k1", 2, b"k1v3").await;
assert_eq!(put_result.unwrap_err(), VssError::ConflictError(ErrorResponse::default()));

// Verify 1 requests hit the server
mock_server.expect(1).assert();
}

#[tokio::test]
async fn test_internal_server_err_handling() {
let base_url = mockito::server_url();
let vss_accessor = VssAccessor::new(&base_url).unwrap();

// Internal Server Error
let error_response = ErrorResponse {
error_code: ErrorCode::InternalServerException.into(),
message: "InternalServerException".to_string(),
};
let mock_server = mockito::mock("POST", Matcher::Any)
.with_status(500)
.with_body(&error_response.encode_to_vec())
.create();

let get_result = vss_accessor.get("store", "key1").await;
assert_eq!(get_result.unwrap_err(), VssError::InternalServerError(ErrorResponse::default()));

let put_result = vss_accessor.put("store", Some(4), "k1", 2, b"k1v3").await;
assert_eq!(put_result.unwrap_err(), VssError::InternalServerError(ErrorResponse::default()));

let list_result = vss_accessor.list_key_versions("store", "k", Some(5), None).await;
assert_eq!(list_result.unwrap_err(), VssError::InternalServerError(ErrorResponse::default()));

// Verify 3 requests hit the server
mock_server.expect(3).assert();
}

#[tokio::test]
async fn test_internal_err_handling() {
let base_url = mockito::server_url();
let vss_accessor = VssAccessor::new(&base_url).unwrap();

let error_response = ErrorResponse { error_code: 999, message: "UnknownException".to_string() };
let mut mock_server = mockito::mock("POST", Matcher::Any)
.with_status(999)
.with_body(&error_response.encode_to_vec())
.create();

let get_result = vss_accessor.get("store", "key1").await;
assert_eq!(get_result.unwrap_err(), VssError::InternalError("InternalError".into()));

let put_result = vss_accessor.put("store", Some(4), "k1", 2, b"k1v3").await;
assert_eq!(put_result.unwrap_err(), VssError::InternalError("InternalError".into()));

let list_result = vss_accessor.list_key_versions("store", "k", Some(5), None).await;
assert_eq!(list_result.unwrap_err(), VssError::InternalError("InternalError".into()));

let malformed_error_response = b"malformed";
mock_server = mockito::mock("POST", Matcher::Any)
.with_status(409)
.with_body(&malformed_error_response)
.create();

let get_malformed_err_response = vss_accessor.get("store", "key1").await;
assert_eq!(get_malformed_err_response.unwrap_err(), VssError::InternalError("InternalError".into()));

let put_malformed_err_response = vss_accessor.put("store", Some(4), "k1", 2, b"k1v3").await;
assert_eq!(put_malformed_err_response.unwrap_err(), VssError::InternalError("InternalError".into()));

let list_malformed_err_response = vss_accessor.list_key_versions("store", "k", Some(5), None).await;
assert_eq!(list_malformed_err_response.unwrap_err(), VssError::InternalError("InternalError".into()));

// Requests to endpoints are no longer mocked and will result in network error.
drop(mock_server);

let get_network_err = vss_accessor.get("store", "key1").await;
assert_eq!(get_network_err.unwrap_err(), VssError::InternalError("InternalError".into()));

let put_network_err = vss_accessor.put("store", Some(4), "k1", 2, b"k1v3").await;
assert_eq!(put_network_err.unwrap_err(), VssError::InternalError("InternalError".into()));

let list_network_err = vss_accessor.list_key_versions("store", "k", Some(5), None).await;
assert_eq!(list_network_err.unwrap_err(), VssError::InternalError("InternalError".into()));
}
}

0 comments on commit 0d4dd8d

Please sign in to comment.