auth_test.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. use std::collections::HashMap;
  2. use assert_json_diff::assert_json_eq;
  3. use collab_database::rows::database_row_document_id_from_row_id;
  4. use collab_define::CollabType;
  5. use collab_document::blocks::DocumentData;
  6. use collab_folder::core::FolderData;
  7. use nanoid::nanoid;
  8. use serde_json::json;
  9. use flowy_core::DEFAULT_NAME;
  10. use flowy_encrypt::decrypt_text;
  11. use flowy_server::supabase::define::{USER_EMAIL, USER_UUID};
  12. use flowy_test::document::document_event::DocumentEventTest;
  13. use flowy_test::event_builder::EventBuilder;
  14. use flowy_test::FlowyCoreTest;
  15. use flowy_user::entities::{
  16. AuthTypePB, ThirdPartyAuthPB, UpdateUserProfilePayloadPB, UserProfilePB,
  17. };
  18. use flowy_user::errors::ErrorCode;
  19. use flowy_user::event_map::UserEvent::*;
  20. use crate::util::*;
  21. #[tokio::test]
  22. async fn third_party_sign_up_test() {
  23. if get_supabase_config().is_some() {
  24. let test = FlowyCoreTest::new();
  25. let mut map = HashMap::new();
  26. map.insert(USER_UUID.to_string(), uuid::Uuid::new_v4().to_string());
  27. map.insert(
  28. USER_EMAIL.to_string(),
  29. format!("{}@appflowy.io", nanoid!(6)),
  30. );
  31. let payload = ThirdPartyAuthPB {
  32. map,
  33. auth_type: AuthTypePB::Supabase,
  34. };
  35. let response = EventBuilder::new(test.clone())
  36. .event(ThirdPartyAuth)
  37. .payload(payload)
  38. .async_send()
  39. .await
  40. .parse::<UserProfilePB>();
  41. dbg!(&response);
  42. }
  43. }
  44. #[tokio::test]
  45. async fn third_party_sign_up_with_encrypt_test() {
  46. if get_supabase_config().is_some() {
  47. let test = FlowyCoreTest::new();
  48. test.supabase_party_sign_up().await;
  49. let user_profile = test.get_user_profile().await.unwrap();
  50. assert!(user_profile.encryption_sign.is_empty());
  51. let secret = test.enable_encryption().await;
  52. let user_profile = test.get_user_profile().await.unwrap();
  53. assert!(!user_profile.encryption_sign.is_empty());
  54. let decryption_sign = decrypt_text(user_profile.encryption_sign, &secret).unwrap();
  55. assert_eq!(decryption_sign, user_profile.id.to_string());
  56. }
  57. }
  58. #[tokio::test]
  59. async fn third_party_sign_up_with_duplicated_uuid() {
  60. if get_supabase_config().is_some() {
  61. let test = FlowyCoreTest::new();
  62. let email = format!("{}@appflowy.io", nanoid!(6));
  63. let mut map = HashMap::new();
  64. map.insert(USER_UUID.to_string(), uuid::Uuid::new_v4().to_string());
  65. map.insert(USER_EMAIL.to_string(), email.clone());
  66. let response_1 = EventBuilder::new(test.clone())
  67. .event(ThirdPartyAuth)
  68. .payload(ThirdPartyAuthPB {
  69. map: map.clone(),
  70. auth_type: AuthTypePB::Supabase,
  71. })
  72. .async_send()
  73. .await
  74. .parse::<UserProfilePB>();
  75. dbg!(&response_1);
  76. let response_2 = EventBuilder::new(test.clone())
  77. .event(ThirdPartyAuth)
  78. .payload(ThirdPartyAuthPB {
  79. map: map.clone(),
  80. auth_type: AuthTypePB::Supabase,
  81. })
  82. .async_send()
  83. .await
  84. .parse::<UserProfilePB>();
  85. assert_eq!(response_1, response_2);
  86. };
  87. }
  88. #[tokio::test]
  89. async fn third_party_sign_up_with_duplicated_email() {
  90. if get_supabase_config().is_some() {
  91. let test = FlowyCoreTest::new();
  92. let email = format!("{}@appflowy.io", nanoid!(6));
  93. test
  94. .third_party_sign_up_with_uuid(&uuid::Uuid::new_v4().to_string(), Some(email.clone()))
  95. .await
  96. .unwrap();
  97. let error = test
  98. .third_party_sign_up_with_uuid(&uuid::Uuid::new_v4().to_string(), Some(email.clone()))
  99. .await
  100. .err()
  101. .unwrap();
  102. assert_eq!(error.code, ErrorCode::Conflict);
  103. };
  104. }
  105. #[tokio::test]
  106. async fn sign_up_as_guest_and_then_update_to_new_cloud_user_test() {
  107. if get_supabase_config().is_some() {
  108. let test = FlowyCoreTest::new_with_guest_user().await;
  109. let old_views = test
  110. .folder_manager
  111. .get_current_workspace_views()
  112. .await
  113. .unwrap();
  114. let old_workspace = test.folder_manager.get_current_workspace().await.unwrap();
  115. let uuid = uuid::Uuid::new_v4().to_string();
  116. test
  117. .third_party_sign_up_with_uuid(&uuid, None)
  118. .await
  119. .unwrap();
  120. let new_views = test
  121. .folder_manager
  122. .get_current_workspace_views()
  123. .await
  124. .unwrap();
  125. let new_workspace = test.folder_manager.get_current_workspace().await.unwrap();
  126. assert_eq!(old_views.len(), new_views.len());
  127. assert_eq!(old_workspace.name, new_workspace.name);
  128. assert_eq!(old_workspace.views.len(), new_workspace.views.len());
  129. for (index, view) in old_views.iter().enumerate() {
  130. assert_eq!(view.name, new_views[index].name);
  131. assert_eq!(view.id, new_views[index].id);
  132. assert_eq!(view.layout, new_views[index].layout);
  133. assert_eq!(view.create_time, new_views[index].create_time);
  134. }
  135. }
  136. }
  137. #[tokio::test]
  138. async fn sign_up_as_guest_and_then_update_to_existing_cloud_user_test() {
  139. if get_supabase_config().is_some() {
  140. let test = FlowyCoreTest::new_with_guest_user().await;
  141. let uuid = uuid::Uuid::new_v4().to_string();
  142. let email = format!("{}@appflowy.io", nanoid!(6));
  143. // The workspace of the guest will be migrated to the new user with given uuid
  144. let _user_profile = test
  145. .third_party_sign_up_with_uuid(&uuid, Some(email.clone()))
  146. .await
  147. .unwrap();
  148. let old_cloud_workspace = test.folder_manager.get_current_workspace().await.unwrap();
  149. let old_cloud_views = test
  150. .folder_manager
  151. .get_current_workspace_views()
  152. .await
  153. .unwrap();
  154. assert_eq!(old_cloud_views.len(), 1);
  155. assert_eq!(old_cloud_views.first().unwrap().child_views.len(), 1);
  156. // sign out and then sign in as a guest
  157. test.sign_out().await;
  158. let _sign_up_context = test.sign_up_as_guest().await;
  159. let new_workspace = test.folder_manager.get_current_workspace().await.unwrap();
  160. test
  161. .create_view(&new_workspace.id, "new workspace child view".to_string())
  162. .await;
  163. let new_workspace = test.folder_manager.get_current_workspace().await.unwrap();
  164. assert_eq!(new_workspace.views.len(), 2);
  165. // upload to cloud user with given uuid. This time the workspace of the guest will not be merged
  166. // because the cloud user already has a workspace
  167. test
  168. .third_party_sign_up_with_uuid(&uuid, Some(email))
  169. .await
  170. .unwrap();
  171. let new_cloud_workspace = test.folder_manager.get_current_workspace().await.unwrap();
  172. let new_cloud_views = test
  173. .folder_manager
  174. .get_current_workspace_views()
  175. .await
  176. .unwrap();
  177. assert_eq!(new_cloud_workspace, old_cloud_workspace);
  178. assert_eq!(new_cloud_views, old_cloud_views);
  179. }
  180. }
  181. #[tokio::test]
  182. async fn check_not_exist_user_test() {
  183. if let Some(test) = FlowySupabaseTest::new() {
  184. let err = test
  185. .check_user_with_uuid(&uuid::Uuid::new_v4().to_string())
  186. .await
  187. .unwrap_err();
  188. assert_eq!(err.code, ErrorCode::RecordNotFound);
  189. }
  190. }
  191. #[tokio::test]
  192. async fn get_user_profile_test() {
  193. if let Some(test) = FlowySupabaseTest::new() {
  194. let uuid = uuid::Uuid::new_v4().to_string();
  195. test
  196. .third_party_sign_up_with_uuid(&uuid, None)
  197. .await
  198. .unwrap();
  199. let result = test.get_user_profile().await;
  200. assert!(result.is_ok());
  201. }
  202. }
  203. #[tokio::test]
  204. async fn update_user_profile_test() {
  205. if let Some(test) = FlowySupabaseTest::new() {
  206. let uuid = uuid::Uuid::new_v4().to_string();
  207. let profile = test
  208. .third_party_sign_up_with_uuid(&uuid, None)
  209. .await
  210. .unwrap();
  211. test
  212. .update_user_profile(UpdateUserProfilePayloadPB::new(profile.id).name("lucas"))
  213. .await;
  214. let new_profile = test.get_user_profile().await.unwrap();
  215. assert_eq!(new_profile.name, "lucas")
  216. }
  217. }
  218. #[tokio::test]
  219. async fn update_user_profile_with_existing_email_test() {
  220. if let Some(test) = FlowySupabaseTest::new() {
  221. let email = format!("{}@appflowy.io", nanoid!(6));
  222. let _ = test
  223. .third_party_sign_up_with_uuid(&uuid::Uuid::new_v4().to_string(), Some(email.clone()))
  224. .await;
  225. let profile = test
  226. .third_party_sign_up_with_uuid(
  227. &uuid::Uuid::new_v4().to_string(),
  228. Some(format!("{}@appflowy.io", nanoid!(6))),
  229. )
  230. .await
  231. .unwrap();
  232. let error = test
  233. .update_user_profile(
  234. UpdateUserProfilePayloadPB::new(profile.id)
  235. .name("lucas")
  236. .email(&email),
  237. )
  238. .await
  239. .unwrap();
  240. assert_eq!(error.code, ErrorCode::Conflict);
  241. }
  242. }
  243. #[tokio::test]
  244. async fn migrate_anon_document_on_cloud_signup() {
  245. if get_supabase_config().is_some() {
  246. let test = FlowyCoreTest::new();
  247. let user_profile = test.sign_up_as_guest().await.user_profile;
  248. let view = test
  249. .create_view(&user_profile.workspace_id, "My first view".to_string())
  250. .await;
  251. let document_event = DocumentEventTest::new_with_core(test.clone());
  252. let block_id = document_event
  253. .insert_index(&view.id, "hello world", 1, None)
  254. .await;
  255. let _ = test.supabase_party_sign_up().await;
  256. // After sign up, the documents should be migrated to the cloud
  257. // So, we can get the document data from the cloud
  258. let data: DocumentData = test
  259. .document_manager
  260. .get_cloud_service()
  261. .get_document_data(&view.id)
  262. .await
  263. .unwrap()
  264. .unwrap();
  265. let block = data.blocks.get(&block_id).unwrap();
  266. assert_json_eq!(
  267. block.data,
  268. json!({
  269. "delta": [
  270. {
  271. "insert": "hello world"
  272. }
  273. ]
  274. })
  275. );
  276. }
  277. }
  278. #[tokio::test]
  279. async fn migrate_anon_data_on_cloud_signup() {
  280. if get_supabase_config().is_some() {
  281. let (cleaner, user_db_path) = unzip_history_user_db(
  282. "./tests/user/supabase_test/history_user_db",
  283. "workspace_sync",
  284. )
  285. .unwrap();
  286. let test = FlowyCoreTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string());
  287. let user_profile = test.supabase_party_sign_up().await;
  288. // Get the folder data from remote
  289. let folder_data: FolderData = test
  290. .folder_manager
  291. .get_cloud_service()
  292. .get_folder_data(&user_profile.workspace_id)
  293. .await
  294. .unwrap()
  295. .unwrap();
  296. let expected_folder_data = expected_workspace_sync_folder_data();
  297. if folder_data.workspaces.len() != expected_folder_data.workspaces.len() {
  298. dbg!(&folder_data.workspaces);
  299. }
  300. assert_eq!(
  301. folder_data.workspaces.len(),
  302. expected_folder_data.workspaces.len()
  303. );
  304. assert_eq!(folder_data.views.len(), expected_folder_data.views.len());
  305. // After migration, the ids of the folder_data should be different from the expected_folder_data
  306. for i in 0..folder_data.views.len() {
  307. let left_view = &folder_data.views[i];
  308. let right_view = &expected_folder_data.views[i];
  309. assert_ne!(left_view.id, right_view.id);
  310. assert_ne!(left_view.parent_view_id, right_view.parent_view_id);
  311. assert_eq!(left_view.name, right_view.name);
  312. }
  313. assert_ne!(
  314. folder_data.current_workspace_id,
  315. expected_folder_data.current_workspace_id
  316. );
  317. assert_ne!(folder_data.current_view, expected_folder_data.current_view);
  318. let database_views = folder_data
  319. .views
  320. .iter()
  321. .filter(|view| view.layout.is_database())
  322. .collect::<Vec<_>>();
  323. // Try to load the database from the cloud.
  324. for (i, database_view) in database_views.iter().enumerate() {
  325. let cloud_service = test.database_manager.get_cloud_service();
  326. let database_id = test
  327. .database_manager
  328. .get_database_id_with_view_id(&database_view.id)
  329. .await
  330. .unwrap();
  331. let editor = test
  332. .database_manager
  333. .get_database(&database_id)
  334. .await
  335. .unwrap();
  336. // The database view setting should be loaded by the view id
  337. let _ = editor
  338. .get_database_view_setting(&database_view.id)
  339. .await
  340. .unwrap();
  341. let rows = editor.get_rows(&database_view.id).await.unwrap();
  342. assert_eq!(rows.len(), 3);
  343. if i == 0 {
  344. let first_row = rows.first().unwrap().as_ref();
  345. let icon_url = first_row.meta.icon_url.clone().unwrap();
  346. assert_eq!(icon_url, "😄");
  347. let document_id = database_row_document_id_from_row_id(&first_row.row.id);
  348. let document_data: DocumentData = test
  349. .document_manager
  350. .get_cloud_service()
  351. .get_document_data(&document_id)
  352. .await
  353. .unwrap()
  354. .unwrap();
  355. let editor = test
  356. .document_manager
  357. .get_document(&document_id)
  358. .await
  359. .unwrap();
  360. let expected_document_data = editor.lock().get_document_data().unwrap();
  361. // let expected_document_data = test
  362. // .document_manager
  363. // .get_document_data(&document_id)
  364. // .await
  365. // .unwrap();
  366. assert_eq!(document_data, expected_document_data);
  367. let json = json!(document_data);
  368. assert_eq!(
  369. json["blocks"]["LPMpo0Qaab"]["data"]["delta"][0]["insert"],
  370. json!("Row document")
  371. );
  372. }
  373. assert!(cloud_service
  374. .get_collab_update(&database_id, CollabType::Database)
  375. .await
  376. .is_ok());
  377. }
  378. drop(cleaner);
  379. }
  380. }
  381. fn expected_workspace_sync_folder_data() -> FolderData {
  382. serde_json::from_value::<FolderData>(json!({
  383. "current_view": "e0811131-9928-4541-a174-20b7553d9e4c",
  384. "current_workspace_id": "8df7f755-fa5d-480e-9f8e-48ea0fed12b3",
  385. "views": [
  386. {
  387. "children": {
  388. "items": [
  389. {
  390. "id": "e0811131-9928-4541-a174-20b7553d9e4c"
  391. },
  392. {
  393. "id": "53333949-c262-447b-8597-107589697059"
  394. }
  395. ]
  396. },
  397. "created_at": 1693147093,
  398. "desc": "",
  399. "icon": null,
  400. "id": "e203afb3-de5d-458a-8380-33cd788a756e",
  401. "is_favorite": false,
  402. "layout": 0,
  403. "name": "⭐️ Getting started",
  404. "parent_view_id": "8df7f755-fa5d-480e-9f8e-48ea0fed12b3"
  405. },
  406. {
  407. "children": {
  408. "items": [
  409. {
  410. "id": "11c697ba-5ed1-41c0-adfc-576db28ad27b"
  411. },
  412. {
  413. "id": "4a5c25e2-a734-440c-973b-4c0e7ab0039c"
  414. }
  415. ]
  416. },
  417. "created_at": 1693147096,
  418. "desc": "",
  419. "icon": null,
  420. "id": "e0811131-9928-4541-a174-20b7553d9e4c",
  421. "is_favorite": false,
  422. "layout": 1,
  423. "name": "database",
  424. "parent_view_id": "e203afb3-de5d-458a-8380-33cd788a756e"
  425. },
  426. {
  427. "children": {
  428. "items": []
  429. },
  430. "created_at": 1693147124,
  431. "desc": "",
  432. "icon": null,
  433. "id": "11c697ba-5ed1-41c0-adfc-576db28ad27b",
  434. "is_favorite": false,
  435. "layout": 3,
  436. "name": "calendar",
  437. "parent_view_id": "e0811131-9928-4541-a174-20b7553d9e4c"
  438. },
  439. {
  440. "children": {
  441. "items": []
  442. },
  443. "created_at": 1693147125,
  444. "desc": "",
  445. "icon": null,
  446. "id": "4a5c25e2-a734-440c-973b-4c0e7ab0039c",
  447. "is_favorite": false,
  448. "layout": 2,
  449. "name": "board",
  450. "parent_view_id": "e0811131-9928-4541-a174-20b7553d9e4c"
  451. },
  452. {
  453. "children": {
  454. "items": []
  455. },
  456. "created_at": 1693147133,
  457. "desc": "",
  458. "icon": null,
  459. "id": "53333949-c262-447b-8597-107589697059",
  460. "is_favorite": false,
  461. "layout": 0,
  462. "name": "document",
  463. "parent_view_id": "e203afb3-de5d-458a-8380-33cd788a756e"
  464. }
  465. ],
  466. "workspaces": [
  467. {
  468. "child_views": {
  469. "items": [
  470. {
  471. "id": "e203afb3-de5d-458a-8380-33cd788a756e"
  472. }
  473. ]
  474. },
  475. "created_at": 1693147093,
  476. "id": "8df7f755-fa5d-480e-9f8e-48ea0fed12b3",
  477. "name": "Workspace"
  478. }
  479. ]
  480. }))
  481. .unwrap()
  482. }