chore: add date filter tests

This commit is contained in:
appflowy 2022-11-14 16:33:24 +08:00
parent 0e137f12f5
commit c80fa5da78
10 changed files with 189 additions and 106 deletions

View File

@ -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<i64>,
}
#[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<i64>,
}
#[derive(Deserialize, Serialize, Default, Clone, Debug)]
pub struct DateFilterContent {
pub start: Option<i64>,
#[pb(index = 5, one_of)]
pub end: Option<i64>,
pub timestamp: Option<i64>,
}
pub struct CreateGridDateFilterParams {
pub field_id: String,
pub field_type: FieldType,
pub condition: DateFilterCondition,
pub start: Option<i64>,
pub end: Option<i64>,
}
impl TryInto<CreateGridDateFilterParams> for CreateGridDateFilterPayload {
type Error = ErrorCode;
fn try_into(self) -> Result<CreateGridDateFilterParams, Self::Error> {
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<i64>,
end: Option<i64>,
}
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<Self, Self::Err> {
@ -96,6 +51,7 @@ pub enum DateFilterCondition {
DateOnOrAfter = 4,
DateWithIn = 5,
DateIsEmpty = 6,
DateIsNotEmpty = 7,
}
impl std::convert::From<DateFilterCondition> for u32 {
@ -133,9 +89,10 @@ impl std::convert::From<Arc<FilterRevision>> 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

View File

@ -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<T: Into<i64>>(&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<T: Into<Option<i64>>>(&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);
}
}
}

View File

@ -32,8 +32,8 @@ impl DateTypeOptionPB {
Self::default()
}
fn today_desc_from_timestamp<T: AsRef<i64>>(&self, timestamp: T) -> DateCellDataPB {
let timestamp = *timestamp.as_ref();
fn today_desc_from_timestamp<T: Into<i64>>(&self, timestamp: T) -> DateCellDataPB {
let timestamp = timestamp.into();
let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
if native.timestamp() == 0 {
return DateCellDataPB::default();

View File

@ -68,14 +68,15 @@ impl ToString for DateCellChangeset {
}
}
pub struct DateTimestamp(i64);
impl AsRef<i64> for DateTimestamp {
fn as_ref(&self) -> &i64 {
&self.0
pub struct DateTimestamp(Option<i64>);
impl std::convert::From<DateTimestamp> for i64 {
fn from(timestamp: DateTimestamp) -> Self {
timestamp.0.unwrap_or(0)
}
}
impl std::convert::From<DateTimestamp> for i64 {
impl std::convert::From<DateTimestamp> for Option<i64> {
fn from(timestamp: DateTimestamp) -> Self {
timestamp.0
}
@ -86,7 +87,7 @@ impl FromCellString for DateTimestamp {
where
Self: Sized,
{
let num = s.parse::<i64>().unwrap_or(0);
let num = s.parse::<i64>().ok();
Ok(DateTimestamp(num))
}
}

View File

@ -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);

View File

@ -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 {

View File

@ -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()
}

View File

@ -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;
}

View File

@ -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<i64>,
end: Option<i64>,
timestamp: Option<i64>,
},
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();
}

View File

@ -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))
}