Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cob: mutable stable time for testing
Fintan Halpenny committed 1 year ago
commit 259adf7dc215f92d1e039d655a7cb4e587055aaf
parent cab56c1113e74c896fa27a6c0991aed538012c1f
4 files changed +111 -19
modified radicle-cob/src/backend/git.rs
@@ -2,6 +2,9 @@

pub mod change;

+
#[cfg(feature = "stable-commit-ids")]
+
pub mod stable;
+

/// Environment variable to set to overwrite the commit date for both the author and the committer.
///
/// The format must be a unix timestamp.
modified radicle-cob/src/backend/git/change.rs
@@ -301,13 +301,16 @@ fn write_commit(

    #[cfg(feature = "stable-commit-ids")]
    // Ensures the commit id doesn't change on every run.
-
    let (author, timestamp) = (
-
        Author {
-
            time: git_ext::author::Time::new(1514817556, 0),
-
            ..author
-
        },
-
        1514817556,
-
    );
+
    let (author, timestamp) = {
+
        let stable = crate::git::stable::read_timestamp();
+
        (
+
            Author {
+
                time: git_ext::author::Time::new(stable, 0),
+
                ..author
+
            },
+
            stable,
+
        )
+
    };
    let (author, timestamp) = if let Ok(s) = std::env::var(crate::git::GIT_COMMITTER_DATE) {
        let Ok(timestamp) = s.trim().parse::<i64>() else {
            panic!(
added radicle-cob/src/backend/git/stable.rs
@@ -0,0 +1,79 @@
+
use std::{cell::Cell, ops::Add};
+

+
thread_local! {
+
    /// The constant time used by the stable-commit-ids feature.
+
    pub static STABLE_TIME: Cell<i64> = const { Cell::new(1514817556) };
+
    /// An incrementing counter to advance the `STABLE_TIME` value with in
+
    /// [`with_advanced_timestamp`].
+
    pub static STEP: Cell<Step> = Cell::new(Step::default());
+
}
+

+
#[derive(Clone, Copy)]
+
struct Step(i64);
+

+
impl Default for Step {
+
    fn default() -> Self {
+
        Self(1)
+
    }
+
}
+

+
impl Add<Step> for i64 {
+
    type Output = i64;
+

+
    fn add(self, rhs: Step) -> Self::Output {
+
        self + rhs.0
+
    }
+
}
+

+
impl Add<i64> for Step {
+
    type Output = Step;
+

+
    fn add(self, rhs: i64) -> Self::Output {
+
        Step(self.0 + rhs)
+
    }
+
}
+

+
/// Read the current value of `STABLE_TIME`.
+
///
+
/// # Panics
+
///
+
/// The `STABLE_TIME` is declared in `thread_local`, and so the panic
+
/// information is repeated here.
+
///
+
/// Panics if the key currently has its destructor running, and it may panic if
+
/// the destructor has previously been run for this thread.
+
#[allow(clippy::unwrap_used)]
+
pub fn read_timestamp() -> i64 {
+
    STABLE_TIME.get()
+
}
+

+
/// Perform an action `f` that would rely on the `STABLE_TIME` value. This will
+
/// advance the `STABLE_TIME` by an increment of `1` for each time it is called,
+
/// within the same thread.
+
///
+
/// # Usage
+
///
+
/// ```no_run
+
/// let oid1 = with_advanced_timestamp(|| cob.update("New revision OID"));
+
/// let oid2 = with_advanced_timestamp(|| cob.update("Another revision OID"));
+
/// ```
+
///
+
/// # Panics
+
///
+
/// The `STABLE_TIME` is declared in `thread_local`, and so the panic
+
/// information is repeated here.
+
///
+
/// Panics if the key currently has its destructor running, and it may panic if
+
/// the destructor has previously been run for this thread.
+
#[allow(clippy::unwrap_used)]
+
pub fn with_advanced_timestamp<F, T>(f: F) -> T
+
where
+
    F: FnOnce() -> T,
+
{
+
    let step = STEP.get();
+
    let original = read_timestamp();
+
    STABLE_TIME.replace(original + step);
+
    let result = f();
+
    STEP.replace(step + 1);
+
    result
+
}
modified radicle/src/cob/identity.rs
@@ -1016,7 +1016,10 @@ mod test {
    use radicle_crypto::test::signer::MockSigner;
    use radicle_crypto::Signer as _;

+
    use crate::cob;
    use crate::crypto::PublicKey;
+
    use crate::identity::did::Did;
+
    use crate::identity::doc::PayloadId;
    use crate::identity::Visibility;
    use crate::rad;
    use crate::storage::git::Storage;
@@ -1025,8 +1028,6 @@ mod test {
    use crate::test::setup::{Network, NodeWithRepo};

    use super::*;
-
    use crate::identity::did::Did;
-
    use crate::identity::doc::PayloadId;

    #[quickcheck]
    fn prop_json_eq_str(pk: PublicKey, proj: RepoId, did: Did) {
@@ -1319,20 +1320,24 @@ mod test {
        eve.repo.fetch(bob);

        let mut bob_identity = Identity::load_mut(&*bob.repo).unwrap();
-
        let b1 = bob_identity.accept(&a2, &bob.signer).unwrap();
+
        let b1 = cob::git::stable::with_advanced_timestamp(|| {
+
            bob_identity.accept(&a2, &bob.signer).unwrap()
+
        });
        assert_eq!(bob_identity.current, a2);

        let mut eve_identity = Identity::load_mut(&*eve.repo).unwrap();
        let mut eve_doc = eve_identity.doc().clone().edit();
        eve_doc.visibility = Visibility::private([eve.signer.public_key().into()]);
-
        let e1 = eve_identity
-
            .update(
-
                "Change visibility",
-
                "",
-
                &eve_doc.verified().unwrap(),
-
                &eve.signer,
-
            )
-
            .unwrap();
+
        let e1 = cob::git::stable::with_advanced_timestamp(|| {
+
            eve_identity
+
                .update(
+
                    "Change visibility",
+
                    "",
+
                    &eve_doc.verified().unwrap(),
+
                    &eve.signer,
+
                )
+
                .unwrap()
+
        });
        // Eve's revision is active.
        assert!(eve_identity.revision(&e1).unwrap().is_active());

@@ -1506,7 +1511,9 @@ mod test {
        assert_eq!(eve_identity.revision(&e1).unwrap().state, State::Active);

        alice_identity.reload().unwrap();
-
        let a2 = alice_identity.accept(&b1, &alice.signer).unwrap();
+
        let a2 = cob::git::stable::with_advanced_timestamp(|| {
+
            alice_identity.accept(&b1, &alice.signer).unwrap()
+
        });

        eve.repo.fetch(alice);
        eve_identity.reload().unwrap();