Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli: Replace `trycmd` with our own test runner
Alexis Sellier committed 3 years ago
commit e63f33489055962845c70976bd9a40d0d84c6b1c
parent ca69fdc4fa532aef074b62fb9a765e38d1db298b
10 files changed +324 -205
modified Cargo.lock
@@ -372,16 +372,6 @@ dependencies = [
]

[[package]]
-
name = "combine"
-
version = "4.6.6"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
-
dependencies = [
-
 "bytes",
-
 "memchr",
-
]
-

-
[[package]]
name = "concolor"
version = "0.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -453,30 +443,6 @@ dependencies = [
]

[[package]]
-
name = "crossbeam-deque"
-
version = "0.8.2"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
-
dependencies = [
-
 "cfg-if",
-
 "crossbeam-epoch",
-
 "crossbeam-utils",
-
]
-

-
[[package]]
-
name = "crossbeam-epoch"
-
version = "0.9.13"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
-
dependencies = [
-
 "autocfg",
-
 "cfg-if",
-
 "crossbeam-utils",
-
 "memoffset",
-
 "scopeguard",
-
]
-

-
[[package]]
name = "crossbeam-utils"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1115,12 +1081,6 @@ dependencies = [
]

[[package]]
-
name = "glob"
-
version = "0.3.0"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
-

-
[[package]]
name = "group"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1261,22 +1221,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"

[[package]]
-
name = "humantime"
-
version = "2.1.0"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
-

-
[[package]]
-
name = "humantime-serde"
-
version = "1.1.1"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c"
-
dependencies = [
-
 "humantime",
-
 "serde",
-
]
-

-
[[package]]
name = "hyper"
version = "0.14.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1445,15 +1389,6 @@ dependencies = [
]

[[package]]
-
name = "itertools"
-
version = "0.10.5"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
-
dependencies = [
-
 "either",
-
]
-

-
[[package]]
name = "itoa"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1626,15 +1561,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"

[[package]]
-
name = "memoffset"
-
version = "0.7.1"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
-
dependencies = [
-
 "autocfg",
-
]
-

-
[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1869,16 +1795,6 @@ dependencies = [
]

[[package]]
-
name = "os_pipe"
-
version = "1.1.2"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "c6a252f1f8c11e84b3ab59d7a488e48e4478a93937e027076638c49536204639"
-
dependencies = [
-
 "libc",
-
 "windows-sys",
-
]
-

-
[[package]]
name = "output_vt100"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2196,16 +2112,18 @@ dependencies = [
 "json-color",
 "lexopt",
 "log",
+
 "pretty_assertions",
 "radicle",
 "radicle-cob",
 "radicle-crypto",
 "serde",
 "serde_json",
 "serde_yaml",
+
 "shlex",
+
 "snapbox",
 "tempfile",
 "thiserror",
 "timeago",
-
 "trycmd",
 "zeroize",
]

@@ -2473,29 +2391,6 @@ dependencies = [
]

[[package]]
-
name = "rayon"
-
version = "1.6.0"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
-
dependencies = [
-
 "crossbeam-deque",
-
 "either",
-
 "rayon-core",
-
]
-

-
[[package]]
-
name = "rayon-core"
-
version = "1.10.1"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
-
dependencies = [
-
 "crossbeam-channel",
-
 "crossbeam-deque",
-
 "crossbeam-utils",
-
 "num_cpus",
-
]
-

-
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2658,12 +2553,6 @@ dependencies = [
]

[[package]]
-
name = "scopeguard"
-
version = "1.1.0"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-

-
[[package]]
name = "scratch"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2822,6 +2711,12 @@ dependencies = [
]

[[package]]
+
name = "similar"
+
version = "2.2.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf"
+

+
[[package]]
name = "siphasher"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2865,12 +2760,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efbd7b250c7243273b5aec4ca366fced84ad716d110bb7baae4814678952ebde"
dependencies = [
 "concolor",
-
 "libc",
 "normalize-line-endings",
-
 "os_pipe",
+
 "similar",
 "snapbox-macros",
-
 "wait-timeout",
-
 "windows-sys",
 "yansi",
]

@@ -3242,28 +3134,6 @@ dependencies = [
]

[[package]]
-
name = "toml_datetime"
-
version = "0.5.0"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "808b51e57d0ef8f71115d8f3a01e7d3750d01c79cac4b3eda910f4389fdf92fd"
-
dependencies = [
-
 "serde",
-
]
-

-
[[package]]
-
name = "toml_edit"
-
version = "0.15.0"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "b1541ba70885967e662f69d31ab3aeca7b1aaecfcd58679590b893e9239c3646"
-
dependencies = [
-
 "combine",
-
 "indexmap",
-
 "itertools",
-
 "serde",
-
 "toml_datetime",
-
]
-

-
[[package]]
name = "tower"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3393,22 +3263,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"

[[package]]
-
name = "trycmd"
-
version = "0.14.5"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "e381af441e13a3635303d26769620a9454aef05ec3303711efc3f1dd785a33af"
-
dependencies = [
-
 "glob",
-
 "humantime",
-
 "humantime-serde",
-
 "rayon",
-
 "serde",
-
 "shlex",
-
 "snapbox",
-
 "toml_edit",
-
]
-

-
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3489,15 +3343,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"

[[package]]
-
name = "wait-timeout"
-
version = "0.2.0"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
-
dependencies = [
-
 "libc",
-
]
-

-
[[package]]
name = "want"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
modified radicle-cli/Cargo.toml
@@ -39,6 +39,8 @@ version = "0"
path = "../radicle-crypto"

[dev-dependencies]
-
trycmd = { version = "0.14.4", default-features = false, features = ["color-auto"] }
+
pretty_assertions = { version = "1.3.0" }
tempfile = { version = "3.3.0" }
radicle = { path = "../radicle", features = ["test"] }
+
shlex = { version = "1.1.0" }
+
snapbox = { version = "0.4.3" }
modified radicle-cli/examples/rad-auth.md
@@ -1,4 +1,3 @@
-

Initializing a new identity with `rad-auth`.
The example below is run with `RAD_PASSPHRASE` set.

@@ -13,7 +12,6 @@ ok Profile z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi created.
Your radicle Node ID is z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi. This identifies your device.

=> To create a radicle project, run `rad init` from a git repository.
-

```

You can get the above information at all times using the `self` command:
@@ -26,5 +24,4 @@ Key (full) AAAAC3NzaC1lZDI1NTE5AAAAIHahWSBEpuT1ESZbynOmBNkLBSnR32Ar4woZqSV2Y
Storage (git)  [..]/storage
Storage (keys) [..]/keys
Node (socket)  [..]/node/radicle.sock
-

```
modified radicle-cli/examples/rad-init.md
@@ -15,19 +15,17 @@ ok Project acme created
}


-
Your project id is [..]. You can show it any time by running:
+
Your project id is rad:z4EXAgDJk11uFThfVir5FivKhiAeK. You can show it any time by running:
   rad .

To publish your project to the network, run:
   rad push

-

```

Projects can be listed with the `ls` command:

```
$ rad ls
-
acme [..] [..] Acme's repository
-

+
acme rad:z4EXAgDJk11uFThfVir5FivKhiAeK cdf76ce Acme's repository
```
modified radicle-cli/src/lib.rs
@@ -6,4 +6,6 @@ pub mod git;
pub mod project;
pub mod terminal;
#[cfg(test)]
+
mod testing;
+
#[cfg(test)]
mod tests;
added radicle-cli/src/testing.rs
@@ -0,0 +1,245 @@
+
#![allow(clippy::collapsible_else_if)]
+
use std::borrow::Cow;
+
use std::collections::HashMap;
+
use std::path::{Path, PathBuf};
+
use std::{fs, io, mem};
+

+
use snapbox::cmd::Command;
+
use snapbox::{Assert, Substitutions};
+
use thiserror::Error;
+

+
#[derive(Error, Debug)]
+
pub enum Error {
+
    #[error("parsing failed")]
+
    Parse,
+
    #[error("i/o: {0}")]
+
    Io(#[from] io::Error),
+
    #[error("snapbox: {0}")]
+
    Snapbox(#[from] snapbox::Error),
+
}
+

+
/// A test which may contain multiple assertions.
+
#[derive(Debug, Default, PartialEq, Eq)]
+
pub struct Test {
+
    /// Human-readable context around the test. Functions as documentation.
+
    context: Vec<String>,
+
    /// Test assertions to run.
+
    assertions: Vec<Assertion>,
+
}
+

+
/// An assertion is a command to run with an expected output.
+
#[derive(Debug, PartialEq, Eq)]
+
pub struct Assertion {
+
    /// Name of program to run, eg. `git`.
+
    program: String,
+
    /// Program arguments, eg. `["push"]`.
+
    args: Vec<String>,
+
    /// Expected output (stdout or stderr).
+
    expected: String,
+
}
+

+
#[derive(Debug, Default, PartialEq, Eq)]
+
pub struct TestFormula {
+
    /// Current working directory to run the test in.
+
    cwd: PathBuf,
+
    /// Environment to pass to the test.
+
    env: HashMap<String, String>,
+
    /// Tests to run.
+
    tests: Vec<Test>,
+
    /// Output substitutions.
+
    subs: Substitutions,
+
}
+

+
impl TestFormula {
+
    pub fn new() -> Self {
+
        Self {
+
            cwd: PathBuf::new(),
+
            env: HashMap::new(),
+
            tests: Vec::new(),
+
            subs: Substitutions::new(),
+
        }
+
    }
+

+
    pub fn cwd(&mut self, path: impl AsRef<Path>) -> &mut Self {
+
        self.cwd = path.as_ref().into();
+
        self
+
    }
+

+
    pub fn env(&mut self, key: impl Into<String>, val: impl Into<String>) -> &mut Self {
+
        self.env.insert(key.into(), val.into());
+
        self
+
    }
+

+
    pub fn file(&mut self, path: impl AsRef<Path>) -> Result<&mut Self, Error> {
+
        let contents = fs::read(path)?;
+
        self.read(io::Cursor::new(contents))
+
    }
+

+
    pub fn read(&mut self, r: impl io::BufRead) -> Result<&mut Self, Error> {
+
        let mut test = Test::default();
+
        let mut fenced = false; // Whether we're inside a fenced code block.
+

+
        for line in r.lines() {
+
            let line = line?;
+

+
            if line.starts_with("```") {
+
                if fenced {
+
                    // End existing code block.
+
                    self.tests.push(mem::take(&mut test));
+
                }
+
                fenced = !fenced;
+

+
                continue;
+
            }
+

+
            if fenced {
+
                if let Some(line) = line.strip_prefix('$') {
+
                    let line = line.trim();
+
                    let parts = shlex::split(line).ok_or(Error::Parse)?;
+
                    let (program, args) = parts.split_first().ok_or(Error::Parse)?;
+

+
                    test.assertions.push(Assertion {
+
                        program: program.to_owned(),
+
                        args: args.to_owned(),
+
                        expected: String::new(),
+
                    });
+
                } else if let Some(test) = test.assertions.last_mut() {
+
                    test.expected.push_str(line.as_str());
+
                    test.expected.push('\n');
+
                } else {
+
                    return Err(Error::Parse);
+
                }
+
            } else {
+
                test.context.push(line);
+
            }
+
        }
+
        Ok(self)
+
    }
+

+
    #[allow(dead_code)]
+
    pub fn substitute(
+
        &mut self,
+
        value: &'static str,
+
        other: impl Into<Cow<'static, str>>,
+
    ) -> Result<&mut Self, Error> {
+
        self.subs.insert(value, other)?;
+
        Ok(self)
+
    }
+

+
    pub fn run(&self) -> Result<bool, io::Error> {
+
        let assert = Assert::new().substitutions(self.subs.clone());
+

+
        for test in &self.tests {
+
            for assertion in &test.assertions {
+
                let program = if assertion.program == "rad" {
+
                    snapbox::cmd::cargo_bin("rad")
+
                } else {
+
                    PathBuf::from(&assertion.program)
+
                };
+

+
                Command::new(program)
+
                    .envs(self.env.clone())
+
                    .args(&assertion.args)
+
                    .with_assert(assert.clone())
+
                    .assert()
+
                    .stdout_matches(&assertion.expected)
+
                    .success();
+
            }
+
        }
+
        Ok(true)
+
    }
+
}
+

+
#[cfg(test)]
+
mod tests {
+
    use super::*;
+

+
    use pretty_assertions::assert_eq;
+

+
    #[test]
+
    fn test_parse() {
+
        let input = r#"
+
Let's try to track @dave and @sean:
+
```
+
$ rad track @dave
+
Tracking relationship established for @dave.
+
Nothing to do.
+

+
$ rad track @sean
+
Tracking relationship established for @sean.
+
Nothing to do.
+
```
+
Super, now let's move on to the next step.
+
```
+
$ rad sync
+
```
+
"#
+
        .trim()
+
        .as_bytes()
+
        .to_owned();
+

+
        let mut actual = TestFormula::new();
+
        actual
+
            .read(io::BufReader::new(io::Cursor::new(input)))
+
            .unwrap();
+

+
        let expected = TestFormula {
+
            cwd: PathBuf::new(),
+
            env: HashMap::new(),
+
            subs: Substitutions::new(),
+
            tests: vec![
+
                Test {
+
                    context: vec![String::from("Let's try to track @dave and @sean:")],
+
                    assertions: vec![
+
                        Assertion {
+
                            program: String::from("rad"),
+
                            args: vec![String::from("track"), String::from("@dave")],
+
                            expected: String::from(
+
                                "Tracking relationship established for @dave.\nNothing to do.\n\n",
+
                            ),
+
                        },
+
                        Assertion {
+
                            program: String::from("rad"),
+
                            args: vec![String::from("track"), String::from("@sean")],
+
                            expected: String::from(
+
                                "Tracking relationship established for @sean.\nNothing to do.\n",
+
                            ),
+
                        },
+
                    ],
+
                },
+
                Test {
+
                    context: vec![String::from("Super, now let's move on to the next step.")],
+
                    assertions: vec![Assertion {
+
                        program: String::from("rad"),
+
                        args: vec![String::from("sync")],
+
                        expected: String::new(),
+
                    }],
+
                },
+
            ],
+
        };
+

+
        assert_eq!(actual, expected);
+
    }
+

+
    #[test]
+
    fn test_run() {
+
        let input = r#"
+
Running a simple command such as `head`:
+
```
+
$ head -n 2 Cargo.toml
+
[package]
+
name = "radicle-cli"
+
```
+
"#
+
        .trim()
+
        .as_bytes()
+
        .to_owned();
+

+
        let mut formula = TestFormula::new();
+
        formula
+
            .cwd(env!("CARGO_MANIFEST_DIR"))
+
            .read(io::BufReader::new(io::Cursor::new(input)))
+
            .unwrap();
+
        formula.run().unwrap();
+
    }
+
}
modified radicle-cli/src/tests/cli.rs
@@ -1,41 +1,57 @@
+
use std::env;
use std::path::Path;
-
use std::{env, time};

use radicle::profile::Profile;
use radicle::test::fixtures;

-
#[test]
-
fn rad_auth() {
+
use crate::testing::TestFormula;
+

+
/// Run a CLI test file.
+
fn test(
+
    path: impl AsRef<Path>,
+
    profile: Option<Profile>,
+
) -> Result<(), Box<dyn std::error::Error>> {
    let base = Path::new(env!("CARGO_MANIFEST_DIR"));
-
    let home = tempfile::tempdir().unwrap();
+
    let tmp = tempfile::tempdir().unwrap();
+
    let home = if let Some(profile) = profile {
+
        profile.home.as_path().to_path_buf()
+
    } else {
+
        tmp.path().to_path_buf()
+
    };

-
    trycmd::TestCases::new()
-
        .env("RAD_DEBUG", "1")
+
    TestFormula::new()
        .env("RAD_PASSPHRASE", "radicle")
-
        .env("RAD_HOME", home.path().to_string_lossy())
-
        .timeout(time::Duration::from_secs(6))
-
        .case(base.join("examples/rad-auth.md"))
-
        .run();
+
        .env("RAD_HOME", home.to_string_lossy())
+
        .env("RAD_DEBUG", "1")
+
        .file(base.join(path))?
+
        .run()?;
+

+
    Ok(())
+
}
+

+
/// Create a new user profile.
+
fn profile(home: &Path) -> Profile {
+
    // Set debug mode, to make test output more predictable.
+
    env::set_var("RAD_DEBUG", "1");
+
    // Setup a new user.
+
    Profile::init(home, "radicle").unwrap()
+
}
+

+
#[test]
+
fn rad_auth() {
+
    test("examples/rad-auth.md", None).unwrap();
}

#[test]
fn rad_init() {
-
    let base = Path::new(env!("CARGO_MANIFEST_DIR"));
    let home = tempfile::tempdir().unwrap();
-
    let cwd = tempfile::tempdir().unwrap();
+
    let working = tempfile::tempdir().unwrap();
+
    let profile = profile(home.path());

    // Setup a test repository.
-
    fixtures::repository(cwd.path());
+
    fixtures::repository(working.path());
    // Navigate to repository.
-
    env::set_current_dir(cwd.path()).unwrap();
-
    // Setup a new user.
-
    Profile::init(home.path(), "radicle").unwrap();
+
    env::set_current_dir(working.path()).unwrap();

-
    trycmd::TestCases::new()
-
        .env("RAD_DEBUG", "1")
-
        .env("RAD_PASSPHRASE", "radicle")
-
        .env("RAD_HOME", home.path().to_string_lossy())
-
        .timeout(time::Duration::from_secs(6))
-
        .case(base.join("examples/rad-init.md"))
-
        .run();
+
    test("examples/rad-init.md", Some(profile)).unwrap();
}
modified radicle/src/git.rs
@@ -254,12 +254,11 @@ pub fn commit<'a>(
    parent: &'a git2::Commit,
    target: &RefStr,
    message: &str,
-
    user: &str,
+
    sig: &git2::Signature,
) -> Result<git2::Commit<'a>, git2::Error> {
-
    let sig = git2::Signature::now(user, "anonymous@radicle.xyz")?;
    let tree_id = repo.index()?.write_tree()?;
    let tree = repo.find_tree(tree_id)?;
-
    let oid = repo.commit(Some(target.as_str()), &sig, &sig, message, &tree, &[parent])?;
+
    let oid = repo.commit(Some(target.as_str()), sig, sig, message, &tree, &[parent])?;
    let commit = repo.find_commit(oid)?;

    Ok(commit)
modified radicle/src/storage/git.rs
@@ -964,9 +964,16 @@ mod tests {

        let alice_proj_storage = alice.repository(proj_id).unwrap();
        let alice_head = proj_repo.find_commit(alice_head).unwrap();
-
        let alice_head = git::commit(&proj_repo, &alice_head, &refname, "Making changes", "Alice")
-
            .unwrap()
-
            .id();
+
        let alice_sig = git2::Signature::now("Alice", "alice@radicle.xyz").unwrap();
+
        let alice_head = git::commit(
+
            &proj_repo,
+
            &alice_head,
+
            &refname,
+
            "Making changes",
+
            &alice_sig,
+
        )
+
        .unwrap()
+
        .id();
        git::push(&proj_repo, "rad", [(&refname, &refname)]).unwrap();
        alice_proj_storage.sign_refs(&alice_signer).unwrap();
        alice_proj_storage.set_head().unwrap();
@@ -1140,7 +1147,7 @@ mod tests {
            &head,
            &git::RefString::try_from(format!("refs/remotes/{alice}/heads/master")).unwrap(),
            "Second commit",
-
            &alice.to_string(),
+
            &sig,
        )
        .unwrap();

modified radicle/src/test/fixtures.rs
@@ -8,6 +8,9 @@ use crate::storage::git::transport;
use crate::storage::git::Storage;
use crate::storage::refs::SignedRefs;

+
/// The birth of the radicle project, January 1st, 2018.
+
const RADICLE_EPOCH: i64 = 1514817556;
+

/// Create a new storage with a project.
pub fn storage<P: AsRef<Path>, G: Signer>(path: P, signer: &G) -> Result<Storage, rad::InitError> {
    let path = path.as_ref();
@@ -52,14 +55,19 @@ pub fn project<P: AsRef<Path>, G: Signer>(
/// Creates a regular repository at the given path with a couple of commits.
pub fn repository<P: AsRef<Path>>(path: P) -> (git2::Repository, git2::Oid) {
    let repo = git2::Repository::init(path).unwrap();
-
    let sig = git2::Signature::now("anonymous", "anonymous@radicle.xyz").unwrap();
+
    let sig = git2::Signature::new(
+
        "anonymous",
+
        "anonymous@radicle.xyz",
+
        &git2::Time::new(RADICLE_EPOCH, 0),
+
    )
+
    .unwrap();
    let head = git::initial_commit(&repo, &sig).unwrap();
    let oid = git::commit(
        &repo,
        &head,
        git::refname!("refs/heads/master").as_refstr(),
        "Second commit",
-
        "anonymous",
+
        &sig,
    )
    .unwrap()
    .id();