mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-22 08:41:08 +00:00
create article in ui
This commit is contained in:
parent
98a9ca2439
commit
a12895f9bf
13 changed files with 220 additions and 124 deletions
|
@ -2,6 +2,7 @@
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# launch a couple of local instances to test federation
|
# launch a couple of local instances to test federation
|
||||||
|
# sometimes ctrl+c doesnt work properly, so you have to kill trunk, cargo-watch and ibis manually
|
||||||
# TODO: somehow instances use wrong port resulting in cors errors
|
# TODO: somehow instances use wrong port resulting in cors errors
|
||||||
(trap 'kill 0' SIGINT;
|
(trap 'kill 0' SIGINT;
|
||||||
sh -c 'TRUNK_SERVE_PORT=8070 IBIS_BACKEND_PORT=8071 IBIS_DATABASE_URL="postgres://ibis:password@localhost:5432/ibis" ./scripts/watch.sh' &
|
sh -c 'TRUNK_SERVE_PORT=8070 IBIS_BACKEND_PORT=8071 IBIS_DATABASE_URL="postgres://ibis:password@localhost:5432/ibis" ./scripts/watch.sh' &
|
||||||
|
|
|
@ -26,14 +26,14 @@ use diffy::create_patch;
|
||||||
/// Create a new article with empty text, and federate it to followers.
|
/// Create a new article with empty text, and federate it to followers.
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
pub(in crate::backend::api) async fn create_article(
|
pub(in crate::backend::api) async fn create_article(
|
||||||
Extension(_user): Extension<LocalUserView>,
|
Extension(user): Extension<LocalUserView>,
|
||||||
data: Data<MyDataHandle>,
|
data: Data<MyDataHandle>,
|
||||||
Form(create_article): Form<CreateArticleData>,
|
Form(create_article): Form<CreateArticleData>,
|
||||||
) -> MyResult<Json<ArticleView>> {
|
) -> MyResult<Json<ArticleView>> {
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
||||||
let ap_id = ObjectId::parse(&format!(
|
let ap_id = ObjectId::parse(&format!(
|
||||||
"http://{}:{}/article/{}",
|
"http://{}:{}/article/{}",
|
||||||
local_instance.ap_id.inner().domain().unwrap(),
|
local_instance.ap_id.inner().host_str().unwrap(),
|
||||||
local_instance.ap_id.inner().port().unwrap(),
|
local_instance.ap_id.inner().port().unwrap(),
|
||||||
create_article.title
|
create_article.title
|
||||||
))?;
|
))?;
|
||||||
|
@ -46,9 +46,19 @@ pub(in crate::backend::api) async fn create_article(
|
||||||
};
|
};
|
||||||
let article = DbArticle::create(&form, &data.db_connection)?;
|
let article = DbArticle::create(&form, &data.db_connection)?;
|
||||||
|
|
||||||
CreateArticle::send_to_followers(article.clone(), &data).await?;
|
let edit_data = EditArticleData {
|
||||||
|
article_id: article.id,
|
||||||
|
new_text: create_article.text,
|
||||||
|
summary: create_article.summary,
|
||||||
|
previous_version_id: article.latest_edit_version(&data.db_connection)?,
|
||||||
|
resolve_conflict_id: None,
|
||||||
|
};
|
||||||
|
let _ = edit_article(Extension(user), data.reset_request_count(), Form(edit_data)).await?;
|
||||||
|
|
||||||
Ok(Json(DbArticle::read_view(article.id, &data.db_connection)?))
|
let article_view = DbArticle::read_view(article.id, &data.db_connection)?;
|
||||||
|
CreateArticle::send_to_followers(article_view.article.clone(), &data).await?;
|
||||||
|
|
||||||
|
Ok(Json(article_view))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Edit an existing article (local or remote).
|
/// Edit an existing article (local or remote).
|
||||||
|
@ -103,7 +113,7 @@ pub(in crate::backend::api) async fn edit_article(
|
||||||
|
|
||||||
let previous_version = DbEdit::read(&edit_form.previous_version_id, &data.db_connection)?;
|
let previous_version = DbEdit::read(&edit_form.previous_version_id, &data.db_connection)?;
|
||||||
let form = DbConflictForm {
|
let form = DbConflictForm {
|
||||||
id: EditVersion::new(&patch.to_string())?,
|
id: EditVersion::new(&patch.to_string()),
|
||||||
diff: patch.to_string(),
|
diff: patch.to_string(),
|
||||||
summary: edit_form.summary.clone(),
|
summary: edit_form.summary.clone(),
|
||||||
creator_id: user.local_user.id,
|
creator_id: user.local_user.id,
|
||||||
|
|
|
@ -32,7 +32,7 @@ impl DbEditForm {
|
||||||
previous_version_id: EditVersion,
|
previous_version_id: EditVersion,
|
||||||
) -> MyResult<Self> {
|
) -> MyResult<Self> {
|
||||||
let diff = create_patch(&original_article.text, updated_text);
|
let diff = create_patch(&original_article.text, updated_text);
|
||||||
let version = EditVersion::new(&diff.to_string())?;
|
let version = EditVersion::new(&diff.to_string());
|
||||||
let ap_id = Self::generate_ap_id(original_article, &version)?;
|
let ap_id = Self::generate_ap_id(original_article, &version)?;
|
||||||
Ok(DbEditForm {
|
Ok(DbEditForm {
|
||||||
hash: version,
|
hash: version,
|
||||||
|
|
|
@ -1,35 +1 @@
|
||||||
use crate::backend::error::MyResult;
|
|
||||||
use crate::common::EditVersion;
|
|
||||||
use sha2::{Digest, Sha256};
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
impl EditVersion {
|
|
||||||
pub fn new(diff: &str) -> MyResult<Self> {
|
|
||||||
let mut sha256 = Sha256::new();
|
|
||||||
sha256.update(diff);
|
|
||||||
let hash_bytes = sha256.finalize();
|
|
||||||
let uuid = Uuid::from_slice(&hash_bytes.as_slice()[..16])?;
|
|
||||||
Ok(EditVersion(uuid))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hash(&self) -> String {
|
|
||||||
hex::encode(self.0.into_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for EditVersion {
|
|
||||||
fn default() -> Self {
|
|
||||||
EditVersion::new("").unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_edit_versions() -> MyResult<()> {
|
|
||||||
let default = EditVersion::default();
|
|
||||||
assert_eq!("e3b0c44298fc1c149afbf4c8996fb924", default.hash());
|
|
||||||
|
|
||||||
let version = EditVersion::new("test")?;
|
|
||||||
assert_eq!("9f86d081884c7d659a2feaa0c55ad015", version.hash());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ impl ActivityHandler for RejectEdit {
|
||||||
let article = self.object.object.dereference(data).await?;
|
let article = self.object.object.dereference(data).await?;
|
||||||
let creator = self.object.attributed_to.dereference(data).await?;
|
let creator = self.object.attributed_to.dereference(data).await?;
|
||||||
let form = DbConflictForm {
|
let form = DbConflictForm {
|
||||||
id: EditVersion::new(&self.object.content)?,
|
id: EditVersion::new(&self.object.content),
|
||||||
diff: self.object.content,
|
diff: self.object.content,
|
||||||
summary: self.object.summary,
|
summary: self.object.summary,
|
||||||
creator_id: creator.id,
|
creator_id: creator.id,
|
||||||
|
|
|
@ -51,7 +51,7 @@ mod test {
|
||||||
Ok(DbEdit {
|
Ok(DbEdit {
|
||||||
id: 0,
|
id: 0,
|
||||||
creator_id: 0,
|
creator_id: 0,
|
||||||
hash: EditVersion::new(&diff)?,
|
hash: EditVersion::new(&diff),
|
||||||
ap_id: ObjectId::parse("http://example.com")?,
|
ap_id: ObjectId::parse("http://example.com")?,
|
||||||
diff,
|
diff,
|
||||||
summary: String::new(),
|
summary: String::new(),
|
||||||
|
@ -79,7 +79,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_generate_invalid_version() -> MyResult<()> {
|
fn test_generate_invalid_version() -> MyResult<()> {
|
||||||
let edits = create_edits()?;
|
let edits = create_edits()?;
|
||||||
let generated = generate_article_version(&edits, &EditVersion::new("invalid")?);
|
let generated = generate_article_version(&edits, &EditVersion::new("invalid"));
|
||||||
assert!(generated.is_err());
|
assert!(generated.is_err());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
|
@ -79,6 +80,26 @@ pub struct DbEdit {
|
||||||
#[cfg_attr(feature = "ssr", derive(diesel_derive_newtype::DieselNewType))]
|
#[cfg_attr(feature = "ssr", derive(diesel_derive_newtype::DieselNewType))]
|
||||||
pub struct EditVersion(pub(crate) Uuid);
|
pub struct EditVersion(pub(crate) Uuid);
|
||||||
|
|
||||||
|
impl EditVersion {
|
||||||
|
pub fn new(diff: &str) -> Self {
|
||||||
|
let mut sha256 = Sha256::new();
|
||||||
|
sha256.update(diff);
|
||||||
|
let hash_bytes = sha256.finalize();
|
||||||
|
let uuid = Uuid::from_slice(&hash_bytes.as_slice()[..16]).unwrap();
|
||||||
|
EditVersion(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hash(&self) -> String {
|
||||||
|
hex::encode(self.0.into_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EditVersion {
|
||||||
|
fn default() -> Self {
|
||||||
|
EditVersion::new("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
pub struct RegisterUserData {
|
pub struct RegisterUserData {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
@ -133,6 +154,8 @@ pub struct DbPerson {
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct CreateArticleData {
|
pub struct CreateArticleData {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
pub text: String,
|
||||||
|
pub summary: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
@ -213,3 +236,12 @@ pub struct InstanceView {
|
||||||
pub followers: Vec<DbPerson>,
|
pub followers: Vec<DbPerson>,
|
||||||
pub following: Vec<DbInstance>,
|
pub following: Vec<DbInstance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_edit_versions() {
|
||||||
|
let default = EditVersion::default();
|
||||||
|
assert_eq!("e3b0c44298fc1c149afbf4c8996fb924", default.hash());
|
||||||
|
|
||||||
|
let version = EditVersion::new("test");
|
||||||
|
assert_eq!("9f86d081884c7d659a2feaa0c55ad015", version.hash());
|
||||||
|
}
|
||||||
|
|
|
@ -56,26 +56,12 @@ impl ApiClient {
|
||||||
handle_json_res::<LocalUserView>(req).await
|
handle_json_res::<LocalUserView>(req).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_article(&self, title: String, new_text: String) -> MyResult<ArticleView> {
|
pub async fn create_article(&self, data: &CreateArticleData) -> MyResult<ArticleView> {
|
||||||
let create_form = CreateArticleData {
|
|
||||||
title: title.clone(),
|
|
||||||
};
|
|
||||||
let req = self
|
let req = self
|
||||||
.client
|
.client
|
||||||
.post(format!("http://{}/api/v1/article", &self.hostname))
|
.post(format!("http://{}/api/v1/article", &self.hostname))
|
||||||
.form(&create_form);
|
.form(data);
|
||||||
let article: ArticleView = handle_json_res(req).await?;
|
handle_json_res(req).await
|
||||||
|
|
||||||
// create initial edit to ensure that conflicts are generated (there are no conflicts on empty file)
|
|
||||||
// TODO: maybe take initial text directly in create article, no reason to have empty article
|
|
||||||
let edit_form = EditArticleData {
|
|
||||||
article_id: article.article.id,
|
|
||||||
new_text,
|
|
||||||
summary: "initial text".to_string(),
|
|
||||||
previous_version_id: article.latest_version,
|
|
||||||
resolve_conflict_id: None,
|
|
||||||
};
|
|
||||||
Ok(self.edit_article(&edit_form).await.unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn edit_article_with_conflict(
|
pub async fn edit_article_with_conflict(
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::common::LocalUserView;
|
use crate::common::LocalUserView;
|
||||||
use crate::frontend::api::ApiClient;
|
use crate::frontend::api::ApiClient;
|
||||||
use crate::frontend::components::nav::Nav;
|
use crate::frontend::components::nav::Nav;
|
||||||
|
use crate::frontend::pages::article::create::CreateArticle;
|
||||||
use crate::frontend::pages::article::edit::EditArticle;
|
use crate::frontend::pages::article::edit::EditArticle;
|
||||||
use crate::frontend::pages::article::history::ArticleHistory;
|
use crate::frontend::pages::article::history::ArticleHistory;
|
||||||
use crate::frontend::pages::article::read::ReadArticle;
|
use crate::frontend::pages::article::read::ReadArticle;
|
||||||
|
@ -74,6 +75,7 @@ pub fn App() -> impl IntoView {
|
||||||
<Route path="/article/:title/edit" view=EditArticle/>
|
<Route path="/article/:title/edit" view=EditArticle/>
|
||||||
<Route path="/article/:title/history" view=ArticleHistory/>
|
<Route path="/article/:title/history" view=ArticleHistory/>
|
||||||
<Route path="/article/:title/diff/:hash" view=EditDiff/>
|
<Route path="/article/:title/diff/:hash" view=EditDiff/>
|
||||||
|
<Route path="/article/create" view=CreateArticle/>
|
||||||
<Route path={Page::Login.path()} view=Login/>
|
<Route path={Page::Login.path()} view=Login/>
|
||||||
<Route path={Page::Register.path()} view=Register/>
|
<Route path={Page::Register.path()} view=Register/>
|
||||||
<Route path="/search" view=Search/>
|
<Route path="/search" view=Search/>
|
||||||
|
|
|
@ -18,6 +18,12 @@ pub fn Nav() -> impl IntoView {
|
||||||
<li>
|
<li>
|
||||||
<A href="/">"Main Page"</A>
|
<A href="/">"Main Page"</A>
|
||||||
</li>
|
</li>
|
||||||
|
<Show
|
||||||
|
when=move || global_state.with(|state| state.my_profile.is_some())>
|
||||||
|
<li>
|
||||||
|
<A href="/article/create">"Create Article"</A>
|
||||||
|
</li>
|
||||||
|
</Show>
|
||||||
<li>
|
<li>
|
||||||
<form on:submit=move |ev| {
|
<form on:submit=move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
|
|
86
src/frontend/pages/article/create.rs
Normal file
86
src/frontend/pages/article/create.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
use crate::common::CreateArticleData;
|
||||||
|
use crate::frontend::app::GlobalState;
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_router::Redirect;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CreateArticle() -> impl IntoView {
|
||||||
|
let (title, set_title) = create_signal(String::new());
|
||||||
|
let (text, set_text) = create_signal(String::new());
|
||||||
|
let (summary, set_summary) = create_signal(String::new());
|
||||||
|
let (create_response, set_create_response) = create_signal(None::<()>);
|
||||||
|
let (create_error, set_create_error) = create_signal(None::<String>);
|
||||||
|
let (wait_for_response, set_wait_for_response) = create_signal(false);
|
||||||
|
let button_is_disabled =
|
||||||
|
Signal::derive(move || wait_for_response.get() || summary.get().is_empty());
|
||||||
|
let submit_action = create_action(move |(title, text, summary): &(String, String, String)| {
|
||||||
|
let title = title.clone();
|
||||||
|
let text = text.clone();
|
||||||
|
let summary = summary.clone();
|
||||||
|
async move {
|
||||||
|
let form = CreateArticleData {
|
||||||
|
title,
|
||||||
|
text,
|
||||||
|
summary,
|
||||||
|
};
|
||||||
|
set_wait_for_response.update(|w| *w = true);
|
||||||
|
let res = GlobalState::api_client().create_article(&form).await;
|
||||||
|
set_wait_for_response.update(|w| *w = false);
|
||||||
|
match res {
|
||||||
|
Ok(_res) => {
|
||||||
|
set_create_response.update(|v| *v = Some(()));
|
||||||
|
set_create_error.update(|e| *e = None);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let msg = err.0.to_string();
|
||||||
|
log::warn!("Unable to create: {msg}");
|
||||||
|
set_create_error.update(|e| *e = Some(msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<Show
|
||||||
|
when=move || create_response.get().is_some()
|
||||||
|
fallback=move || {
|
||||||
|
view! {
|
||||||
|
<div class="item-view">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
placeholder="Title"
|
||||||
|
prop:disabled=move || wait_for_response.get()
|
||||||
|
on:keyup=move |ev| {
|
||||||
|
let val = event_target_value(&ev);
|
||||||
|
set_title.update(|v| *v = val);
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<textarea placeholder="Article text..." on:keyup=move |ev| {
|
||||||
|
let val = event_target_value(&ev);
|
||||||
|
set_text.update(|p| *p = val);
|
||||||
|
} >
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
{move || {
|
||||||
|
create_error
|
||||||
|
.get()
|
||||||
|
.map(|err| {
|
||||||
|
view! { <p style="color:red;">{err}</p> }
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
<input type="text" on:keyup=move |ev| {
|
||||||
|
let val = event_target_value(&ev);
|
||||||
|
set_summary.update(|p| *p = val);
|
||||||
|
}/>
|
||||||
|
<button
|
||||||
|
prop:disabled=move || button_is_disabled.get()
|
||||||
|
on:click=move |_| submit_action.dispatch((title.get(), text.get(), summary.get()))>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
<Redirect path={format!("/article/{}", title.get())} />
|
||||||
|
</Show>
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod create;
|
||||||
pub mod edit;
|
pub mod edit;
|
||||||
pub mod history;
|
pub mod history;
|
||||||
pub mod read;
|
pub mod read;
|
||||||
|
|
134
tests/test.rs
134
tests/test.rs
|
@ -3,8 +3,8 @@ extern crate ibis_lib;
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use crate::common::{TestData, TEST_ARTICLE_DEFAULT_TEXT};
|
use crate::common::{TestData, TEST_ARTICLE_DEFAULT_TEXT};
|
||||||
use ibis_lib::common::SearchArticleData;
|
|
||||||
use ibis_lib::common::{ArticleView, EditArticleData, ForkArticleData, GetArticleData};
|
use ibis_lib::common::{ArticleView, EditArticleData, ForkArticleData, GetArticleData};
|
||||||
|
use ibis_lib::common::{CreateArticleData, SearchArticleData};
|
||||||
use ibis_lib::common::{LoginUserData, RegisterUserData};
|
use ibis_lib::common::{LoginUserData, RegisterUserData};
|
||||||
use ibis_lib::frontend::error::MyResult;
|
use ibis_lib::frontend::error::MyResult;
|
||||||
use pretty_assertions::{assert_eq, assert_ne};
|
use pretty_assertions::{assert_eq, assert_ne};
|
||||||
|
@ -15,12 +15,13 @@ async fn test_create_read_and_edit_local_article() -> MyResult<()> {
|
||||||
let data = TestData::start().await;
|
let data = TestData::start().await;
|
||||||
|
|
||||||
// create article
|
// create article
|
||||||
let title = "Manu_Chao".to_string();
|
let create_form = CreateArticleData {
|
||||||
let create_res = data
|
title: "Manu_Chao".to_string(),
|
||||||
.alpha
|
text: TEST_ARTICLE_DEFAULT_TEXT.to_string(),
|
||||||
.create_article(title.clone(), TEST_ARTICLE_DEFAULT_TEXT.to_string())
|
summary: "create article".to_string(),
|
||||||
.await?;
|
};
|
||||||
assert_eq!(title, create_res.article.title);
|
let create_res = data.alpha.create_article(&create_form).await?;
|
||||||
|
assert_eq!(create_form.title, create_res.article.title);
|
||||||
assert!(create_res.article.local);
|
assert!(create_res.article.local);
|
||||||
|
|
||||||
// now article can be read
|
// now article can be read
|
||||||
|
@ -30,7 +31,7 @@ async fn test_create_read_and_edit_local_article() -> MyResult<()> {
|
||||||
id: None,
|
id: None,
|
||||||
};
|
};
|
||||||
let get_res = data.alpha.get_article(get_article_data.clone()).await?;
|
let get_res = data.alpha.get_article(get_article_data.clone()).await?;
|
||||||
assert_eq!(title, get_res.article.title);
|
assert_eq!(create_form.title, get_res.article.title);
|
||||||
assert_eq!(TEST_ARTICLE_DEFAULT_TEXT, get_res.article.text);
|
assert_eq!(TEST_ARTICLE_DEFAULT_TEXT, get_res.article.text);
|
||||||
assert!(get_res.article.local);
|
assert!(get_res.article.local);
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ async fn test_create_read_and_edit_local_article() -> MyResult<()> {
|
||||||
assert_eq!(edit_form.summary, edit_res.edits[1].summary);
|
assert_eq!(edit_form.summary, edit_res.edits[1].summary);
|
||||||
|
|
||||||
let search_form = SearchArticleData {
|
let search_form = SearchArticleData {
|
||||||
query: title.clone(),
|
query: create_form.title.clone(),
|
||||||
};
|
};
|
||||||
let search_res = data.alpha.search(&search_form).await?;
|
let search_res = data.alpha.search(&search_form).await?;
|
||||||
assert_eq!(1, search_res.len());
|
assert_eq!(1, search_res.len());
|
||||||
|
@ -66,18 +67,16 @@ async fn test_create_duplicate_article() -> MyResult<()> {
|
||||||
let data = TestData::start().await;
|
let data = TestData::start().await;
|
||||||
|
|
||||||
// create article
|
// create article
|
||||||
let title = "Manu_Chao".to_string();
|
let create_form = CreateArticleData {
|
||||||
let create_res = data
|
title: "Manu_Chao".to_string(),
|
||||||
.alpha
|
text: TEST_ARTICLE_DEFAULT_TEXT.to_string(),
|
||||||
.create_article(title.clone(), TEST_ARTICLE_DEFAULT_TEXT.to_string())
|
summary: "create article".to_string(),
|
||||||
.await?;
|
};
|
||||||
assert_eq!(title, create_res.article.title);
|
let create_res = data.alpha.create_article(&create_form).await?;
|
||||||
|
assert_eq!(create_form.title, create_res.article.title);
|
||||||
assert!(create_res.article.local);
|
assert!(create_res.article.local);
|
||||||
|
|
||||||
let create_res = data
|
let create_res = data.alpha.create_article(&create_form).await;
|
||||||
.alpha
|
|
||||||
.create_article(title.clone(), TEST_ARTICLE_DEFAULT_TEXT.to_string())
|
|
||||||
.await;
|
|
||||||
assert!(create_res.is_err());
|
assert!(create_res.is_err());
|
||||||
|
|
||||||
data.stop()
|
data.stop()
|
||||||
|
@ -123,12 +122,13 @@ async fn test_synchronize_articles() -> MyResult<()> {
|
||||||
let data = TestData::start().await;
|
let data = TestData::start().await;
|
||||||
|
|
||||||
// create article on alpha
|
// create article on alpha
|
||||||
let title = "Manu_Chao".to_string();
|
let create_form = CreateArticleData {
|
||||||
let create_res = data
|
title: "Manu_Chao".to_string(),
|
||||||
.alpha
|
text: TEST_ARTICLE_DEFAULT_TEXT.to_string(),
|
||||||
.create_article(title.clone(), TEST_ARTICLE_DEFAULT_TEXT.to_string())
|
summary: "create article".to_string(),
|
||||||
.await?;
|
};
|
||||||
assert_eq!(title, create_res.article.title);
|
let create_res = data.alpha.create_article(&create_form).await?;
|
||||||
|
assert_eq!(create_form.title, create_res.article.title);
|
||||||
assert_eq!(1, create_res.edits.len());
|
assert_eq!(1, create_res.edits.len());
|
||||||
assert!(create_res.article.local);
|
assert!(create_res.article.local);
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ async fn test_synchronize_articles() -> MyResult<()> {
|
||||||
get_article_data.instance_id = Some(instance.id);
|
get_article_data.instance_id = Some(instance.id);
|
||||||
let get_res = data.beta.get_article(get_article_data).await?;
|
let get_res = data.beta.get_article(get_article_data).await?;
|
||||||
assert_eq!(create_res.article.ap_id, get_res.article.ap_id);
|
assert_eq!(create_res.article.ap_id, get_res.article.ap_id);
|
||||||
assert_eq!(title, get_res.article.title);
|
assert_eq!(create_form.title, get_res.article.title);
|
||||||
assert_eq!(2, get_res.edits.len());
|
assert_eq!(2, get_res.edits.len());
|
||||||
assert_eq!(edit_form.new_text, get_res.article.text);
|
assert_eq!(edit_form.new_text, get_res.article.text);
|
||||||
assert!(!get_res.article.local);
|
assert!(!get_res.article.local);
|
||||||
|
@ -177,12 +177,13 @@ async fn test_edit_local_article() -> MyResult<()> {
|
||||||
let beta_instance = data.alpha.follow_instance(&data.beta.hostname).await?;
|
let beta_instance = data.alpha.follow_instance(&data.beta.hostname).await?;
|
||||||
|
|
||||||
// create new article
|
// create new article
|
||||||
let title = "Manu_Chao".to_string();
|
let create_form = CreateArticleData {
|
||||||
let create_res = data
|
title: "Manu_Chao".to_string(),
|
||||||
.beta
|
text: TEST_ARTICLE_DEFAULT_TEXT.to_string(),
|
||||||
.create_article(title.clone(), TEST_ARTICLE_DEFAULT_TEXT.to_string())
|
summary: "create article".to_string(),
|
||||||
.await?;
|
};
|
||||||
assert_eq!(title, create_res.article.title);
|
let create_res = data.beta.create_article(&create_form).await?;
|
||||||
|
assert_eq!(create_form.title, create_res.article.title);
|
||||||
assert!(create_res.article.local);
|
assert!(create_res.article.local);
|
||||||
|
|
||||||
// article should be federated to alpha
|
// article should be federated to alpha
|
||||||
|
@ -230,12 +231,13 @@ async fn test_edit_remote_article() -> MyResult<()> {
|
||||||
let beta_id_on_gamma = data.gamma.follow_instance(&data.beta.hostname).await?;
|
let beta_id_on_gamma = data.gamma.follow_instance(&data.beta.hostname).await?;
|
||||||
|
|
||||||
// create new article
|
// create new article
|
||||||
let title = "Manu_Chao".to_string();
|
let create_form = CreateArticleData {
|
||||||
let create_res = data
|
title: "Manu_Chao".to_string(),
|
||||||
.beta
|
text: TEST_ARTICLE_DEFAULT_TEXT.to_string(),
|
||||||
.create_article(title.clone(), TEST_ARTICLE_DEFAULT_TEXT.to_string())
|
summary: "create article".to_string(),
|
||||||
.await?;
|
};
|
||||||
assert_eq!(&title, &create_res.article.title);
|
let create_res = data.beta.create_article(&create_form).await?;
|
||||||
|
assert_eq!(&create_form.title, &create_res.article.title);
|
||||||
assert!(create_res.article.local);
|
assert!(create_res.article.local);
|
||||||
|
|
||||||
// article should be federated to alpha and gamma
|
// article should be federated to alpha and gamma
|
||||||
|
@ -299,12 +301,13 @@ async fn test_local_edit_conflict() -> MyResult<()> {
|
||||||
let data = TestData::start().await;
|
let data = TestData::start().await;
|
||||||
|
|
||||||
// create new article
|
// create new article
|
||||||
let title = "Manu_Chao".to_string();
|
let create_form = CreateArticleData {
|
||||||
let create_res = data
|
title: "Manu_Chao".to_string(),
|
||||||
.alpha
|
text: TEST_ARTICLE_DEFAULT_TEXT.to_string(),
|
||||||
.create_article(title.clone(), TEST_ARTICLE_DEFAULT_TEXT.to_string())
|
summary: "create article".to_string(),
|
||||||
.await?;
|
};
|
||||||
assert_eq!(title, create_res.article.title);
|
let create_res = data.alpha.create_article(&create_form).await?;
|
||||||
|
assert_eq!(create_form.title, create_res.article.title);
|
||||||
assert!(create_res.article.local);
|
assert!(create_res.article.local);
|
||||||
|
|
||||||
// one user edits article
|
// one user edits article
|
||||||
|
@ -361,12 +364,13 @@ async fn test_federated_edit_conflict() -> MyResult<()> {
|
||||||
let beta_id_on_alpha = data.alpha.follow_instance(&data.beta.hostname).await?;
|
let beta_id_on_alpha = data.alpha.follow_instance(&data.beta.hostname).await?;
|
||||||
|
|
||||||
// create new article
|
// create new article
|
||||||
let title = "Manu_Chao".to_string();
|
let create_form = CreateArticleData {
|
||||||
let create_res = data
|
title: "Manu_Chao".to_string(),
|
||||||
.beta
|
text: TEST_ARTICLE_DEFAULT_TEXT.to_string(),
|
||||||
.create_article(title.clone(), TEST_ARTICLE_DEFAULT_TEXT.to_string())
|
summary: "create article".to_string(),
|
||||||
.await?;
|
};
|
||||||
assert_eq!(title, create_res.article.title);
|
let create_res = data.beta.create_article(&create_form).await?;
|
||||||
|
assert_eq!(create_form.title, create_res.article.title);
|
||||||
assert!(create_res.article.local);
|
assert!(create_res.article.local);
|
||||||
|
|
||||||
// fetch article to gamma
|
// fetch article to gamma
|
||||||
|
@ -378,7 +382,7 @@ async fn test_federated_edit_conflict() -> MyResult<()> {
|
||||||
|
|
||||||
// alpha edits article
|
// alpha edits article
|
||||||
let get_article_data = GetArticleData {
|
let get_article_data = GetArticleData {
|
||||||
title: Some(title.to_string()),
|
title: Some(create_form.title.to_string()),
|
||||||
instance_id: Some(beta_id_on_alpha.id),
|
instance_id: Some(beta_id_on_alpha.id),
|
||||||
id: None,
|
id: None,
|
||||||
};
|
};
|
||||||
|
@ -441,12 +445,13 @@ async fn test_overlapping_edits_no_conflict() -> MyResult<()> {
|
||||||
let data = TestData::start().await;
|
let data = TestData::start().await;
|
||||||
|
|
||||||
// create new article
|
// create new article
|
||||||
let title = "Manu_Chao".to_string();
|
let create_form = CreateArticleData {
|
||||||
let create_res = data
|
title: "Manu_Chao".to_string(),
|
||||||
.alpha
|
text: TEST_ARTICLE_DEFAULT_TEXT.to_string(),
|
||||||
.create_article(title.clone(), TEST_ARTICLE_DEFAULT_TEXT.to_string())
|
summary: "create article".to_string(),
|
||||||
.await?;
|
};
|
||||||
assert_eq!(title, create_res.article.title);
|
let create_res = data.alpha.create_article(&create_form).await?;
|
||||||
|
assert_eq!(create_form.title, create_res.article.title);
|
||||||
assert!(create_res.article.local);
|
assert!(create_res.article.local);
|
||||||
|
|
||||||
// one user edits article
|
// one user edits article
|
||||||
|
@ -483,12 +488,13 @@ async fn test_fork_article() -> MyResult<()> {
|
||||||
let data = TestData::start().await;
|
let data = TestData::start().await;
|
||||||
|
|
||||||
// create article
|
// create article
|
||||||
let title = "Manu_Chao".to_string();
|
let create_form = CreateArticleData {
|
||||||
let create_res = data
|
title: "Manu_Chao".to_string(),
|
||||||
.alpha
|
text: TEST_ARTICLE_DEFAULT_TEXT.to_string(),
|
||||||
.create_article(title.clone(), TEST_ARTICLE_DEFAULT_TEXT.to_string())
|
summary: "create article".to_string(),
|
||||||
.await?;
|
};
|
||||||
assert_eq!(title, create_res.article.title);
|
let create_res = data.alpha.create_article(&create_form).await?;
|
||||||
|
assert_eq!(create_form.title, create_res.article.title);
|
||||||
assert!(create_res.article.local);
|
assert!(create_res.article.local);
|
||||||
|
|
||||||
// fetch on beta
|
// fetch on beta
|
||||||
|
@ -520,7 +526,7 @@ async fn test_fork_article() -> MyResult<()> {
|
||||||
|
|
||||||
// now search returns two articles for this title (original and forked)
|
// now search returns two articles for this title (original and forked)
|
||||||
let search_form = SearchArticleData {
|
let search_form = SearchArticleData {
|
||||||
query: title.clone(),
|
query: create_form.title.clone(),
|
||||||
};
|
};
|
||||||
let search_res = data.beta.search(&search_form).await?;
|
let search_res = data.beta.search(&search_form).await?;
|
||||||
assert_eq!(2, search_res.len());
|
assert_eq!(2, search_res.len());
|
||||||
|
|
Loading…
Reference in a new issue