Skip to content

Commit

Permalink
Merge pull request #8 from unbekanntes-pferd/bugfix/0KBupload
Browse files Browse the repository at this point in the history
Bugfix/0 k bupload
  • Loading branch information
unbekanntes-pferd committed Aug 9, 2023
2 parents fa13c55 + 8c60a1f commit 36631bf
Show file tree
Hide file tree
Showing 2 changed files with 223 additions and 15 deletions.
26 changes: 13 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "dco3"
version = "0.4.0"
version = "0.4.1"
edition = "2021"
authors = ["Octavio Simone"]
repository = "https://github.com/unbekanntes-pferd/dco3"
Expand All @@ -15,40 +15,40 @@ description = "Async API wrapper for DRACOON in Rust."

[dependencies]
# HTTP client
reqwest = {version = "0.11.14", features = ["json", "stream"]}
reqwest = {version = "0.11.18", features = ["json", "stream"]}
reqwest-middleware = "0.2.2"
reqwest-retry = "0.2.2"

# crypto
dco3_crypto = "0.5.0"

# async runtime and utils
tokio = { version = "1.27.0", features = ["full"] }
tokio-util = { version = "0.7.7", features = ["full"] }
async-trait = "0.1.68"
tokio = { version = "1.29.1", features = ["full"] }
tokio-util = { version = "0.7.8", features = ["full"] }
async-trait = "0.1.72"
async-stream = "0.3.5"
futures-util = "0.3.28"
bytes = "1.4.0"

# parsing
serde = { version = "1.0.159", features = ["derive"] }
serde = { version = "1.0.178", features = ["derive"] }
serde-xml-rs = "0.6.0"
serde_json = "1.0.95"
serde_json = "1.0.104"

# error handling
thiserror = "1.0.2"
retry-policies = "0.1.0"
thiserror = "1.0.44"
retry-policies = "0.1.2"

# logging and tracing
tracing = "0.1.37"
tracing-subscriber = {version = "0.3.17", features = ["env-filter"]}

# utils
url = "2.3.1"
base64 = "0.21.0"
chrono = "0.4.1"
url = "2.4.0"
base64 = "0.21.2"
chrono = "0.4.26"


[dev-dependencies]
mockito = "1.0.2"
mockito = "1.1.0"
tokio-test = "0.4.2"
212 changes: 210 additions & 2 deletions src/nodes/upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,6 @@ impl<R: AsyncRead + Sync + Send + Unpin + 'static> UploadInternal<R> for Dracoon
.expect("size not larger than 32 MB")
];
match reader.read_exact(&mut buffer).await {
Ok(0) => unreachable!("last chunk is empty"),
Ok(n) => {
buffer.truncate(n);
let chunk = bytes::Bytes::from(buffer);
Expand Down Expand Up @@ -579,7 +578,6 @@ impl<R: AsyncRead + Sync + Send + Unpin + 'static> UploadInternal<R> for Dracoon
.expect("size not larger than 32 MB")
];
match crypto_reader.read_exact(&mut buffer).await {
Ok(0) => unreachable!("last chunk is empty"),
Ok(n) => {
buffer.truncate(n);
let chunk = bytes::Bytes::from(buffer);
Expand Down Expand Up @@ -1210,6 +1208,96 @@ mod tests {
assert_node(&node);
}

#[tokio::test]
async fn test_upload_to_s3_unencrypted_no_content() {
let (client, mut mock_server) = get_connected_client().await;

let parent_node: Node =
serde_json::from_str(include_str!("../tests/responses/nodes/node_ok.json")).unwrap();

// test 0KB file
let mock_bytes: Vec<u8> = vec![];

let reader = Cursor::new(mock_bytes);
let reader_clone = BufReader::new(reader);

let file_meta = FileMeta::builder()
.with_name("test".into())
.with_size(0)
.build();

let upload_options = UploadOptions::default();

// mock upload channel
let channel_res = include_str!("../tests/responses/upload/upload_channel_ok.json");

let upload_channel_mock = mock_server
.mock("POST", "/api/v4/nodes/files/uploads")
.with_status(201)
.with_body(channel_res)
.with_header("content-type", "application/json")
.create();

// mock S3 urls
let s3_urls_response =
include_str!("../tests/responses/upload/s3_urls_ok_with_placeholder.json");
let s3_urls_response =
s3_urls_response.replace("$base_url/", client.get_base_url().as_str());

let s3_urls_mock = mock_server
.mock("POST", "/api/v4/nodes/files/uploads/string/s3_urls")
.with_status(201)
.with_body(s3_urls_response.clone())
.with_header("content-type", "application/json")
.create();

let upload_res =
serde_json::from_str::<PresignedUrlList>(s3_urls_response.as_str()).unwrap();

// mock upload to S3
let upload_mock = mock_server
.mock("PUT", "/upload_url")
.with_status(202)
.with_header("etag", "string")
.create();

// mock finalize upload
let finalize_mock = mock_server
.mock("PUT", "/api/v4/nodes/files/uploads/string/s3")
.with_status(202)
.create();

// mock upload status
let status_res = include_str!("../tests/responses/upload/upload_status_ok.json");
let status_mock = mock_server
.mock("GET", "/api/v4/nodes/files/uploads/string")
.with_status(200)
.with_body(status_res)
.with_header("content-type", "application/json")
.create();

let node =
<Dracoon<Connected> as UploadInternal<Cursor<Vec<u8>>>>::upload_to_s3_unencrypted(
&client,
file_meta,
&parent_node,
upload_options,
reader_clone,
None,
None,
)
.await
.unwrap();

upload_channel_mock.assert();
s3_urls_mock.assert();
upload_mock.assert();
finalize_mock.assert();
status_mock.assert();

assert_node(&node);
}

#[tokio::test]
async fn test_upload_to_s3_encrypted() {
let (client, mut mock_server) = get_connected_client().await;
Expand Down Expand Up @@ -1330,4 +1418,124 @@ mod tests {

assert_node(&node);
}

#[tokio::test]
async fn test_upload_to_s3_encrypted_no_content() {
let (client, mut mock_server) = get_connected_client().await;

let parent_node: Node =
serde_json::from_str(include_str!("../tests/responses/nodes/node_ok.json")).unwrap();

// empty file
let mock_bytes: Vec<u8> = vec![];

let reader = Cursor::new(mock_bytes);
let reader_clone = BufReader::new(reader);

let keypair =
DracoonCrypto::create_plain_user_keypair(dco3_crypto::UserKeyPairVersion::RSA4096)
.unwrap();
let enc_keypair =
DracoonCrypto::encrypt_private_key("TopSecret1234!", keypair.clone()).unwrap();
let enc_keypair_json = serde_json::to_string(&enc_keypair).unwrap();

let keypair_mock = mock_server
.mock("GET", "/api/v4/user/account/keypair")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(enc_keypair_json)
.create();

let _kp = client
.get_keypair(Some("TopSecret1234!".into()))
.await
.unwrap();

keypair_mock.assert();

let file_meta = FileMeta::builder()
.with_name("test".into())
.with_size(0)
.build();

let upload_options = UploadOptions::default();

// mock upload channel
let channel_res = include_str!("../tests/responses/upload/upload_channel_ok.json");

let upload_channel_mock = mock_server
.mock("POST", "/api/v4/nodes/files/uploads")
.with_status(201)
.with_body(channel_res)
.with_header("content-type", "application/json")
.create();

// mock S3 urls
let s3_urls_response =
include_str!("../tests/responses/upload/s3_urls_ok_with_placeholder.json");
let s3_urls_response =
s3_urls_response.replace("$base_url/", client.get_base_url().as_str());

let s3_urls_mock = mock_server
.mock("POST", "/api/v4/nodes/files/uploads/string/s3_urls")
.with_status(201)
.with_body(s3_urls_response.clone())
.with_header("content-type", "application/json")
.create();

let upload_res =
serde_json::from_str::<PresignedUrlList>(s3_urls_response.as_str()).unwrap();

// mock upload to S3
let upload_mock = mock_server
.mock("PUT", "/upload_url")
.with_status(202)
.with_header("etag", "string")
.create();

// mock finalize upload
let finalize_mock = mock_server
.mock("PUT", "/api/v4/nodes/files/uploads/string/s3")
.with_status(202)
.create();

// mock upload status
let status_res = include_str!("../tests/responses/upload/upload_status_ok.json");
let status_mock = mock_server
.mock("GET", "/api/v4/nodes/files/uploads/string")
.with_status(200)
.with_body(status_res)
.with_header("content-type", "application/json")
.create();

// mock missing file keys
let missing_keys = include_str!("../tests/responses/nodes/missing_file_keys_empty_ok.json");
let keys_mock = mock_server
.mock("GET", "/api/v4/nodes/missingFileKeys?file_id=2&limit=50")
.with_status(200)
.with_body(missing_keys)
.with_header("content-type", "application/json")
.create();

let node = <Dracoon<Connected> as UploadInternal<Cursor<Vec<u8>>>>::upload_to_s3_encrypted(
&client,
file_meta,
&parent_node,
upload_options,
reader_clone,
None,
None,
)
.await
.unwrap();

upload_channel_mock.assert();
s3_urls_mock.assert();
upload_mock.assert();
finalize_mock.assert();
status_mock.assert();
keys_mock.assert();

assert_node(&node);
}
}

0 comments on commit 36631bf

Please sign in to comment.