use super::{EventPath, base64_decode_standard, base64_standard};
fn decode(text: &str) -> Vec<u8> {
let alphabet: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
assert_eq!(text.len() % 4, 0, "padded base64 comes in groups of four");
let mut bytes: Vec<u8> = Vec::new();
for group in text.as_bytes().chunks(4) {
let mut values: Vec<u32> = Vec::new();
for character in group {
if *character == b'=' {
continue;
}
let position: usize = alphabet
.find(char::from(*character))
.expect("alphabet character");
values.push(position as u32);
}
let mut joined: u32 = 0;
for (index, value) in values.iter().enumerate() {
joined |= value << (18 - 6 * index as u32);
}
let produced: usize = values.len() - 1;
for index in 0..produced {
bytes.push((joined >> (16 - 8 * index as u32)) as u8);
}
}
return bytes;
}
#[test]
fn base64_matches_the_rfc_vectors() {
for (input, expected) in [
("", ""),
("f", "Zg=="),
("fo", "Zm8="),
("foo", "Zm9v"),
("foob", "Zm9vYg=="),
("fooba", "Zm9vYmE="),
("foobar", "Zm9vYmFy"),
] {
assert_eq!(base64_standard(input.as_bytes()), expected, "{input:?}");
}
}
#[test]
fn base64_encodes_the_brief_example() {
assert_eq!(
base64_standard(b"caf\xe9.txt"),
"Y2Fm6S50eHQ=",
"doc/planning/cli-git-rust-open-decisions.md, Option C"
);
}
#[test]
fn base64_round_trips_every_byte_value() {
let every: Vec<u8> = (0..=255).collect();
let encoded: String = base64_standard(every.as_slice());
assert!(encoded.contains('+') && encoded.contains('/'), "{encoded}");
assert_eq!(encoded.len(), 344);
assert_eq!(decode(encoded.as_str()), every);
for length in 0..=7 {
let prefix: &[u8] = &every[250 - length..250];
assert_eq!(decode(base64_standard(prefix).as_str()), prefix);
}
}
#[test]
fn a_utf8_name_has_no_exact_bytes() {
for name in ["a.txt", "doc/café.md", "\u{1}tab\there.md", "😀/x", ""] {
let path: EventPath = EventPath::from_git_bytes(name.as_bytes());
assert_eq!((path.text(), path.exact()), (name, None), "{name:?}");
assert_eq!(path.bytes(), name.as_bytes());
}
}
#[test]
fn a_name_that_is_not_utf8_keeps_its_bytes() {
let cases: [(&[u8], &str); 7] = [
(b"caf\xe9.txt", "caf\u{fffd}.txt"),
(b"\xff\xfe\xfd", "\u{fffd}\u{fffd}\u{fffd}"),
("é".as_bytes(), "é"),
(b"\xc3\xa9\xff/x.md", "é\u{fffd}/x.md"),
(b"a\xe2\x82", "a\u{fffd}"),
(b"\xc0\xaf", "\u{fffd}\u{fffd}"),
(b"\xed\xa0\x80.md", "\u{fffd}\u{fffd}\u{fffd}.md"),
];
for (bytes, text) in cases {
let path: EventPath = EventPath::from_git_bytes(bytes);
assert_eq!(path.text(), text, "{bytes:?}");
let valid: bool = std::str::from_utf8(bytes).is_ok();
if valid {
assert_eq!(path.exact(), None, "{bytes:?}");
} else {
assert_eq!(path.exact(), Some(bytes), "{bytes:?}");
}
assert_eq!(path.bytes(), bytes);
}
}
#[test]
fn names_that_read_alike_stay_apart() {
let every: Vec<u8> = (1..=255).collect();
let path: EventPath = EventPath::from_git_bytes(every.as_slice());
assert_eq!(path.exact(), Some(every.as_slice()));
assert_eq!(decode(base64_standard(path.bytes()).as_str()), every);
let first: EventPath = EventPath::from_git_bytes(b"caf\xe9.txt");
let second: EventPath = EventPath::from_git_bytes(b"caf\xe8.txt");
assert_eq!(first.text(), second.text());
assert_ne!(first, second);
}
#[test]
fn a_display_path_never_has_bytes() {
let path: EventPath = EventPath::display("folder/\\xffname");
assert_eq!((path.text(), path.exact()), ("folder/\\xffname", None));
assert_eq!(path.bytes(), b"folder/\\xffname");
}
#[test]
fn base64_decoding_matches_the_rfc_vectors() {
for (expected, encoded) in [
("", ""),
("f", "Zg=="),
("fo", "Zm8="),
("foo", "Zm9v"),
("foob", "Zm9vYg=="),
("fooba", "Zm9vYmE="),
("foobar", "Zm9vYmFy"),
] {
assert_eq!(base64_decode_standard(encoded), Some(expected.as_bytes().to_vec()), "{encoded}");
}
}
#[test]
fn base64_decoding_round_trips_every_byte_value() {
let every: Vec<u8> = (0..=255).collect();
assert_eq!(base64_decode_standard(base64_standard(every.as_slice()).as_str()), Some(every.clone()));
for length in 0..=7 {
let prefix: &[u8] = &every[250 - length..250];
assert_eq!(base64_decode_standard(base64_standard(prefix).as_str()), Some(prefix.to_vec()), "{length}");
}
}
#[test]
fn base64_decoding_refuses_what_the_encoder_never_prints() {
for text in [
"Zg=",
"Zg===",
"Zm9v!A==",
"Zm9-",
"Z===",
"====",
"Zg==Zm9v",
"Z=g=",
"A=BC",
"Zh==",
"Zm9=",
"Zg ==",
"Zg\n==",
"Zm9v\n",
] {
assert_eq!(base64_decode_standard(text), None, "{text:?}");
}
}