From c80fa5da78b6b67e3ee0588962df32fa8360e18b Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 14 Nov 2022 16:33:24 +0800 Subject: [PATCH] chore: add date filter tests --- .../entities/filter_entities/date_filter.rs | 71 +++-------- .../date_type_option/date_filter.rs | 117 +++++++++++++----- .../date_type_option/date_type_option.rs | 4 +- .../date_type_option_entities.rs | 13 +- .../flowy-grid/tests/grid/block_test/util.rs | 1 + .../flowy-grid/tests/grid/cell_test/script.rs | 2 +- .../flowy-grid/tests/grid/field_test/util.rs | 1 + .../grid/filter_test/date_filter_test.rs | 67 +++++++++- .../tests/grid/filter_test/script.rs | 15 ++- .../flowy-grid/tests/grid/grid_editor.rs | 4 +- 10 files changed, 189 insertions(+), 106 deletions(-) diff --git a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs index 820971cf36..b1098f7e3e 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs @@ -1,5 +1,3 @@ -use crate::entities::parser::NotEmptyStr; -use crate::entities::FieldType; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::ErrorCode; use grid_rev_model::FilterRevision; @@ -17,68 +15,25 @@ pub struct DateFilterPB { #[pb(index = 3, one_of)] pub end: Option, -} - -#[derive(ProtoBuf, Default, Clone, Debug)] -pub struct CreateGridDateFilterPayload { - #[pb(index = 1)] - pub field_id: String, - - #[pb(index = 2)] - pub field_type: FieldType, - - #[pb(index = 3)] - pub condition: DateFilterCondition, #[pb(index = 4, one_of)] + pub timestamp: Option, +} + +#[derive(Deserialize, Serialize, Default, Clone, Debug)] +pub struct DateFilterContent { pub start: Option, - - #[pb(index = 5, one_of)] pub end: Option, + pub timestamp: Option, } -pub struct CreateGridDateFilterParams { - pub field_id: String, - - pub field_type: FieldType, - - pub condition: DateFilterCondition, - - pub start: Option, - - pub end: Option, -} - -impl TryInto for CreateGridDateFilterPayload { - type Error = ErrorCode; - - fn try_into(self) -> Result { - let field_id = NotEmptyStr::parse(self.field_id) - .map_err(|_| ErrorCode::FieldIdIsEmpty)? - .0; - Ok(CreateGridDateFilterParams { - field_id, - condition: self.condition, - start: self.start, - field_type: self.field_type, - end: self.end, - }) - } -} - -#[derive(Serialize, Deserialize, Default)] -struct DateRange { - start: Option, - end: Option, -} - -impl ToString for DateRange { +impl ToString for DateFilterContent { fn to_string(&self) -> String { - serde_json::to_string(self).unwrap_or_else(|_| "".to_string()) + serde_json::to_string(self).unwrap() } } -impl FromStr for DateRange { +impl FromStr for DateFilterContent { type Err = serde_json::Error; fn from_str(s: &str) -> Result { @@ -96,6 +51,7 @@ pub enum DateFilterCondition { DateOnOrAfter = 4, DateWithIn = 5, DateIsEmpty = 6, + DateIsNotEmpty = 7, } impl std::convert::From for u32 { @@ -133,9 +89,10 @@ impl std::convert::From> for DateFilterPB { ..Default::default() }; - if let Ok(range) = DateRange::from_str(&rev.content) { - filter.start = range.start; - filter.end = range.end; + if let Ok(content) = DateFilterContent::from_str(&rev.content) { + filter.start = content.start; + filter.end = content.end; + filter.timestamp = content.timestamp; }; filter diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_filter.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_filter.rs index ab997cc029..f8c8f19bb5 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_filter.rs @@ -1,30 +1,60 @@ use crate::entities::{DateFilterCondition, DateFilterPB}; use crate::services::cell::{AnyCellData, CellData, CellFilterOperation}; use crate::services::field::{DateTimestamp, DateTypeOptionPB}; +use chrono::NaiveDateTime; use flowy_error::FlowyResult; impl DateFilterPB { - pub fn is_visible>(&self, cell_timestamp: T) -> bool { - if self.start.is_none() { - return false; - } - let cell_timestamp = cell_timestamp.into(); - let start_timestamp = *self.start.as_ref().unwrap(); - // We assume that the cell_timestamp doesn't contain hours, just day. - match self.condition { - DateFilterCondition::DateIs => cell_timestamp == start_timestamp, - DateFilterCondition::DateBefore => cell_timestamp < start_timestamp, - DateFilterCondition::DateAfter => cell_timestamp > start_timestamp, - DateFilterCondition::DateOnOrBefore => cell_timestamp <= start_timestamp, - DateFilterCondition::DateOnOrAfter => cell_timestamp >= start_timestamp, - DateFilterCondition::DateWithIn => { - if let Some(end_timestamp) = self.end.as_ref() { - cell_timestamp >= start_timestamp && cell_timestamp <= *end_timestamp - } else { - false + pub fn is_visible>>(&self, cell_timestamp: T) -> bool { + match cell_timestamp.into() { + None => DateFilterCondition::DateIsEmpty == self.condition, + Some(timestamp) => { + match self.condition { + DateFilterCondition::DateIsNotEmpty => { + return true; + } + DateFilterCondition::DateIsEmpty => { + return false; + } + _ => {} + } + + let cell_time = NaiveDateTime::from_timestamp(timestamp, 0); + let cell_date = cell_time.date(); + match self.timestamp { + None => { + if self.start.is_none() { + return true; + } + + if self.end.is_none() { + return true; + } + + let start_time = NaiveDateTime::from_timestamp(*self.start.as_ref().unwrap(), 0); + let start_date = start_time.date(); + + let end_time = NaiveDateTime::from_timestamp(*self.end.as_ref().unwrap(), 0); + let end_date = end_time.date(); + + cell_date >= start_date && cell_date <= end_date + } + Some(timestamp) => { + let expected_timestamp = NaiveDateTime::from_timestamp(timestamp, 0); + let expected_date = expected_timestamp.date(); + + // We assume that the cell_timestamp doesn't contain hours, just day. + match self.condition { + DateFilterCondition::DateIs => cell_date == expected_date, + DateFilterCondition::DateBefore => cell_date < expected_date, + DateFilterCondition::DateAfter => cell_date > expected_date, + DateFilterCondition::DateOnOrBefore => cell_date <= expected_date, + DateFilterCondition::DateOnOrAfter => cell_date >= expected_date, + _ => true, + } + } } } - DateFilterCondition::DateIsEmpty => cell_timestamp == 0_i64, } } } @@ -49,11 +79,12 @@ mod tests { fn date_filter_is_test() { let filter = DateFilterPB { condition: DateFilterCondition::DateIs, - start: Some(123), + timestamp: Some(1668387885), end: None, + start: None, }; - for (val, visible) in vec![(123, true), (12, false)] { + for (val, visible) in vec![(1668387885, true), (1647251762, false)] { assert_eq!(filter.is_visible(val as i64), visible); } } @@ -61,23 +92,26 @@ mod tests { fn date_filter_before_test() { let filter = DateFilterPB { condition: DateFilterCondition::DateBefore, - start: Some(123), + timestamp: Some(1668387885), + start: None, end: None, }; - for (val, visible) in vec![(123, false), (122, true)] { - assert_eq!(filter.is_visible(val as i64), visible); + for (val, visible, msg) in vec![(1668387884, false, "1"), (1647251762, true, "2")] { + assert_eq!(filter.is_visible(val as i64), visible, "{}", msg); } } + #[test] fn date_filter_before_or_on_test() { let filter = DateFilterPB { condition: DateFilterCondition::DateOnOrBefore, - start: Some(123), + timestamp: Some(1668387885), + start: None, end: None, }; - for (val, visible) in vec![(123, true), (122, true)] { + for (val, visible) in vec![(1668387884, true), (1668387885, true)] { assert_eq!(filter.is_visible(val as i64), visible); } } @@ -85,24 +119,45 @@ mod tests { fn date_filter_after_test() { let filter = DateFilterPB { condition: DateFilterCondition::DateAfter, - start: Some(123), + timestamp: Some(1668387885), + start: None, end: None, }; - for (val, visible) in vec![(1234, true), (122, false), (0, false)] { + for (val, visible) in vec![(1668387888, false), (1668531885, true), (0, false)] { assert_eq!(filter.is_visible(val as i64), visible); } } + #[test] fn date_filter_within_test() { let filter = DateFilterPB { condition: DateFilterCondition::DateWithIn, - start: Some(123), - end: Some(130), + start: Some(1668272685), // 11/13 + end: Some(1668618285), // 11/17 + timestamp: None, }; - for (val, visible) in vec![(123, true), (130, true), (132, false)] { + for (val, visible, _msg) in vec![ + (1668272685, true, "11/13"), + (1668359085, true, "11/14"), + (1668704685, false, "11/18"), + ] { assert_eq!(filter.is_visible(val as i64), visible); } } + + #[test] + fn date_filter_is_empty_test() { + let filter = DateFilterPB { + condition: DateFilterCondition::DateIsEmpty, + start: None, + end: None, + timestamp: None, + }; + + for (val, visible) in vec![(None, true), (Some(123), false)] { + assert_eq!(filter.is_visible(val), visible); + } + } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs index 911b037441..18b5a894cf 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs @@ -32,8 +32,8 @@ impl DateTypeOptionPB { Self::default() } - fn today_desc_from_timestamp>(&self, timestamp: T) -> DateCellDataPB { - let timestamp = *timestamp.as_ref(); + fn today_desc_from_timestamp>(&self, timestamp: T) -> DateCellDataPB { + let timestamp = timestamp.into(); let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0); if native.timestamp() == 0 { return DateCellDataPB::default(); diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs index f4767f3e05..05b23f93f5 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs @@ -68,14 +68,15 @@ impl ToString for DateCellChangeset { } } -pub struct DateTimestamp(i64); -impl AsRef for DateTimestamp { - fn as_ref(&self) -> &i64 { - &self.0 +pub struct DateTimestamp(Option); + +impl std::convert::From for i64 { + fn from(timestamp: DateTimestamp) -> Self { + timestamp.0.unwrap_or(0) } } -impl std::convert::From for i64 { +impl std::convert::From for Option { fn from(timestamp: DateTimestamp) -> Self { timestamp.0 } @@ -86,7 +87,7 @@ impl FromCellString for DateTimestamp { where Self: Sized, { - let num = s.parse::().unwrap_or(0); + let num = s.parse::().ok(); Ok(DateTimestamp(num)) } } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs index 7809c66ac9..6179168ce1 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs @@ -41,6 +41,7 @@ impl<'a> GridRowTestBuilder<'a> { let value = serde_json::to_string(&DateCellChangeset { date: Some(data.to_string()), time: None, + is_utc: true, }) .unwrap(); let date_field = self.field_rev_with_type(&FieldType::DateTime); diff --git a/frontend/rust-lib/flowy-grid/tests/grid/cell_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/cell_test/script.rs index 670ba6327c..ad184682bd 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/cell_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/cell_test/script.rs @@ -29,7 +29,7 @@ impl GridCellTest { match script { CellScript::UpdateCell { changeset, is_err } => { - let result = self.editor.update_cell(changeset).await; + let result = self.editor.update_cell_with_changeset(changeset).await; if is_err { assert!(result.is_err()) } else { diff --git a/frontend/rust-lib/flowy-grid/tests/grid/field_test/util.rs b/frontend/rust-lib/flowy-grid/tests/grid/field_test/util.rs index 1baf49f947..052962db78 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/field_test/util.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/field_test/util.rs @@ -58,6 +58,7 @@ pub fn make_date_cell_string(s: &str) -> String { serde_json::to_string(&DateCellChangeset { date: Some(s.to_string()), time: None, + is_utc: true, }) .unwrap() } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/date_filter_test.rs b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/date_filter_test.rs index c8906154d0..5ab9ac339e 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/date_filter_test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/date_filter_test.rs @@ -3,15 +3,76 @@ use crate::grid::filter_test::script::GridFilterTest; use flowy_grid::entities::DateFilterCondition; #[tokio::test] -#[should_panic] -async fn grid_filter_date_is_check_test() { +async fn grid_filter_date_is_test() { let mut test = GridFilterTest::new().await; let scripts = vec![ CreateDateFilter { condition: DateFilterCondition::DateIs, - content: "1647251762".to_string(), + start: None, + end: None, + timestamp: Some(1647251762), + }, + AssertNumberOfRows { expected: 3 }, + ]; + test.run_scripts(scripts).await; +} + +#[tokio::test] +async fn grid_filter_date_after_test() { + let mut test = GridFilterTest::new().await; + let scripts = vec![ + CreateDateFilter { + condition: DateFilterCondition::DateAfter, + start: None, + end: None, + timestamp: Some(1647251762), }, AssertNumberOfRows { expected: 2 }, ]; test.run_scripts(scripts).await; } + +#[tokio::test] +async fn grid_filter_date_on_or_after_test() { + let mut test = GridFilterTest::new().await; + let scripts = vec![ + CreateDateFilter { + condition: DateFilterCondition::DateOnOrAfter, + start: None, + end: None, + timestamp: Some(1668359085), + }, + AssertNumberOfRows { expected: 2 }, + ]; + test.run_scripts(scripts).await; +} + +#[tokio::test] +async fn grid_filter_date_on_or_before_test() { + let mut test = GridFilterTest::new().await; + let scripts = vec![ + CreateDateFilter { + condition: DateFilterCondition::DateOnOrBefore, + start: None, + end: None, + timestamp: Some(1668359085), + }, + AssertNumberOfRows { expected: 4 }, + ]; + test.run_scripts(scripts).await; +} + +#[tokio::test] +async fn grid_filter_date_within_test() { + let mut test = GridFilterTest::new().await; + let scripts = vec![ + CreateDateFilter { + condition: DateFilterCondition::DateWithIn, + start: Some(1647251762), + end: Some(1668704685), + timestamp: None, + }, + AssertNumberOfRows { expected: 5 }, + ]; + test.run_scripts(scripts).await; +} diff --git a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs index 39c4c6d9c3..4c1fa1a079 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs @@ -4,7 +4,7 @@ #![allow(unused_imports)] use futures::TryFutureExt; -use flowy_grid::entities::{CreateFilterParams, CreateFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB, RowPB, TextFilterCondition, FieldType, NumberFilterCondition, CheckboxFilterCondition, DateFilterCondition}; +use flowy_grid::entities::{CreateFilterParams, CreateFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB, RowPB, TextFilterCondition, FieldType, NumberFilterCondition, CheckboxFilterCondition, DateFilterCondition, DateFilterContent}; use flowy_grid::services::setting::GridSettingChangesetBuilder; use grid_rev_model::{FieldRevision, FieldTypeRevision}; use flowy_grid::services::filter::FilterType; @@ -27,7 +27,9 @@ pub enum FilterScript { }, CreateDateFilter{ condition: DateFilterCondition, - content: String, + start: Option, + end: Option, + timestamp: Option, }, AssertFilterCount { count: i32, @@ -91,10 +93,16 @@ impl GridFilterTest { CreateFilterPayloadPB::new(field_rev, condition, "".to_string()); self.insert_filter(payload).await; } - FilterScript::CreateDateFilter { condition, content} => { + FilterScript::CreateDateFilter { condition, start, end, timestamp} => { let field_rev = self.get_field_rev(FieldType::DateTime); + let content = DateFilterContent { + start, + end, + timestamp, + }.to_string(); let payload = CreateFilterPayloadPB::new(field_rev, condition, content); + self.insert_filter(payload).await; } FilterScript::AssertFilterCount { count } => { @@ -125,7 +133,6 @@ impl GridFilterTest { } async fn insert_filter(&self, payload: CreateFilterPayloadPB) { - let params: CreateFilterParams = payload.try_into().unwrap(); let _ = self.editor.create_filter(params).await.unwrap(); } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs index 0399442474..0a6153f1e4 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs @@ -236,7 +236,7 @@ fn make_test_grid() -> BuildGridContext { match field_type { FieldType::RichText => row_builder.insert_text_cell("DA"), FieldType::Number => row_builder.insert_number_cell("4"), - FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::DateTime => row_builder.insert_date_cell("1668704685"), FieldType::SingleSelect => { row_builder.insert_single_select_cell(|mut options| options.remove(1)) } @@ -250,7 +250,7 @@ fn make_test_grid() -> BuildGridContext { match field_type { FieldType::RichText => row_builder.insert_text_cell("AE"), FieldType::Number => row_builder.insert_number_cell(""), - FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::DateTime => row_builder.insert_date_cell("1668359085"), FieldType::SingleSelect => { row_builder.insert_single_select_cell(|mut options| options.remove(2)) }