test: expand snapshot coverage (#4024)

This commit is contained in:
Zhengchao An
2026-06-29 07:56:37 +08:00
committed by GitHub
parent 1104d630d1
commit e1c1d8edcb
30 changed files with 633 additions and 8 deletions
+1
View File
@@ -27,6 +27,7 @@ thiserror = { workspace = true }
tracing = { workspace = true }
[dev-dependencies]
insta = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true, features = ["test-util","macros","rt-multi-thread"] }
@@ -0,0 +1,24 @@
---
source: crates/concurrency/src/workload.rs
expression: "serde_json::to_value(&registry).expect(\"workload admission registry should serialize\")"
---
{
"entries": [
{
"active": null,
"class": "scanner",
"limit": null,
"queued": null,
"reason": null,
"state": "unknown"
},
{
"active": 3,
"class": "foreground_read",
"limit": 8,
"queued": 1,
"reason": null,
"state": "open"
}
]
}
@@ -0,0 +1,12 @@
---
source: crates/concurrency/src/workload.rs
expression: encoded
---
{
"active": 0,
"class": "foreground_write",
"limit": 0,
"queued": 0,
"reason": "foreground write admission not exposed",
"state": "disabled"
}
+25 -2
View File
@@ -181,7 +181,25 @@ pub trait WorkloadAdmissionSnapshotProvider {
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
use serde_json::{Value, json};
fn sorted_json_value(value: Value) -> Value {
match value {
Value::Array(values) => Value::Array(values.into_iter().map(sorted_json_value).collect()),
Value::Object(object) => {
let mut entries: Vec<_> = object.into_iter().collect();
entries.sort_by(|(left, _), (right, _)| left.cmp(right));
Value::Object(
entries
.into_iter()
.map(|(key, value)| (key, sorted_json_value(value)))
.collect(),
)
}
value => value,
}
}
#[test]
fn workload_contract_covers_required_classes() {
@@ -211,6 +229,10 @@ mod tests {
);
let registry = WorkloadAdmissionRegistrySnapshot::new(vec![unknown.clone(), open.clone()]);
insta::assert_json_snapshot!(
"workload_admission_registry_snapshot",
sorted_json_value(serde_json::to_value(&registry).expect("workload admission registry should serialize"))
);
assert_eq!(registry.entries(), &[unknown, open]);
assert_eq!(
registry.get(WorkloadClass::Scanner).map(|snapshot| snapshot.state),
@@ -262,8 +284,9 @@ mod tests {
let snapshot = WorkloadAdmissionSnapshot::new(WorkloadClass::ForegroundWrite, AdmissionState::Disabled)
.with_counts(Some(0), Some(0), Some(0))
.with_reason("foreground write admission not exposed");
let encoded = serde_json::to_value(&snapshot).expect("serialize workload admission snapshot");
let encoded = sorted_json_value(serde_json::to_value(&snapshot).expect("serialize workload admission snapshot"));
insta::assert_json_snapshot!("workload_admission_snapshot", encoded);
assert_eq!(encoded["class"], json!("foreground_write"));
assert_eq!(encoded["state"], json!("disabled"));
+1
View File
@@ -64,6 +64,7 @@ reqwest = { workspace = true }
vaultrs = { workspace = true }
[dev-dependencies]
insta = { workspace = true }
tempfile = { workspace = true }
temp-env = { workspace = true }
+120
View File
@@ -487,6 +487,29 @@ impl ConfigureKmsRequest {
mod tests {
use super::*;
use crate::config::REDACTED_SECRET;
use serde_json::Value;
fn stable_json_value(value: impl serde::Serialize) -> Value {
sorted_json_value(serde_json::to_value(value).expect("KMS snapshot value should serialize"))
}
fn sorted_json_value(value: Value) -> Value {
match value {
Value::Array(values) => Value::Array(values.into_iter().map(sorted_json_value).collect()),
Value::Object(object) => {
let mut entries: Vec<_> = object.into_iter().collect();
entries.sort_by(|(left, _), (right, _)| left.cmp(right));
Value::Object(
entries
.into_iter()
.map(|(key, value)| (key, sorted_json_value(value)))
.collect(),
)
}
value => value,
}
}
#[test]
fn test_deserialize_vault_kv2_configure_request_accepts_type_aliases() {
@@ -650,6 +673,7 @@ mod tests {
};
let summary = KmsConfigSummary::from(&config);
insta::assert_json_snapshot!("kms_vault_transit_config_summary", stable_json_value(&summary));
assert_eq!(summary.backend_type, KmsBackend::VaultTransit);
assert_eq!(summary.timeout_seconds, 30);
assert_eq!(summary.retry_attempts, 3);
@@ -777,6 +801,102 @@ mod tests {
assert!(rendered.contains("has_master_key") || rendered.contains("has_stored_credentials"));
}
}
#[test]
fn kms_management_responses_have_stable_json_shapes() {
insta::assert_json_snapshot!(
"kms_configure_response",
stable_json_value(ConfigureKmsResponse {
success: true,
message: "kms configured".to_string(),
status: KmsServiceStatus::Configured,
})
);
insta::assert_json_snapshot!(
"kms_start_response",
stable_json_value(StartKmsResponse {
success: true,
message: "kms started".to_string(),
status: KmsServiceStatus::Running,
})
);
insta::assert_json_snapshot!(
"kms_stop_response",
stable_json_value(StopKmsResponse {
success: true,
message: "kms stopped".to_string(),
status: KmsServiceStatus::Configured,
})
);
insta::assert_json_snapshot!(
"kms_status_response",
stable_json_value(KmsStatusResponse {
status: KmsServiceStatus::Running,
backend_type: Some(KmsBackend::VaultTransit),
healthy: Some(true),
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 {
success: true,
message: "key description updated".to_string(),
key_id: "key-a".to_string(),
})
);
insta::assert_json_snapshot!(
"kms_tag_key_response",
stable_json_value(TagKeyResponse {
success: true,
message: "key tags updated".to_string(),
key_id: "key-a".to_string(),
})
);
insta::assert_json_snapshot!(
"kms_untag_key_response",
stable_json_value(UntagKeyResponse {
success: true,
message: "key tags removed".to_string(),
key_id: "key-a".to_string(),
})
);
}
}
// ========================================
@@ -0,0 +1,9 @@
---
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
}
@@ -0,0 +1,9 @@
---
source: crates/kms/src/api_types.rs
expression: "serde_json::to_value(ConfigureKmsResponse\n{\n success: true, message: \"kms configured\".to_string(), status:\n KmsServiceStatus::Configured,\n}).expect(\"configure response should serialize\")"
---
{
"message": "kms configured",
"status": "Configured",
"success": true
}
@@ -0,0 +1,10 @@
---
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
}
@@ -0,0 +1,9 @@
---
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
}
@@ -0,0 +1,14 @@
---
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
}
@@ -0,0 +1,9 @@
---
source: crates/kms/src/api_types.rs
expression: "serde_json::to_value(StartKmsResponse\n{\n success: true, message: \"kms started\".to_string(), status:\n KmsServiceStatus::Running,\n}).expect(\"start response should serialize\")"
---
{
"message": "kms started",
"status": "Running",
"success": true
}
@@ -0,0 +1,10 @@
---
source: crates/kms/src/api_types.rs
expression: "serde_json::to_value(KmsStatusResponse\n{\n status: KmsServiceStatus::Running, backend_type:\n Some(KmsBackend::VaultTransit), healthy: Some(true), config_summary: None,\n}).expect(\"status response should serialize\")"
---
{
"backend_type": "VaultTransit",
"config_summary": null,
"healthy": true,
"status": "Running"
}
@@ -0,0 +1,9 @@
---
source: crates/kms/src/api_types.rs
expression: "serde_json::to_value(StopKmsResponse\n{\n success: true, message: \"kms stopped\".to_string(), status:\n KmsServiceStatus::Configured,\n}).expect(\"stop response should serialize\")"
---
{
"message": "kms stopped",
"status": "Configured",
"success": true
}
@@ -0,0 +1,9 @@
---
source: crates/kms/src/api_types.rs
expression: "serde_json::to_value(TagKeyResponse\n{\n success: true, message: \"key tags updated\".to_string(), key_id:\n \"key-a\".to_string(),\n}).expect(\"tag key response should serialize\")"
---
{
"key_id": "key-a",
"message": "key tags updated",
"success": true
}
@@ -0,0 +1,9 @@
---
source: crates/kms/src/api_types.rs
expression: "serde_json::to_value(UntagKeyResponse\n{\n success: true, message: \"key tags removed\".to_string(), key_id:\n \"key-a\".to_string(),\n}).expect(\"untag key response should serialize\")"
---
{
"key_id": "key-a",
"message": "key tags removed",
"success": true
}
@@ -0,0 +1,9 @@
---
source: crates/kms/src/api_types.rs
expression: "serde_json::to_value(UpdateKeyDescriptionResponse\n{\n success: true, message: \"key description updated\".to_string(), key_id:\n \"key-a\".to_string(),\n}).expect(\"update description response should serialize\")"
---
{
"key_id": "key-a",
"message": "key description updated",
"success": true
}
@@ -0,0 +1,27 @@
---
source: crates/kms/src/api_types.rs
expression: "serde_json::to_value(&summary).expect(\"KMS config summary should serialize\")"
---
{
"backend_summary": {
"address": "http://127.0.0.1:8200",
"auth_method_type": "token",
"backend_type": "vault-transit",
"has_stored_credentials": true,
"mount_path": "transit",
"namespace": "tenant-a",
"skip_tls_verify": false
},
"backend_type": "VaultTransit",
"cache_summary": {
"enable_metrics": true,
"max_keys": 1000,
"ttl_seconds": 3600
},
"cache_ttl_seconds": 3600,
"default_key_id": "rustfs-master-key",
"enable_cache": true,
"max_cached_keys": 1000,
"retry_attempts": 3,
"timeout_seconds": 30
}
+1
View File
@@ -38,6 +38,7 @@ time.workspace = true
uuid.workspace = true
[dev-dependencies]
insta = { workspace = true }
serde_json.workspace = true
tokio = { workspace = true, features = ["macros", "rt"] }
+24 -1
View File
@@ -95,13 +95,36 @@ impl std::error::Error for CapabilitySnapshotError {}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::Value;
fn sorted_json_value(value: Value) -> Value {
match value {
Value::Array(values) => Value::Array(values.into_iter().map(sorted_json_value).collect()),
Value::Object(object) => {
let mut entries: Vec<_> = object.into_iter().collect();
entries.sort_by(|(left, _), (right, _)| left.cmp(right));
Value::Object(
entries
.into_iter()
.map(|(key, value)| (key, sorted_json_value(value)))
.collect(),
)
}
value => value,
}
}
#[test]
fn capability_status_serializes_unknown_and_unsupported_states() {
let unknown = CapabilityStatus::unknown();
let unsupported = CapabilityStatus::unsupported().with_reason("target does not expose profiler");
let encoded = serde_json::to_string(&(unknown, unsupported)).expect("serialize capability statuses");
let encoded = serde_json::to_string(&(&unknown, &unsupported)).expect("serialize capability statuses");
insta::assert_json_snapshot!(
"capability_status_contract",
sorted_json_value(serde_json::to_value((&unknown, &unsupported)).expect("capability statuses should serialize"))
);
let decoded: (CapabilityStatus, CapabilityStatus) =
serde_json::from_str(&encoded).expect("deserialize capability statuses");
@@ -0,0 +1,13 @@
---
source: crates/storage-api/src/capability.rs
expression: "serde_json::to_value((&unknown,\n&unsupported)).expect(\"capability statuses should serialize\")"
---
[
{
"state": "unknown"
},
{
"reason": "target does not expose profiler",
"state": "unsupported"
}
]
@@ -0,0 +1,83 @@
---
source: crates/storage-api/src/topology.rs
expression: "serde_json::to_value(&snapshot).expect(\"topology snapshot should serialize\")"
---
{
"capabilities": {
"failure_domain_labels": {
"state": "supported"
},
"media_labels": {
"state": "supported"
},
"numa": {
"state": "unknown"
},
"profiling": {
"state": "supported"
}
},
"pools": [
{
"labels": {
"additional": {
"room": "a"
},
"media": null,
"node": null,
"numa_node": null,
"rack": null,
"zone": null
},
"pool_id": null,
"pool_index": 0,
"sets": [
{
"disks": [
{
"capabilities": {
"failure_domain": {
"state": "unknown"
},
"media_type": {
"state": "supported"
},
"numa": {
"reason": "not reported",
"state": "unsupported"
},
"profiling": {
"state": "disabled"
}
},
"disk_id": "disk-2",
"disk_index": 2,
"labels": {
"additional": {
"slot": "nvme0"
},
"media": "ssd",
"node": null,
"numa_node": null,
"rack": null,
"zone": null
},
"pool_index": 0,
"set_index": 1
}
],
"labels": {
"media": null,
"node": null,
"numa_node": null,
"rack": null,
"zone": null
},
"pool_index": 0,
"set_id": null,
"set_index": 1
}
]
}
]
}
+23
View File
@@ -95,6 +95,25 @@ pub trait TopologySnapshotProvider: Send + Sync + Debug {
mod tests {
use super::*;
use crate::CapabilityState;
use serde_json::Value;
fn sorted_json_value(value: Value) -> Value {
match value {
Value::Array(values) => Value::Array(values.into_iter().map(sorted_json_value).collect()),
Value::Object(object) => {
let mut entries: Vec<_> = object.into_iter().collect();
entries.sort_by(|(left, _), (right, _)| left.cmp(right));
Value::Object(
entries
.into_iter()
.map(|(key, value)| (key, sorted_json_value(value)))
.collect(),
)
}
value => value,
}
}
#[test]
fn topology_snapshot_allows_missing_and_extra_labels() {
@@ -141,6 +160,10 @@ mod tests {
}"#;
let snapshot: TopologySnapshot = serde_json::from_str(raw).expect("deserialize topology snapshot");
insta::assert_json_snapshot!(
"topology_snapshot_contract",
sorted_json_value(serde_json::to_value(&snapshot).expect("topology snapshot should serialize"))
);
let disk = &snapshot.pools[0].sets[0].disks[0];
assert_eq!(snapshot.pools[0].labels.zone, None);