From 2698a03582df52ef7d5b816b4a74c89becdb7de8 Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Sun, 2 Aug 2026 19:20:17 +0800 Subject: [PATCH] test(kms): pin admin KMS response shapes where they are served (#5626) The snapshots in crates/kms/src/api_types.rs pinned DeleteKeyResponse, ListKeysResponse, DescribeKeyResponse and CancelKeyDeletionResponse, none of which is serialized by any handler: those endpoints answer with DeleteKmsKeyResponse and siblings in rustfs/src/admin/handlers/kms_keys.rs, separate types carrying different fields. A breaking change to an admin response could not fail them. Tag, untag and update-description had the same gap, where the handler discards the kms-side response and serves its own KmsKeyMetadataResponse. Pin the shapes in the crate that produces them, and delete the four kms mirrors. They were never in the pub use api_types list, had no constructors and no callers, and only looked live because those snapshots named them. Keep the api_types snapshots that pin something real: configure, start, stop and status are served verbatim by kms_dynamic, and the tag family are live ObjectEncryptionService return types whose snapshots pin this crate's public API rather than a wire shape. --- crates/kms/src/api_types.rs | 115 +++-------- ...sts__kms_cancel_key_deletion_response.snap | 9 - ...types__tests__kms_delete_key_response.snap | 10 - ...ts__kms_describe_key_response_missing.snap | 9 - ..._types__tests__kms_list_keys_response.snap | 14 -- rustfs/src/admin/handlers/kms_key_metadata.rs | 43 ++++- rustfs/src/admin/handlers/kms_keys.rs | 182 +++++++++++++++++- ...ests__kms_admin_key_metadata_response.snap | 22 +++ ...ey_metadata_response_without_metadata.snap | 10 + ...ms_admin_cancel_key_deletion_response.snap | 22 +++ ...ts__kms_admin_create_key_api_response.snap | 20 ++ ..._tests__kms_admin_create_key_response.snap | 22 +++ ..._tests__kms_admin_delete_key_response.snap | 31 +++ ...in_delete_key_response_without_impact.snap | 11 ++ ...__kms_admin_describe_key_api_response.snap | 19 ++ ...ests__kms_admin_describe_key_response.snap | 42 ++++ ...s_admin_describe_key_response_missing.snap | 10 + ..._admin_generate_data_key_api_response.snap | 9 + ...sts__kms_admin_list_keys_api_response.snap | 27 +++ ...__tests__kms_admin_list_keys_response.snap | 29 +++ 20 files changed, 518 insertions(+), 138 deletions(-) delete mode 100644 crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_cancel_key_deletion_response.snap delete mode 100644 crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_delete_key_response.snap delete mode 100644 crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_describe_key_response_missing.snap delete mode 100644 crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_list_keys_response.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_key_metadata__tests__kms_admin_key_metadata_response.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_key_metadata__tests__kms_admin_key_metadata_response_without_metadata.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_cancel_key_deletion_response.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_create_key_api_response.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_create_key_response.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_delete_key_response.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_delete_key_response_without_impact.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_describe_key_api_response.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_describe_key_response.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_describe_key_response_missing.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_generate_data_key_api_response.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_list_keys_api_response.snap create mode 100644 rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_list_keys_response.snap diff --git a/crates/kms/src/api_types.rs b/crates/kms/src/api_types.rs index c0c50c2bf..b53aa4f34 100644 --- a/crates/kms/src/api_types.rs +++ b/crates/kms/src/api_types.rs @@ -21,7 +21,6 @@ use crate::config::{ redacted_secret, redacted_secret_option, }; use crate::service_manager::KmsServiceStatus; -use crate::types::KeyMetadata; use serde::{Deserialize, Deserializer, Serialize}; use std::collections::HashMap; use std::fmt; @@ -1199,6 +1198,19 @@ mod tests { } } + /// The shapes this crate owns, and only those. + /// + /// The first four are served verbatim by the dynamic-configuration admin + /// handlers, so pinning them here pins the wire. The last three never + /// reach a socket: `ObjectEncryptionService` returns them and the admin + /// layer answers with its own `KmsKeyMetadataResponse` instead, so what + /// they pin is this crate's public API, not the wire. + /// + /// No key-management response belongs in this test. Those endpoints are + /// served from types defined in the `rustfs` crate, and a copy here could + /// only ever agree with them by accident — see + /// `kms_key_admin_responses_have_stable_json_shapes` in + /// `rustfs/src/admin/handlers/kms_keys.rs`. #[test] fn kms_management_responses_have_stable_json_shapes() { insta::assert_json_snapshot!( @@ -1234,41 +1246,6 @@ mod tests { config_summary: None, }) ); - insta::assert_json_snapshot!( - "kms_delete_key_response", - stable_json_value(DeleteKeyResponse { - success: true, - message: "key scheduled for deletion".to_string(), - key_id: "key-a".to_string(), - deletion_date: Some("2026-07-01T00:00:00Z".to_string()), - }) - ); - insta::assert_json_snapshot!( - "kms_list_keys_response", - stable_json_value(ListKeysResponse { - success: true, - message: "keys listed".to_string(), - keys: vec!["key-a".to_string(), "key-b".to_string()], - truncated: true, - next_marker: Some("key-b".to_string()), - }) - ); - insta::assert_json_snapshot!( - "kms_describe_key_response_missing", - stable_json_value(DescribeKeyResponse { - success: false, - message: "key not found".to_string(), - key_metadata: None, - }) - ); - insta::assert_json_snapshot!( - "kms_cancel_key_deletion_response", - stable_json_value(CancelKeyDeletionResponse { - success: true, - message: "key deletion canceled".to_string(), - key_id: "key-a".to_string(), - }) - ); insta::assert_json_snapshot!( "kms_update_key_description_response", stable_json_value(UpdateKeyDescriptionResponse { @@ -1299,60 +1276,18 @@ mod tests { // ======================================== // Key Management API Types // ======================================== - -/// JSON shape returned by the admin delete-key endpoint. -/// -/// The delete *request* shape lives in [`crate::types::DeleteKeyRequest`] — -/// there is deliberately no copy here, because the immediate-deletion gate -/// (`force_immediate` + `confirm_key_id`) must have exactly one definition. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DeleteKeyResponse { - /// Success flag - pub success: bool, - /// Status message - pub message: String, - /// Key ID that was deleted or scheduled for deletion - pub key_id: String, - /// Deletion date (if scheduled) - pub deletion_date: Option, -} - -/// Response from list keys operation -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ListKeysResponse { - /// Success flag - pub success: bool, - /// Status message - pub message: String, - /// List of key IDs - pub keys: Vec, - /// Whether more keys are available - pub truncated: bool, - /// Next marker for pagination - pub next_marker: Option, -} - -/// Response from describe key operation -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DescribeKeyResponse { - /// Success flag - pub success: bool, - /// Status message - pub message: String, - /// Key metadata - pub key_metadata: Option, -} - -/// Response from cancel key deletion operation -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CancelKeyDeletionResponse { - /// Success flag - pub success: bool, - /// Status message - pub message: String, - /// Key ID - pub key_id: String, -} +// +// What remains here is the key-metadata trio, and nothing else belongs. +// Create, delete, list, describe and cancel-deletion are served from types +// defined in the `rustfs` crate (`rustfs/src/admin/handlers/kms_keys.rs`) +// carrying fields this crate knows nothing about, so a copy here would shadow +// `crate::types` under the same name while agreeing with the wire only by +// accident. +// +// The same holds for `DeleteKeyRequest`: it lives in `crate::types` alone, so +// the immediate-deletion gate (`force_immediate` + `confirm_key_id`) has +// exactly one definition and cannot be silently dropped by deserializing into +// a copy that lacks it. /// Request to update key description #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_cancel_key_deletion_response.snap b/crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_cancel_key_deletion_response.snap deleted file mode 100644 index 5daac3fb8..000000000 --- a/crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_cancel_key_deletion_response.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: crates/kms/src/api_types.rs -expression: "serde_json::to_value(CancelKeyDeletionResponse\n{\n success: true, message: \"key deletion canceled\".to_string(), key_id:\n \"key-a\".to_string(),\n}).expect(\"cancel deletion response should serialize\")" ---- -{ - "key_id": "key-a", - "message": "key deletion canceled", - "success": true -} diff --git a/crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_delete_key_response.snap b/crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_delete_key_response.snap deleted file mode 100644 index c5862e213..000000000 --- a/crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_delete_key_response.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: crates/kms/src/api_types.rs -expression: "serde_json::to_value(DeleteKeyResponse\n{\n success: true, message: \"key scheduled for deletion\".to_string(), key_id:\n \"key-a\".to_string(), deletion_date:\n Some(\"2026-07-01T00:00:00Z\".to_string()),\n}).expect(\"delete key response should serialize\")" ---- -{ - "deletion_date": "2026-07-01T00:00:00Z", - "key_id": "key-a", - "message": "key scheduled for deletion", - "success": true -} diff --git a/crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_describe_key_response_missing.snap b/crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_describe_key_response_missing.snap deleted file mode 100644 index d8dc89abd..000000000 --- a/crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_describe_key_response_missing.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: crates/kms/src/api_types.rs -expression: "serde_json::to_value(DescribeKeyResponse\n{\n success: false, message: \"key not found\".to_string(), key_metadata: None,\n}).expect(\"describe key response should serialize\")" ---- -{ - "key_metadata": null, - "message": "key not found", - "success": false -} diff --git a/crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_list_keys_response.snap b/crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_list_keys_response.snap deleted file mode 100644 index c6afb89a7..000000000 --- a/crates/kms/src/snapshots/rustfs_kms__api_types__tests__kms_list_keys_response.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: crates/kms/src/api_types.rs -expression: "serde_json::to_value(ListKeysResponse\n{\n success: true, message: \"keys listed\".to_string(), keys:\n vec![\"key-a\".to_string(), \"key-b\".to_string()], truncated: true,\n next_marker: Some(\"key-b\".to_string()),\n}).expect(\"list keys response should serialize\")" ---- -{ - "keys": [ - "key-a", - "key-b" - ], - "message": "keys listed", - "next_marker": "key-b", - "success": true, - "truncated": true -} diff --git a/rustfs/src/admin/handlers/kms_key_metadata.rs b/rustfs/src/admin/handlers/kms_key_metadata.rs index d252afd61..679e501fd 100644 --- a/rustfs/src/admin/handlers/kms_key_metadata.rs +++ b/rustfs/src/admin/handlers/kms_key_metadata.rs @@ -407,12 +407,13 @@ impl Operation for UntagKmsKeyHandler { #[cfg(test)] mod tests { use super::*; + use crate::admin::handlers::kms_keys::stable_json_value; use base64::Engine as _; use rustfs_kms::KmsManager; use rustfs_kms::backends::local::LocalKmsBackend; use rustfs_kms::backends::static_kms::StaticKmsBackend; use rustfs_kms::config::KmsConfig; - use rustfs_kms::types::CreateKeyRequest; + use rustfs_kms::types::{CreateKeyRequest, KeyState, KeyUsage}; use rustfs_policy::policy::action::AdminAction; use std::sync::Arc; @@ -617,6 +618,46 @@ mod tests { assert_eq!(metadata_error_status(&KmsError::key_not_found("gone")), StatusCode::NOT_FOUND); } + /// The single JSON body all three metadata endpoints answer with. + /// + /// `rustfs-kms` returns a separate `TagKeyResponse`/`UntagKeyResponse`/ + /// `UpdateKeyDescriptionResponse` per operation, but this handler discards + /// those and builds its own reply, so only a snapshot in this crate can + /// fail when the served shape changes. + #[test] + fn kms_key_metadata_response_has_a_stable_json_shape() { + insta::assert_json_snapshot!( + "kms_admin_key_metadata_response", + stable_json_value(KmsKeyMetadataResponse { + success: true, + message: "key tags updated".to_string(), + key_id: "key-a".to_string(), + key_metadata: Some(KeyMetadata { + key_id: "key-a".to_string(), + key_state: KeyState::Enabled, + key_usage: KeyUsage::EncryptDecrypt, + description: Some("snapshot key".to_string()), + creation_date: "2026-01-01T00:00:00Z[UTC]".parse().expect("snapshot timestamp should parse"), + deletion_date: None, + origin: "RUSTFS_KMS".to_string(), + key_manager: "RUSTFS".to_string(), + tags: HashMap::from([("name".to_string(), "key-a".to_string())]), + }), + }) + ); + // A describe that failed after the update succeeded drops the echo, + // and the field stays present as `null` rather than disappearing. + insta::assert_json_snapshot!( + "kms_admin_key_metadata_response_without_metadata", + stable_json_value(KmsKeyMetadataResponse { + success: false, + message: "Failed to tag key: key not found".to_string(), + key_id: "key-a".to_string(), + key_metadata: None, + }) + ); + } + #[test] fn metadata_auth_actions_use_dedicated_kms_actions() { assert_eq!( diff --git a/rustfs/src/admin/handlers/kms_keys.rs b/rustfs/src/admin/handlers/kms_keys.rs index 29fd16ec7..a856e379e 100644 --- a/rustfs/src/admin/handlers/kms_keys.rs +++ b/rustfs/src/admin/handlers/kms_keys.rs @@ -438,17 +438,46 @@ impl Operation for DescribeKeyHandler { } } +/// Serialize a KMS admin response into a snapshot-stable JSON value. +/// +/// Response bodies carry `HashMap` tags and metadata, whose iteration order +/// varies per run; sorting every object by key is what makes a snapshot of +/// them reproducible. Shared with [`super::kms_key_metadata`], which pins its +/// own response shape the same way. +#[cfg(test)] +pub(super) fn stable_json_value(value: impl serde::Serialize) -> serde_json::Value { + fn sorted(value: serde_json::Value) -> serde_json::Value { + match value { + serde_json::Value::Array(values) => serde_json::Value::Array(values.into_iter().map(sorted).collect()), + serde_json::Value::Object(map) => { + let mut entries = map.into_iter().collect::>(); + entries.sort_by(|(left, _), (right, _)| left.cmp(right)); + serde_json::Value::Object(entries.into_iter().map(|(key, value)| (key, sorted(value))).collect()) + } + value => value, + } + } + + sorted(serde_json::to_value(value).expect("admin KMS response should serialize")) +} + #[cfg(test)] mod tests { use super::{ - CancelKmsKeyDeletionRequest, CreateKeyApiRequest, CreateKmsKeyRequest, DeleteKmsKeyRequest, DeleteKmsKeyResponse, - DescribeKmsKeyResponse, GenerateDataKeyApiRequest, delete_key_error_status, delete_request_from_query, extract_key_id, - extract_query_params, key_impact_if_requested, key_list_filters, kms_create_key_actions, kms_delete_key_actions, - kms_describe_key_actions, kms_generate_data_key_actions, kms_list_keys_actions, scoped_key_id, wants_key_impact, + CancelKmsKeyDeletionRequest, CancelKmsKeyDeletionResponse, CreateKeyApiRequest, CreateKeyApiResponse, + CreateKmsKeyRequest, CreateKmsKeyResponse, DeleteKmsKeyRequest, DeleteKmsKeyResponse, DescribeKeyApiResponse, + DescribeKmsKeyResponse, GenerateDataKeyApiRequest, GenerateDataKeyApiResponse, ListKeysApiResponse, ListKmsKeysResponse, + delete_key_error_status, delete_request_from_query, extract_key_id, extract_query_params, key_impact_if_requested, + key_list_filters, kms_create_key_actions, kms_delete_key_actions, kms_describe_key_actions, + kms_generate_data_key_actions, kms_list_keys_actions, scoped_key_id, stable_json_value, wants_key_impact, }; use http::Uri; use hyper::StatusCode; - use rustfs_kms::{KeyImpactReport, KeyReference, KeyReferenceKind, KeyStatus, KeyUsage, KmsError, ReferenceScope}; + use jiff::Zoned; + use rustfs_kms::{ + KeyImpactReport, KeyInfo, KeyMetadata, KeyReference, KeyReferenceKind, KeyState, KeyStatus, KeyUsage, KmsError, + ReferenceScope, + }; use rustfs_policy::policy::action::{Action, AdminAction, KmsAction}; use rustfs_policy::policy::{Args, Policy}; use std::collections::HashMap; @@ -885,6 +914,149 @@ mod tests { impact } + fn fixed_zoned(value: &str) -> Zoned { + value.parse().expect("snapshot timestamp should parse") + } + + fn snapshot_key_metadata() -> KeyMetadata { + KeyMetadata { + key_id: "key-a".to_string(), + key_state: KeyState::Enabled, + key_usage: KeyUsage::EncryptDecrypt, + description: Some("snapshot key".to_string()), + creation_date: fixed_zoned("2026-01-01T00:00:00Z[UTC]"), + deletion_date: Some(fixed_zoned("2026-02-01T00:00:00Z[UTC]")), + origin: "RUSTFS_KMS".to_string(), + key_manager: "RUSTFS".to_string(), + tags: HashMap::from([("name".to_string(), "key-a".to_string())]), + } + } + + fn snapshot_key_info() -> KeyInfo { + KeyInfo { + key_id: "key-a".to_string(), + description: Some("snapshot key".to_string()), + algorithm: "AES_256".to_string(), + usage: KeyUsage::EncryptDecrypt, + status: KeyStatus::Active, + version: 1, + metadata: HashMap::from([("origin".to_string(), "RUSTFS_KMS".to_string())]), + tags: HashMap::from([("name".to_string(), "key-a".to_string())]), + created_at: fixed_zoned("2026-01-01T00:00:00Z[UTC]"), + rotated_at: Some(fixed_zoned("2026-01-15T00:00:00Z[UTC]")), + created_by: Some("admin".to_string()), + } + } + + /// Every JSON body this module serves, pinned where it is served from. + /// + /// `rustfs-kms` owns look-alike response types with the same operation + /// names and different fields. A snapshot over there cannot fail when one + /// of the types below changes, so pinning the admin wire contract has to + /// happen in this crate, next to the types that are actually serialized. + /// + /// `Option` fields are covered in both states: none of these types skip a + /// `None`, and a consumer that reads `impact` or `key_metadata` as + /// explicit `null` breaks if one later starts being omitted instead. + #[test] + fn kms_key_admin_responses_have_stable_json_shapes() { + insta::assert_json_snapshot!( + "kms_admin_create_key_response", + stable_json_value(CreateKmsKeyResponse { + success: true, + message: "key created successfully".to_string(), + key_id: "key-a".to_string(), + key_metadata: Some(snapshot_key_metadata()), + }) + ); + insta::assert_json_snapshot!( + "kms_admin_create_key_api_response", + stable_json_value(CreateKeyApiResponse { + key_id: "key-a".to_string(), + key_metadata: snapshot_key_metadata(), + }) + ); + insta::assert_json_snapshot!( + "kms_admin_describe_key_api_response", + stable_json_value(DescribeKeyApiResponse { + key_metadata: snapshot_key_metadata(), + }) + ); + insta::assert_json_snapshot!( + "kms_admin_list_keys_api_response", + stable_json_value(ListKeysApiResponse { + keys: vec![snapshot_key_info()], + truncated: true, + next_marker: Some("key-b".to_string()), + }) + ); + insta::assert_json_snapshot!( + "kms_admin_generate_data_key_api_response", + stable_json_value(GenerateDataKeyApiResponse { + key_id: "key-a".to_string(), + plaintext_key: "cGxhaW50ZXh0LXBsYWNlaG9sZGVy".to_string(), + ciphertext_blob: "Y2lwaGVydGV4dC1wbGFjZWhvbGRlcg==".to_string(), + }) + ); + insta::assert_json_snapshot!( + "kms_admin_delete_key_response", + stable_json_value(DeleteKmsKeyResponse { + success: true, + message: "key scheduled for deletion".to_string(), + key_id: "key-a".to_string(), + deletion_date: Some("2026-02-01T00:00:00Z".to_string()), + impact: Some(referenced_impact()), + }) + ); + insta::assert_json_snapshot!( + "kms_admin_delete_key_response_without_impact", + stable_json_value(DeleteKmsKeyResponse { + success: false, + message: "key not found".to_string(), + key_id: "key-a".to_string(), + deletion_date: None, + impact: None, + }) + ); + insta::assert_json_snapshot!( + "kms_admin_cancel_key_deletion_response", + stable_json_value(CancelKmsKeyDeletionResponse { + success: true, + message: "key deletion canceled".to_string(), + key_id: "key-a".to_string(), + key_metadata: Some(snapshot_key_metadata()), + }) + ); + insta::assert_json_snapshot!( + "kms_admin_list_keys_response", + stable_json_value(ListKmsKeysResponse { + success: true, + message: "keys listed".to_string(), + keys: vec![snapshot_key_info()], + truncated: true, + next_marker: Some("key-b".to_string()), + }) + ); + insta::assert_json_snapshot!( + "kms_admin_describe_key_response", + stable_json_value(DescribeKmsKeyResponse { + success: true, + message: "key described successfully".to_string(), + key_metadata: Some(snapshot_key_metadata()), + impact: Some(referenced_impact()), + }) + ); + insta::assert_json_snapshot!( + "kms_admin_describe_key_response_missing", + stable_json_value(DescribeKmsKeyResponse { + success: false, + message: "key not found".to_string(), + key_metadata: None, + impact: None, + }) + ); + } + /// Scheduling a deletion for a key that bucket configuration still points /// at succeeds — it destroys nothing and stays cancellable — but the /// caller has to be told, in the same response, what will refuse the diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_key_metadata__tests__kms_admin_key_metadata_response.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_key_metadata__tests__kms_admin_key_metadata_response.snap new file mode 100644 index 000000000..b4a464adc --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_key_metadata__tests__kms_admin_key_metadata_response.snap @@ -0,0 +1,22 @@ +--- +source: rustfs/src/admin/handlers/kms_key_metadata.rs +expression: "stable_json_value(KmsKeyMetadataResponse\n{\n success: true, message: \"key tags updated\".to_string(), key_id:\n \"key-a\".to_string(), key_metadata:\n Some(KeyMetadata\n {\n key_id: \"key-a\".to_string(), key_state: KeyState::Enabled, key_usage:\n KeyUsage::EncryptDecrypt, description:\n Some(\"snapshot key\".to_string()), creation_date:\n \"2026-01-01T00:00:00Z[UTC]\".parse().expect(\"snapshot timestamp should parse\"),\n deletion_date: None, origin: \"RUSTFS_KMS\".to_string(), key_manager:\n \"RUSTFS\".to_string(), tags:\n HashMap::from([(\"name\".to_string(), \"key-a\".to_string())]),\n }),\n})" +--- +{ + "key_id": "key-a", + "key_metadata": { + "creation_date": "2026-01-01T00:00:00+00:00[UTC]", + "deletion_date": null, + "description": "snapshot key", + "key_id": "key-a", + "key_manager": "RUSTFS", + "key_state": "Enabled", + "key_usage": "EncryptDecrypt", + "origin": "RUSTFS_KMS", + "tags": { + "name": "key-a" + } + }, + "message": "key tags updated", + "success": true +} diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_key_metadata__tests__kms_admin_key_metadata_response_without_metadata.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_key_metadata__tests__kms_admin_key_metadata_response_without_metadata.snap new file mode 100644 index 000000000..b65d1c388 --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_key_metadata__tests__kms_admin_key_metadata_response_without_metadata.snap @@ -0,0 +1,10 @@ +--- +source: rustfs/src/admin/handlers/kms_key_metadata.rs +expression: "stable_json_value(KmsKeyMetadataResponse\n{\n success: false, message: \"Failed to tag key: key not found\".to_string(),\n key_id: \"key-a\".to_string(), key_metadata: None,\n})" +--- +{ + "key_id": "key-a", + "key_metadata": null, + "message": "Failed to tag key: key not found", + "success": false +} diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_cancel_key_deletion_response.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_cancel_key_deletion_response.snap new file mode 100644 index 000000000..2b19fd768 --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_cancel_key_deletion_response.snap @@ -0,0 +1,22 @@ +--- +source: rustfs/src/admin/handlers/kms_keys.rs +expression: "stable_json_value(CancelKmsKeyDeletionResponse\n{\n success: true, message: \"key deletion canceled\".to_string(), key_id:\n \"key-a\".to_string(), key_metadata: Some(snapshot_key_metadata()),\n})" +--- +{ + "key_id": "key-a", + "key_metadata": { + "creation_date": "2026-01-01T00:00:00+00:00[UTC]", + "deletion_date": "2026-02-01T00:00:00+00:00[UTC]", + "description": "snapshot key", + "key_id": "key-a", + "key_manager": "RUSTFS", + "key_state": "Enabled", + "key_usage": "EncryptDecrypt", + "origin": "RUSTFS_KMS", + "tags": { + "name": "key-a" + } + }, + "message": "key deletion canceled", + "success": true +} diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_create_key_api_response.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_create_key_api_response.snap new file mode 100644 index 000000000..bcb0a3b7c --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_create_key_api_response.snap @@ -0,0 +1,20 @@ +--- +source: rustfs/src/admin/handlers/kms_keys.rs +expression: "stable_json_value(CreateKeyApiResponse\n{ key_id: \"key-a\".to_string(), key_metadata: snapshot_key_metadata(), })" +--- +{ + "key_id": "key-a", + "key_metadata": { + "creation_date": "2026-01-01T00:00:00+00:00[UTC]", + "deletion_date": "2026-02-01T00:00:00+00:00[UTC]", + "description": "snapshot key", + "key_id": "key-a", + "key_manager": "RUSTFS", + "key_state": "Enabled", + "key_usage": "EncryptDecrypt", + "origin": "RUSTFS_KMS", + "tags": { + "name": "key-a" + } + } +} diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_create_key_response.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_create_key_response.snap new file mode 100644 index 000000000..46bd71ef1 --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_create_key_response.snap @@ -0,0 +1,22 @@ +--- +source: rustfs/src/admin/handlers/kms_keys.rs +expression: "stable_json_value(CreateKmsKeyResponse\n{\n success: true, message: \"key created successfully\".to_string(), key_id:\n \"key-a\".to_string(), key_metadata: Some(snapshot_key_metadata()),\n})" +--- +{ + "key_id": "key-a", + "key_metadata": { + "creation_date": "2026-01-01T00:00:00+00:00[UTC]", + "deletion_date": "2026-02-01T00:00:00+00:00[UTC]", + "description": "snapshot key", + "key_id": "key-a", + "key_manager": "RUSTFS", + "key_state": "Enabled", + "key_usage": "EncryptDecrypt", + "origin": "RUSTFS_KMS", + "tags": { + "name": "key-a" + } + }, + "message": "key created successfully", + "success": true +} diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_delete_key_response.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_delete_key_response.snap new file mode 100644 index 000000000..ab79ae03a --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_delete_key_response.snap @@ -0,0 +1,31 @@ +--- +source: rustfs/src/admin/handlers/kms_keys.rs +expression: "stable_json_value(DeleteKmsKeyResponse\n{\n success: true, message: \"key scheduled for deletion\".to_string(), key_id:\n \"key-a\".to_string(), deletion_date:\n Some(\"2026-02-01T00:00:00Z\".to_string()), impact:\n Some(referenced_impact()),\n})" +--- +{ + "deletion_date": "2026-02-01T00:00:00Z", + "impact": { + "completeness": "exact", + "coverage": { + "not_scanned": [ + "object-envelopes", + "in-progress-multipart-uploads" + ], + "scanned": [ + "bucket-default-encryption", + "service-default-key" + ] + }, + "key_id": "key-a", + "references": [ + { + "detail": "bucket default encryption names this key", + "id": "sse-bucket", + "kind": "bucket-default-encryption" + } + ] + }, + "key_id": "key-a", + "message": "key scheduled for deletion", + "success": true +} diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_delete_key_response_without_impact.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_delete_key_response_without_impact.snap new file mode 100644 index 000000000..7a63de6e0 --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_delete_key_response_without_impact.snap @@ -0,0 +1,11 @@ +--- +source: rustfs/src/admin/handlers/kms_keys.rs +expression: "stable_json_value(DeleteKmsKeyResponse\n{\n success: false, message: \"key not found\".to_string(), key_id:\n \"key-a\".to_string(), deletion_date: None, impact: None,\n})" +--- +{ + "deletion_date": null, + "impact": null, + "key_id": "key-a", + "message": "key not found", + "success": false +} diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_describe_key_api_response.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_describe_key_api_response.snap new file mode 100644 index 000000000..dbdde0c51 --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_describe_key_api_response.snap @@ -0,0 +1,19 @@ +--- +source: rustfs/src/admin/handlers/kms_keys.rs +expression: "stable_json_value(DescribeKeyApiResponse\n{ key_metadata: snapshot_key_metadata(), })" +--- +{ + "key_metadata": { + "creation_date": "2026-01-01T00:00:00+00:00[UTC]", + "deletion_date": "2026-02-01T00:00:00+00:00[UTC]", + "description": "snapshot key", + "key_id": "key-a", + "key_manager": "RUSTFS", + "key_state": "Enabled", + "key_usage": "EncryptDecrypt", + "origin": "RUSTFS_KMS", + "tags": { + "name": "key-a" + } + } +} diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_describe_key_response.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_describe_key_response.snap new file mode 100644 index 000000000..03e362887 --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_describe_key_response.snap @@ -0,0 +1,42 @@ +--- +source: rustfs/src/admin/handlers/kms_keys.rs +expression: "stable_json_value(DescribeKmsKeyResponse\n{\n success: true, message: \"key described successfully\".to_string(),\n key_metadata: Some(snapshot_key_metadata()), impact:\n Some(referenced_impact()),\n})" +--- +{ + "impact": { + "completeness": "exact", + "coverage": { + "not_scanned": [ + "object-envelopes", + "in-progress-multipart-uploads" + ], + "scanned": [ + "bucket-default-encryption", + "service-default-key" + ] + }, + "key_id": "key-a", + "references": [ + { + "detail": "bucket default encryption names this key", + "id": "sse-bucket", + "kind": "bucket-default-encryption" + } + ] + }, + "key_metadata": { + "creation_date": "2026-01-01T00:00:00+00:00[UTC]", + "deletion_date": "2026-02-01T00:00:00+00:00[UTC]", + "description": "snapshot key", + "key_id": "key-a", + "key_manager": "RUSTFS", + "key_state": "Enabled", + "key_usage": "EncryptDecrypt", + "origin": "RUSTFS_KMS", + "tags": { + "name": "key-a" + } + }, + "message": "key described successfully", + "success": true +} diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_describe_key_response_missing.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_describe_key_response_missing.snap new file mode 100644 index 000000000..5f641df87 --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_describe_key_response_missing.snap @@ -0,0 +1,10 @@ +--- +source: rustfs/src/admin/handlers/kms_keys.rs +expression: "stable_json_value(DescribeKmsKeyResponse\n{\n success: false, message: \"key not found\".to_string(), key_metadata: None,\n impact: None,\n})" +--- +{ + "impact": null, + "key_metadata": null, + "message": "key not found", + "success": false +} diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_generate_data_key_api_response.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_generate_data_key_api_response.snap new file mode 100644 index 000000000..f30441956 --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_generate_data_key_api_response.snap @@ -0,0 +1,9 @@ +--- +source: rustfs/src/admin/handlers/kms_keys.rs +expression: "stable_json_value(GenerateDataKeyApiResponse\n{\n key_id: \"key-a\".to_string(), plaintext_key:\n \"cGxhaW50ZXh0LXBsYWNlaG9sZGVy\".to_string(), ciphertext_blob:\n \"Y2lwaGVydGV4dC1wbGFjZWhvbGRlcg==\".to_string(),\n})" +--- +{ + "ciphertext_blob": "Y2lwaGVydGV4dC1wbGFjZWhvbGRlcg==", + "key_id": "key-a", + "plaintext_key": "cGxhaW50ZXh0LXBsYWNlaG9sZGVy" +} diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_list_keys_api_response.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_list_keys_api_response.snap new file mode 100644 index 000000000..e91584d06 --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_list_keys_api_response.snap @@ -0,0 +1,27 @@ +--- +source: rustfs/src/admin/handlers/kms_keys.rs +expression: "stable_json_value(ListKeysApiResponse\n{\n keys: vec![snapshot_key_info()], truncated: true, next_marker:\n Some(\"key-b\".to_string()),\n})" +--- +{ + "keys": [ + { + "algorithm": "AES_256", + "created_at": "2026-01-01T00:00:00+00:00[UTC]", + "created_by": "admin", + "description": "snapshot key", + "key_id": "key-a", + "metadata": { + "origin": "RUSTFS_KMS" + }, + "rotated_at": "2026-01-15T00:00:00+00:00[UTC]", + "status": "Active", + "tags": { + "name": "key-a" + }, + "usage": "EncryptDecrypt", + "version": 1 + } + ], + "next_marker": "key-b", + "truncated": true +} diff --git a/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_list_keys_response.snap b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_list_keys_response.snap new file mode 100644 index 000000000..b9b213235 --- /dev/null +++ b/rustfs/src/admin/handlers/snapshots/rustfs__admin__handlers__kms_keys__tests__kms_admin_list_keys_response.snap @@ -0,0 +1,29 @@ +--- +source: rustfs/src/admin/handlers/kms_keys.rs +expression: "stable_json_value(ListKmsKeysResponse\n{\n success: true, message: \"keys listed\".to_string(), keys:\n vec![snapshot_key_info()], truncated: true, next_marker:\n Some(\"key-b\".to_string()),\n})" +--- +{ + "keys": [ + { + "algorithm": "AES_256", + "created_at": "2026-01-01T00:00:00+00:00[UTC]", + "created_by": "admin", + "description": "snapshot key", + "key_id": "key-a", + "metadata": { + "origin": "RUSTFS_KMS" + }, + "rotated_at": "2026-01-15T00:00:00+00:00[UTC]", + "status": "Active", + "tags": { + "name": "key-a" + }, + "usage": "EncryptDecrypt", + "version": 1 + } + ], + "message": "keys listed", + "next_marker": "key-b", + "success": true, + "truncated": true +}