mirror of
https://github.com/dani-garcia/vaultwarden.wiki.git
synced 2026-11-16 00:25:56 +02:00
Run the Bitwarden SDK's live end-to-end tests against Vaultwarden (#7953)
* Add a test seeder behind the unsafe_test_mode feature For the end-to-end tests: with the feature, /test/seeder answers the calls the SDK's live tests make to a seeder API (POST /seed with SingleUserScene or TestVectorUserScene, DELETE /seed/<playId>), behind HTTP basic auth from TEST_SEEDER_USERNAME and TEST_SEEDER_PASSWORD, and 404 without them. It creates accounts without any checks, so no release build has the feature. CI builds and tests it with sqlite. * Run the SDK live tests against the test seeder tools/sdk-live-tests/run.sh builds the server with unsafe_test_mode and runs the live tests of the SDK's e2e/wasm suite. For SDK versions whose tests log in to one given account, each user test vector with a master password is seeded with its vault on a fresh server; for versions that seed their own accounts, the tests get the seeder's address. Every vector runs, then a summary. The workflow runs it by hand, and on changes to the seeder or the runner. * Run the SDK live tests daily * Test the published SDK package by default in the SDK live tests
This commit is contained in:
1 parent
730048e3c6
commit
d2d1294098
12 files changed
+997
-1
No files matched your search
@@ -822,6 +822,68 @@ fn set_cipher_content(cipher: &mut Cipher, data: &mut CipherData) -> EmptyResult
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Stores a personal cipher that is already encrypted, as [`save_cipher_data`] stores it but without its checks, events
|
||||
/// and pushes, for the test seeder (`unsafe_test_mode`). `folder_ids` maps the cipher's folder to a seeded one.
|
||||
#[cfg(feature = "unsafe_test_mode")]
|
||||
pub async fn seed_cipher(
|
||||
encrypted: Value,
|
||||
user_id: &UserId,
|
||||
folder_ids: &HashMap<FolderId, FolderId>,
|
||||
conn: &DbConn,
|
||||
) -> ApiResult<CipherId> {
|
||||
// Not in the request model, but a trashed cipher has to stay trashed
|
||||
let deleted_at = encrypted.get("deletedDate").and_then(Value::as_str).map(parse_seed_date).transpose()?;
|
||||
let mut data: CipherData = serde_json::from_value(encrypted)?;
|
||||
let is_blob = data.is_blob();
|
||||
data.validate_content(is_blob)?;
|
||||
data.validate_type_data(is_blob)?;
|
||||
|
||||
let mut cipher = Cipher::new(data.r#type);
|
||||
cipher.user_uuid = Some(user_id.clone());
|
||||
cipher.deleted_at = deleted_at;
|
||||
set_cipher_content(&mut cipher, &mut data)?;
|
||||
cipher.save(conn).await?;
|
||||
|
||||
let folder_id = data.folder_id.and_then(|id| folder_ids.get(&id).cloned());
|
||||
cipher.move_to_folder(folder_id, user_id, conn).await?;
|
||||
cipher.set_favorite(data.favorite, user_id, conn).await?;
|
||||
if let Some(archived_date) = data.archived_date {
|
||||
cipher.set_archived_at(parse_seed_date(&archived_date)?, user_id, conn).await?;
|
||||
}
|
||||
|
||||
// Only the metadata: the files of a seeded cipher's attachments don't exist
|
||||
for attachment in data.attachments.as_ref().and_then(Value::as_array).into_iter().flatten() {
|
||||
let (Some(id), Some(file_name)) = (attachment["id"].as_str(), attachment["fileName"].as_str()) else {
|
||||
err!("A seeded attachment needs an id and a file name")
|
||||
};
|
||||
// Clients send the size as a string
|
||||
let size = match &attachment["size"] {
|
||||
Value::Number(size) => size.as_i64(),
|
||||
Value::String(size) => size.parse().ok(),
|
||||
_ => None,
|
||||
};
|
||||
// Saving is an upsert, and destroying the play deletes the attachment: an id that exists isn't the seeding's
|
||||
if id.is_empty() || !id.bytes().all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-') {
|
||||
err!(format!("Invalid attachment id {}", id.escape_debug()))
|
||||
}
|
||||
let id = AttachmentId(id.to_owned());
|
||||
if Attachment::find_by_id(&id, conn).await.is_some() {
|
||||
err!(format!("An attachment with id {id} already exists"))
|
||||
}
|
||||
let key = attachment["key"].as_str().map(str::to_owned);
|
||||
Attachment::new(id, cipher.uuid.clone(), file_name.to_owned(), size.unwrap_or(0), key).save(conn).await?;
|
||||
}
|
||||
Ok(cipher.uuid)
|
||||
}
|
||||
|
||||
#[cfg(feature = "unsafe_test_mode")]
|
||||
fn parse_seed_date(date: &str) -> ApiResult<NaiveDateTime> {
|
||||
match chrono::DateTime::parse_from_rfc3339(date) {
|
||||
Ok(date) => Ok(date.naive_utc()),
|
||||
Err(e) => err!(format!("Invalid date {}: {e}", date.escape_debug())),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ImportData {
|
||||
|
||||
Reference in new issue
Block a user