shank_parse::shank_parse!("../idl/full_shank.json");
#[cfg(test)]
mod tests {
use super::full_shank::accounts::*;
use super::full_shank::errors;
use super::full_shank::events::*;
use super::full_shank::instructions::*;
use super::full_shank::types::*;
use super::full_shank::ID;
use shank_parse::__private::{base64_encode, Pubkey};
#[test]
fn test_program_id() {
assert_eq!(ID.to_string(), "FuLLShankNoteBoardProg111111111111111111111");
}
#[test]
fn test_user_status_from_u8() {
assert_eq!(UserStatus::from_u8(0), Some(UserStatus::Active));
assert_eq!(UserStatus::from_u8(1), Some(UserStatus::Suspended));
assert_eq!(UserStatus::from_u8(99), None);
}
#[test]
fn test_user_status_to_u8() {
assert_eq!(UserStatus::Active.to_u8(), 0);
assert_eq!(UserStatus::Suspended.to_u8(), 1);
}
#[test]
fn test_post_status_from_u8() {
assert_eq!(PostStatus::from_u8(0), Some(PostStatus::Published));
assert_eq!(PostStatus::from_u8(1), Some(PostStatus::Deleted));
assert_eq!(PostStatus::from_u8(5), None);
}
#[test]
fn test_post_status_to_u8() {
assert_eq!(PostStatus::Published.to_u8(), 0);
assert_eq!(PostStatus::Deleted.to_u8(), 1);
}
#[test]
fn test_error_codes() {
assert_eq!(errors::NOT_ADMIN, 0);
assert_eq!(errors::BOARD_ALREADY_INITIALIZED, 1);
assert_eq!(errors::BOARD_NOT_INITIALIZED, 2);
assert_eq!(errors::PROFILE_ALREADY_EXISTS, 3);
assert_eq!(errors::PROFILE_NOT_FOUND, 4);
assert_eq!(errors::NOT_AUTHOR, 5);
assert_eq!(errors::POST_ALREADY_DELETED, 6);
assert_eq!(errors::USER_SUSPENDED, 7);
assert_eq!(errors::NUMERICAL_OVERFLOW, 8);
}
#[test]
fn test_board_config_from_account_data() {
let admin = [1u8; 32];
let post_fee: u64 = 5_000_000;
let post_count: u64 = 42;
let is_paused = false;
let bump: u8 = 254;
let mut data = Vec::new();
data.extend_from_slice(&admin);
data.extend_from_slice(&post_fee.to_le_bytes());
data.extend_from_slice(&post_count.to_le_bytes());
data.push(is_paused as u8);
data.push(bump);
let config = BoardConfig::from_account_data(&data).unwrap();
assert_eq!(config.admin, admin);
assert_eq!(config.post_fee, post_fee);
assert_eq!(config.post_count, post_count);
assert_eq!(config.is_paused, is_paused);
assert_eq!(config.bump, bump);
}
#[test]
fn test_board_config_paused() {
let mut data = vec![0u8; 32]; data.extend_from_slice(&0u64.to_le_bytes()); data.extend_from_slice(&0u64.to_le_bytes()); data.push(1u8); data.push(1u8);
let config = BoardConfig::from_account_data(&data).unwrap();
assert!(config.is_paused);
}
#[test]
fn test_board_config_insufficient_data() {
let data = vec![0u8; 10];
assert!(BoardConfig::from_account_data(&data).is_err());
}
#[test]
fn test_user_profile_active() {
let authority = [2u8; 32];
let username = [3u8; 32];
let post_count: u32 = 7;
let bump: u8 = 200;
let mut data = Vec::new();
data.extend_from_slice(&authority);
data.extend_from_slice(&username);
data.extend_from_slice(&post_count.to_le_bytes());
data.push(UserStatus::Active.to_u8());
data.push(bump);
let profile = UserProfile::from_account_data(&data).unwrap();
assert_eq!(profile.authority, authority);
assert_eq!(profile.username, username);
assert_eq!(profile.post_count, post_count);
assert_eq!(profile.status, UserStatus::Active);
assert_eq!(profile.bump, bump);
}
#[test]
fn test_user_profile_suspended() {
let mut data = vec![0u8; 32]; data.extend_from_slice(&[0u8; 32]); data.extend_from_slice(&0u32.to_le_bytes()); data.push(UserStatus::Suspended.to_u8());
data.push(1u8);
let profile = UserProfile::from_account_data(&data).unwrap();
assert_eq!(profile.status, UserStatus::Suspended);
}
#[test]
fn test_post_with_reply_to() {
let author = [4u8; 32];
let content = [b'X'; 128];
let reply_to_key = [5u8; 32];
let post_index: u32 = 99;
let bump: u8 = 250;
let mut data = Vec::new();
data.extend_from_slice(&author);
data.extend_from_slice(&content);
data.push(1u8); data.extend_from_slice(&reply_to_key);
data.push(PostStatus::Published.to_u8());
data.extend_from_slice(&post_index.to_le_bytes());
data.push(bump);
let post = Post::from_account_data(&data).unwrap();
assert_eq!(post.author, author);
assert_eq!(post.content, content);
assert_eq!(post.reply_to, Some(reply_to_key));
assert_eq!(post.status, PostStatus::Published);
assert_eq!(post.post_index, post_index);
assert_eq!(post.bump, bump);
}
#[test]
fn test_post_without_reply_to() {
let mut data = Vec::new();
data.extend_from_slice(&[1u8; 32]); data.extend_from_slice(&[0u8; 128]); data.push(0u8); data.push(PostStatus::Deleted.to_u8());
data.extend_from_slice(&0u32.to_le_bytes()); data.push(100u8);
let post = Post::from_account_data(&data).unwrap();
assert_eq!(post.reply_to, None);
assert_eq!(post.status, PostStatus::Deleted);
}
#[test]
fn test_post_insufficient_data() {
assert!(Post::from_account_data(&[0u8; 10]).is_err());
}
#[test]
fn test_init_board_instruction() {
let program_id = Pubkey::new_from_array([1u8; 32]);
let accounts = InitBoardAccounts {
admin: Pubkey::new_from_array([2u8; 32]),
config: Pubkey::new_from_array([3u8; 32]),
system_program: Pubkey::new_from_array([4u8; 32]),
};
let args = InitBoardArgs { post_fee: 1_000_000 };
let ix = init_board(&program_id, &accounts, &args);
assert_eq!(ix.program_id, program_id);
assert_eq!(ix.accounts.len(), 3);
assert_eq!(ix.data[0], 0); assert_eq!(
u64::from_le_bytes(ix.data[1..9].try_into().unwrap()),
1_000_000
);
assert!(!ix.accounts[0].is_writable); assert!(ix.accounts[0].is_signer); assert!(ix.accounts[1].is_writable); assert!(!ix.accounts[1].is_signer); assert!(!ix.accounts[2].is_writable); assert!(!ix.accounts[2].is_signer); }
#[test]
fn test_create_profile_instruction() {
let program_id = Pubkey::new_from_array([1u8; 32]);
let accounts = CreateProfileAccounts {
payer: Pubkey::new_from_array([2u8; 32]),
profile: Pubkey::new_from_array([3u8; 32]),
referrer: Pubkey::new_from_array([4u8; 32]),
system_program: Pubkey::new_from_array([5u8; 32]),
};
let username = [b'A'; 32];
let args = CreateProfileArgs { username };
let ix = create_profile(&program_id, &accounts, &args);
assert_eq!(ix.data[0], 1); assert_eq!(&ix.data[1..33], &username);
assert_eq!(ix.accounts.len(), 4);
assert!(ix.accounts[0].is_signer); assert!(ix.accounts[1].is_writable); assert!(!ix.accounts[2].is_signer); assert!(!ix.accounts[2].is_writable); }
#[test]
fn test_post_message_no_reply_to() {
let program_id = Pubkey::new_from_array([1u8; 32]);
let accounts = PostMessageAccounts {
author: Pubkey::new_from_array([2u8; 32]),
profile: Pubkey::new_from_array([3u8; 32]),
post: Pubkey::new_from_array([4u8; 32]),
system_program: Pubkey::new_from_array([5u8; 32]),
};
let args = PostMessageArgs {
content: [b'M'; 128],
reply_to: None,
};
let ix = post_message(&program_id, &accounts, &args);
assert_eq!(ix.data[0], 2); assert_eq!(&ix.data[1..129], &[b'M'; 128]); assert_eq!(ix.data[129], 0); assert_eq!(ix.data.len(), 130);
assert_eq!(ix.accounts.len(), 4);
assert!(ix.accounts[0].is_signer); assert!(ix.accounts[1].is_writable); assert!(ix.accounts[2].is_writable); }
#[test]
fn test_post_message_with_reply_to() {
let program_id = Pubkey::new_from_array([1u8; 32]);
let accounts = PostMessageAccounts {
author: Pubkey::new_from_array([2u8; 32]),
profile: Pubkey::new_from_array([3u8; 32]),
post: Pubkey::new_from_array([4u8; 32]),
system_program: Pubkey::new_from_array([5u8; 32]),
};
let reply_key = [9u8; 32];
let args = PostMessageArgs {
content: [0u8; 128],
reply_to: Some(reply_key),
};
let ix = post_message(&program_id, &accounts, &args);
assert_eq!(ix.data[129], 1); assert_eq!(&ix.data[130..162], &reply_key);
assert_eq!(ix.data.len(), 162);
}
#[test]
fn test_update_username_instruction() {
let program_id = Pubkey::new_from_array([1u8; 32]);
let accounts = UpdateUsernameAccounts {
authority: Pubkey::new_from_array([2u8; 32]),
profile: Pubkey::new_from_array([3u8; 32]),
};
let new_name = [b'Z'; 32];
let args = UpdateUsernameArgs { username: new_name };
let ix = update_username(&program_id, &accounts, &args);
assert_eq!(ix.data[0], 3); assert_eq!(&ix.data[1..33], &new_name);
assert_eq!(ix.accounts.len(), 2);
assert!(ix.accounts[0].is_signer); assert!(ix.accounts[1].is_writable); }
#[test]
fn test_delete_post_instruction() {
let program_id = Pubkey::new_from_array([1u8; 32]);
let accounts = DeletePostAccounts {
authority: Pubkey::new_from_array([2u8; 32]),
post: Pubkey::new_from_array([3u8; 32]),
refund_to: Pubkey::new_from_array([4u8; 32]),
config: Pubkey::new_from_array([5u8; 32]),
};
let ix = delete_post(&program_id, &accounts);
assert_eq!(ix.data, vec![4u8]); assert_eq!(ix.accounts.len(), 4);
assert!(ix.accounts[0].is_signer); assert!(ix.accounts[1].is_writable); assert!(ix.accounts[2].is_writable); assert!(!ix.accounts[3].is_writable); }
#[test]
fn test_suspend_profile_instruction() {
let program_id = Pubkey::new_from_array([1u8; 32]);
let accounts = SuspendProfileAccounts {
admin: Pubkey::new_from_array([2u8; 32]),
config: Pubkey::new_from_array([3u8; 32]),
profile: Pubkey::new_from_array([4u8; 32]),
co_admin: Pubkey::new_from_array([5u8; 32]),
};
let ix = suspend_profile(&program_id, &accounts);
assert_eq!(ix.data, vec![5u8]); assert_eq!(ix.accounts.len(), 4);
assert!(ix.accounts[0].is_signer); assert!(ix.accounts[1].is_writable); assert!(ix.accounts[2].is_writable); assert!(!ix.accounts[3].is_signer); }
#[test]
fn test_board_initialized_from_logs() {
let admin = [10u8; 32];
let post_fee: u64 = 500_000;
let mut event_data = vec![0u8]; event_data.extend_from_slice(&admin);
event_data.extend_from_slice(&post_fee.to_le_bytes());
let log = format!("Program data: {}", base64_encode(&event_data));
let events = BoardInitialized::from_logs(&[log.as_str()]);
assert_eq!(events.len(), 1);
assert_eq!(events[0].admin, admin);
assert_eq!(events[0].post_fee, post_fee);
}
#[test]
fn test_profile_created_from_logs() {
let authority = [11u8; 32];
let username = [12u8; 32];
let mut event_data = vec![1u8]; event_data.extend_from_slice(&authority);
event_data.extend_from_slice(&username);
let log = format!("Program data: {}", base64_encode(&event_data));
let events = ProfileCreated::from_logs(&[log.as_str()]);
assert_eq!(events.len(), 1);
assert_eq!(events[0].authority, authority);
assert_eq!(events[0].username, username);
}
#[test]
fn test_username_updated_from_logs() {
let authority = [13u8; 32];
let new_username = [14u8; 32];
let mut event_data = vec![2u8]; event_data.extend_from_slice(&authority);
event_data.extend_from_slice(&new_username);
let log = format!("Program data: {}", base64_encode(&event_data));
let events = UsernameUpdated::from_logs(&[log.as_str()]);
assert_eq!(events.len(), 1);
assert_eq!(events[0].authority, authority);
assert_eq!(events[0].new_username, new_username);
}
#[test]
fn test_message_posted_from_logs_no_reply() {
let author = [15u8; 32];
let post_index: u32 = 42;
let mut event_data = vec![3u8]; event_data.extend_from_slice(&author);
event_data.extend_from_slice(&post_index.to_le_bytes());
event_data.push(0u8); let log = format!("Program data: {}", base64_encode(&event_data));
let events = MessagePosted::from_logs(&[log.as_str()]);
assert_eq!(events.len(), 1);
assert_eq!(events[0].author, author);
assert_eq!(events[0].post_index, post_index);
assert_eq!(events[0].reply_to, None);
}
#[test]
fn test_message_posted_from_logs_with_reply() {
let author = [16u8; 32];
let post_index: u32 = 7;
let reply_to = [17u8; 32];
let mut event_data = vec![3u8]; event_data.extend_from_slice(&author);
event_data.extend_from_slice(&post_index.to_le_bytes());
event_data.push(1u8); event_data.extend_from_slice(&reply_to);
let log = format!("Program data: {}", base64_encode(&event_data));
let events = MessagePosted::from_logs(&[log.as_str()]);
assert_eq!(events.len(), 1);
assert_eq!(events[0].reply_to, Some(reply_to));
}
#[test]
fn test_message_deleted_from_logs() {
let post = [18u8; 32];
let deleted_by = [19u8; 32];
let mut event_data = vec![4u8]; event_data.extend_from_slice(&post);
event_data.extend_from_slice(&deleted_by);
let log = format!("Program data: {}", base64_encode(&event_data));
let events = MessageDeleted::from_logs(&[log.as_str()]);
assert_eq!(events.len(), 1);
assert_eq!(events[0].post, post);
assert_eq!(events[0].deleted_by, deleted_by);
}
#[test]
fn test_profile_suspended_from_logs() {
let profile_authority = [20u8; 32];
let mut event_data = vec![5u8]; event_data.extend_from_slice(&profile_authority);
let log = format!("Program data: {}", base64_encode(&event_data));
let events = ProfileSuspended::from_logs(&[log.as_str()]);
assert_eq!(events.len(), 1);
assert_eq!(events[0].profile_authority, profile_authority);
}
#[test]
fn test_board_event_enum_multiple() {
let mut data0 = vec![0u8];
data0.extend_from_slice(&[1u8; 32]);
data0.extend_from_slice(&100u64.to_le_bytes());
let log0 = format!("Program data: {}", base64_encode(&data0));
let mut data1 = vec![1u8];
data1.extend_from_slice(&[2u8; 32]);
data1.extend_from_slice(&[3u8; 32]);
let log1 = format!("Program data: {}", base64_encode(&data1));
let mut data3 = vec![3u8];
data3.extend_from_slice(&[4u8; 32]);
data3.extend_from_slice(&5u32.to_le_bytes());
data3.push(0u8); let log3 = format!("Program data: {}", base64_encode(&data3));
let logs: Vec<&str> = vec![
log0.as_str(),
"Program log: something else",
log1.as_str(),
log3.as_str(),
];
let events = BoardEvent::from_logs(&logs);
assert_eq!(events.len(), 3);
match &events[0] {
BoardEvent::BoardInitialized(e) => assert_eq!(e.post_fee, 100),
_ => panic!("expected BoardInitialized"),
}
match &events[1] {
BoardEvent::ProfileCreated(e) => assert_eq!(e.authority, [2u8; 32]),
_ => panic!("expected ProfileCreated"),
}
match &events[2] {
BoardEvent::MessagePosted(e) => {
assert_eq!(e.post_index, 5);
assert_eq!(e.reply_to, None);
}
_ => panic!("expected MessagePosted"),
}
}
#[test]
fn test_board_event_ignores_non_event_logs() {
let logs: Vec<&str> = vec![
"Program log: something",
"random log",
"Program data: not_valid_base64!!!",
];
let events = BoardEvent::from_logs(&logs);
assert!(events.is_empty());
}
#[test]
fn test_board_event_profile_suspended() {
let profile_authority = [99u8; 32];
let mut data5 = vec![5u8]; data5.extend_from_slice(&profile_authority);
let log = format!("Program data: {}", base64_encode(&data5));
let events = BoardEvent::from_logs(&[log.as_str()]);
assert_eq!(events.len(), 1);
match &events[0] {
BoardEvent::ProfileSuspended(e) => assert_eq!(e.profile_authority, profile_authority),
_ => panic!("expected ProfileSuspended"),
}
}
}