mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-04-27 22:36:36 +02:00
test: e2e for encrypted sync
This commit is contained in:
@@ -841,3 +841,210 @@ async fn test_profile_bypass_rules_sync() {
|
||||
client.delete(&test_key, None).await.unwrap();
|
||||
client.delete(&empty_key, None).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_encrypted_profile_sync() {
|
||||
use donutbrowser_lib::sync::encryption::{
|
||||
decrypt_bytes, derive_profile_key, encrypt_bytes, generate_salt,
|
||||
};
|
||||
|
||||
ensure_sync_server_available().await;
|
||||
let client = TestClient::new();
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let profile_id = uuid::Uuid::new_v4().to_string();
|
||||
let test_key = format!("profiles/{}.tar.gz.enc", profile_id);
|
||||
|
||||
let bundle = create_test_profile_bundle(temp_dir.path());
|
||||
|
||||
let salt = generate_salt();
|
||||
let password = "test-e2e-encryption-password";
|
||||
let key = derive_profile_key(password, &salt).unwrap();
|
||||
|
||||
let encrypted = encrypt_bytes(&key, &bundle).unwrap();
|
||||
assert_ne!(
|
||||
encrypted, bundle,
|
||||
"Encrypted data should differ from plaintext"
|
||||
);
|
||||
assert!(
|
||||
encrypted.len() > bundle.len(),
|
||||
"Encrypted data includes nonce + auth tag overhead"
|
||||
);
|
||||
|
||||
let presign = client
|
||||
.presign_upload(&test_key, "application/octet-stream")
|
||||
.await
|
||||
.unwrap();
|
||||
client
|
||||
.upload_bytes(&presign.url, &encrypted, "application/octet-stream")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let stat = client.stat(&test_key).await.unwrap();
|
||||
assert!(stat.exists);
|
||||
assert_eq!(stat.size, Some(encrypted.len() as u64));
|
||||
|
||||
let download_presign = client.presign_download(&test_key).await.unwrap();
|
||||
let downloaded = client.download_bytes(&download_presign.url).await.unwrap();
|
||||
assert_eq!(downloaded.len(), encrypted.len());
|
||||
|
||||
let decrypted = decrypt_bytes(&key, &downloaded).unwrap();
|
||||
assert_eq!(
|
||||
decrypted, bundle,
|
||||
"Decrypted content should match original bundle"
|
||||
);
|
||||
|
||||
let extract_dir = temp_dir.path().join("extracted");
|
||||
fs::create_dir_all(&extract_dir).unwrap();
|
||||
let metadata = extract_bundle(&decrypted, &extract_dir);
|
||||
|
||||
assert_eq!(metadata["id"], "test-profile-id");
|
||||
assert_eq!(metadata["name"], "Test Profile");
|
||||
assert_eq!(metadata["browser"], "chromium");
|
||||
assert_eq!(metadata["version"], "120.0.0");
|
||||
assert!(metadata["sync_enabled"].as_bool().unwrap());
|
||||
let tags = metadata["tags"].as_array().unwrap();
|
||||
assert_eq!(tags.len(), 2);
|
||||
assert_eq!(tags[0], "test");
|
||||
assert_eq!(tags[1], "e2e");
|
||||
|
||||
let test_file = extract_dir.join("profile").join("test_file.txt");
|
||||
assert!(test_file.exists());
|
||||
assert_eq!(fs::read_to_string(test_file).unwrap(), "test content");
|
||||
|
||||
let wrong_key = derive_profile_key("wrong-password", &salt).unwrap();
|
||||
assert!(
|
||||
decrypt_bytes(&wrong_key, &downloaded).is_err(),
|
||||
"Decryption with wrong key should fail"
|
||||
);
|
||||
|
||||
let different_salt = generate_salt();
|
||||
let wrong_salt_key = derive_profile_key(password, &different_salt).unwrap();
|
||||
assert!(
|
||||
decrypt_bytes(&wrong_salt_key, &downloaded).is_err(),
|
||||
"Decryption with key derived from wrong salt should fail"
|
||||
);
|
||||
|
||||
client.delete(&test_key, None).await.unwrap();
|
||||
let final_stat = client.stat(&test_key).await.unwrap();
|
||||
assert!(!final_stat.exists);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_encrypted_delta_sync() {
|
||||
use donutbrowser_lib::sync::encryption::{
|
||||
decrypt_bytes, derive_profile_key, encrypt_bytes, generate_salt,
|
||||
};
|
||||
|
||||
ensure_sync_server_available().await;
|
||||
let client = TestClient::new();
|
||||
let profile_id = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
let salt = generate_salt();
|
||||
let password = "delta-sync-test-password";
|
||||
let key = derive_profile_key(password, &salt).unwrap();
|
||||
|
||||
let file1_key = format!("profiles/{}/files/file1.txt.enc", profile_id);
|
||||
let file2_key = format!("profiles/{}/files/file2.txt.enc", profile_id);
|
||||
let file3_key = format!("profiles/{}/files/file3.txt.enc", profile_id);
|
||||
|
||||
let content1 = b"file one content";
|
||||
let content2 = b"file two content";
|
||||
let content3 = b"file three content";
|
||||
|
||||
let encrypted1 = encrypt_bytes(&key, content1).unwrap();
|
||||
let encrypted2 = encrypt_bytes(&key, content2).unwrap();
|
||||
let encrypted3 = encrypt_bytes(&key, content3).unwrap();
|
||||
|
||||
let presign1 = client
|
||||
.presign_upload(&file1_key, "application/octet-stream")
|
||||
.await
|
||||
.unwrap();
|
||||
client
|
||||
.upload_bytes(&presign1.url, &encrypted1, "application/octet-stream")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let presign2 = client
|
||||
.presign_upload(&file2_key, "application/octet-stream")
|
||||
.await
|
||||
.unwrap();
|
||||
client
|
||||
.upload_bytes(&presign2.url, &encrypted2, "application/octet-stream")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let presign3 = client
|
||||
.presign_upload(&file3_key, "application/octet-stream")
|
||||
.await
|
||||
.unwrap();
|
||||
client
|
||||
.upload_bytes(&presign3.url, &encrypted3, "application/octet-stream")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
for (file_key, expected_content) in [
|
||||
(&file1_key, content1.as_slice()),
|
||||
(&file2_key, content2.as_slice()),
|
||||
(&file3_key, content3.as_slice()),
|
||||
] {
|
||||
let dl_presign = client.presign_download(file_key).await.unwrap();
|
||||
let downloaded = client.download_bytes(&dl_presign.url).await.unwrap();
|
||||
let decrypted = decrypt_bytes(&key, &downloaded).unwrap();
|
||||
assert_eq!(
|
||||
decrypted, expected_content,
|
||||
"Decrypted content mismatch for {file_key}"
|
||||
);
|
||||
}
|
||||
|
||||
let stat1_before = client.stat(&file1_key).await.unwrap();
|
||||
let stat2_before = client.stat(&file2_key).await.unwrap();
|
||||
let stat3_before = client.stat(&file3_key).await.unwrap();
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
|
||||
let updated_content2 = b"file two content -- updated with new data";
|
||||
let encrypted2_updated = encrypt_bytes(&key, updated_content2).unwrap();
|
||||
|
||||
let presign2_update = client
|
||||
.presign_upload(&file2_key, "application/octet-stream")
|
||||
.await
|
||||
.unwrap();
|
||||
client
|
||||
.upload_bytes(
|
||||
&presign2_update.url,
|
||||
&encrypted2_updated,
|
||||
"application/octet-stream",
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let stat2_after = client.stat(&file2_key).await.unwrap();
|
||||
assert_ne!(
|
||||
stat2_before.size, stat2_after.size,
|
||||
"File2 size should have changed after update"
|
||||
);
|
||||
|
||||
let stat1_after = client.stat(&file1_key).await.unwrap();
|
||||
let stat3_after = client.stat(&file3_key).await.unwrap();
|
||||
assert_eq!(
|
||||
stat1_before.size, stat1_after.size,
|
||||
"File1 should be unchanged"
|
||||
);
|
||||
assert_eq!(
|
||||
stat3_before.size, stat3_after.size,
|
||||
"File3 should be unchanged"
|
||||
);
|
||||
|
||||
let dl_presign2 = client.presign_download(&file2_key).await.unwrap();
|
||||
let downloaded2 = client.download_bytes(&dl_presign2.url).await.unwrap();
|
||||
let decrypted2 = decrypt_bytes(&key, &downloaded2).unwrap();
|
||||
assert_eq!(
|
||||
decrypted2,
|
||||
updated_content2.to_vec(),
|
||||
"Updated file2 should decrypt to new content"
|
||||
);
|
||||
|
||||
client.delete(&file1_key, None).await.unwrap();
|
||||
client.delete(&file2_key, None).await.unwrap();
|
||||
client.delete(&file3_key, None).await.unwrap();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user