helper.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. use backend::{
  2. application::{get_connection_pool, init_app_context, Application},
  3. config::{get_configuration, DatabaseSettings},
  4. context::AppContext,
  5. };
  6. use flowy_backend_api::{user_request::*, workspace_request::*};
  7. use flowy_document::services::server::read_doc_request;
  8. use flowy_document_infra::entities::doc::{Doc, DocIdentifier};
  9. use flowy_net::errors::ServerError;
  10. use flowy_user_infra::entities::*;
  11. use flowy_workspace_infra::entities::prelude::*;
  12. use sqlx::{Connection, Executor, PgConnection, PgPool};
  13. use uuid::Uuid;
  14. pub struct TestUserServer {
  15. pub host: String,
  16. pub port: u16,
  17. pub pg_pool: PgPool,
  18. pub user_token: Option<String>,
  19. pub user_id: Option<String>,
  20. }
  21. impl TestUserServer {
  22. pub async fn new() -> Self {
  23. let mut server: TestUserServer = spawn_server().await.into();
  24. let response = server.register_user().await;
  25. server.user_token = Some(response.token);
  26. server.user_id = Some(response.user_id);
  27. server
  28. }
  29. pub async fn sign_in(&self, params: SignInParams) -> Result<SignInResponse, ServerError> {
  30. let url = format!("{}/api/auth", self.http_addr());
  31. let resp = user_sign_in_request(params, &url).await?;
  32. Ok(resp)
  33. }
  34. pub async fn sign_out(&self) {
  35. let url = format!("{}/api/auth", self.http_addr());
  36. let _ = user_sign_out_request(self.user_token(), &url).await.unwrap();
  37. }
  38. pub fn user_token(&self) -> &str { self.user_token.as_ref().expect("must call register_user first ") }
  39. pub fn user_id(&self) -> &str { self.user_id.as_ref().expect("must call register_user first ") }
  40. pub async fn get_user_profile(&self) -> UserProfile {
  41. let url = format!("{}/api/user", self.http_addr());
  42. let user_profile = get_user_profile_request(self.user_token(), &url).await.unwrap();
  43. user_profile
  44. }
  45. pub async fn update_user_profile(&self, params: UpdateUserParams) -> Result<(), ServerError> {
  46. let url = format!("{}/api/user", self.http_addr());
  47. let _ = update_user_profile_request(self.user_token(), params, &url).await?;
  48. Ok(())
  49. }
  50. pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> Workspace {
  51. let url = format!("{}/api/workspace", self.http_addr());
  52. let workspace = create_workspace_request(self.user_token(), params, &url).await.unwrap();
  53. workspace
  54. }
  55. pub async fn read_workspaces(&self, params: WorkspaceIdentifier) -> RepeatedWorkspace {
  56. let url = format!("{}/api/workspace", self.http_addr());
  57. let workspaces = read_workspaces_request(self.user_token(), params, &url).await.unwrap();
  58. workspaces
  59. }
  60. pub async fn update_workspace(&self, params: UpdateWorkspaceParams) {
  61. let url = format!("{}/api/workspace", self.http_addr());
  62. update_workspace_request(self.user_token(), params, &url).await.unwrap();
  63. }
  64. pub async fn delete_workspace(&self, params: WorkspaceIdentifier) {
  65. let url = format!("{}/api/workspace", self.http_addr());
  66. delete_workspace_request(self.user_token(), params, &url).await.unwrap();
  67. }
  68. pub async fn create_app(&self, params: CreateAppParams) -> App {
  69. let url = format!("{}/api/app", self.http_addr());
  70. let app = create_app_request(self.user_token(), params, &url).await.unwrap();
  71. app
  72. }
  73. pub async fn read_app(&self, params: AppIdentifier) -> Option<App> {
  74. let url = format!("{}/api/app", self.http_addr());
  75. let app = read_app_request(self.user_token(), params, &url).await.unwrap();
  76. app
  77. }
  78. pub async fn update_app(&self, params: UpdateAppParams) {
  79. let url = format!("{}/api/app", self.http_addr());
  80. update_app_request(self.user_token(), params, &url).await.unwrap();
  81. }
  82. pub async fn delete_app(&self, params: AppIdentifier) {
  83. let url = format!("{}/api/app", self.http_addr());
  84. delete_app_request(self.user_token(), params, &url).await.unwrap();
  85. }
  86. pub async fn create_view(&self, params: CreateViewParams) -> View {
  87. let url = format!("{}/api/view", self.http_addr());
  88. let view = create_view_request(self.user_token(), params, &url).await.unwrap();
  89. view
  90. }
  91. pub async fn read_view(&self, params: ViewIdentifier) -> Option<View> {
  92. let url = format!("{}/api/view", self.http_addr());
  93. let view = read_view_request(self.user_token(), params, &url).await.unwrap();
  94. view
  95. }
  96. pub async fn update_view(&self, params: UpdateViewParams) {
  97. let url = format!("{}/api/view", self.http_addr());
  98. update_view_request(self.user_token(), params, &url).await.unwrap();
  99. }
  100. pub async fn delete_view(&self, params: ViewIdentifiers) {
  101. let url = format!("{}/api/view", self.http_addr());
  102. delete_view_request(self.user_token(), params, &url).await.unwrap();
  103. }
  104. pub async fn create_view_trash(&self, view_id: &str) {
  105. let identifier = TrashIdentifier {
  106. id: view_id.to_string(),
  107. ty: TrashType::View,
  108. };
  109. let url = format!("{}/api/trash", self.http_addr());
  110. create_trash_request(self.user_token(), vec![identifier].into(), &url)
  111. .await
  112. .unwrap();
  113. }
  114. pub async fn delete_view_trash(&self, trash_identifiers: TrashIdentifiers) {
  115. let url = format!("{}/api/trash", self.http_addr());
  116. delete_trash_request(self.user_token(), trash_identifiers, &url)
  117. .await
  118. .unwrap();
  119. }
  120. pub async fn read_trash(&self) -> RepeatedTrash {
  121. let url = format!("{}/api/trash", self.http_addr());
  122. read_trash_request(self.user_token(), &url).await.unwrap()
  123. }
  124. pub async fn read_doc(&self, params: DocIdentifier) -> Option<Doc> {
  125. let url = format!("{}/api/doc", self.http_addr());
  126. let doc = read_doc_request(self.user_token(), params, &url).await.unwrap();
  127. doc
  128. }
  129. pub async fn register_user(&self) -> SignUpResponse {
  130. let params = SignUpParams {
  131. email: "[email protected]".to_string(),
  132. name: "annie".to_string(),
  133. password: "HelloAppFlowy123!".to_string(),
  134. };
  135. self.register(params).await
  136. }
  137. pub async fn register(&self, params: SignUpParams) -> SignUpResponse {
  138. let url = format!("{}/api/register", self.http_addr());
  139. let response = user_sign_up_request(params, &url).await.unwrap();
  140. response
  141. }
  142. pub fn http_addr(&self) -> String { format!("http://{}", self.host) }
  143. pub fn ws_addr(&self) -> String { format!("ws://{}/ws/{}", self.host, self.user_token.as_ref().unwrap()) }
  144. }
  145. impl std::convert::From<TestServer> for TestUserServer {
  146. fn from(server: TestServer) -> Self {
  147. TestUserServer {
  148. host: server.host,
  149. port: server.port,
  150. pg_pool: server.pg_pool,
  151. user_token: None,
  152. user_id: None,
  153. }
  154. }
  155. }
  156. pub async fn spawn_user_server() -> TestUserServer {
  157. let server: TestUserServer = spawn_server().await.into();
  158. server
  159. }
  160. pub struct TestServer {
  161. pub host: String,
  162. pub port: u16,
  163. pub pg_pool: PgPool,
  164. pub app_ctx: AppContext,
  165. }
  166. pub async fn spawn_server() -> TestServer {
  167. let database_name = format!("{}", Uuid::new_v4().to_string());
  168. let configuration = {
  169. let mut c = get_configuration().expect("Failed to read configuration.");
  170. c.database.database_name = database_name.clone();
  171. // Use a random OS port
  172. c.application.port = 0;
  173. c
  174. };
  175. let _ = configure_database(&configuration.database).await;
  176. let app_ctx = init_app_context(&configuration).await;
  177. let application = Application::build(configuration.clone(), app_ctx.clone())
  178. .await
  179. .expect("Failed to build application.");
  180. let application_port = application.port();
  181. let _ = tokio::spawn(async {
  182. let _ = application.run_until_stopped();
  183. // drop_test_database(database_name).await;
  184. });
  185. TestServer {
  186. host: format!("localhost:{}", application_port),
  187. port: application_port,
  188. pg_pool: get_connection_pool(&configuration.database)
  189. .await
  190. .expect("Failed to connect to the database"),
  191. app_ctx,
  192. }
  193. }
  194. async fn configure_database(config: &DatabaseSettings) -> PgPool {
  195. // Create database
  196. let mut connection = PgConnection::connect_with(&config.without_db())
  197. .await
  198. .expect("Failed to connect to Postgres");
  199. connection
  200. .execute(&*format!(r#"CREATE DATABASE "{}";"#, config.database_name))
  201. .await
  202. .expect("Failed to create database.");
  203. // Migrate database
  204. let connection_pool = PgPool::connect_with(config.with_db())
  205. .await
  206. .expect("Failed to connect to Postgres.");
  207. sqlx::migrate!("./migrations")
  208. .run(&connection_pool)
  209. .await
  210. .expect("Failed to migrate the database");
  211. connection_pool
  212. }
  213. #[allow(dead_code)]
  214. async fn drop_test_database(database_name: String) {
  215. // https://stackoverflow.com/questions/36502401/postgres-drop-database-error-pq-cannot-drop-the-currently-open-database?rq=1
  216. let configuration = {
  217. let mut c = get_configuration().expect("Failed to read configuration.");
  218. c.database.database_name = "flowy".to_owned();
  219. c.application.port = 0;
  220. c
  221. };
  222. let mut connection = PgConnection::connect_with(&configuration.database.without_db())
  223. .await
  224. .expect("Failed to connect to Postgres");
  225. connection
  226. .execute(&*format!(r#"Drop DATABASE "{}";"#, database_name))
  227. .await
  228. .expect("Failed to drop database.");
  229. }
  230. pub async fn create_test_workspace(server: &TestUserServer) -> Workspace {
  231. let params = CreateWorkspaceParams {
  232. name: "My first workspace".to_string(),
  233. desc: "This is my first workspace".to_string(),
  234. };
  235. let workspace = server.create_workspace(params).await;
  236. workspace
  237. }
  238. pub async fn create_test_app(server: &TestUserServer, workspace_id: &str) -> App {
  239. let params = CreateAppParams {
  240. workspace_id: workspace_id.to_owned(),
  241. name: "My first app".to_string(),
  242. desc: "This is my first app".to_string(),
  243. color_style: ColorStyle::default(),
  244. };
  245. let app = server.create_app(params).await;
  246. app
  247. }
  248. pub async fn create_test_view(application: &TestUserServer, app_id: &str) -> View {
  249. let name = "My first view".to_string();
  250. let desc = "This is my first view".to_string();
  251. let thumbnail = "http://1.png".to_string();
  252. let params = CreateViewParams::new(app_id.to_owned(), name, desc, ViewType::Doc, thumbnail);
  253. let app = application.create_view(params).await;
  254. app
  255. }
  256. pub struct WorkspaceTest {
  257. pub server: TestUserServer,
  258. pub workspace: Workspace,
  259. }
  260. impl WorkspaceTest {
  261. pub async fn new() -> Self {
  262. let server = TestUserServer::new().await;
  263. let workspace = create_test_workspace(&server).await;
  264. Self { server, workspace }
  265. }
  266. pub async fn create_app(&self) -> App { create_test_app(&self.server, &self.workspace.id).await }
  267. }
  268. pub struct AppTest {
  269. pub server: TestUserServer,
  270. pub workspace: Workspace,
  271. pub app: App,
  272. }
  273. impl AppTest {
  274. pub async fn new() -> Self {
  275. let server = TestUserServer::new().await;
  276. let workspace = create_test_workspace(&server).await;
  277. let app = create_test_app(&server, &workspace.id).await;
  278. Self { server, workspace, app }
  279. }
  280. }
  281. pub struct ViewTest {
  282. pub server: TestUserServer,
  283. pub workspace: Workspace,
  284. pub app: App,
  285. pub view: View,
  286. }
  287. impl ViewTest {
  288. pub async fn new() -> Self {
  289. let server = TestUserServer::new().await;
  290. let workspace = create_test_workspace(&server).await;
  291. let app = create_test_app(&server, &workspace.id).await;
  292. let view = create_test_view(&server, &app.id).await;
  293. Self {
  294. server,
  295. workspace,
  296. app,
  297. view,
  298. }
  299. }
  300. }