auth_test.rs 15 KB

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