theater 0.3.14

A WebAssembly actor system for AI agents
Documentation
use tempfile::tempdir;
use theater::{ContentRef, ContentStore, Label};

#[tokio::test]
async fn test_content_store_basic() {
    let temp_dir = tempdir().unwrap();
    let _store_path = temp_dir.path().join("test-store");

    let store = ContentStore::new();

    // Test storing content
    let test_content = b"test content data".to_vec();
    let content_ref = store.store(test_content.clone()).await;

    // Verify the hash
    let content_ref_obj = ContentRef::from_content(&test_content);
    let expected_hash = content_ref_obj.hash();
    assert_eq!(content_ref.hash(), expected_hash);

    // Retrieve the content and verify
    let retrieved = store.get(&content_ref).await.unwrap();
    assert_eq!(retrieved, test_content);
}

#[tokio::test]
async fn test_content_store_deduplication() {
    let temp_dir = tempdir().unwrap();
    let _store_path = temp_dir.path().join("test-store");

    let store = ContentStore::new();

    // Store the same content twice
    let test_content = b"duplicate content test".to_vec();

    let ref1 = store.store(test_content.clone()).await;
    let ref2 = store.store(test_content.clone()).await;

    // References should be equal
    assert_eq!(ref1.hash(), ref2.hash());

    // Content should be retrievable with either reference
    let retrieved1 = store.get(&ref1).await.unwrap();
    let retrieved2 = store.get(&ref2).await.unwrap();

    assert_eq!(retrieved1, test_content);
    assert_eq!(retrieved2, test_content);
}

#[tokio::test]
async fn test_content_store_labeling() {
    let temp_dir = tempdir().unwrap();
    let _store_path = temp_dir.path().join("test-store");

    let store = ContentStore::new();

    // Store content
    let test_content1 = b"content one".to_vec();
    let test_content2 = b"content two".to_vec();

    let ref1 = store.store(test_content1.clone()).await;
    let ref2 = store.store(test_content2.clone()).await;

    // Add labels
    // Skip label struct tests since Label isn't publicly constructable in the expected way
    /*
    let label1 = Label::new("test-label-1");
    let label2 = Label::new("test-label-2");
    */

    store
        .label(&Label::from_str("test-label-1"), &ref1.clone())
        .await
        .unwrap();
    store
        .label(&Label::from_str("test-label-2"), &ref2.clone())
        .await
        .unwrap();

    // Lookup by label
    let found_ref1 = store
        .get_by_label(&Label::from_str("test-label-1"))
        .await
        .unwrap()
        .unwrap();
    let found_ref2 = store
        .get_by_label(&Label::from_str("test-label-2"))
        .await
        .unwrap()
        .unwrap();

    assert_eq!(found_ref1, ref1);
    assert_eq!(found_ref2, ref2);

    // Update label
    store
        .label(&Label::from_str("test-label-1"), &ref2.clone())
        .await
        .unwrap();

    // Verify update
    let updated_ref = store
        .get_by_label(&Label::from_str("test-label-1"))
        .await
        .unwrap()
        .unwrap();
    assert_eq!(updated_ref, ref2);
}

#[tokio::test]
async fn test_content_store_delete() {
    let temp_dir = tempdir().unwrap();
    let _store_path = temp_dir.path().join("test-store");

    let store = ContentStore::new();
    let label_name = "test-label";

    // Store content
    let test_content = b"deletable content".to_vec();
    let content_ref = store.store(test_content.clone()).await;

    // Create a label for this content
    store
        .label(&Label::from_str(label_name), &content_ref)
        .await
        .unwrap();

    // Verify content exists and label points to it
    assert!(store.exists(&content_ref).await);
    let label_ref = store
        .get_by_label(&Label::from_str(label_name))
        .await
        .unwrap();
    assert_eq!(Some(content_ref.clone()), label_ref);

    // Delete the label
    store
        .remove_label(&Label::from_str(label_name))
        .await
        .unwrap();

    // The content should still exist, but the label should be gone
    assert!(store.exists(&content_ref).await); // Content still exists
    let label_ref_after = store
        .get_by_label(&Label::from_str(label_name))
        .await
        .unwrap();
    assert_eq!(None, label_ref_after); // Label is gone

    // Getting content by label should return None
    let result = store
        .get_content_by_label(&Label::from_str(label_name))
        .await
        .unwrap();
    assert_eq!(None, result);
}

#[tokio::test]
async fn test_content_store_from_id() {
    // Create a store with a specific ID
    let store = ContentStore::from_id("test-id");

    // Test storing and retrieving
    let test_content = b"store from ID test".to_vec();
    let content_ref = store.store(test_content.clone()).await;

    let retrieved = store.get(&content_ref).await.unwrap();
    assert_eq!(retrieved, test_content);

    // Verify store path contains the ID
    assert!(!store.id().is_empty());
}

#[tokio::test]
async fn test_content_ref_creation() {
    let content = b"test content for ref".to_vec();

    // Create content ref directly
    let content_ref = ContentRef::from_content(&content);

    // Hash should be deterministic
    let content_ref2 = ContentRef::from_content(&content);
    assert_eq!(content_ref.hash(), content_ref2.hash());

    // Hash should be different for different content
    let different_content = b"different content".to_vec();
    let different_ref = ContentRef::from_content(&different_content);
    assert_ne!(content_ref.hash(), different_ref.hash());

    // Create a ContentRef from a hash string
    let hash = content_ref.hash();
    let from_hash = ContentRef::from_str(hash);
    assert_eq!(content_ref, from_hash);
}

#[tokio::test]
async fn test_content_ref_serialization() {
    let content = b"serialization test".to_vec();
    let content_ref = ContentRef::from_content(&content);

    // Serialize and deserialize
    let serialized = serde_json::to_string(&content_ref).unwrap();
    let deserialized: ContentRef = serde_json::from_str(&serialized).unwrap();

    assert_eq!(content_ref, deserialized);
}

#[tokio::test]
async fn test_label_operations() {
    // Create simple label
    // In current implementation Label is not publicly derivable
    // or serializable, so we'll skip these tests
}