use std::fs;
use dotling::{
commands::encrypt::{decrypt_single_entry, encrypt_single_entry},
config::Entry,
};
fn test_key() -> [u8; 32] {
[0x42u8; 32]
}
fn make_entry(source: &str, target: &str, template: bool, encrypted: bool) -> Entry {
Entry {
source: source.into(),
target: target.into(),
method: None,
encrypted,
directory: false,
template,
os: None,
permissions: None,
before: None,
after: None,
}
}
fn make_dir_entry(source: &str, target: &str, encrypted: bool) -> Entry {
Entry {
source: source.into(),
target: target.into(),
method: None,
encrypted,
directory: true,
template: false,
os: None,
permissions: None,
before: None,
after: None,
}
}
#[test]
fn template_encrypt_decrypt_roundtrip() {
let temp = tempfile::tempdir().unwrap();
let repo = temp.path().join("repo");
fs::create_dir_all(repo.join("shell")).unwrap();
let key = test_key();
let original = b"# zshrc template\nexport EDITOR={{ var.editor | default \"vim\" }}\n";
fs::write(repo.join("shell/zshrc.dtmpl"), original).unwrap();
let mut entry = make_entry("shell/zshrc.dtmpl", "~/.zshrc", true, false);
encrypt_single_entry(&mut entry, &repo, &key).unwrap();
assert!(entry.encrypted);
let enc = fs::read(repo.join("shell/zshrc.dtmpl")).unwrap();
assert!(dotling::crypto::is_encrypted_content(&enc));
let dec = dotling::crypto::decrypt_with_key(&enc, &key).unwrap();
assert_eq!(dec, original);
decrypt_single_entry(&mut entry, &repo, &key).unwrap();
assert!(!entry.encrypted);
let content = fs::read(repo.join("shell/zshrc.dtmpl")).unwrap();
assert_eq!(
content, original,
"decrypted content must match original template"
);
}
#[test]
fn template_encrypt_decrypt_with_enc_in_source() {
let temp = tempfile::tempdir().unwrap();
let repo = temp.path().join("repo");
fs::create_dir_all(repo.join("shell")).unwrap();
let key = test_key();
let original = b"{{ var.name }}";
let source_path = repo.join("shell/zshrc.dtmpl.enc");
let encrypted = dotling::crypto::encrypt_with_key(original, &key).unwrap();
dotling::fs::atomic_write(&source_path, &encrypted).unwrap();
let mut entry = make_entry("shell/zshrc.dtmpl.enc", "~/.zshrc", true, true);
decrypt_single_entry(&mut entry, &repo, &key).unwrap();
assert!(!entry.encrypted);
assert_eq!(fs::read(&source_path).unwrap(), original);
}
#[test]
fn plain_file_encrypt_decrypt_roundtrip() {
let temp = tempfile::tempdir().unwrap();
let repo = temp.path().join("repo");
fs::create_dir_all(repo.join("shell")).unwrap();
let key = test_key();
let original = b"# .zshrc\nexport PATH=$HOME/bin:$PATH\nalias ll='ls -la'\n";
fs::write(repo.join("shell/zshrc"), original).unwrap();
let mut entry = make_entry("shell/zshrc", "~/.zshrc", false, false);
encrypt_single_entry(&mut entry, &repo, &key).unwrap();
assert!(entry.encrypted);
let enc = fs::read(repo.join("shell/zshrc")).unwrap();
assert!(dotling::crypto::is_encrypted_content(&enc));
decrypt_single_entry(&mut entry, &repo, &key).unwrap();
assert!(!entry.encrypted);
assert_eq!(fs::read(repo.join("shell/zshrc")).unwrap(), original);
}
#[test]
fn directory_encrypt_decrypt_roundtrip() {
let temp = tempfile::tempdir().unwrap();
let repo = temp.path().join("repo");
let dir = repo.join("secrets");
fs::create_dir_all(dir.join("sub")).unwrap();
let key = test_key();
fs::write(dir.join("id_rsa"), b"private-key-data").unwrap();
fs::write(dir.join("id_rsa.pub"), b"public-key-data").unwrap();
fs::write(dir.join("sub/config"), b"ssh config").unwrap();
let mut entry = make_dir_entry("secrets", "~/.ssh", false);
encrypt_single_entry(&mut entry, &repo, &key).unwrap();
assert!(entry.encrypted);
assert!(dotling::crypto::is_encrypted_content(
&fs::read(dir.join("id_rsa")).unwrap()
));
assert!(dotling::crypto::is_encrypted_content(
&fs::read(dir.join("id_rsa.pub")).unwrap()
));
assert!(dotling::crypto::is_encrypted_content(
&fs::read(dir.join("sub/config")).unwrap()
));
decrypt_single_entry(&mut entry, &repo, &key).unwrap();
assert!(!entry.encrypted);
assert_eq!(fs::read(dir.join("id_rsa")).unwrap(), b"private-key-data");
assert_eq!(
fs::read(dir.join("id_rsa.pub")).unwrap(),
b"public-key-data"
);
assert_eq!(fs::read(dir.join("sub/config")).unwrap(), b"ssh config");
}
#[test]
fn double_roundtrip_preserves_content() {
let temp = tempfile::tempdir().unwrap();
let repo = temp.path().join("repo");
fs::create_dir_all(repo.join("git")).unwrap();
let key = test_key();
let original = b"[user]\n name = Test\n email = test@example.com\n";
fs::write(repo.join("git/gitconfig"), original).unwrap();
let mut entry = make_entry("git/gitconfig", "~/.gitconfig", false, false);
encrypt_single_entry(&mut entry, &repo, &key).unwrap();
decrypt_single_entry(&mut entry, &repo, &key).unwrap();
assert_eq!(fs::read(repo.join("git/gitconfig")).unwrap(), original);
encrypt_single_entry(&mut entry, &repo, &key).unwrap();
decrypt_single_entry(&mut entry, &repo, &key).unwrap();
assert_eq!(fs::read(repo.join("git/gitconfig")).unwrap(), original);
}