Skip to content

Commit facaa72

Browse files
committed
WIP: Add lots of unit tests
1 parent 00ef2f5 commit facaa72

File tree

43 files changed

+22936
-11
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+22936
-11
lines changed

crates/core/src/commands/forget.rs

+228-11
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,17 @@ pub(crate) fn get_forget_snapshots<P: ProgressBars, S: Open>(
8585
filter: impl FnMut(&SnapshotFile) -> bool,
8686
) -> RusticResult<ForgetGroups> {
8787
let now = Local::now();
88-
if !keep.is_valid() {
89-
return Err(CommandErrorKind::NoKeepOption.into());
90-
}
9188

9289
let groups = repo
9390
.get_snapshot_group(&[], group_by, filter)?
9491
.into_iter()
95-
.map(|(group, snapshots)| ForgetGroup {
96-
group,
97-
snapshots: keep.apply(snapshots, now),
92+
.map(|(group, snapshots)| -> RusticResult<_> {
93+
Ok(ForgetGroup {
94+
group,
95+
snapshots: keep.apply(snapshots, now)?,
96+
})
9897
})
99-
.collect();
98+
.collect::<RusticResult<_>>()?;
10099

101100
Ok(ForgetGroups(groups))
102101
}
@@ -113,14 +112,16 @@ pub(crate) fn get_forget_snapshots<P: ProgressBars, S: Open>(
113112
pub struct KeepOptions {
114113
/// Keep snapshots with this taglist (can be specified multiple times)
115114
#[cfg_attr(feature = "clap", clap(long, value_name = "TAG[,TAG,..]"))]
116-
#[serde_as(as = "OneOrMany<DisplayFromStr>")]
117115
#[cfg_attr(feature = "merge", merge(strategy=merge::vec::overwrite_empty))]
116+
#[serde_as(as = "OneOrMany<DisplayFromStr>")]
117+
#[serde(skip_serializing_if = "Vec::is_empty")]
118118
pub keep_tags: Vec<StringList>,
119119

120120
/// Keep snapshots ids that start with ID (can be specified multiple times)
121121
#[cfg_attr(feature = "clap", clap(long = "keep-id", value_name = "ID"))]
122122
#[cfg_attr(feature = "merge", merge(strategy=merge::vec::overwrite_empty))]
123123
#[serde_as(as = "OneOrMany<_>")]
124+
#[serde(skip_serializing_if = "Vec::is_empty")]
124125
pub keep_ids: Vec<String>,
125126

126127
/// Keep the last N snapshots (N == -1: keep all snapshots)
@@ -222,6 +223,7 @@ pub struct KeepOptions {
222223
/// Allow to keep no snapshot
223224
#[cfg_attr(feature = "clap", clap(long))]
224225
#[cfg_attr(feature = "merge", merge(strategy=merge::bool::overwrite_false))]
226+
#[serde(skip_serializing_if = "std::ops::Not::not")]
225227
pub keep_none: bool,
226228
}
227229

@@ -352,6 +354,7 @@ impl KeepOptions {
352354
|| self.keep_monthly.is_some()
353355
|| self.keep_quarter_yearly.is_some()
354356
|| self.keep_half_yearly.is_some()
357+
|| self.keep_within.is_some()
355358
|| self.keep_yearly.is_some()
356359
|| self.keep_within_hourly.is_some()
357360
|| self.keep_within_daily.is_some()
@@ -500,11 +503,15 @@ impl KeepOptions {
500503
&self,
501504
mut snapshots: Vec<SnapshotFile>,
502505
now: DateTime<Local>,
503-
) -> Vec<ForgetSnapshot> {
506+
) -> RusticResult<Vec<ForgetSnapshot>> {
507+
if !self.is_valid() {
508+
return Err(CommandErrorKind::NoKeepOption.into());
509+
}
510+
504511
let mut group_keep = self.clone();
505512
let mut snaps = Vec::new();
506513
if snapshots.is_empty() {
507-
return snaps;
514+
return Ok(snaps);
508515
}
509516

510517
snapshots.sort_unstable_by(|sn1, sn2| sn1.cmp(sn2).reverse());
@@ -534,6 +541,216 @@ impl KeepOptions {
534541
reasons: reasons.iter().map(ToString::to_string).collect(),
535542
});
536543
}
537-
snaps
544+
Ok(snaps)
545+
}
546+
}
547+
548+
#[cfg(test)]
549+
mod tests {
550+
use std::str::FromStr;
551+
552+
use super::*;
553+
use anyhow::Result;
554+
use chrono::{Local, NaiveDateTime, TimeZone};
555+
use humantime::Duration;
556+
use insta::{assert_ron_snapshot, Settings};
557+
use rstest::{fixture, rstest};
558+
use serde_json;
559+
560+
#[fixture]
561+
fn test_snapshots() -> Result<Vec<SnapshotFile>> {
562+
let snaps = [
563+
("2014-09-01 10:20:30", ""),
564+
("2014-09-02 10:20:30", ""),
565+
("2014-09-05 10:20:30", ""),
566+
("2014-09-06 10:20:30", ""),
567+
("2014-09-08 10:20:30", ""),
568+
("2014-09-09 10:20:30", ""),
569+
("2014-09-10 10:20:30", ""),
570+
("2014-09-11 10:20:30", ""),
571+
("2014-09-20 10:20:30", ""),
572+
("2014-09-22 10:20:30", ""),
573+
("2014-08-08 10:20:30", ""),
574+
("2014-08-10 10:20:30", ""),
575+
("2014-08-12 10:20:30", ""),
576+
("2014-08-13 10:20:30", ""),
577+
("2014-08-15 10:20:30", ""),
578+
("2014-08-18 10:20:30", ""),
579+
("2014-08-20 10:20:30", ""),
580+
("2014-08-21 10:20:30", ""),
581+
("2014-08-22 10:20:30", ""),
582+
("2014-10-01 10:20:30", "foo"),
583+
("2014-10-02 10:20:30", "foo"),
584+
("2014-10-05 10:20:30", "foo"),
585+
("2014-10-06 10:20:30", "foo"),
586+
("2014-10-08 10:20:30", "foo"),
587+
("2014-10-09 10:20:30", "foo"),
588+
("2014-10-10 10:20:30", "foo"),
589+
("2014-10-11 10:20:30", "foo"),
590+
("2014-10-20 10:20:30", "foo"),
591+
("2014-10-22 10:20:30", "foo"),
592+
("2014-11-08 10:20:30", "foo"),
593+
("2014-11-10 10:20:30", "foo"),
594+
("2014-11-12 10:20:30", "foo"),
595+
("2014-11-13 10:20:30", "foo"),
596+
("2014-11-15 10:20:30", "foo,bar"),
597+
("2014-11-18 10:20:30", ""),
598+
("2014-11-20 10:20:30", ""),
599+
("2014-11-21 10:20:30", ""),
600+
("2014-11-22 10:20:30", ""),
601+
("2015-09-01 10:20:30", ""),
602+
("2015-09-02 10:20:30", ""),
603+
("2015-09-05 10:20:30", ""),
604+
("2015-09-06 10:20:30", ""),
605+
("2015-09-08 10:20:30", ""),
606+
("2015-09-09 10:20:30", ""),
607+
("2015-09-10 10:20:30", ""),
608+
("2015-09-11 10:20:30", ""),
609+
("2015-09-20 10:20:30", ""),
610+
("2015-09-22 10:20:30", ""),
611+
("2015-08-08 10:20:30", ""),
612+
("2015-08-10 10:20:30", ""),
613+
("2015-08-12 10:20:30", ""),
614+
("2015-08-13 10:20:30", ""),
615+
("2015-08-15 10:20:30", ""),
616+
("2015-08-18 10:20:30", ""),
617+
("2015-08-20 10:20:30", ""),
618+
("2015-08-21 10:20:30", ""),
619+
("2015-08-22 10:20:30", ""),
620+
("2015-10-01 10:20:30", ""),
621+
("2015-10-02 10:20:30", ""),
622+
("2015-10-05 10:20:30", ""),
623+
("2015-10-06 10:20:30", ""),
624+
("2015-10-08 10:20:30", ""),
625+
("2015-10-09 10:20:30", ""),
626+
("2015-10-10 10:20:30", ""),
627+
("2015-10-11 10:20:30", ""),
628+
("2015-10-20 10:20:30", ""),
629+
("2015-10-22 10:20:30", ""),
630+
("2015-10-22 10:20:30", ""),
631+
("2015-10-22 10:20:30", "foo,bar"),
632+
("2015-10-22 10:20:30", "foo,bar"),
633+
("2015-11-08 10:20:30", ""),
634+
("2015-11-10 10:20:30", ""),
635+
("2015-11-12 10:20:30", ""),
636+
("2015-11-13 10:20:30", ""),
637+
("2015-11-15 10:20:30", ""),
638+
("2015-11-18 10:20:30", ""),
639+
("2015-11-20 10:20:30", ""),
640+
("2015-11-21 10:20:30", ""),
641+
("2015-11-22 10:20:30", ""),
642+
("2016-01-01 01:02:03", ""),
643+
("2016-01-01 01:03:03", ""),
644+
("2016-01-01 07:08:03", ""),
645+
("2016-01-03 07:02:03", ""),
646+
("2016-01-04 10:23:03", ""),
647+
("2016-01-04 11:23:03", ""),
648+
("2016-01-04 12:23:03", ""),
649+
("2016-01-04 12:24:03", ""),
650+
("2016-01-04 12:28:03", ""),
651+
("2016-01-04 12:30:03", ""),
652+
("2016-01-04 16:23:03", ""),
653+
("2016-01-05 09:02:03", ""),
654+
("2016-01-06 08:02:03", ""),
655+
("2016-01-07 10:02:03", ""),
656+
("2016-01-08 20:02:03", ""),
657+
("2016-01-09 21:02:03", ""),
658+
("2016-01-12 21:02:03", ""),
659+
("2016-01-12 21:08:03", ""),
660+
("2016-01-18 12:02:03", ""),
661+
];
662+
663+
let mut snaps: Vec<_> = snaps
664+
.into_iter()
665+
.map(|(time, tags)| -> Result<_> {
666+
let time = NaiveDateTime::parse_from_str(time, "%Y-%m-%d %H:%M:%S")?;
667+
let opts = &crate::SnapshotOptions::default()
668+
.time(Local::from_utc_datetime(&Local, &time))
669+
.tag(vec![StringList::from_str(tags)?]);
670+
Ok(SnapshotFile::from_options(opts)?)
671+
})
672+
.collect::<Result<_>>()?;
673+
674+
snaps.sort_unstable_by(|sn1, sn2| sn1.cmp(sn2).reverse());
675+
Ok(snaps)
676+
}
677+
678+
#[fixture]
679+
fn insta_forget_snapshots_redaction() -> Settings {
680+
let mut settings = Settings::clone_current();
681+
settings.add_redaction(".**.snapshot", "[snapshot]");
682+
settings
683+
}
684+
685+
#[rstest]
686+
#[case(KeepOptions::default())]
687+
fn test_apply_fails(
688+
#[case] options: KeepOptions,
689+
test_snapshots: Result<Vec<SnapshotFile>>,
690+
) -> Result<()> {
691+
let now = Local::now();
692+
let result = options.apply(test_snapshots?, now);
693+
assert!(result.is_err());
694+
Ok(())
695+
}
696+
697+
#[rstest]
698+
#[case(KeepOptions::default().keep_last(10))]
699+
#[case(KeepOptions::default().keep_last(15))]
700+
#[case(KeepOptions::default().keep_last(99))]
701+
#[case(KeepOptions::default().keep_last(200))]
702+
#[case(KeepOptions::default().keep_hourly(20))]
703+
#[case(KeepOptions::default().keep_daily(3))]
704+
#[case(KeepOptions::default().keep_daily(10))]
705+
#[case(KeepOptions::default().keep_daily(30))]
706+
#[case(KeepOptions::default().keep_last(5).keep_daily(5))]
707+
#[case(KeepOptions::default().keep_last(2).keep_daily(10))]
708+
#[case(KeepOptions::default().keep_weekly(2))]
709+
#[case(KeepOptions::default().keep_weekly(4))]
710+
#[case(KeepOptions::default().keep_daily(3).keep_weekly(4))]
711+
#[case(KeepOptions::default().keep_monthly(6))]
712+
#[case(KeepOptions::default().keep_daily(2).keep_weekly(2).keep_monthly(6))]
713+
#[case(KeepOptions::default().keep_yearly(10))]
714+
#[case(KeepOptions::default().keep_quarter_yearly(10))]
715+
#[case(KeepOptions::default().keep_half_yearly(10))]
716+
#[case(KeepOptions::default().keep_daily(7).keep_weekly(2).keep_monthly(3).keep_yearly(10))]
717+
/*
718+
#[case(KeepOptions::default().keep_tags("foo"))]
719+
#[case(KeepOptions::default().keep_tags("foo,bar"))]
720+
*/
721+
#[case(KeepOptions::default().keep_within(Duration::from_str("1d").unwrap()))]
722+
#[case(KeepOptions::default().keep_within(Duration::from_str("2d").unwrap()))]
723+
#[case(KeepOptions::default().keep_within(Duration::from_str("7d").unwrap()))]
724+
#[case(KeepOptions::default().keep_within(Duration::from_str("1m").unwrap()))]
725+
#[case(KeepOptions::default().keep_within(Duration::from_str("1M14d").unwrap()))]
726+
#[case(KeepOptions::default().keep_within(Duration::from_str("1y1d1M").unwrap()))]
727+
#[case(KeepOptions::default().keep_within(Duration::from_str("13d23h").unwrap()))]
728+
#[case(KeepOptions::default().keep_within(Duration::from_str("2M2h").unwrap()))]
729+
#[case(KeepOptions::default().keep_within(Duration::from_str("1y2M3d3h").unwrap()))]
730+
#[case(KeepOptions::default().keep_within_hourly(Duration::from_str("1y2M3d3h").unwrap()))]
731+
#[case(KeepOptions::default().keep_within_daily(Duration::from_str("1y2M3d3h").unwrap()))]
732+
#[case(KeepOptions::default().keep_within_weekly(Duration::from_str("1y2M3d3h").unwrap()))]
733+
#[case(KeepOptions::default().keep_within_monthly(Duration::from_str("1y2M3d3h").unwrap()))]
734+
#[case(KeepOptions::default().keep_within_quarter_yearly(Duration::from_str("1y2M3d3h").unwrap()))]
735+
#[case(KeepOptions::default().keep_within_half_yearly(Duration::from_str("1y2M3d3h").unwrap()))]
736+
#[case(KeepOptions::default().keep_within_yearly(Duration::from_str("1y2M3d3h").unwrap()))]
737+
#[case(KeepOptions::default().keep_within(Duration::from_str("1h").unwrap()).keep_within_hourly(Duration::from_str("1d").unwrap()).keep_within_daily(Duration::from_str("1w").unwrap()).keep_within_weekly(Duration::from_str("1M").unwrap()).keep_within_monthly(Duration::from_str("1y").unwrap()).keep_within_yearly(Duration::from_str("9999y").unwrap()))]
738+
#[case(KeepOptions::default().keep_last(-1))]
739+
#[case(KeepOptions::default().keep_last(-1).keep_hourly(-1))]
740+
#[case(KeepOptions::default().keep_hourly(-1))]
741+
#[case(KeepOptions::default().keep_daily(3).keep_weekly(2).keep_monthly(-1).keep_yearly(-1))]
742+
#[case(KeepOptions::default().keep_none(true))]
743+
fn test_apply(
744+
#[case] options: KeepOptions,
745+
test_snapshots: Result<Vec<SnapshotFile>>,
746+
insta_forget_snapshots_redaction: Settings,
747+
) -> Result<()> {
748+
let now = Local::now();
749+
let result = options.apply(test_snapshots?, now)?;
750+
let options = serde_json::to_string(&options)?;
751+
insta_forget_snapshots_redaction.bind(|| {
752+
assert_ron_snapshot!(options, result);
753+
});
754+
Ok(())
538755
}
539756
}

0 commit comments

Comments
 (0)