0.4.7-pre.008

This commit is contained in:
2026-08-02 11:58:34 +02:00
parent d831623980
commit d6cb4b0cce
11 changed files with 534 additions and 11 deletions

View File

@@ -0,0 +1,170 @@
// file: kb-pipeline-demo-scenarios/src/solana_metaplex_token_metadata_scenarios.rs
// version: 1
//! Deterministic Metaplex Token Metadata scenario inventory for tests and Devnet campaigns.
/// Stable asset family covered by one Metaplex scenario.
#[derive(
Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
)]
#[serde(rename_all = "snake_case")]
pub enum MetaplexTokenMetadataAssetFamily {
/// Non-fungible token.
Nft,
/// Semi-fungible token.
Sft,
/// Fungible token with Metaplex metadata.
Fungible,
/// Collection metadata and verification flow.
Collection,
/// Programmable non-fungible token.
ProgrammableNft,
}
/// Execution mode accepted by one scenario.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MetaplexTokenMetadataScenarioMode {
/// Purely deterministic scenario using synthetic accounts and plans.
Synthetic,
/// Devnet or Testnet simulation without submission.
NetworkSimulation,
/// Explicitly authorized Devnet or Testnet submission.
NetworkSubmission,
}
/// One stable Metaplex scenario reusable by automated tests and demo adapters.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct MetaplexTokenMetadataScenario {
/// Stable scenario identifier.
pub id: std::string::String,
/// Asset family exercised by the scenario.
pub asset_family: crate::MetaplexTokenMetadataAssetFamily,
/// Execution mode.
pub mode: crate::MetaplexTokenMetadataScenarioMode,
/// Stable operation codes executed in order.
pub operation_codes: std::vec::Vec<std::string::String>,
/// Whether the scenario requires a collection account.
pub requires_collection: bool,
/// Whether the scenario requires programmable authorization rules.
pub requires_programmable_rules: bool,
/// Whether a confirmed postcondition is mandatory.
pub requires_postcondition: bool,
}
/// Returns the complete ordered synthetic Metaplex scenario inventory.
pub fn metaplex_token_metadata_synthetic_scenarios()
-> std::vec::Vec<crate::MetaplexTokenMetadataScenario> {
return vec![
crate::MetaplexTokenMetadataScenario {
id: "metaplex_nft_create_update".to_string(),
asset_family: crate::MetaplexTokenMetadataAssetFamily::Nft,
mode: crate::MetaplexTokenMetadataScenarioMode::Synthetic,
operation_codes: vec![
kb_lib::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_UPDATE_OPERATION.to_string(),
],
requires_collection: false,
requires_programmable_rules: false,
requires_postcondition: true,
},
crate::MetaplexTokenMetadataScenario {
id: "metaplex_sft_create_mint".to_string(),
asset_family: crate::MetaplexTokenMetadataAssetFamily::Sft,
mode: crate::MetaplexTokenMetadataScenarioMode::Synthetic,
operation_codes: vec![
kb_lib::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_MINT_OPERATION.to_string(),
],
requires_collection: false,
requires_programmable_rules: false,
requires_postcondition: true,
},
crate::MetaplexTokenMetadataScenario {
id: "metaplex_fungible_create_update".to_string(),
asset_family: crate::MetaplexTokenMetadataAssetFamily::Fungible,
mode: crate::MetaplexTokenMetadataScenarioMode::Synthetic,
operation_codes: vec![
kb_lib::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_UPDATE_OPERATION.to_string(),
],
requires_collection: false,
requires_programmable_rules: false,
requires_postcondition: true,
},
crate::MetaplexTokenMetadataScenario {
id: "metaplex_collection_verify_unverify".to_string(),
asset_family: crate::MetaplexTokenMetadataAssetFamily::Collection,
mode: crate::MetaplexTokenMetadataScenarioMode::Synthetic,
operation_codes: vec![
kb_lib::EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_UNVERIFY_OPERATION.to_string(),
],
requires_collection: true,
requires_programmable_rules: false,
requires_postcondition: true,
},
crate::MetaplexTokenMetadataScenario {
id: "metaplex_pnft_delegate_transfer_revoke".to_string(),
asset_family: crate::MetaplexTokenMetadataAssetFamily::ProgrammableNft,
mode: crate::MetaplexTokenMetadataScenarioMode::Synthetic,
operation_codes: vec![
kb_lib::EX_METAPLEX_TOKEN_METADATA_DELEGATE_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_TRANSFER_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_REVOKE_OPERATION.to_string(),
],
requires_collection: false,
requires_programmable_rules: true,
requires_postcondition: true,
},
];
}
#[cfg(test)]
mod tests {
#[test]
fn synthetic_inventory_is_exact_unique_and_covers_every_required_asset_family() {
let scenarios = crate::metaplex_token_metadata_synthetic_scenarios();
assert_eq!(scenarios.len(), 5);
let ids = scenarios
.iter()
.map(|scenario| return scenario.id.as_str())
.collect::<std::collections::BTreeSet<&str>>();
assert_eq!(ids.len(), scenarios.len());
let families = scenarios
.iter()
.map(|scenario| return scenario.asset_family)
.collect::<std::collections::BTreeSet<crate::MetaplexTokenMetadataAssetFamily>>(
);
assert_eq!(families.len(), 5);
}
#[test]
fn synthetic_scenarios_are_simulation_safe_and_require_postconditions() {
for scenario in crate::metaplex_token_metadata_synthetic_scenarios() {
assert_eq!(scenario.mode, crate::MetaplexTokenMetadataScenarioMode::Synthetic);
assert!(!scenario.operation_codes.is_empty());
assert!(scenario.requires_postcondition);
}
}
#[test]
fn programmable_and_collection_requirements_are_declared_only_where_needed() {
let scenarios = crate::metaplex_token_metadata_synthetic_scenarios();
let programmable = scenarios
.iter()
.filter(|scenario| return scenario.requires_programmable_rules)
.collect::<std::vec::Vec<&crate::MetaplexTokenMetadataScenario>>();
assert_eq!(programmable.len(), 1);
assert_eq!(
programmable[0].asset_family,
crate::MetaplexTokenMetadataAssetFamily::ProgrammableNft
);
let collection = scenarios
.iter()
.filter(|scenario| return scenario.requires_collection)
.collect::<std::vec::Vec<&crate::MetaplexTokenMetadataScenario>>();
assert_eq!(collection.len(), 1);
assert_eq!(collection[0].asset_family, crate::MetaplexTokenMetadataAssetFamily::Collection);
}
}