0.4.7-pre.008
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user