- 1
//! The managed install lifecycle, driven as a real user drives it: by - 2
//! running the `vak` binary, not by calling into its modules. - 3
//! - 4
//! Everything here is about a property the unit tests structurally cannot - 5
//! reach — what happens when the binary performing an operation is *itself* - 6
//! inside the prefix that operation rewrites. A module-level test always - 7
//! runs from `target/debug/deps/`, which is never the install root, so the - 8
//! interesting case never occurred. - 9
//! - 10
//! It is not a hypothetical. `self reinstall`, run from the installed CLI — - 11
//! which is the `vak` on a person's PATH, and therefore the overwhelmingly - 12
//! common invocation — used to clear the prefix, fail to find the source it - 13
//! had just deleted, and leave the install EMPTY. The repair command - 14
//! destroyed the thing it repairs. - 15
- 16
#![allow(clippy::expect_used, clippy::unwrap_used)] - 17
- 18
use std::path::{Path, PathBuf}; - 19
use std::process::Command; - 20
- 21
fn build_cli() -> PathBuf { - 22
PathBuf::from(env!("CARGO_BIN_EXE_vak")) - 23
} - 24
- 25
/// Run a `vak self …` subcommand and return (exit code, stdout+stderr). - 26
fn run(cli: &Path, args: &[&str]) -> (i32, String) { - 27
let out = Command::new(cli) - 28
.args(args) - 29
.output() - 30
.expect("run the vak binary"); - 31
let mut text = String::from_utf8_lossy(&out.stdout).into_owned(); - 32
text.push_str(&String::from_utf8_lossy(&out.stderr)); - 33
(out.status.code().unwrap_or(-1), text) - 34
} - 35
- 36
fn install_to(prefix: &Path) { - 37
let (code, text) = run( - 38
&build_cli(), - 39
&["self", "install", "--prefix", &prefix.to_string_lossy()], - 40
); - 41
assert_eq!(code, 0, "install failed: {text}"); - 42
} - 43
- 44
/// The regression. Reinstalling from the installed binary must leave a - 45
/// working install, not a hole where one used to be. - 46
#[test] - 47
fn reinstall_from_the_installed_binary_leaves_a_working_install() { - 48
let dir = tempfile::tempdir().unwrap(); - 49
let prefix = dir.path().join("prefix"); - 50
install_to(&prefix); - 51
- 52
let installed = prefix.join("bin").join("vak"); - 53
assert!(installed.exists(), "install placed no CLI"); - 54
- 55
let (code, text) = run( - 56
&installed, - 57
&[ - 58
"self", - 59
"reinstall", - 60
"--prefix", - 61
&prefix.to_string_lossy(), - 62
"--yes", - 63
], - 64
); - 65
assert_eq!( - 66
code, 0, - 67
"reinstall from the installed binary failed: {text}" - 68
); - 69
- 70
assert!( - 71
installed.exists(), - 72
"reinstall deleted the CLI and did not put it back — the prefix was \ - 73
cleared while the binary being run was inside it" - 74
); - 75
let (code, text) = run(&installed, &["--version"]); - 76
assert_eq!(code, 0, "the reinstalled CLI does not run: {text}"); - 77
- 78
let (code, text) = run( - 79
&installed, - 80
&["self", "verify", "--prefix", &prefix.to_string_lossy()], - 81
); - 82
assert_eq!(code, 0, "the reinstalled install does not verify: {text}"); - 83
} - 84
- 85
/// Verify has to see through to the filesystem, not just re-read what the - 86
/// manifest says about itself. - 87
#[test] - 88
fn verify_catches_a_component_that_was_tampered_with() { - 89
let dir = tempfile::tempdir().unwrap(); - 90
let prefix = dir.path().join("prefix"); - 91
install_to(&prefix); - 92
- 93
let victim = prefix.join("bin").join("vak-delivery-worker"); - 94
if !victim.exists() { - 95
// Optional component; nothing to prove on a build without it. - 96
return; - 97
} - 98
let mut bytes = std::fs::read(&victim).unwrap(); - 99
bytes.push(0); - 100
std::fs::write(&victim, bytes).unwrap(); - 101
- 102
let (code, text) = run( - 103
&build_cli(), - 104
&["self", "verify", "--prefix", &prefix.to_string_lossy()], - 105
); - 106
assert_ne!(code, 0, "verify passed on a tampered component: {text}"); - 107
assert!( - 108
text.contains("vak-delivery-worker"), - 109
"verify must name the component it found wrong, got: {text}" - 110
); - 111
} - 112
- 113
/// Uninstall is the reverse of install: the prefix goes, the data home - 114
/// stays. `--purge` is the only thing that touches data, and is not - 115
/// exercised here for the obvious reason. - 116
#[test] - 117
fn uninstall_removes_the_prefix_and_leaves_nothing_behind() { - 118
let dir = tempfile::tempdir().unwrap(); - 119
let prefix = dir.path().join("prefix"); - 120
install_to(&prefix); - 121
assert!(prefix.join("bin").join("vak").exists()); - 122
- 123
let (code, text) = run( - 124
&build_cli(), - 125
&[ - 126
"self", - 127
"uninstall", - 128
"--prefix", - 129
&prefix.to_string_lossy(), - 130
"--yes", - 131
], - 132
); - 133
assert_eq!(code, 0, "uninstall failed: {text}"); - 134
assert!( - 135
!prefix.exists() || std::fs::read_dir(&prefix).unwrap().next().is_none(), - 136
"uninstall left files under {}", - 137
prefix.display() - 138
); - 139
assert!( - 140
text.contains("kept"), - 141
"uninstall must say that configuration and data were kept, got: {text}" - 142
); - 143
} - 144
- 145
/// A fresh install verifies clean. If this ever fails, every other - 146
/// assertion in this file is measuring the wrong baseline. - 147
#[test] - 148
fn a_fresh_install_verifies_clean() { - 149
let dir = tempfile::tempdir().unwrap(); - 150
let prefix = dir.path().join("prefix"); - 151
install_to(&prefix); - 152
- 153
let (code, text) = run( - 154
&build_cli(), - 155
&["self", "verify", "--prefix", &prefix.to_string_lossy()], - 156
); - 157
assert_eq!(code, 0, "a fresh install does not verify: {text}"); - 158
assert!(text.contains("verifies clean"), "unexpected output: {text}"); - 159
} - 160
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.