use alloy::primitives::FixedBytes;
use anyhow::Result;
use bytes::Bytes;
use serial_test::serial;
use std::time::{SystemTime, UNIX_EPOCH};
use golem_base_sdk::entity::{Create, Extend, GolemBaseTransaction, Update};
use golem_base_test_utils::get_client;
#[tokio::test]
#[serial]
async fn test_create_and_retrieve_entry() -> Result<()> {
let client = get_client()?;
let start_block = client.get_current_block_number().await?;
log::info!("Starting at block: {start_block}");
let test_payload = b"test payload".to_vec();
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
let create_tx = Create::new(test_payload.clone(), 1000)
.annotate_string("test_type", "Test")
.annotate_number("test_timestamp", timestamp);
let tx_results = client.create_entities(vec![create_tx]).await?;
let entity_result = &tx_results[0];
log::info!("Entry created with ID: {entity_result:?}");
let entry_str = client
.get_storage_value::<Bytes>(entity_result.entity_key)
.await?;
log::info!("Retrieved value: {entry_str:?}");
assert_eq!(entry_str, String::from_utf8(test_payload)?);
let metadata = client.get_entity_metadata(entity_result.entity_key).await?;
log::info!("Retrieved metadata: {metadata:?}");
assert_eq!(metadata.string_annotations[0].value, "Test");
assert_eq!(metadata.numeric_annotations[0].value, timestamp);
assert_eq!(metadata.owner, client.get_owner_address());
assert_eq!(metadata.expires_at_block.unwrap(), start_block + 1001);
Ok(())
}
#[tokio::test]
#[serial]
async fn test_entity_operations() -> Result<()> {
let client = get_client()?;
let payload1 = b"first entity".to_vec();
let timestamp1 = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
let create1 = Create::new(payload1.clone(), 1000)
.annotate_string("test_type", "First")
.annotate_number("test_timestamp", timestamp1);
let tx1_results = client.create_entities(vec![create1]).await?;
let entity1_result = &tx1_results[0];
log::info!("Entry created with ID: {entity1_result:?}");
let payload2 = b"second entity".to_vec();
let timestamp2 = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
let create2 = Create::new(payload2.clone(), 1000)
.annotate_string("test_type", "Second")
.annotate_number("test_timestamp", timestamp2);
let tx2_results = client.create_entities(vec![create2]).await?;
let entity2_result = &tx2_results[0];
log::info!("Entry created with ID: {entity1_result:?}");
let entity1_str = String::from_utf8(
client
.get_storage_value::<Bytes>(entity1_result.entity_key)
.await?
.to_vec(),
)
.unwrap();
let entity2_str = String::from_utf8(
client
.get_storage_value::<Bytes>(entity2_result.entity_key)
.await?
.to_vec(),
)
.unwrap();
log::info!("Retrieved first entry: {entity1_str}");
log::info!("Retrieved second entry: {entity2_str}");
assert_eq!(entity1_str, String::from_utf8(payload1)?);
assert_eq!(entity2_str, String::from_utf8(payload2)?);
let updated_payload = b"updated first entity".to_vec();
let updated_timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
let update = Update::new(entity1_result.entity_key, updated_payload.clone(), 1000)
.annotate_string("test_type", "Updated")
.annotate_number("test_timestamp", updated_timestamp);
client.update_entities(vec![update]).await?;
log::info!("First entry updated");
let updated_str = String::from_utf8(
client
.get_storage_value::<Vec<u8>>(entity1_result.entity_key)
.await?,
)
.unwrap();
log::info!("Retrieved updated first entry: {updated_str}");
assert_eq!(updated_str, String::from_utf8(updated_payload.clone())?);
client
.delete_entities(vec![entity2_result.entity_key])
.await?;
log::info!("Second entry removed");
let result = client.get_entity_metadata(entity2_result.entity_key).await;
assert!(
result.is_err(),
"Second entity should be removed. Instead got metadata: {:?}",
result.unwrap()
);
assert!(result.unwrap_err().to_string().contains("not found"));
let final_str = String::from_utf8(
client
.get_storage_value::<Bytes>(entity1_result.entity_key)
.await?
.to_vec(),
)
.unwrap();
log::info!("Retrieved final first entry: {final_str}");
assert_eq!(final_str, String::from_utf8(updated_payload)?);
Ok(())
}
#[tokio::test]
#[serial]
async fn test_concurrent_entity_creation_batch() -> Result<()> {
let client = get_client()?;
const ENTITIES_PER_TASK: usize = 15;
let task1 = tokio::spawn({
let client = client.clone();
async move {
let mut creates = Vec::with_capacity(ENTITIES_PER_TASK);
for i in 0..ENTITIES_PER_TASK {
let payload = format!("task1_entity_{}", i).into_bytes();
let entry = Create::new(payload, 300)
.annotate_string("task", "task1")
.annotate_number("index", i as u64);
creates.push(entry);
}
let results = client.create_entities(creates).await?;
Ok::<_, anyhow::Error>(results)
}
});
let task2 = tokio::spawn({
let client = client.clone();
async move {
let mut creates = Vec::with_capacity(ENTITIES_PER_TASK);
for i in 0..ENTITIES_PER_TASK {
let payload = format!("task2_entity_{}", i).into_bytes();
let entry = Create::new(payload, 300)
.annotate_string("task", "task2")
.annotate_number("index", i as u64);
creates.push(entry);
}
let results = client.create_entities(creates).await?;
Ok::<_, anyhow::Error>(results)
}
});
let (task1_results, task2_results) = tokio::join!(task1, task2);
let task1_entities = task1_results??;
let task2_entities = task2_results??;
for (i, result) in task1_entities.iter().enumerate() {
let entry_str = String::from_utf8(
client
.get_storage_value::<Vec<u8>>(result.entity_key)
.await?,
)
.unwrap();
assert_eq!(entry_str, format!("task1_entity_{}", i));
let metadata = client.get_entity_metadata(result.entity_key).await?;
assert_eq!(metadata.string_annotations[0].value, "task1");
assert_eq!(metadata.numeric_annotations[0].value, i as u64);
}
for (i, result) in task2_entities.iter().enumerate() {
let entry_str = String::from_utf8(
client
.get_storage_value::<Vec<u8>>(result.entity_key)
.await?,
)
.unwrap();
assert_eq!(entry_str, format!("task2_entity_{}", i));
let metadata = client.get_entity_metadata(result.entity_key).await?;
assert_eq!(metadata.string_annotations[0].value, "task2");
assert_eq!(metadata.numeric_annotations[0].value, i as u64);
}
log::info!(
"Successfully verified {} concurrent batch entity creations",
ENTITIES_PER_TASK * 2
);
Ok(())
}
#[tokio::test]
#[serial]
async fn test_failed_tx_explicit_gas() -> Result<()> {
let client = get_client()?;
let start_block = client.get_current_block_number().await?;
log::info!("Starting at block: {start_block}");
let create_tx = GolemBaseTransaction::builder()
.extensions(vec![Extend::new(FixedBytes::with_last_byte(1), 1000)])
.gas_limit(235200)
.build();
let tx_results = client.send_transaction(create_tx).await;
assert!(
tx_results
.unwrap_err()
.to_string()
.contains("Error during tx execution: ")
);
Ok(())
}