tandem-memory 0.6.0

Memory storage and embedding utilities for Tandem
Documentation
#[cfg(test)]
mod tenant_scope_tests {
    use super::*;
    use tempfile::TempDir;

    async fn setup_test_manager() -> (MemoryManager, TempDir) {
        let temp_dir = TempDir::new().unwrap();
        let db_path = temp_dir.path().join("test_memory.db");
        let manager = MemoryManager::new(&db_path).await.unwrap();
        (manager, temp_dir)
    }

    fn tenant_scope(org_id: &str, workspace_id: &str) -> MemoryTenantScope {
        MemoryTenantScope {
            org_id: org_id.to_string(),
            workspace_id: workspace_id.to_string(),
            deployment_id: Some("deployment-1".to_string()),
        }
    }

    #[tokio::test]
    async fn promoted_knowledge_item_remains_bound_to_owning_tenant() {
        let (manager, _temp) = setup_test_manager().await;
        let tenant_a = tenant_scope("org-a", "workspace-a");
        let tenant_b = tenant_scope("org-b", "workspace-b");
        let now = chrono::Utc::now().timestamp_millis() as u64;

        let space = KnowledgeSpaceRecord {
            id: "tenant-b-promoted-space".to_string(),
            scope: KnowledgeScope::Project,
            project_id: Some("shared-project".to_string()),
            namespace: Some("support/runbooks".to_string()),
            title: Some("Tenant B runbooks".to_string()),
            description: None,
            trust_level: KnowledgeTrustLevel::Promoted,
            metadata: None,
            created_at_ms: now,
            updated_at_ms: now,
        };
        manager
            .upsert_knowledge_space_for_tenant(&space, &tenant_b)
            .await
            .unwrap();

        let item = KnowledgeItemRecord {
            id: "tenant-b-promoted-item".to_string(),
            space_id: space.id.clone(),
            coverage_key: "shared-project::support/runbooks::billing::refunds".to_string(),
            dedupe_key: "tenant-b-promoted-dedupe".to_string(),
            item_type: "runbook".to_string(),
            title: "Tenant B refund runbook".to_string(),
            summary: Some("Tenant B internal refund steps.".to_string()),
            payload: serde_json::json!({"tenant": "b", "action": "refund"}),
            trust_level: KnowledgeTrustLevel::Working,
            status: crate::types::KnowledgeItemStatus::Working,
            run_id: Some("tenant-b-run".to_string()),
            artifact_refs: vec!["artifact://tenant-b/refunds".to_string()],
            source_memory_ids: vec!["memory://tenant-b/refunds".to_string()],
            freshness_expires_at_ms: None,
            metadata: None,
            created_at_ms: now,
            updated_at_ms: now,
        };
        manager
            .upsert_knowledge_item_for_tenant(&item, &tenant_b)
            .await
            .unwrap();

        let promote = KnowledgePromotionRequest {
            item_id: item.id.clone(),
            target_status: crate::types::KnowledgeItemStatus::Promoted,
            promoted_at_ms: now + 10,
            freshness_expires_at_ms: Some(now + 86_400_000),
            reviewer_id: None,
            approval_id: None,
            reason: Some("ct-03 tenant scope regression".to_string()),
        };
        assert!(manager
            .promote_knowledge_item_for_tenant(&promote, &tenant_a)
            .await
            .unwrap()
            .is_none());

        let promoted = manager
            .promote_knowledge_item_for_tenant(&promote, &tenant_b)
            .await
            .unwrap()
            .expect("tenant-b promotion");
        assert_eq!(
            promoted.item.status,
            crate::types::KnowledgeItemStatus::Promoted
        );
        assert_eq!(
            promoted.coverage.latest_item_id.as_deref(),
            Some(item.id.as_str())
        );

        assert!(manager
            .get_knowledge_item_for_tenant(&item.id, &tenant_a)
            .await
            .unwrap()
            .is_none());
        assert!(manager
            .list_knowledge_items_for_tenant(&space.id, Some(&item.coverage_key), &tenant_a)
            .await
            .unwrap()
            .is_empty());
        assert!(manager
            .get_knowledge_coverage_for_tenant(&item.coverage_key, &space.id, &tenant_a)
            .await
            .unwrap()
            .is_none());
        assert!(manager
            .get_knowledge_item_for_tenant(&item.id, &tenant_b)
            .await
            .unwrap()
            .is_some());
    }
}