Skip to content

Commit 6585cb9

Browse files
aawsomesimonsan
andauthored
test: add backup integration tests using snapshots (#175)
This PR adds some integration tests (mostly inspired by restic's integration tests). We use `insta` for "snapshotting" internal state and then compare against it depending on which operating system we are on. --------- Signed-off-by: simonsan <[email protected]> Co-authored-by: simonsan <[email protected]>
1 parent 6c6240c commit 6585cb9

27 files changed

+1096
-29
lines changed

.github/workflows/ci-heavy.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ jobs:
8383
toolchain: ${{ matrix.rust }}
8484
- uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2
8585
- name: Run Cargo Test
86-
run: cargo +${{ matrix.rust }} test -r --all-targets --all-features --workspace --examples
86+
run: cargo +${{ matrix.rust }} test --all-targets --all-features --workspace --examples
8787
id: run_tests
8888
env:
8989
INSTA_UPDATE: new

.github/workflows/ci.yml

+15-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ concurrency:
44
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
55
cancel-in-progress: true
66

7+
env:
8+
CI: true
9+
710
on:
811
pull_request:
912

@@ -40,6 +43,8 @@ jobs:
4043
name: Test
4144
runs-on: ${{ matrix.job.os }}
4245
strategy:
46+
# Don't fail fast, so we can actually see all the results
47+
fail-fast: false
4348
matrix:
4449
rust: [stable]
4550
job:
@@ -64,7 +69,16 @@ jobs:
6469
toolchain: ${{ matrix.rust }}
6570
- uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2
6671
- name: Run Cargo Test
67-
run: cargo +${{ matrix.rust }} test -r --all-targets --all-features --workspace --examples
72+
run: cargo +${{ matrix.rust }} test --all-targets --all-features --workspace --examples
73+
id: run_tests
74+
env:
75+
INSTA_UPDATE: new
76+
- name: Upload snapshots of failed tests
77+
if: ${{ failure() && steps.run_tests.outcome == 'failure' }}
78+
uses: actions/upload-artifact@v3
79+
with:
80+
name: failed-snapshots-${{ matrix.job.os }}
81+
path: "**/snapshots/*.snap.new"
6882

6983
docs:
7084
name: Build docs

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ coverage/*.info
1515

1616
# local repo config
1717
.cargo/config.toml
18+
19+
# Generated by Tests
20+
crates/core/tests/generated/

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ rust-version = "1.72.1"
1313

1414
[workspace.dependencies]
1515
aho-corasick = "1.1.2"
16+
anyhow = "1.0.81"
17+
bytes = "1.5.0"
18+
enum-map = "2.7.3"
1619
rustic_backend = { path = "crates/backend" }
1720
rustic_core = { path = "crates/core" }
21+
rustic_testing = { path = "crates/testing" }
1822
simplelog = "0.12.2"
1923

2024
# dev-dependencies

crates/core/Cargo.toml

+7-3
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,11 @@ futures = { version = "0.3", optional = true }
102102
runtime-format = "0.1.3"
103103

104104
# other dependencies
105-
anyhow = "1.0.81"
106-
bytes = "1.5.0"
105+
anyhow = { workspace = true }
106+
bytes = { workspace = true }
107107
bytesize = "1.3.0"
108108
chrono = { version = "0.4.35", default-features = false, features = ["clock", "serde"] }
109-
enum-map = "2.7.3"
109+
enum-map = { workspace = true }
110110
enum-map-derive = "0.17.0"
111111
enumset = { version = "1.1.3", features = ["serde"] }
112112
gethostname = "0.4.3"
@@ -132,6 +132,8 @@ xattr = "1"
132132

133133
[dev-dependencies]
134134
expect-test = "1.4.1"
135+
flate2 = "1.0.28"
136+
insta = { version = "1.36.1", features = ["redactions", "ron"] }
135137
mockall = "0.12.1"
136138
pretty_assertions = "1.4.0"
137139
quickcheck = "1.0.3"
@@ -140,8 +142,10 @@ rstest = { workspace = true }
140142
rustdoc-json = "0.8.9"
141143
# We need to have rustic_backend here, because the doc-tests in lib.rs of rustic_core
142144
rustic_backend = { workspace = true }
145+
rustic_testing = { workspace = true }
143146
rustup-toolchain = "0.1.6"
144147
simplelog = "0.12.2"
148+
tar = "0.4.40"
145149
tempfile = { workspace = true }
146150

147151
[lints]

crates/core/src/backend.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ use std::{io::Read, ops::Deref, path::PathBuf, sync::Arc};
1313

1414
use anyhow::Result;
1515
use bytes::Bytes;
16+
use enum_map::Enum;
1617
use log::trace;
18+
1719
#[cfg(test)]
18-
use mockall::*;
20+
use mockall::mock;
21+
1922
use serde_derive::{Deserialize, Serialize};
2023

2124
use crate::{
@@ -34,7 +37,7 @@ pub const ALL_FILE_TYPES: [FileType; 4] = [
3437
];
3538

3639
/// Type for describing the kind of a file that can occur.
37-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
40+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Enum)]
3841
pub enum FileType {
3942
/// Config file
4043
#[serde(rename = "config")]
@@ -295,7 +298,9 @@ pub trait WriteBackend: ReadBackend {
295298
/// # Returns
296299
///
297300
/// The result of the creation.
298-
fn create(&self) -> Result<()>;
301+
fn create(&self) -> Result<()> {
302+
Ok(())
303+
}
299304

300305
/// Writes bytes to the given file.
301306
///

crates/core/src/commands/merge.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ pub(crate) fn merge_snapshots<P: ProgressBars, S: IndexedTree>(
3939
) -> RusticResult<SnapshotFile> {
4040
let now = Local::now();
4141

42-
let paths = PathList::from_strings(snapshots.iter().flat_map(|snap| snap.paths.iter())).merge();
42+
let paths = snapshots
43+
.iter()
44+
.flat_map(|snap| snap.paths.iter())
45+
.collect::<PathList>()
46+
.merge();
47+
4348
snap.paths.set_paths(&paths.paths())?;
4449

4550
// set snapshot time to time of latest snapshot to be merged

crates/core/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ implement [`serde::Serialize`] and [`serde::Deserialize`].
4545
4646
let config_opts = ConfigOptions::default();
4747
48-
let _repo = Repository::new(&repo_opts, backends.clone()).unwrap().init(&key_opts, &config_opts).unwrap();
48+
let _repo = Repository::new(&repo_opts, &backends.clone()).unwrap().init(&key_opts, &config_opts).unwrap();
4949
5050
// We could have used _repo directly, but open the repository again to show how to open it...
51-
let repo = Repository::new(&repo_opts, backends).unwrap().open().unwrap();
51+
let repo = Repository::new(&repo_opts, &backends).unwrap().open().unwrap();
5252
5353
// Get all snapshots from the repository
5454
let snaps = repo.get_all_snapshots().unwrap();

crates/core/src/repofile/snapshotfile.rs

+24-18
Original file line numberDiff line numberDiff line change
@@ -1100,25 +1100,31 @@ impl Display for PathList {
11001100
}
11011101
}
11021102

1103-
impl PathList {
1104-
/// Create a `PathList` from `String`s.
1105-
///
1106-
/// # Arguments
1107-
///
1108-
/// * `source` - The `String`s to use
1109-
pub fn from_strings<I>(source: I) -> Self
1110-
where
1111-
I: IntoIterator,
1112-
I::Item: AsRef<str>,
1113-
{
1114-
Self(
1115-
source
1116-
.into_iter()
1117-
.map(|source| PathBuf::from(source.as_ref()))
1118-
.collect(),
1119-
)
1103+
impl FromIterator<PathBuf> for PathList {
1104+
fn from_iter<I: IntoIterator<Item = PathBuf>>(iter: I) -> Self {
1105+
Self(iter.into_iter().collect())
11201106
}
1107+
}
11211108

1109+
impl<'a> FromIterator<&'a String> for PathList {
1110+
fn from_iter<I: IntoIterator<Item = &'a String>>(iter: I) -> Self {
1111+
Self(iter.into_iter().map(PathBuf::from).collect())
1112+
}
1113+
}
1114+
1115+
impl FromIterator<String> for PathList {
1116+
fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Self {
1117+
Self(iter.into_iter().map(PathBuf::from).collect())
1118+
}
1119+
}
1120+
1121+
impl<'a> FromIterator<&'a str> for PathList {
1122+
fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> Self {
1123+
Self(iter.into_iter().map(PathBuf::from).collect())
1124+
}
1125+
}
1126+
1127+
impl PathList {
11221128
/// Create a `PathList` by parsing a Strings containing paths separated by whitspaces.
11231129
///
11241130
/// # Arguments
@@ -1132,7 +1138,7 @@ impl PathList {
11321138
/// [`SnapshotFileErrorKind::FromSplitError`]: crate::error::SnapshotFileErrorKind::FromSplitError
11331139
pub fn from_string(sources: &str) -> RusticResult<Self> {
11341140
let sources = split(sources).map_err(SnapshotFileErrorKind::FromSplitError)?;
1135-
Ok(Self::from_strings(sources))
1141+
Ok(Self::from_iter(sources))
11361142
}
11371143

11381144
/// Number of paths in the `PathList`.

crates/core/src/repository.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,7 @@ impl<P, S: IndexedTree> Repository<P, S> {
14521452
/// # Arguments
14531453
///
14541454
/// * `id` - The `Id` of the tree
1455+
// TODO!: This ID should be a tree ID, we should refactor it to wrap it in a TreeId type
14551456
///
14561457
/// # Errors
14571458
///
@@ -1475,6 +1476,7 @@ impl<P, S: IndexedTree> Repository<P, S> {
14751476
/// # Arguments
14761477
///
14771478
/// * `root_tree` - The `Id` of the root tree
1479+
// TODO!: This ID should be a tree ID, we should refactor it to wrap it in a TreeId type
14781480
/// * `path` - The path
14791481
///
14801482
/// # Errors
11.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)