Skip to content

Commit

Permalink
sign: Implement a test signing backend and add a few basic tests
Browse files Browse the repository at this point in the history
  • Loading branch information
necauqua committed Nov 30, 2023
1 parent 70c9d78 commit 3a9de3c
Show file tree
Hide file tree
Showing 10 changed files with 272 additions and 11 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion cli/examples/custom-backend/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ use jj_lib::git_backend::GitBackend;
use jj_lib::repo::StoreFactories;
use jj_lib::repo_path::RepoPath;
use jj_lib::settings::UserSettings;
use jj_lib::workspace::Workspace;
use jj_lib::signing::Signer;
use jj_lib::workspace::{Workspace, WorkspaceInitError};

#[derive(clap::Parser, Clone, Debug)]
enum CustomCommands {
Expand Down Expand Up @@ -59,6 +60,8 @@ fn run_custom_command(
command_helper.settings(),
wc_path,
&|settings, store_path| Ok(Box::new(JitBackend::init(settings, store_path)?)),
Signer::from_settings(command_helper.settings())
.map_err(WorkspaceInitError::SignInit)?,
)?;
Ok(())
}
Expand Down
7 changes: 6 additions & 1 deletion cli/examples/custom-working-copy/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@ use jj_lib::op_store::{OperationId, WorkspaceId};
use jj_lib::repo::ReadonlyRepo;
use jj_lib::repo_path::RepoPathBuf;
use jj_lib::settings::UserSettings;
use jj_lib::signing::Signer;
use jj_lib::store::Store;
use jj_lib::working_copy::{
CheckoutError, CheckoutStats, LockedWorkingCopy, ResetError, SnapshotError, SnapshotOptions,
WorkingCopy, WorkingCopyStateError,
};
use jj_lib::workspace::{default_working_copy_factories, WorkingCopyInitializer, Workspace};
use jj_lib::workspace::{
default_working_copy_factories, WorkingCopyInitializer, Workspace, WorkspaceInitError,
};

#[derive(clap::Parser, Clone, Debug)]
enum CustomCommands {
Expand All @@ -58,6 +61,8 @@ fn run_custom_command(
command_helper.settings(),
wc_path,
&backend_initializer,
Signer::from_settings(command_helper.settings())
.map_err(WorkspaceInitError::SignInit)?,
&ReadonlyRepo::default_op_store_initializer(),
&ReadonlyRepo::default_op_heads_store_initializer(),
&ReadonlyRepo::default_index_store_initializer(),
Expand Down
5 changes: 2 additions & 3 deletions lib/src/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ pub enum RepoInitError {
Backend(#[from] BackendInitError),
#[error(transparent)]
Path(#[from] PathError),
#[error(transparent)]
SignInit(#[from] SignInitError),
}

impl ReadonlyRepo {
Expand All @@ -143,10 +141,12 @@ impl ReadonlyRepo {
&|_settings, store_path| Box::new(DefaultSubmoduleStore::init(store_path))
}

#[allow(clippy::too_many_arguments)]
pub fn init(
user_settings: &UserSettings,
repo_path: &Path,
backend_initializer: &BackendInitializer,
signer: Signer,
op_store_initializer: &OpStoreInitializer,
op_heads_store_initializer: &OpHeadsStoreInitializer,
index_store_initializer: &IndexStoreInitializer,
Expand All @@ -159,7 +159,6 @@ impl ReadonlyRepo {
let backend = backend_initializer(user_settings, &store_path)?;
let backend_path = store_path.join("type");
fs::write(&backend_path, backend.name()).context(&backend_path)?;
let signer = Signer::from_settings(user_settings)?;
let store = Store::new(backend, signer, user_settings.use_tree_conflict_format());
let repo_settings = user_settings.with_repo(&repo_path).unwrap();

Expand Down
19 changes: 13 additions & 6 deletions lib/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use crate::repo::{
StoreFactories, StoreLoadError, SubmoduleStoreInitializer,
};
use crate::settings::UserSettings;
use crate::signing::SignInitError;
use crate::signing::{SignInitError, Signer};
use crate::store::Store;
use crate::working_copy::{
CheckoutError, CheckoutStats, LockedWorkingCopy, WorkingCopy, WorkingCopyStateError,
Expand Down Expand Up @@ -149,7 +149,8 @@ impl Workspace {
) -> Result<(Self, Arc<ReadonlyRepo>), WorkspaceInitError> {
let backend_initializer: &'static BackendInitializer =
&|_settings, store_path| Ok(Box::new(LocalBackend::init(store_path)));
Self::init_with_backend(user_settings, workspace_root, backend_initializer)
let signer = Signer::from_settings(user_settings)?;
Self::init_with_backend(user_settings, workspace_root, backend_initializer, signer)
}

/// Initializes a workspace with a new Git backend and bare Git repo in
Expand All @@ -160,7 +161,8 @@ impl Workspace {
) -> Result<(Self, Arc<ReadonlyRepo>), WorkspaceInitError> {
let backend_initializer: &'static BackendInitializer =
&|settings, store_path| Ok(Box::new(GitBackend::init_internal(settings, store_path)?));
Self::init_with_backend(user_settings, workspace_root, backend_initializer)
let signer = Signer::from_settings(user_settings)?;
Self::init_with_backend(user_settings, workspace_root, backend_initializer, signer)
}

/// Initializes a workspace with a new Git backend and Git repo that shares
Expand Down Expand Up @@ -189,7 +191,8 @@ impl Workspace {
Ok(Box::new(backend))
}
};
Self::init_with_backend(user_settings, workspace_root, &backend_initializer)
let signer = Signer::from_settings(user_settings)?;
Self::init_with_backend(user_settings, workspace_root, &backend_initializer, signer)
}

/// Initializes a workspace with an existing Git repo at the specified path.
Expand Down Expand Up @@ -221,14 +224,16 @@ impl Workspace {
Ok(Box::new(backend))
}
};
Self::init_with_backend(user_settings, workspace_root, &backend_initializer)
let signer = Signer::from_settings(user_settings)?;
Self::init_with_backend(user_settings, workspace_root, &backend_initializer, signer)
}

#[allow(clippy::too_many_arguments)]
pub fn init_with_factories(
user_settings: &UserSettings,
workspace_root: &Path,
backend_initializer: &BackendInitializer,
signer: Signer,
op_store_initializer: &OpStoreInitializer,
op_heads_store_initializer: &OpHeadsStoreInitializer,
index_store_initializer: &IndexStoreInitializer,
Expand All @@ -244,6 +249,7 @@ impl Workspace {
user_settings,
&repo_dir,
backend_initializer,
signer,
op_store_initializer,
op_heads_store_initializer,
index_store_initializer,
Expand All @@ -252,7 +258,6 @@ impl Workspace {
.map_err(|repo_init_err| match repo_init_err {
RepoInitError::Backend(err) => WorkspaceInitError::Backend(err),
RepoInitError::Path(err) => WorkspaceInitError::Path(err),
RepoInitError::SignInit(err) => WorkspaceInitError::SignInit(err),
})?;
let (working_copy, repo) = init_working_copy(
user_settings,
Expand All @@ -276,11 +281,13 @@ impl Workspace {
user_settings: &UserSettings,
workspace_root: &Path,
backend_initializer: &BackendInitializer,
signer: Signer,
) -> Result<(Self, Arc<ReadonlyRepo>), WorkspaceInitError> {
Self::init_with_factories(
user_settings,
workspace_root,
backend_initializer,
signer,
ReadonlyRepo::default_op_store_initializer(),
ReadonlyRepo::default_op_heads_store_initializer(),
ReadonlyRepo::default_index_store_initializer(),
Expand Down
4 changes: 4 additions & 0 deletions lib/tests/test_git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use jj_lib::op_store::{BranchTarget, RefTarget, RemoteRef, RemoteRefState};
use jj_lib::refs::BranchPushUpdate;
use jj_lib::repo::{MutableRepo, ReadonlyRepo, Repo};
use jj_lib::settings::{GitSettings, UserSettings};
use jj_lib::signing::Signer;
use jj_lib::str_util::StringPattern;
use jj_lib::workspace::Workspace;
use maplit::{btreemap, hashset};
Expand Down Expand Up @@ -1125,6 +1126,7 @@ impl GitRepoData {
&git_repo_dir,
)?))
},
Signer::from_settings(&settings).unwrap(),
ReadonlyRepo::default_op_store_initializer(),
ReadonlyRepo::default_op_heads_store_initializer(),
ReadonlyRepo::default_index_store_initializer(),
Expand Down Expand Up @@ -1990,6 +1992,7 @@ fn test_init() {
&git_repo_dir,
)?))
},
Signer::from_settings(&settings).unwrap(),
ReadonlyRepo::default_op_store_initializer(),
ReadonlyRepo::default_op_heads_store_initializer(),
ReadonlyRepo::default_index_store_initializer(),
Expand Down Expand Up @@ -2315,6 +2318,7 @@ fn set_up_push_repos(settings: &UserSettings, temp_dir: &TempDir) -> PushTestSet
&clone_repo_dir,
)?))
},
Signer::from_settings(settings).unwrap(),
ReadonlyRepo::default_op_store_initializer(),
ReadonlyRepo::default_op_heads_store_initializer(),
ReadonlyRepo::default_index_store_initializer(),
Expand Down
171 changes: 171 additions & 0 deletions lib/tests/test_signing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
use jj_lib::backend::{MillisSinceEpoch, Signature, Timestamp};
use jj_lib::repo::Repo;
use jj_lib::settings::UserSettings;
use jj_lib::signing::{SigStatus, SignBehavior, Signer, Verification};
use test_case::test_case;
use testutils::test_signing_backend::TestSigningBackend;
use testutils::{create_random_commit, write_random_commit, TestRepoBackend, TestWorkspace};

fn user_settings(sign_all: bool) -> UserSettings {
let config = testutils::base_config()
.add_source(config::File::from_str(
&format!(
r#"
signing.key = "impeccable"
signing.sign-all = {sign_all}
"#
),
config::FileFormat::Toml,
))
.build()
.unwrap();
UserSettings::from_config(config)
}

fn someone_else() -> Signature {
Signature {
name: "Someone Else".to_string(),
email: "[email protected]".to_string(),
timestamp: Timestamp {
timestamp: MillisSinceEpoch(0),
tz_offset: 0,
},
}
}

fn good_verification() -> Option<Verification> {
Some(Verification {
status: SigStatus::Good,
key: Some("impeccable".to_owned()),
display: None,
})
}

#[test_case(TestRepoBackend::Local ; "local backend")]
#[test_case(TestRepoBackend::Git ; "git backend")]
fn manual(backend: TestRepoBackend) {
let settings = user_settings(true);

let signer = Signer::new(Some(Box::new(TestSigningBackend)), vec![]);
let test_workspace = TestWorkspace::init_with_backend_and_signer(&settings, backend, signer);

let repo = &test_workspace.repo;

let settings = settings.clone();
let repo = repo.clone();
let mut tx = repo.start_transaction(&settings, "test");
let commit1 = create_random_commit(tx.mut_repo(), &settings)
.set_sign_behavior(SignBehavior::Own)
.write()
.unwrap();
let commit2 = create_random_commit(tx.mut_repo(), &settings)
.set_sign_behavior(SignBehavior::Own)
.set_author(someone_else())
.write()
.unwrap();
tx.commit();

let commit1 = repo.store().get_commit(commit1.id()).unwrap();
assert_eq!(commit1.verification().unwrap(), good_verification());

let commit2 = repo.store().get_commit(commit2.id()).unwrap();
assert_eq!(commit2.verification().unwrap(), None);
}

#[test_case(TestRepoBackend::Git ; "git backend")]
fn keep_on_rewrite(backend: TestRepoBackend) {
let settings = user_settings(true);

let signer = Signer::new(Some(Box::new(TestSigningBackend)), vec![]);
let test_workspace = TestWorkspace::init_with_backend_and_signer(&settings, backend, signer);

let repo = &test_workspace.repo;

let settings = settings.clone();
let repo = repo.clone();
let mut tx = repo.start_transaction(&settings, "test");
let commit = create_random_commit(tx.mut_repo(), &settings)
.set_sign_behavior(SignBehavior::Own)
.write()
.unwrap();
tx.commit();

let mut tx = repo.start_transaction(&settings, "test");
let mut_repo = tx.mut_repo();
let rewritten = mut_repo.rewrite_commit(&settings, &commit).write().unwrap();

let commit = repo.store().get_commit(rewritten.id()).unwrap();
assert_eq!(commit.verification().unwrap(), good_verification());
}

#[test_case(TestRepoBackend::Git ; "git backend")]
fn manual_drop_on_rewrite(backend: TestRepoBackend) {
let settings = user_settings(true);

let signer = Signer::new(Some(Box::new(TestSigningBackend)), vec![]);
let test_workspace = TestWorkspace::init_with_backend_and_signer(&settings, backend, signer);

let repo = &test_workspace.repo;

let settings = settings.clone();
let repo = repo.clone();
let mut tx = repo.start_transaction(&settings, "test");
let commit = create_random_commit(tx.mut_repo(), &settings)
.set_sign_behavior(SignBehavior::Own)
.write()
.unwrap();
tx.commit();

let mut tx = repo.start_transaction(&settings, "test");
let mut_repo = tx.mut_repo();
let rewritten = mut_repo
.rewrite_commit(&settings, &commit)
.set_sign_behavior(SignBehavior::Drop)
.write()
.unwrap();

let commit = repo.store().get_commit(rewritten.id()).unwrap();
assert_eq!(commit.verification().unwrap(), None);
}

#[test_case(TestRepoBackend::Git ; "git backend")]
fn forced(backend: TestRepoBackend) {
let settings = user_settings(true);

let signer = Signer::new(Some(Box::new(TestSigningBackend)), vec![]);
let test_workspace = TestWorkspace::init_with_backend_and_signer(&settings, backend, signer);

let repo = &test_workspace.repo;

let settings = settings.clone();
let repo = repo.clone();
let mut tx = repo.start_transaction(&settings, "test");
let commit = create_random_commit(tx.mut_repo(), &settings)
.set_sign_behavior(SignBehavior::Force)
.set_author(someone_else())
.write()
.unwrap();
tx.commit();

let commit = repo.store().get_commit(commit.id()).unwrap();
assert_eq!(commit.verification().unwrap(), good_verification());
}

#[test_case(TestRepoBackend::Git ; "git backend")]
fn configured(backend: TestRepoBackend) {
let settings = user_settings(true);

let signer = Signer::new(Some(Box::new(TestSigningBackend)), vec![]);
let test_workspace = TestWorkspace::init_with_backend_and_signer(&settings, backend, signer);

let repo = &test_workspace.repo;

let settings = settings.clone();
let repo = repo.clone();
let mut tx = repo.start_transaction(&settings, "test");
let commit = write_random_commit(tx.mut_repo(), &settings);
tx.commit();

let commit = repo.store().get_commit(commit.id()).unwrap();
assert_eq!(commit.verification().unwrap(), good_verification());
}
1 change: 1 addition & 0 deletions lib/testutils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ readme = { workspace = true }
async-trait = { workspace = true }
config = { workspace = true }
git2 = { workspace = true }
hex = { workspace = true }
itertools = { workspace = true }
jj-lib = { workspace = true }
rand = { workspace = true }
Expand Down
Loading

0 comments on commit 3a9de3c

Please sign in to comment.