Feat/restore revision (#1549)

* chore: write snapshot

* chore: add tests

* chore: sync close

* chore: restore from snapshot

* chore: delete invalid revisions after restored from snapshot

* chore: create default view if it fail to deserialize view's revisions when there is no snapshot

* chore: auto generate snapshot

Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
Nathan.fooo
2022-12-09 09:19:47 +08:00
committed by GitHub
parent a507fb8ec6
commit 8c225fe547
67 changed files with 1140 additions and 582 deletions

View File

@ -60,7 +60,7 @@ impl GridFieldTest {
FieldScript::CreateField { params } => {
self.field_count += 1;
self.editor
.create_new_field_rev(&params.field_type, params.type_option_data)
.create_new_field_rev_with_type_option(&params.field_type, params.type_option_data)
.await
.unwrap();
self.field_revs = self.editor.get_field_revs(None).await.unwrap();

View File

@ -462,7 +462,7 @@ async fn group_insert_single_select_option_test() {
AssertGroupCount(5),
];
test.run_scripts(scripts).await;
let new_group = test.group_at_index(1).await;
let new_group = test.group_at_index(4).await;
assert_eq!(new_group.desc, new_option_name);
}

View File

@ -4,3 +4,4 @@ mod field_test;
mod filter_test;
mod grid_editor;
mod group_test;
mod snapshot_test;

View File

@ -0,0 +1,2 @@
mod script;
mod test;

View File

@ -0,0 +1,105 @@
use crate::grid::grid_editor::GridEditorTest;
use flowy_http_model::revision::Revision;
use flowy_revision::{RevisionSnapshot, REVISION_WRITE_INTERVAL_IN_MILLIS};
use flowy_sync::client_grid::{GridOperations, GridRevisionPad};
use grid_rev_model::FieldRevision;
use std::time::Duration;
use tokio::time::sleep;
pub enum SnapshotScript {
WriteSnapshot,
#[allow(dead_code)]
AssertSnapshot {
rev_id: i64,
expected: Option<RevisionSnapshot>,
},
AssertSnapshotContent {
snapshot: RevisionSnapshot,
expected: String,
},
CreateField {
field_rev: FieldRevision,
},
DeleteField {
field_rev: FieldRevision,
},
}
pub struct GridSnapshotTest {
inner: GridEditorTest,
pub current_snapshot: Option<RevisionSnapshot>,
pub current_revision: Option<Revision>,
}
impl GridSnapshotTest {
pub async fn new() -> Self {
let editor_test = GridEditorTest::new_table().await;
Self {
inner: editor_test,
current_snapshot: None,
current_revision: None,
}
}
pub fn grid_id(&self) -> String {
self.grid_id.clone()
}
pub async fn grid_pad(&self) -> GridRevisionPad {
let pad = self.editor.grid_pad();
let pad = (*pad.read().await).clone();
pad
}
pub async fn run_scripts(&mut self, scripts: Vec<SnapshotScript>) {
for script in scripts {
self.run_script(script).await;
}
}
pub async fn get_latest_snapshot(&self) -> Option<RevisionSnapshot> {
self.editor.rev_manager().read_snapshot(None).await.unwrap()
}
pub async fn run_script(&mut self, script: SnapshotScript) {
let rev_manager = self.editor.rev_manager();
match script {
SnapshotScript::WriteSnapshot => {
sleep(Duration::from_millis(2 * REVISION_WRITE_INTERVAL_IN_MILLIS)).await;
rev_manager.generate_snapshot().await;
self.current_snapshot = rev_manager.read_snapshot(None).await.unwrap();
}
SnapshotScript::AssertSnapshot { rev_id, expected } => {
let snapshot = rev_manager.read_snapshot(Some(rev_id)).await.unwrap();
assert_eq!(snapshot, expected);
}
SnapshotScript::AssertSnapshotContent { snapshot, expected } => {
let operations = GridOperations::from_bytes(snapshot.data).unwrap();
let pad = GridRevisionPad::from_operations(operations).unwrap();
assert_eq!(pad.json_str().unwrap(), expected);
}
SnapshotScript::CreateField { field_rev } => {
self.editor.create_new_field_rev(field_rev).await.unwrap();
let current_rev_id = rev_manager.rev_id();
self.current_revision = rev_manager.get_revision(current_rev_id).await;
}
SnapshotScript::DeleteField { field_rev } => {
self.editor.delete_field(&field_rev.id).await.unwrap();
}
}
}
}
impl std::ops::Deref for GridSnapshotTest {
type Target = GridEditorTest;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl std::ops::DerefMut for GridSnapshotTest {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}

View File

@ -0,0 +1,45 @@
use crate::grid::field_test::util::create_text_field;
use crate::grid::snapshot_test::script::{GridSnapshotTest, SnapshotScript::*};
#[tokio::test]
async fn snapshot_create_test() {
let mut test = GridSnapshotTest::new().await;
let (_, field_rev) = create_text_field(&test.grid_id());
let scripts = vec![CreateField { field_rev }, WriteSnapshot];
test.run_scripts(scripts).await;
let snapshot = test.current_snapshot.clone().unwrap();
let content = test.grid_pad().await.json_str().unwrap();
test.run_scripts(vec![AssertSnapshotContent {
snapshot,
expected: content,
}])
.await;
}
#[tokio::test]
async fn snapshot_multi_version_test() {
let mut test = GridSnapshotTest::new().await;
let original_content = test.grid_pad().await.json_str().unwrap();
// Create a field
let (_, field_rev) = create_text_field(&test.grid_id());
let scripts = vec![
CreateField {
field_rev: field_rev.clone(),
},
WriteSnapshot,
];
test.run_scripts(scripts).await;
// Delete a field
let scripts = vec![DeleteField { field_rev }, WriteSnapshot];
test.run_scripts(scripts).await;
// The latest snapshot will be the same as the original content.
test.run_scripts(vec![AssertSnapshotContent {
snapshot: test.get_latest_snapshot().await.unwrap(),
expected: original_content,
}])
.await;
}