use std::ffi::OsString;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use second_brain_sync::ssh;
fn osstr(s: &str) -> OsString {
OsString::from(s)
}
#[test]
fn ssh_args_use_strict_checking_and_pinned_known_hosts() {
let known_hosts = PathBuf::from("/tmp/test_known_hosts");
let args = ssh::ssh_args(&known_hosts);
let mut pairs: Vec<(OsString, OsString)> = Vec::new();
let mut iter = args.into_iter();
while let Some(flag) = iter.next() {
assert_eq!(flag, osstr("-o"), "every option entry must be -o prefixed");
let value = iter.next().expect("-o without value");
let value_str = value.to_string_lossy().to_string();
let (k, v) = value_str.split_once('=').expect("option must be KEY=VALUE");
pairs.push((OsString::from(k), OsString::from(v)));
}
let by_key: std::collections::HashMap<_, _> = pairs.into_iter().collect();
assert_eq!(
by_key.get(&osstr("StrictHostKeyChecking")),
Some(&osstr("yes")),
"StrictHostKeyChecking must be yes so a missing/mismatched host key blocks the connection"
);
assert_eq!(
by_key.get(&osstr("UserKnownHostsFile")),
Some(&osstr("/tmp/test_known_hosts")),
"UserKnownHostsFile must point at the sync-owned file"
);
assert_eq!(
by_key.get(&osstr("GlobalKnownHostsFile")),
Some(&osstr("/dev/null")),
"GlobalKnownHostsFile must be /dev/null so /etc/ssh/ssh_known_hosts cannot grant trust"
);
assert!(
by_key.contains_key(&osstr("ServerAliveInterval")),
"ServerAliveInterval keeps long sync streams alive"
);
}
#[test]
fn known_hosts_path_in_config_dir_is_named_known_hosts() {
let config_dir = PathBuf::from("/home/example/.second-brain");
let path = ssh::known_hosts_path_in(&config_dir);
assert_eq!(
path,
PathBuf::from("/home/example/.second-brain/known_hosts")
);
}
#[test]
fn parse_keyscan_output_strips_comments_and_blanks() {
let raw = "# pi.local SSH-2.0-OpenSSH_8.4\n\
pi.local ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleEdKey\n\
\n\
# pi.local SSH-2.0-OpenSSH_8.4\n\
pi.local ssh-rsa AAAAB3NzaC1yc2EAAAExampleRsaKey\n\
# trailing comment\n";
let lines = ssh::parse_keyscan_output(raw);
assert_eq!(lines.len(), 2, "exactly two key lines expected");
assert_eq!(lines[0].host, "pi.local");
assert_eq!(lines[0].key_type, "ssh-ed25519");
assert_eq!(lines[0].public_key, "AAAAC3NzaC1lZDI1NTE5AAAAIExampleEdKey");
assert_eq!(lines[1].host, "pi.local");
assert_eq!(lines[1].key_type, "ssh-rsa");
}
#[test]
fn parse_keyscan_output_rejects_malformed_lines() {
let raw = "pi.local ssh-ed25519\n\
pi.local\n\
pi.local ssh-rsa AAAA validkeyline\n";
let lines = ssh::parse_keyscan_output(raw);
assert_eq!(
lines.len(),
1,
"lines lacking host/type/key must be discarded"
);
}
#[test]
fn compose_known_host_entry_emits_canonical_line() {
let line = ssh::KeyscanLine {
host: "pi.local".to_string(),
key_type: "ssh-ed25519".to_string(),
public_key: "AAAAC3NzaC1lZDI1NTE5AAAAIExampleEdKey".to_string(),
};
let entry = ssh::compose_known_host_entry(&line);
assert_eq!(
entry,
"pi.local ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleEdKey\n"
);
}
fn ed_line(host: &str) -> ssh::KeyscanLine {
ssh::KeyscanLine {
host: host.to_string(),
key_type: "ssh-ed25519".to_string(),
public_key: "AAAAC3NzaC1lZDI1NTE5AAAAIExampleEdKey".to_string(),
}
}
#[test]
fn append_known_host_creates_file_with_0600_mode() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("known_hosts");
ssh::append_known_host(&path, &[ed_line("pi.local")]).unwrap();
let meta = std::fs::metadata(&path).unwrap();
let mode = meta.permissions().mode() & 0o777;
assert_eq!(
mode, 0o600,
"known_hosts must be 0600; secrets-equivalent file"
);
let body = std::fs::read_to_string(&path).unwrap();
assert_eq!(
body,
"pi.local ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleEdKey\n"
);
}
#[test]
fn append_known_host_tightens_permissions_on_existing_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("known_hosts");
std::fs::write(&path, "").unwrap();
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o644)).unwrap();
ssh::append_known_host(&path, &[ed_line("pi.local")]).unwrap();
let mode = std::fs::metadata(&path).unwrap().permissions().mode() & 0o777;
assert_eq!(
mode, 0o600,
"permissive existing file must be tightened on append"
);
}
#[test]
fn append_known_host_is_idempotent() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("known_hosts");
let line = ed_line("pi.local");
ssh::append_known_host(&path, &[line.clone()]).unwrap();
ssh::append_known_host(&path, &[line.clone()]).unwrap();
let body = std::fs::read_to_string(&path).unwrap();
let occurrences = body.matches("ssh-ed25519").count();
assert_eq!(
occurrences, 1,
"appending an identical entry twice must not duplicate it"
);
}
#[test]
fn untrusted_host_hint_names_the_remediation_command() {
let hint = ssh::untrusted_host_hint("pi.local");
assert!(
hint.contains("sb sync trust pi.local"),
"hint must spell out the exact recovery command, got: {hint}"
);
assert!(
hint.contains("known_hosts"),
"hint should reference known_hosts so user knows where trust is stored"
);
}
#[test]
fn append_known_host_keeps_distinct_keys_for_same_host() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("known_hosts");
let ed = ed_line("pi.local");
let rsa = ssh::KeyscanLine {
host: "pi.local".to_string(),
key_type: "ssh-rsa".to_string(),
public_key: "AAAAB3NzaC1yc2EAAAExampleRsaKey".to_string(),
};
ssh::append_known_host(&path, &[ed, rsa]).unwrap();
let body = std::fs::read_to_string(&path).unwrap();
assert!(body.contains("ssh-ed25519"));
assert!(body.contains("ssh-rsa"));
}