use std::collections::HashMap;use std::sync::Arc;use std::time::Duration; use chrono::{DateTime, Utc};use cloud_object_client::MockObjectClient;use cloud_object_models::JsonSerializer;use futures_lite::future;use settings::{RespectUserSyncSetting, SyncToCloud};use warp_core::features::FeatureFlag;use warp_graphql::object_permissions::AccessLevel;use warp_graphql::scalars::time::ServerTimestamp;use warpui::{App, ModelHandle, SingletonEntity}; use super::{GetCloudObjectResponse, InitialLoadResponse, UpdateManager};use crate::ASSETS;use crate::ai::cloud_environments::{ AmbientAgentEnvironment, CloudAmbientAgentEnvironment, CloudAmbientAgentEnvironmentModel,};use crate::auth::UserUid;use crate::auth::user::TEST_USER_UID;use crate::cloud_object::model::actions::{ ObjectAction, ObjectActionHistory, ObjectActionSubtype, ObjectActionType, ObjectActions,};use crate::cloud_object::model::generic_string_model::GenericStringObjectId;use crate::cloud_object::model::persistence::{CloudModel, CloudModelEvent, UpdateSource};use crate::cloud_object::{ BulkCreateCloudObjectResult, CloudModelType, CloudObjectEventEntrypoint, CloudObjectGuest, CloudObjectLocation, ConflictStatus, CreateCloudObjectResult, CreatedCloudObject, GenericCloudObject, GenericStringObjectFormat, JsonObjectType, ObjectDeleteResult, ObjectIdType, ObjectMetadataUpdateResult, ObjectPermissionsUpdateData, ObjectType, Owner, Revision, RevisionAndLastEditor, ServerCloudObject, ServerFolder, ServerGuestSubject, ServerNotebook, ServerObject, ServerObjectGuest, ServerPreference, ServerWorkflow, ServerWorkflowEnum, Space, UpdateCloudObjectResult,};use crate::drive::CloudObjectTypeAndId;use crate::drive::folders::{CloudFolder, CloudFolderModel, FolderId};use crate::drive::sharing::{SharingAccessLevel, Subject, UserKind};use crate::notebooks::{CloudNotebook, CloudNotebookModel, NotebookId};use crate::persistence::ModelEvent;use crate::server::cloud_objects::listener::ObjectUpdateMessage;use crate::server::cloud_objects::test_utils::{ UpdateManagerStruct, create_update_manager_struct, initialize_app, mock_server_api,};use crate::server::cloud_objects::update_manager::{ FetchSingleObjectOption, GenericStringObjectInput, InitiatedBy, ServerMetadata, ServerPermissions, get_duplicate_object_name,};use crate::server::ids::{ ClientId, HashableId, ObjectUid, ServerId, ServerIdAndType, SyncId, ToServerId,};use crate::server::sync_queue::SyncQueue;use crate::settings::{CloudPreferenceModel, Preference};use crate::workflows::workflow::{Argument, ArgumentType, Workflow};use crate::workflows::workflow_enum::{ CloudWorkflowEnum, CloudWorkflowEnumModel, EnumVariants, WorkflowEnum,};use crate::workflows::{CloudWorkflow, CloudWorkflowModel, WorkflowId};use crate::workspaces::user_profiles::{UserProfileWithUID, UserProfiles}; fn create_object<K, M>( app: &mut App, update_manager: &ModelHandle<UpdateManager>, client_id: ClientId, model: M,) -> GenericCloudObject<K, M>where K: HashableId + ToServerId + std::fmt::Debug + Into<String> + Clone + Copy + Send + Sync + 'static, M: CloudModelType<IdType = K, CloudObjectType = GenericCloudObject<K, M>> + 'static,{ update_manager.update(app, |update_manager, ctx| { update_manager.create_object( model, Owner::mock_current_user(), client_id, CloudObjectEventEntrypoint::Unknown, true, None, InitiatedBy::User, ctx, ); }); CloudModel::handle(app).read(app, |cloud_model, _ctx| { cloud_model .get_object_of_type::<K, M>(&SyncId::ClientId(client_id)) .expect("object should exist") .clone() })} fn create_object_result<K: HashableId + ToServerId>( client_id: ClientId, object_id: K, object_id_type: ObjectIdType,) -> CreateCloudObjectResult { CreateCloudObjectResult::Success { created_cloud_object: CreatedCloudObject { client_id, revision_and_editor: RevisionAndLastEditor { revision: Revision::now(), last_editor_uid: Some("34jkaosdfj".to_string()), }, metadata_ts: DateTime::<Utc>::default().into(), creator_uid: None, server_id_and_type: ServerIdAndType { id: object_id.to_server_id(), id_type: object_id_type, }, permissions: ServerPermissions::mock_personal(), }, }} fn create_notebook( app: &mut App, update_manager: &ModelHandle<UpdateManager>, client_id: ClientId,) { update_manager.update(app, |update_manager, ctx| { update_manager.create_notebook( client_id, Owner::mock_current_user(), None, Default::default(), CloudObjectEventEntrypoint::Unknown, true, ctx, ); });} fn receive_object_update_from_rtc( app: &mut App, update_manager: &ModelHandle<UpdateManager>, item: ObjectUpdateMessage,) { update_manager.update(app, move |update_manager, ctx| { update_manager.received_message_from_server(item, ctx); })} fn receive_initial_load_or_polling_update( app: &mut App, update_manager: &ModelHandle<UpdateManager>, force_refresh: bool, mocked_response: InitialLoadResponse,) { update_manager.update(app, move |update_manager, ctx| { update_manager.on_changed_objects_fetched(mocked_response, force_refresh, ctx); })} fn mock_server_permissions(owner: Owner) -> ServerPermissions { ServerPermissions { space: owner, guests: Vec::new(), anyone_link_sharing: None, permissions_last_updated_ts: Utc::now().into(), }} fn create_workflow( client_id: ClientId, app: &mut App, update_manager: &ModelHandle<UpdateManager>,) { create_workflow_internal( app, update_manager, client_id, "client_workflow".to_string(), "echo client".to_string(), Owner::mock_current_user(), None, )} fn create_workflow_internal( app: &mut App, update_manager: &ModelHandle<UpdateManager>, client_id: ClientId, workflow_name: String, workflow_command: String, owner: Owner, initial_folder_id: Option<SyncId>,) { update_manager.update(app, |update_manager, ctx| { update_manager.create_workflow( Workflow::new(workflow_name, workflow_command), owner, initial_folder_id, client_id, CloudObjectEventEntrypoint::Unknown, true, ctx, ); });} fn get_workflow(app: &App, sync_id: SyncId) -> CloudWorkflow { CloudModel::handle(app).read(app, |cloud_model, _ctx| { cloud_model .get_workflow(&sync_id) .expect("workflow should exist") .clone() })} fn create_workflow_enum( client_id: ClientId, app: &mut App, update_manager: &ModelHandle<UpdateManager>,) { create_workflow_enum_internal( app, update_manager, client_id, "workflow_enum".to_string(), vec!["variant 1".to_string(), "variant 2".to_string()], Owner::mock_current_user(), )} fn create_workflow_enum_internal( app: &mut App, update_manager: &ModelHandle<UpdateManager>, client_id: ClientId, enum_name: String, enum_variants: Vec<String>, owner: Owner,) { update_manager.update(app, |update_manager, ctx| { update_manager.create_workflow_enum( WorkflowEnum { name: enum_name, variants: EnumVariants::Static(enum_variants), is_shared: false, }, owner, client_id, CloudObjectEventEntrypoint::Unknown, true, ctx, ); });} fn mock_server_workflow(id: WorkflowId, owner: Owner, metadata: ServerMetadata) -> ServerWorkflow { ServerWorkflow::new( SyncId::ServerId(id.into()), CloudWorkflowModel::new(Workflow::new(format!("w{id}"), format!("c{id}"))), metadata, mock_server_permissions(owner), )} fn mock_server_workflow_with_enum( id: WorkflowId, enum_id: GenericStringObjectId, owner: Owner, metadata: ServerMetadata,) -> (ServerWorkflow, ServerWorkflowEnum) { let workflow = ServerWorkflow::new( SyncId::ServerId(id.into()), CloudWorkflowModel::new( Workflow::new(format!("w{id}"), format!("c{id}")).with_arguments(vec![Argument { name: format!("e{enum_id}"), default_value: None, description: None, arg_type: ArgumentType::Enum { enum_id: SyncId::ServerId(enum_id.into()), }, }]), ), metadata.clone(), mock_server_permissions(owner), ); let workflow_enum = ServerWorkflowEnum::new( SyncId::ServerId(enum_id.into()), CloudWorkflowEnumModel::new(WorkflowEnum { name: format!("e{id}"), is_shared: false, variants: EnumVariants::Static(vec!["v1".to_string(), "v2".to_string()]), }), metadata, mock_server_permissions(owner), ); (workflow, workflow_enum)} fn mock_server_notebook(id: NotebookId, owner: Owner, metadata: ServerMetadata) -> ServerNotebook { ServerNotebook::new( SyncId::ServerId(id.into()), CloudNotebookModel { title: format!("n{id}"), data: format!("n{id}"), ai_document_id: None, conversation_id: None, }, metadata, mock_server_permissions(owner), )} fn mock_server_folder(id: FolderId, owner: Owner, metadata: ServerMetadata) -> ServerFolder { ServerFolder::new( SyncId::ServerId(id.into()), CloudFolderModel { name: format!("f{id}"), is_open: false, is_warp_pack: false, }, metadata, mock_server_permissions(owner), )} fn update_notebook( app: &mut App, update_manager: &ModelHandle<UpdateManager>, client_id: ClientId, title: String,) { update_manager.update(app, |update_manager, ctx| { update_manager.update_notebook_title(Arc::new(title), SyncId::ClientId(client_id), ctx); });} fn update_workflow(app: &mut App, update_manager: &ModelHandle<UpdateManager>, sync_id: SyncId) { update_manager.update(app, |update_manager, ctx| { update_manager.update_workflow( Workflow::new("client workflow 2", "echo client 2"), sync_id, None, ctx, ) });} #[track_caller]fn assert_pending_online_only_change_for_object(app: &mut App, uid: &ObjectUid, status: bool) { CloudModel::handle(app).update(app, |cloud_model, _| { if let Some(object) = cloud_model.get_mut_by_uid(uid) { assert_eq!( object.metadata().has_pending_online_only_change(), status, "Expected has_pending_online_only_change for {uid} to be {status}" ); } else { panic!("object should have been in cloud model, but wasn't"); } });} fn assert_pending_status_for_object(app: &mut App, uid: &ObjectUid, status: bool) { CloudModel::handle(app).update(app, |cloud_model, _| { if let Some(object) = cloud_model.get_mut_by_uid(uid) { assert_eq!( object.metadata().has_pending_content_changes(), status, "Expected has_pending_content_changes for {uid} to be {status}" ); } else { panic!("object should have been in cloud model, but wasn't"); } });} fn assert_errored_status_for_object(app: &mut App, uid: &ObjectUid, is_errored: bool) { CloudModel::handle(app).update(app, |cloud_model, _| { if let Some(object) = cloud_model.get_mut_by_uid(uid) { assert_eq!( object.metadata().is_errored(), is_errored, "Expected error status for {uid} to be {is_errored}" ); } else { panic!("object should have been in cloud model, but wasn't"); } });} fn assert_trashed_status_for_object(app: &mut App, uid: &ObjectUid, is_trashed: bool) { CloudModel::handle(app).update(app, |cloud_model, _| { if let Some(object) = cloud_model.get_mut_by_uid(uid) { assert_eq!( object.metadata().trashed_ts.is_some(), is_trashed, "Expected trashed status for {uid} to be {is_trashed}" ); } else { panic!("object should have been in cloud model, but wasn't"); } });} fn assert_conflict_status_for_object(app: &mut App, uid: &ObjectUid, has_conflicts: bool) { CloudModel::handle(app).read(app, |cloud_model, _| { if let Some(object) = cloud_model.get_by_uid(uid) { assert_eq!( object.has_conflicting_changes(), has_conflicts, "Expected conflict status for {uid} to be {has_conflicts}" ); } else { panic!("object {uid} should have been in cloud model, but wasn't"); } })} fn assert_root_level_for_object(app: &mut App, uid: &ObjectUid, is_root_level: bool) { CloudModel::handle(app).update(app, |cloud_model, _| { if let Some(object) = cloud_model.get_mut_by_uid(uid) { assert_eq!(object.metadata().folder_id.is_none(), is_root_level); } else { panic!("object should have been in cloud model, but wasn't"); } });} fn assert_folder_for_object(app: &App, uid: &ObjectUid, folder_id: Option<SyncId>) { CloudModel::handle(app).read(app, |cloud_model, _| { if let Some(object) = cloud_model.get_by_uid(uid) { assert_eq!(object.metadata().folder_id, folder_id); } else { panic!("object should have been in cloud model, but wasn't"); } });} fn assert_space_for_object(app: &App, uid: &ObjectUid, space: Space) { CloudModel::handle(app).read(app, |cloud_model, ctx| { if let Some(object) = cloud_model.get_by_uid(uid) { assert_eq!(object.space(ctx), space); } else { panic!("object should have been in cloud model, but wasn't"); } });} /// Asserts that the current editor email address of the object matches the expected value.fn assert_current_editor_for_object(app: &mut App, uid: &ObjectUid, current_editor: Option<&str>) { app.read(|ctx| { if let Some(object) = CloudModel::as_ref(ctx).get_by_uid(uid) { assert_eq!( object.metadata().current_editor_uid.as_deref(), current_editor ); } else { panic!("object {uid} should have been in cloud model, but wasn't"); } });} /// Asserts that the `metadata_last_updated_ts` timestamp for an object has the expected value.fn assert_metadata_ts_for_object(app: &mut App, uid: &ObjectUid, metadata_ts: ServerTimestamp) { app.read(|ctx| { if let Some(object) = CloudModel::as_ref(ctx).get_by_uid(uid) { assert_eq!( object.metadata().metadata_last_updated_ts, Some(metadata_ts) ); } else { panic!("object {uid} should have been in cloud model, but wasn't"); } });} /// Asserts that the `revision` timestamp for an object has the expected value.fn assert_revision_for_object(app: &App, uid: &ObjectUid, revision: impl Into<Revision>) { let revision = revision.into(); app.read(|ctx| { if let Some(object) = CloudModel::as_ref(ctx).get_by_uid(uid) { assert_eq!(object.metadata().revision, Some(revision)); } else { panic!("object {uid} should have been in cloud model, but wasn't"); } });} fn assert_workflow_name(app: &mut App, sync_id: SyncId, expected_name: &str) { let workflow = get_workflow(app, sync_id); assert_eq!(workflow.model().data.name(), expected_name);} fn assert_notebook_data(app: &App, sync_id: SyncId, expected_data: &str) { CloudModel::handle(app).read(app, |cloud_model, _| { match cloud_model.get_notebook(&sync_id) { Some(notebook) => assert_eq!(¬ebook.model().data, expected_data), None => panic!("notebook {sync_id:?} should have been in cloud model, but wasn't"),Lines 1–500 of 7512. Load more
use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; use chrono::{DateTime, Utc}; use cloud_object_client::MockObjectClient; use cloud_object_models::JsonSerializer; use futures_lite::future; use settings::{RespectUserSyncSetting, SyncToCloud}; use warp_core::features::FeatureFlag; use warp_graphql::object_permissions::AccessLevel; use warp_graphql::scalars::time::ServerTimestamp; use warpui::{App, ModelHandle, SingletonEntity}; use super::{GetCloudObjectResponse, InitialLoadResponse, UpdateManager}; use crate::ASSETS; use crate::ai::cloud_environments::{ AmbientAgentEnvironment, CloudAmbientAgentEnvironment, CloudAmbientAgentEnvironmentModel, }; use crate::auth::UserUid; use crate::auth::user::TEST_USER_UID; use crate::cloud_object::model::actions::{ ObjectAction, ObjectActionHistory, ObjectActionSubtype, ObjectActionType, ObjectActions, }; use crate::cloud_object::model::generic_string_model::GenericStringObjectId; use crate::cloud_object::model::persistence::{CloudModel, CloudModelEvent, UpdateSource}; use crate::cloud_object::{ BulkCreateCloudObjectResult, CloudModelType, CloudObjectEventEntrypoint, CloudObjectGuest, CloudObjectLocation, ConflictStatus, CreateCloudObjectResult, CreatedCloudObject, GenericCloudObject, GenericStringObjectFormat, JsonObjectType, ObjectDeleteResult, ObjectIdType, ObjectMetadataUpdateResult, ObjectPermissionsUpdateData, ObjectType, Owner, Revision, RevisionAndLastEditor, ServerCloudObject, ServerFolder, ServerGuestSubject, ServerNotebook, ServerObject, ServerObjectGuest, ServerPreference, ServerWorkflow, ServerWorkflowEnum, Space, UpdateCloudObjectResult, }; use crate::drive::CloudObjectTypeAndId; use crate::drive::folders::{CloudFolder, CloudFolderModel, FolderId}; use crate::drive::sharing::{SharingAccessLevel, Subject, UserKind}; use crate::notebooks::{CloudNotebook, CloudNotebookModel, NotebookId}; use crate::persistence::ModelEvent; use crate::server::cloud_objects::listener::ObjectUpdateMessage; use crate::server::cloud_objects::test_utils::{ UpdateManagerStruct, create_update_manager_struct, initialize_app, mock_server_api, }; use crate::server::cloud_objects::update_manager::{ FetchSingleObjectOption, GenericStringObjectInput, InitiatedBy, ServerMetadata, ServerPermissions, get_duplicate_object_name, }; use crate::server::ids::{ ClientId, HashableId, ObjectUid, ServerId, ServerIdAndType, SyncId, ToServerId, }; use crate::server::sync_queue::SyncQueue; use crate::settings::{CloudPreferenceModel, Preference}; use crate::workflows::workflow::{Argument, ArgumentType, Workflow}; use crate::workflows::workflow_enum::{ CloudWorkflowEnum, CloudWorkflowEnumModel, EnumVariants, WorkflowEnum, }; use crate::workflows::{CloudWorkflow, CloudWorkflowModel, WorkflowId}; use crate::workspaces::user_profiles::{UserProfileWithUID, UserProfiles}; fn create_object<K, M>( app: &mut App, update_manager: &ModelHandle<UpdateManager>, client_id: ClientId, model: M, ) -> GenericCloudObject<K, M> where K: HashableId + ToServerId + std::fmt::Debug + Into<String> + Clone + Copy + Send + Sync + 'static, M: CloudModelType<IdType = K, CloudObjectType = GenericCloudObject<K, M>> + 'static, { update_manager.update(app, |update_manager, ctx| { update_manager.create_object( model, Owner::mock_current_user(), client_id, CloudObjectEventEntrypoint::Unknown, true, None, InitiatedBy::User, ctx, ); }); CloudModel::handle(app).read(app, |cloud_model, _ctx| { cloud_model .get_object_of_type::<K, M>(&SyncId::ClientId(client_id)) .expect("object should exist") .clone() }) } fn create_object_result<K: HashableId + ToServerId>( client_id: ClientId, object_id: K, object_id_type: ObjectIdType, ) -> CreateCloudObjectResult { CreateCloudObjectResult::Success { created_cloud_object: CreatedCloudObject { client_id, revision_and_editor: RevisionAndLastEditor { revision: Revision::now(), last_editor_uid: Some("34jkaosdfj".to_string()), }, metadata_ts: DateTime::<Utc>::default().into(), creator_uid: None, server_id_and_type: ServerIdAndType { id: object_id.to_server_id(), id_type: object_id_type, }, permissions: ServerPermissions::mock_personal(), }, } } fn create_notebook( app: &mut App, update_manager: &ModelHandle<UpdateManager>, client_id: ClientId, ) { update_manager.update(app, |update_manager, ctx| { update_manager.create_notebook( client_id, Owner::mock_current_user(), None, Default::default(), CloudObjectEventEntrypoint::Unknown, true, ctx, ); }); } fn receive_object_update_from_rtc( app: &mut App, update_manager: &ModelHandle<UpdateManager>, item: ObjectUpdateMessage, ) { update_manager.update(app, move |update_manager, ctx| { update_manager.received_message_from_server(item, ctx); }) } fn receive_initial_load_or_polling_update( app: &mut App, update_manager: &ModelHandle<UpdateManager>, force_refresh: bool, mocked_response: InitialLoadResponse, ) { update_manager.update(app, move |update_manager, ctx| { update_manager.on_changed_objects_fetched(mocked_response, force_refresh, ctx); }) } fn mock_server_permissions(owner: Owner) -> ServerPermissions { ServerPermissions { space: owner, guests: Vec::new(), anyone_link_sharing: None, permissions_last_updated_ts: Utc::now().into(), } } fn create_workflow( client_id: ClientId, app: &mut App, update_manager: &ModelHandle<UpdateManager>, ) { create_workflow_internal( app, update_manager, client_id, "client_workflow".to_string(), "echo client".to_string(), Owner::mock_current_user(), None, ) } fn create_workflow_internal( app: &mut App, update_manager: &ModelHandle<UpdateManager>, client_id: ClientId, workflow_name: String, workflow_command: String, owner: Owner, initial_folder_id: Option<SyncId>, ) { update_manager.update(app, |update_manager, ctx| { update_manager.create_workflow( Workflow::new(workflow_name, workflow_command), owner, initial_folder_id, client_id, CloudObjectEventEntrypoint::Unknown, true, ctx, ); }); } fn get_workflow(app: &App, sync_id: SyncId) -> CloudWorkflow { CloudModel::handle(app).read(app, |cloud_model, _ctx| { cloud_model .get_workflow(&sync_id) .expect("workflow should exist") .clone() }) } fn create_workflow_enum( client_id: ClientId, app: &mut App, update_manager: &ModelHandle<UpdateManager>, ) { create_workflow_enum_internal( app, update_manager, client_id, "workflow_enum".to_string(), vec!["variant 1".to_string(), "variant 2".to_string()], Owner::mock_current_user(), ) } fn create_workflow_enum_internal( app: &mut App, update_manager: &ModelHandle<UpdateManager>, client_id: ClientId, enum_name: String, enum_variants: Vec<String>, owner: Owner, ) { update_manager.update(app, |update_manager, ctx| { update_manager.create_workflow_enum( WorkflowEnum { name: enum_name, variants: EnumVariants::Static(enum_variants), is_shared: false, }, owner, client_id, CloudObjectEventEntrypoint::Unknown, true, ctx, ); }); } fn mock_server_workflow(id: WorkflowId, owner: Owner, metadata: ServerMetadata) -> ServerWorkflow { ServerWorkflow::new( SyncId::ServerId(id.into()), CloudWorkflowModel::new(Workflow::new(format!("w{id}"), format!("c{id}"))), metadata, mock_server_permissions(owner), ) } fn mock_server_workflow_with_enum( id: WorkflowId, enum_id: GenericStringObjectId, owner: Owner, metadata: ServerMetadata, ) -> (ServerWorkflow, ServerWorkflowEnum) { let workflow = ServerWorkflow::new( SyncId::ServerId(id.into()), CloudWorkflowModel::new( Workflow::new(format!("w{id}"), format!("c{id}")).with_arguments(vec![Argument { name: format!("e{enum_id}"), default_value: None, description: None, arg_type: ArgumentType::Enum { enum_id: SyncId::ServerId(enum_id.into()), }, }]), ), metadata.clone(), mock_server_permissions(owner), ); let workflow_enum = ServerWorkflowEnum::new( SyncId::ServerId(enum_id.into()), CloudWorkflowEnumModel::new(WorkflowEnum { name: format!("e{id}"), is_shared: false, variants: EnumVariants::Static(vec!["v1".to_string(), "v2".to_string()]), }), metadata, mock_server_permissions(owner), ); (workflow, workflow_enum) } fn mock_server_notebook(id: NotebookId, owner: Owner, metadata: ServerMetadata) -> ServerNotebook { ServerNotebook::new( SyncId::ServerId(id.into()), CloudNotebookModel { title: format!("n{id}"), data: format!("n{id}"), ai_document_id: None, conversation_id: None, }, metadata, mock_server_permissions(owner), ) } fn mock_server_folder(id: FolderId, owner: Owner, metadata: ServerMetadata) -> ServerFolder { ServerFolder::new( SyncId::ServerId(id.into()), CloudFolderModel { name: format!("f{id}"), is_open: false, is_warp_pack: false, }, metadata, mock_server_permissions(owner), ) } fn update_notebook( app: &mut App, update_manager: &ModelHandle<UpdateManager>, client_id: ClientId, title: String, ) { update_manager.update(app, |update_manager, ctx| { update_manager.update_notebook_title(Arc::new(title), SyncId::ClientId(client_id), ctx); }); } fn update_workflow(app: &mut App, update_manager: &ModelHandle<UpdateManager>, sync_id: SyncId) { update_manager.update(app, |update_manager, ctx| { update_manager.update_workflow( Workflow::new("client workflow 2", "echo client 2"), sync_id, None, ctx, ) }); } #[track_caller] fn assert_pending_online_only_change_for_object(app: &mut App, uid: &ObjectUid, status: bool) { CloudModel::handle(app).update(app, |cloud_model, _| { if let Some(object) = cloud_model.get_mut_by_uid(uid) { assert_eq!( object.metadata().has_pending_online_only_change(), status, "Expected has_pending_online_only_change for {uid} to be {status}" ); } else { panic!("object should have been in cloud model, but wasn't"); } }); } fn assert_pending_status_for_object(app: &mut App, uid: &ObjectUid, status: bool) { CloudModel::handle(app).update(app, |cloud_model, _| { if let Some(object) = cloud_model.get_mut_by_uid(uid) { assert_eq!( object.metadata().has_pending_content_changes(), status, "Expected has_pending_content_changes for {uid} to be {status}" ); } else { panic!("object should have been in cloud model, but wasn't"); } }); } fn assert_errored_status_for_object(app: &mut App, uid: &ObjectUid, is_errored: bool) { CloudModel::handle(app).update(app, |cloud_model, _| { if let Some(object) = cloud_model.get_mut_by_uid(uid) { assert_eq!( object.metadata().is_errored(), is_errored, "Expected error status for {uid} to be {is_errored}" ); } else { panic!("object should have been in cloud model, but wasn't"); } }); } fn assert_trashed_status_for_object(app: &mut App, uid: &ObjectUid, is_trashed: bool) { CloudModel::handle(app).update(app, |cloud_model, _| { if let Some(object) = cloud_model.get_mut_by_uid(uid) { assert_eq!( object.metadata().trashed_ts.is_some(), is_trashed, "Expected trashed status for {uid} to be {is_trashed}" ); } else { panic!("object should have been in cloud model, but wasn't"); } }); } fn assert_conflict_status_for_object(app: &mut App, uid: &ObjectUid, has_conflicts: bool) { CloudModel::handle(app).read(app, |cloud_model, _| { if let Some(object) = cloud_model.get_by_uid(uid) { assert_eq!( object.has_conflicting_changes(), has_conflicts, "Expected conflict status for {uid} to be {has_conflicts}" ); } else { panic!("object {uid} should have been in cloud model, but wasn't"); } }) } fn assert_root_level_for_object(app: &mut App, uid: &ObjectUid, is_root_level: bool) { CloudModel::handle(app).update(app, |cloud_model, _| { if let Some(object) = cloud_model.get_mut_by_uid(uid) { assert_eq!(object.metadata().folder_id.is_none(), is_root_level); } else { panic!("object should have been in cloud model, but wasn't"); } }); } fn assert_folder_for_object(app: &App, uid: &ObjectUid, folder_id: Option<SyncId>) { CloudModel::handle(app).read(app, |cloud_model, _| { if let Some(object) = cloud_model.get_by_uid(uid) { assert_eq!(object.metadata().folder_id, folder_id); } else { panic!("object should have been in cloud model, but wasn't"); } }); } fn assert_space_for_object(app: &App, uid: &ObjectUid, space: Space) { CloudModel::handle(app).read(app, |cloud_model, ctx| { if let Some(object) = cloud_model.get_by_uid(uid) { assert_eq!(object.space(ctx), space); } else { panic!("object should have been in cloud model, but wasn't"); } }); } /// Asserts that the current editor email address of the object matches the expected value. fn assert_current_editor_for_object(app: &mut App, uid: &ObjectUid, current_editor: Option<&str>) { app.read(|ctx| { if let Some(object) = CloudModel::as_ref(ctx).get_by_uid(uid) { assert_eq!( object.metadata().current_editor_uid.as_deref(), current_editor ); } else { panic!("object {uid} should have been in cloud model, but wasn't"); } }); } /// Asserts that the `metadata_last_updated_ts` timestamp for an object has the expected value. fn assert_metadata_ts_for_object(app: &mut App, uid: &ObjectUid, metadata_ts: ServerTimestamp) { app.read(|ctx| { if let Some(object) = CloudModel::as_ref(ctx).get_by_uid(uid) { assert_eq!( object.metadata().metadata_last_updated_ts, Some(metadata_ts) ); } else { panic!("object {uid} should have been in cloud model, but wasn't"); } }); } /// Asserts that the `revision` timestamp for an object has the expected value. fn assert_revision_for_object(app: &App, uid: &ObjectUid, revision: impl Into<Revision>) { let revision = revision.into(); app.read(|ctx| { if let Some(object) = CloudModel::as_ref(ctx).get_by_uid(uid) { assert_eq!(object.metadata().revision, Some(revision)); } else { panic!("object {uid} should have been in cloud model, but wasn't"); } }); } fn assert_workflow_name(app: &mut App, sync_id: SyncId, expected_name: &str) { let workflow = get_workflow(app, sync_id); assert_eq!(workflow.model().data.name(), expected_name); } fn assert_notebook_data(app: &App, sync_id: SyncId, expected_data: &str) { CloudModel::handle(app).read(app, |cloud_model, _| { match cloud_model.get_notebook(&sync_id) { Some(notebook) => assert_eq!(¬ebook.model().data, expected_data), None => panic!("notebook {sync_id:?} should have been in cloud model, but wasn't"),