123 lines
4.8 KiB
Rust
123 lines
4.8 KiB
Rust
//! The shipped seed: where it is found, what it serves, and that a tampered file is never used.
|
|
|
|
mod addons_support;
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use addons_support::{TINY, entry, index};
|
|
use notescraft_engine::addons::{Ctx, FakeTransport, FakeVerifier, Kind, Seed, SeedDir, Source, Store, Version, install, key, stored_file};
|
|
|
|
const GRAMMAR: &str = "twig.sublime-syntax";
|
|
const V1: Version = Version { major: 1, minor: 0, patch: 0 };
|
|
|
|
struct Temp(PathBuf);
|
|
|
|
impl Temp {
|
|
fn new(name: &str) -> Self {
|
|
let dir = std::env::temp_dir().join(format!("notescraft-addons-seed-{}-{name}", std::process::id()));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
Self(dir)
|
|
}
|
|
|
|
fn path(&self) -> &Path {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl Drop for Temp {
|
|
fn drop(&mut self) {
|
|
let _ = std::fs::remove_dir_all(&self.0);
|
|
}
|
|
}
|
|
|
|
fn put(seed: &Path, rel: &str, bytes: &[u8]) {
|
|
let target = seed.join(rel);
|
|
std::fs::create_dir_all(target.parent().unwrap()).unwrap();
|
|
std::fs::write(target, bytes).unwrap();
|
|
}
|
|
|
|
fn put_grammar(seed: &Path, bytes: &[u8]) {
|
|
put(seed, &format!("files/grammar/twig/1.0.0/{GRAMMAR}"), bytes);
|
|
}
|
|
|
|
const OK: FakeVerifier = FakeVerifier { accept: true };
|
|
|
|
fn ctx<'a>(store: &'a Store, transport: &'a FakeTransport, seed: &'a SeedDir) -> Ctx<'a> {
|
|
Ctx { store, transport, verifier: &OK, app_version: Version { major: 0, minor: 5, patch: 0 }, seed: Some(seed) }
|
|
}
|
|
|
|
#[test]
|
|
fn locate_finds_macos_bundle_resources() {
|
|
let tmp = Temp::new("mac");
|
|
let seed = tmp.path().join("NotesCraft.app/Contents/Resources/registry-seed");
|
|
std::fs::create_dir_all(&seed).unwrap();
|
|
let exe = tmp.path().join("NotesCraft.app/Contents/MacOS/notescraft");
|
|
assert_eq!(SeedDir::locate_from(&exe).unwrap().root(), seed);
|
|
}
|
|
|
|
#[test]
|
|
fn locate_finds_seed_beside_the_exe() {
|
|
let tmp = Temp::new("beside");
|
|
std::fs::create_dir_all(tmp.path().join("registry-seed")).unwrap();
|
|
assert_eq!(SeedDir::locate_from(&tmp.path().join("notescraft.exe")).unwrap().root(), tmp.path().join("registry-seed"));
|
|
}
|
|
|
|
#[test]
|
|
fn missing_seed_is_fine() {
|
|
let tmp = Temp::new("none");
|
|
assert!(SeedDir::locate_from(&tmp.path().join("notescraft")).is_none());
|
|
assert!(SeedDir::new(tmp.path().join("nope")).read(Kind::Grammar, "twig", V1, GRAMMAR).is_none());
|
|
assert!(SeedDir::new(tmp.path().join("nope")).index_bytes().is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn reads_files_and_the_index_and_refuses_hostile_paths() {
|
|
let tmp = Temp::new("read");
|
|
put_grammar(tmp.path(), TINY.as_bytes());
|
|
put(tmp.path(), "index.json", b"{}");
|
|
put(tmp.path(), "secret.txt", b"x");
|
|
let seed = SeedDir::new(tmp.path().to_path_buf());
|
|
assert_eq!(seed.read(Kind::Grammar, "twig", V1, GRAMMAR).unwrap(), TINY.as_bytes());
|
|
assert_eq!(seed.index_bytes().unwrap(), b"{}");
|
|
assert!(seed.read(Kind::Grammar, "twig", V1, "../../../secret.txt").is_none());
|
|
assert!(seed.read(Kind::Grammar, "../twig", V1, GRAMMAR).is_none());
|
|
assert!(seed.read(Kind::Grammar, "twig", Version { major: 2, minor: 0, patch: 0 }, GRAMMAR).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn installs_offline_from_the_seed() {
|
|
let tmp = Temp::new("offline");
|
|
let seed_root = tmp.path().join("seed");
|
|
put_grammar(&seed_root, TINY.as_bytes());
|
|
let store = Store::open(&tmp.path().join("data")).unwrap();
|
|
let idx = index(&[entry("grammar", "twig", "1.0.0", &[(GRAMMAR, TINY.as_bytes())], &[])], &[]);
|
|
let seed = SeedDir::new(seed_root);
|
|
let report = install(&ctx(&store, &FakeTransport::new(), &seed), &idx, Kind::Grammar, "twig", None).unwrap();
|
|
assert_eq!(report.source, Source::Seed);
|
|
assert!(store.load().unwrap().installed.contains_key(&key(Kind::Grammar, "twig")));
|
|
assert_eq!(stored_file(tmp.path().join("data").as_path(), Kind::Grammar, "twig", GRAMMAR).unwrap(), TINY.as_bytes());
|
|
}
|
|
|
|
#[test]
|
|
fn tampered_seed_file_is_rejected() {
|
|
let tmp = Temp::new("tamper");
|
|
let seed_root = tmp.path().join("seed");
|
|
put_grammar(&seed_root, b"tampered bytes");
|
|
let store = Store::open(&tmp.path().join("data")).unwrap();
|
|
let idx = index(&[entry("grammar", "twig", "1.0.0", &[(GRAMMAR, TINY.as_bytes())], &[])], &[]);
|
|
let seed = SeedDir::new(seed_root);
|
|
assert!(install(&ctx(&store, &FakeTransport::new(), &seed), &idx, Kind::Grammar, "twig", None).is_err());
|
|
assert!(store.load().unwrap().installed.is_empty());
|
|
let served = addons_support::serve(FakeTransport::new(), "grammar", "twig", "1.0.0", &[(GRAMMAR, TINY.as_bytes())]);
|
|
let report = install(&ctx(&store, &served, &seed), &idx, Kind::Grammar, "twig", None).unwrap();
|
|
assert_eq!(report.source, Source::Registry);
|
|
}
|
|
|
|
#[test]
|
|
fn stored_file_is_none_when_nothing_is_installed() {
|
|
let tmp = Temp::new("empty");
|
|
assert!(stored_file(tmp.path(), Kind::Font, "stix-math", "x.ttf").is_none());
|
|
assert!(!tmp.path().join("registry").exists());
|
|
}
|