0.5.1-pre.002
This commit is contained in:
42
ks-lib/tests/external_decoder_api.rs
Normal file
42
ks-lib/tests/external_decoder_api.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
// file: ks-lib/tests/external_decoder_api.rs
|
||||
// version: 2
|
||||
|
||||
//! Downstream-style compilation contract for externally implemented decoders.
|
||||
|
||||
struct ExternalDecoder;
|
||||
|
||||
impl ks_lib::DcApiProtocolDecoder for ExternalDecoder {
|
||||
fn decoder_name(&self) -> &'static str {
|
||||
return "external_decoder";
|
||||
}
|
||||
|
||||
fn decoder_version(&self) -> &'static str {
|
||||
return "1";
|
||||
}
|
||||
|
||||
fn program_ids(&self) -> &'static [&'static str] {
|
||||
return &[];
|
||||
}
|
||||
|
||||
fn supports_observation(
|
||||
&self,
|
||||
_observation: &ks_lib::MdProgramObservation,
|
||||
) -> ks_lib::DcApiDecoderSupport {
|
||||
return ks_lib::DcApiDecoderSupport::No;
|
||||
}
|
||||
|
||||
fn decode_observation(
|
||||
&self,
|
||||
_observation: &ks_lib::MdProgramObservation,
|
||||
) -> ks_core::Result<std::vec::Vec<ks_lib::MdDecodedProtocolEvent>> {
|
||||
return std::result::Result::Ok(std::vec::Vec::new());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_decoder_uses_only_the_public_ks_lib_contract() {
|
||||
let decoder: &dyn ks_lib::DcApiProtocolDecoder = &ExternalDecoder;
|
||||
assert_eq!(decoder.decoder_name(), "external_decoder");
|
||||
assert_eq!(decoder.decoder_version(), "1");
|
||||
assert!(decoder.program_ids().is_empty());
|
||||
}
|
||||
165
ks-lib/tests/external_executor_api.rs
Normal file
165
ks-lib/tests/external_executor_api.rs
Normal file
@@ -0,0 +1,165 @@
|
||||
// file: ks-lib/tests/external_executor_api.rs
|
||||
// version: 5
|
||||
|
||||
//! Downstream-style compilation contract for externally implemented executors.
|
||||
|
||||
struct ExternalExecutor;
|
||||
|
||||
struct ExternalTypedExecutor;
|
||||
|
||||
impl ks_lib::ExApiInstructionExecutor for ExternalExecutor {
|
||||
fn executor_name(&self) -> &'static str {
|
||||
return "external_executor";
|
||||
}
|
||||
|
||||
fn executor_version(&self) -> &'static str {
|
||||
return "1";
|
||||
}
|
||||
|
||||
fn program_ids(&self) -> &'static [&'static str] {
|
||||
return &[];
|
||||
}
|
||||
|
||||
fn supports_request(
|
||||
&self,
|
||||
_request: &ks_lib::ExApiExecutionRequest,
|
||||
) -> ks_lib::ExApiExecutionSupport {
|
||||
return ks_lib::ExApiExecutionSupport::No;
|
||||
}
|
||||
|
||||
fn build_plan(
|
||||
&self,
|
||||
request: &ks_lib::ExApiExecutionRequest,
|
||||
) -> ks_core::Result<ks_lib::ExApiExecutionPlan> {
|
||||
return std::result::Result::Ok(ks_lib::ExApiExecutionPlan {
|
||||
executor_name: self.executor_name().to_string(),
|
||||
instruction_count: 0,
|
||||
payload_json: request.payload_json.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ks_lib::ExApiTypedInstructionExecutor for ExternalTypedExecutor {
|
||||
type Intent = ks_lib::MdPubkey;
|
||||
|
||||
fn capability(
|
||||
&self,
|
||||
_program_id: &ks_lib::MdProgramId,
|
||||
operation_code: &str,
|
||||
) -> ks_lib::ExApiExecutionCapability {
|
||||
return ks_lib::ExApiExecutionCapability::unsupported("external_executor", operation_code);
|
||||
}
|
||||
|
||||
fn build_prepared_plan(
|
||||
&self,
|
||||
intent: &Self::Intent,
|
||||
) -> ks_core::Result<ks_lib::ExApiPreparedExecutionPlan> {
|
||||
return std::result::Result::Ok(ks_lib::ExApiPreparedExecutionPlan {
|
||||
executor_name: "external_typed_executor".to_string(),
|
||||
executor_version: "1".to_string(),
|
||||
intent_id: "external-intent".to_string(),
|
||||
operation_code: "external-operation".to_string(),
|
||||
fee_payer: intent.clone(),
|
||||
instructions: std::vec::Vec::new(),
|
||||
required_signers: std::vec::Vec::new(),
|
||||
policy: ks_lib::ExApiExecutionPolicy::default(),
|
||||
requested_spend_lamports: 0,
|
||||
requested_compute_unit_price_micro_lamports: std::option::Option::None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_executor_uses_only_the_public_ks_lib_contract() {
|
||||
let executor: &dyn ks_lib::ExApiInstructionExecutor = &ExternalExecutor;
|
||||
assert_eq!(executor.executor_name(), "external_executor");
|
||||
assert_eq!(executor.executor_version(), "1");
|
||||
assert!(executor.program_ids().is_empty());
|
||||
let typed_executor = ExternalTypedExecutor;
|
||||
let program_id = ks_lib::MdProgramId("external-program".to_string());
|
||||
let capability = ks_lib::ExApiTypedInstructionExecutor::capability(
|
||||
&typed_executor,
|
||||
&program_id,
|
||||
"external-operation",
|
||||
);
|
||||
assert!(!capability.is_supported());
|
||||
let fee_payer = ks_lib::MdPubkey("external-fee-payer".to_string());
|
||||
let plan =
|
||||
ks_lib::ExApiTypedInstructionExecutor::build_prepared_plan(&typed_executor, &fee_payer)
|
||||
.unwrap_or_else(|error| panic!("external typed executor failed: {error}"));
|
||||
assert_eq!(plan.fee_payer, fee_payer);
|
||||
assert!(plan.instructions.is_empty());
|
||||
assert!(plan.required_signers.is_empty());
|
||||
assert_eq!(plan.policy.simulation, ks_lib::ExApiExecutionSimulationPolicy::Required);
|
||||
assert!(plan.policy.dry_run);
|
||||
let solana_core_executor = ks_lib::ExSolanaCoreExecutor;
|
||||
let solana_core_program_id =
|
||||
ks_lib::MdProgramId(ks_program_ids::COMPUTE_BUDGET_PROGRAM_ID.to_string());
|
||||
let solana_core_capability = ks_lib::ExApiTypedInstructionExecutor::capability(
|
||||
&solana_core_executor,
|
||||
&solana_core_program_id,
|
||||
ks_lib::EX_SOLANA_CORE_COMPUTE_BUDGET_SET_UNIT_LIMIT_OPERATION,
|
||||
);
|
||||
assert!(solana_core_capability.is_supported());
|
||||
let solana_core_intent = ks_lib::ExSolanaCoreExecutionIntent {
|
||||
intent_id: "external-solana-core".to_string(),
|
||||
fee_payer: ks_lib::MdPubkey(ks_program_ids::SYSTEM_PROGRAM_ID.to_string()),
|
||||
policy: ks_lib::ExApiExecutionPolicy::default(),
|
||||
operation: ks_lib::ExSolanaCoreOperation::ComputeBudgetSetUnitLimit { units: 500_000 },
|
||||
};
|
||||
let solana_core_plan = ks_lib::ExApiTypedInstructionExecutor::build_prepared_plan(
|
||||
&solana_core_executor,
|
||||
&solana_core_intent,
|
||||
)
|
||||
.unwrap_or_else(|error| panic!("Solana core executor failed: {error}"));
|
||||
assert_eq!(solana_core_plan.instructions.len(), 1);
|
||||
assert_eq!(
|
||||
solana_core_plan.operation_code,
|
||||
ks_lib::EX_SOLANA_CORE_COMPUTE_BUDGET_SET_UNIT_LIMIT_OPERATION
|
||||
);
|
||||
let memo_executor = ks_lib::ExSplMemoExecutor;
|
||||
let memo_program_id = ks_lib::MdProgramId(ks_program_ids::SPL_MEMO_V4_PROGRAM_ID.to_string());
|
||||
let memo_capability = ks_lib::ExApiTypedInstructionExecutor::capability(
|
||||
&memo_executor,
|
||||
&memo_program_id,
|
||||
ks_lib::EX_SPL_MEMO_ADD_MEMO_OPERATION,
|
||||
);
|
||||
assert!(memo_capability.is_supported());
|
||||
let memo_intent = ks_lib::ExSplMemoExecutionIntent {
|
||||
intent_id: "external-spl-memo".to_string(),
|
||||
fee_payer: ks_lib::MdPubkey(ks_program_ids::SYSTEM_PROGRAM_ID.to_string()),
|
||||
policy: ks_lib::ExApiExecutionPolicy {
|
||||
cost_limit: ks_lib::ExApiExecutionCostLimit {
|
||||
max_spend_lamports: std::option::Option::Some(0),
|
||||
max_fee_lamports: std::option::Option::Some(10_000),
|
||||
max_compute_unit_price_micro_lamports: std::option::Option::None,
|
||||
},
|
||||
authorized_signers: vec![ks_lib::MdPubkey(
|
||||
ks_program_ids::SYSTEM_PROGRAM_ID.to_string(),
|
||||
)],
|
||||
post_execution_validation: ks_lib::ExApiPostExecutionValidationPolicy {
|
||||
canonical_insert_required: true,
|
||||
core_extraction_required: true,
|
||||
decode_replay_required: true,
|
||||
materialization_required: true,
|
||||
},
|
||||
..ks_lib::ExApiExecutionPolicy::default()
|
||||
},
|
||||
operation: ks_lib::ExSplMemoOperation::AddMemo {
|
||||
generation: ks_lib::ExSplMemoGeneration::V4,
|
||||
message: "external memo".to_string(),
|
||||
signers: std::vec::Vec::new(),
|
||||
},
|
||||
};
|
||||
let memo_plan =
|
||||
ks_lib::ExApiTypedInstructionExecutor::build_prepared_plan(&memo_executor, &memo_intent)
|
||||
.unwrap_or_else(|error| panic!("SPL Memo executor failed: {error}"));
|
||||
assert_eq!(memo_plan.instructions.len(), 1);
|
||||
assert_eq!(
|
||||
ks_lib::ExSafetyChecker
|
||||
.evaluate_prepared_plan(&memo_plan)
|
||||
.unwrap_or_else(|error| panic!("safety evaluation failed: {error}"))
|
||||
.decision,
|
||||
ks_lib::ExSafetyDecision::Allow
|
||||
);
|
||||
}
|
||||
98
ks-lib/tests/external_materializer_api.rs
Normal file
98
ks-lib/tests/external_materializer_api.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
// file: ks-lib/tests/external_materializer_api.rs
|
||||
// version: 9
|
||||
|
||||
//! Downstream-style compilation contract for externally implemented materializers.
|
||||
|
||||
const ACCEPTED_FAMILIES: &[ks_lib::MdEventFamily] = &[ks_lib::MdEventFamily::Audit];
|
||||
|
||||
struct ExternalAuditMaterializer;
|
||||
|
||||
fn assert_event_materializer<Materializer: ks_lib::MtApiEventMaterializer>() {}
|
||||
|
||||
fn assert_legacy_materializer<Materializer: ks_lib::MtMaterializer>() {}
|
||||
|
||||
impl ks_lib::MtApiEventMaterializer for ExternalAuditMaterializer {
|
||||
fn identity(&self) -> ks_lib::MtApiMaterializerIdentity {
|
||||
return ks_lib::MtApiMaterializerIdentity {
|
||||
name: "materializer.external.audit".to_string(),
|
||||
version: "1".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
fn accepted_families(&self) -> &'static [ks_lib::MdEventFamily] {
|
||||
return ACCEPTED_FAMILIES;
|
||||
}
|
||||
|
||||
fn transaction_policy(
|
||||
&self,
|
||||
_family: ks_lib::MdEventFamily,
|
||||
) -> ks_lib::MtApiMaterializationTransactionPolicy {
|
||||
return ks_lib::MtApiMaterializationTransactionPolicy::SuccessfulOrFailedAudit;
|
||||
}
|
||||
|
||||
fn materialize(
|
||||
&self,
|
||||
_observation: &ks_lib::DcApiDecodedObservation,
|
||||
) -> ks_lib::MtApiMaterializerExecutionResult {
|
||||
return ks_lib::MtApiMaterializerExecutionResult::ignored();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_materializer_uses_only_the_public_ks_lib_contract() {
|
||||
let materializer: &dyn ks_lib::MtApiEventMaterializer = &ExternalAuditMaterializer;
|
||||
assert_eq!(materializer.identity().name, "materializer.external.audit");
|
||||
assert_eq!(materializer.accepted_families(), ACCEPTED_FAMILIES);
|
||||
assert_event_materializer::<ks_lib::MtAdminMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtBridgeMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtComplianceAuditMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtFeesMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtGovernanceMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtLendingMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtLifecycleMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtMetadataMetaplexTokenMetadataMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtMetadataSolanaProgramMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtMetadataToken2022Materializer>();
|
||||
assert_event_materializer::<ks_lib::MtLiquidityMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtNftMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtOracleMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtOrderbookMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtPerpetualsMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtPoolStateMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtRewardsMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtRiskMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtRoutingMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtStakingMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtTokenAccountsMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtTokenMetadataRiskMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtTradesMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtTransactionAnnotationMaterializer>();
|
||||
assert_event_materializer::<ks_lib::MtVaultMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtBridgeMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtGovernanceMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtLendingMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtLiquidityMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtMetadataMetaplexTokenMetadataMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtMetadataSolanaProgramMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtMetadataToken2022Materializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtNftMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtOracleMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtOrderbookMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtPerpetualsMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtPoolStateMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtRewardsMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtRoutingMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtTokenMetadataRiskMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtTradesMaterializer>();
|
||||
assert_legacy_materializer::<ks_lib::MtVaultMaterializer>();
|
||||
let _ = ks_lib::materializer_admin_materialize_elgamal_registry_state_snapshot;
|
||||
let _ = ks_lib::materializer_metadata_materialize_metaplex_account_snapshot;
|
||||
let _ = ks_lib::materializer_metadata_materialize_metaplex_metadata_snapshot;
|
||||
let _ = ks_lib::materializer_metadata_materialize_solana_program_metadata_account_snapshot;
|
||||
let _ = ks_lib::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot;
|
||||
let _ = ks_lib::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot;
|
||||
let _ = ks_lib::materializer_admin_materialize_token_2022_state_snapshots;
|
||||
let _ = ks_lib::materializer_fees_materialize_token_2022_state_snapshots;
|
||||
let _ = ks_lib::materializer_metadata_materialize_token_2022_snapshot;
|
||||
let _ = ks_lib::materializer_token_materialize_token_2022_state_snapshot;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// file: ks-lib/tests/external_metaplex_token_metadata_executor_api.rs
|
||||
// version: 2
|
||||
|
||||
//! Downstream-style compilation contract for the Metaplex Token Metadata executor.
|
||||
|
||||
#[test]
|
||||
fn external_consumers_inspect_metaplex_executor_capabilities_only_through_crate_root_exports() {
|
||||
let executor = ks_lib::ExMetadataMetaplexTokenMetadataExecutor;
|
||||
let program_id = ks_lib::MdProgramId(
|
||||
ks_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID.to_string(),
|
||||
);
|
||||
assert_eq!(ks_lib::EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES.len(), 35);
|
||||
assert_eq!(ks_lib::EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES.len(), 15);
|
||||
for operation_code in ks_lib::EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES {
|
||||
let capability = ks_lib::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&program_id,
|
||||
operation_code,
|
||||
);
|
||||
assert!(capability.is_supported());
|
||||
}
|
||||
for operation_code in ks_lib::EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES {
|
||||
assert!(
|
||||
ks_lib::EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES.contains(operation_code)
|
||||
);
|
||||
}
|
||||
let foreign_program_id = ks_lib::MdProgramId(ks_program_ids::SYSTEM_PROGRAM_ID.to_string());
|
||||
let foreign_capability = ks_lib::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&foreign_program_id,
|
||||
ks_lib::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION,
|
||||
);
|
||||
assert!(!foreign_capability.is_supported());
|
||||
}
|
||||
35
ks-lib/tests/external_solana_program_metadata_account_api.rs
Normal file
35
ks-lib/tests/external_solana_program_metadata_account_api.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
// file: ks-lib/tests/external_solana_program_metadata_account_api.rs
|
||||
// version: 2
|
||||
|
||||
//! Downstream-style compilation contract for Solana Program Metadata account decoders.
|
||||
|
||||
#[test]
|
||||
fn external_consumers_decode_accounts_only_through_crate_root_exports() {
|
||||
let account = solana_pubkey::Pubkey::new_from_array([17_u8; 32]);
|
||||
let program = solana_pubkey::Pubkey::new_from_array([18_u8; 32]);
|
||||
let authority = solana_pubkey::Pubkey::new_from_array([19_u8; 32]);
|
||||
let seed = ks_lib::DcMetadataSpmSeed::from_bytes([20_u8; 16]);
|
||||
let mut data = vec![0_u8; ks_lib::DC_METADATA_SPM_BUFFER_HEADER_BYTES];
|
||||
data[0] = ks_lib::DcMetadataSpmAccountDiscriminator::Buffer.wire_value();
|
||||
data[1..33].copy_from_slice(&program.to_bytes());
|
||||
data[33..65].copy_from_slice(&authority.to_bytes());
|
||||
data[65] = 1;
|
||||
data[66..82].copy_from_slice(seed.as_bytes());
|
||||
data.extend_from_slice(&[21_u8, 22_u8]);
|
||||
let result = ks_lib::decoder_metadata_solana_program_metadata_decode_account(
|
||||
account.to_string().as_str(),
|
||||
ks_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID,
|
||||
&data,
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
if let std::result::Result::Ok(ks_lib::DcMetadataSpmAccountSnapshot::Buffer(snapshot)) = result
|
||||
{
|
||||
assert_eq!(snapshot.account, account.to_string());
|
||||
assert_eq!(snapshot.program, std::option::Option::Some(program.to_string()));
|
||||
assert_eq!(snapshot.authority, std::option::Option::Some(authority.to_string()));
|
||||
assert_eq!(snapshot.data, vec![21_u8, 22_u8]);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
assert_eq!(ks_lib::DC_METADATA_SPM_MAX_ACCOUNT_BYTES, 10_485_760);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// file: ks-lib/tests/external_solana_program_metadata_executor_api.rs
|
||||
// version: 2
|
||||
|
||||
//! Downstream-style compilation contract for the Solana Program Metadata executor.
|
||||
|
||||
#[test]
|
||||
fn external_consumers_build_solana_program_metadata_plans_only_through_crate_root_exports() {
|
||||
let authority = ks_lib::MdPubkey(ks_program_ids::SYSTEM_PROGRAM_ID.to_string());
|
||||
let account = ks_lib::MdPubkey(ks_program_ids::VOTE_PROGRAM_ID.to_string());
|
||||
let policy = ks_lib::ExApiExecutionPolicy {
|
||||
cost_limit: ks_lib::ExApiExecutionCostLimit {
|
||||
max_spend_lamports: std::option::Option::Some(0),
|
||||
max_fee_lamports: std::option::Option::Some(20_000),
|
||||
max_compute_unit_price_micro_lamports: std::option::Option::None,
|
||||
},
|
||||
authorized_signers: std::vec![authority.clone()],
|
||||
post_execution_validation: ks_lib::ExApiPostExecutionValidationPolicy {
|
||||
canonical_insert_required: true,
|
||||
core_extraction_required: true,
|
||||
decode_replay_required: true,
|
||||
materialization_required: true,
|
||||
},
|
||||
..ks_lib::ExApiExecutionPolicy::default()
|
||||
};
|
||||
let intent = ks_lib::ExMetadataSpmExecutionIntent {
|
||||
intent_id: "external-spm-extend".to_string(),
|
||||
fee_payer: authority.clone(),
|
||||
policy,
|
||||
allow_destructive_operation: false,
|
||||
operation: ks_lib::ExMetadataSpmOperation::Extend {
|
||||
account,
|
||||
authority,
|
||||
length: 64,
|
||||
program_context: std::option::Option::None,
|
||||
},
|
||||
};
|
||||
let executor = ks_lib::ExMetadataSolanaProgramMetadataExecutor;
|
||||
let program_id = ks_lib::MdProgramId(
|
||||
ks_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID.to_string(),
|
||||
);
|
||||
let capability = ks_lib::ExApiTypedInstructionExecutor::capability(
|
||||
&executor,
|
||||
&program_id,
|
||||
ks_lib::EX_METADATA_SPM_EXTEND_OPERATION,
|
||||
);
|
||||
assert!(capability.is_supported());
|
||||
let plan = ks_lib::ExApiTypedInstructionExecutor::build_prepared_plan(&executor, &intent);
|
||||
assert!(plan.is_ok());
|
||||
let plan = match plan {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert_eq!(plan.operation_code, ks_lib::EX_METADATA_SPM_EXTEND_OPERATION);
|
||||
assert_eq!(plan.instructions.len(), 1);
|
||||
assert_eq!(plan.instructions[0].data, std::vec![8, 64, 0]);
|
||||
assert_eq!(plan.instructions[0].accounts.len(), 4);
|
||||
let evaluation = ks_lib::ExSafetyChecker.evaluate_prepared_plan(&plan);
|
||||
assert!(evaluation.is_ok());
|
||||
assert_eq!(
|
||||
evaluation.as_ref().map(|value| return value.decision),
|
||||
std::result::Result::Ok(ks_lib::ExSafetyDecision::Allow)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// file: ks-lib/tests/external_solana_program_metadata_instruction_api.rs
|
||||
// version: 2
|
||||
|
||||
//! Downstream-style compilation contract for the Solana Program Metadata instruction decoder.
|
||||
|
||||
#[test]
|
||||
fn external_consumers_decode_instructions_only_through_crate_root_exports() {
|
||||
let account = solana_pubkey::Pubkey::new_from_array([31_u8; 32]).to_string();
|
||||
let authority = solana_pubkey::Pubkey::new_from_array([32_u8; 32]).to_string();
|
||||
let placeholder = ks_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID;
|
||||
let account_keys = serde_json::json!([
|
||||
{
|
||||
"accountIndex": 0,
|
||||
"accountKey": account,
|
||||
"source": "static",
|
||||
"signer": false,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"accountIndex": 1,
|
||||
"accountKey": authority,
|
||||
"source": "static",
|
||||
"signer": true,
|
||||
"writable": false
|
||||
},
|
||||
{
|
||||
"accountIndex": 2,
|
||||
"accountKey": placeholder,
|
||||
"source": "static",
|
||||
"signer": false,
|
||||
"writable": false
|
||||
},
|
||||
{
|
||||
"accountIndex": 3,
|
||||
"accountKey": placeholder,
|
||||
"source": "static",
|
||||
"signer": false,
|
||||
"writable": false
|
||||
}
|
||||
]);
|
||||
let instruction_accounts = serde_json::json!([
|
||||
{"accountIndex": 0, "accountKey": account},
|
||||
{"accountIndex": 1, "accountKey": authority},
|
||||
{"accountIndex": 2, "accountKey": placeholder},
|
||||
{"accountIndex": 3, "accountKey": placeholder}
|
||||
]);
|
||||
let input_result = ks_lib::MdCoreInstructionReplayInput::new(
|
||||
"spm:external:extend",
|
||||
"signature",
|
||||
42,
|
||||
"0",
|
||||
placeholder,
|
||||
false,
|
||||
std::option::Option::None,
|
||||
account_keys,
|
||||
instruction_accounts,
|
||||
std::option::Option::Some(serde_json::json!({"dataBase64": "CAEA"})),
|
||||
std::option::Option::None,
|
||||
serde_json::json!([]),
|
||||
serde_json::json!([]),
|
||||
serde_json::json!([]),
|
||||
serde_json::json!([]),
|
||||
);
|
||||
assert!(input_result.is_ok());
|
||||
let input: ks_lib::MdCoreInstructionReplayInput;
|
||||
if let std::result::Result::Ok(value) = input_result {
|
||||
input = value;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
let decoder = ks_lib::DcMetadataSolanaProgramMetadataDecoder;
|
||||
let recognition = ks_lib::DcApiInstructionDecoder::recognize(&decoder, &input);
|
||||
assert!(recognition.compatible);
|
||||
assert!(recognition.exact);
|
||||
assert_eq!(recognition.entry_code.as_deref(), std::option::Option::Some("extend"));
|
||||
let result = ks_lib::DcApiInstructionDecoder::decode(&decoder, &input);
|
||||
assert_eq!(result.status, ks_lib::DcApiDecoderOutcomeStatus::Decoded);
|
||||
assert_eq!(result.recognized_entry_code.as_deref(), std::option::Option::Some("extend"));
|
||||
assert_eq!(result.observations.len(), 1);
|
||||
assert_eq!(result.observations[0].payload_json["parameters"]["length"], 1);
|
||||
}
|
||||
30
ks-lib/tests/external_solana_program_metadata_model_api.rs
Normal file
30
ks-lib/tests/external_solana_program_metadata_model_api.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
// file: ks-lib/tests/external_solana_program_metadata_model_api.rs
|
||||
// version: 2
|
||||
|
||||
//! Downstream-style compilation contract for Solana Program Metadata models.
|
||||
|
||||
#[test]
|
||||
fn external_consumers_use_only_crate_root_model_exports() {
|
||||
let seed_result = ks_lib::DcMetadataSpmSeed::from_utf8("idl");
|
||||
assert!(seed_result.is_ok());
|
||||
let seed;
|
||||
if let std::result::Result::Ok(value) = seed_result {
|
||||
seed = value;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
assert_eq!(seed.trimmed_utf8(), std::result::Result::Ok("idl"));
|
||||
assert_eq!(seed.as_bytes().len(), ks_lib::DC_METADATA_SPM_SEED_BYTES);
|
||||
let external = ks_lib::DcMetadataSpmExternalData::from_zeroable_length(
|
||||
solana_pubkey::Pubkey::new_from_array([9_u8; 32]),
|
||||
4,
|
||||
8,
|
||||
);
|
||||
let data = ks_lib::DcMetadataSpmData::External(external);
|
||||
assert_eq!(data.source(), ks_lib::DcMetadataSpmDataSource::External);
|
||||
assert_eq!(data.wire_length_u32(), std::result::Result::Ok(40),);
|
||||
assert_eq!(ks_lib::DcMetadataSpmProgramError::InvalidDataLength.code(), 4);
|
||||
assert_eq!(ks_lib::DC_METADATA_SPM_BUFFER_HEADER_BYTES, 96);
|
||||
assert_eq!(ks_lib::DC_METADATA_SPM_METADATA_HEADER_BYTES, 96);
|
||||
assert_eq!(ks_lib::DC_METADATA_SPM_EXTERNAL_DATA_BYTES, 40);
|
||||
}
|
||||
66
ks-lib/tests/operation_naming_matrix.rs
Normal file
66
ks-lib/tests/operation_naming_matrix.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
// file: ks-lib/tests/operation_naming_matrix.rs
|
||||
// version: 3
|
||||
|
||||
//! Integration guard for the persisted operation naming contract.
|
||||
|
||||
#[test]
|
||||
fn operation_naming_matrix_is_machine_readable_and_uses_canonical_dot_hierarchy()
|
||||
-> std::result::Result<(), std::string::String> {
|
||||
let matrix_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../test-fixtures/contract-matrices/OPERATION_NAMING_MATRIX.json");
|
||||
let matrix_text = match std::fs::read_to_string(matrix_path) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(std::format!(
|
||||
"operation naming matrix must be readable: {error}"
|
||||
));
|
||||
},
|
||||
};
|
||||
let matrix: serde_json::Value = match serde_json::from_str(matrix_text.as_str()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(std::format!(
|
||||
"operation naming matrix must be valid JSON: {error}"
|
||||
));
|
||||
},
|
||||
};
|
||||
let entries = match matrix["entries"].as_array() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(std::string::String::from(
|
||||
"operation naming matrix must expose entries",
|
||||
));
|
||||
},
|
||||
};
|
||||
assert!(!entries.is_empty());
|
||||
let obsolete_prefixes = [
|
||||
"solana_core.",
|
||||
"solana_native_",
|
||||
"spl_memo",
|
||||
"spl_token.",
|
||||
"spl_token_2022.",
|
||||
"spl_associated_token_account.",
|
||||
"spl_elgamal_registry.",
|
||||
"metadata_metaplex_token_metadata.",
|
||||
];
|
||||
for entry in entries {
|
||||
for field in [
|
||||
"processorName",
|
||||
"surfaceCode",
|
||||
"operationCode",
|
||||
"eventCode",
|
||||
"decoderName",
|
||||
"executorName",
|
||||
] {
|
||||
let std::option::Option::Some(value) = entry[field].as_str() else {
|
||||
continue;
|
||||
};
|
||||
assert!(!value.is_empty());
|
||||
assert!(
|
||||
!obsolete_prefixes.iter().any(|prefix| return value.starts_with(prefix)),
|
||||
"obsolete persisted identifier in {field}: {value}"
|
||||
);
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
Reference in New Issue
Block a user