Implement credential-safe programs smoke gate (#97)
Some checks failed
Native code generation / deterministic (push) Failing after 2m12s
Imaging and meshing gate / native (push) Failing after 5m40s
JPEG 2000 feature / linux (push) Successful in 2m50s
Native Rust workspace compile / compile (push) Failing after 56s
Skia feature / linux (push) Successful in 31m24s
Some checks failed
Native code generation / deterministic (push) Failing after 2m12s
Imaging and meshing gate / native (push) Failing after 5m40s
JPEG 2000 feature / linux (push) Successful in 2m50s
Native Rust workspace compile / compile (push) Failing after 56s
Skia feature / linux (push) Successful in 31m24s
This commit is contained in:
178
programs/tests/live_grid_smoke_cli.rs
Normal file
178
programs/tests/live_grid_smoke_cli.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use serde_json::Value;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Output, Stdio};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
const EXIT_USAGE: i32 = 2;
|
||||
const EXIT_INPUT: i32 = 3;
|
||||
static TEMP_ID: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
struct TestDir(PathBuf);
|
||||
|
||||
impl TestDir {
|
||||
fn new() -> Self {
|
||||
let id = TEMP_ID.fetch_add(1, Ordering::Relaxed);
|
||||
let path = std::env::temp_dir().join(format!(
|
||||
"metacrate-live-grid-smoke-{}-{id}",
|
||||
std::process::id()
|
||||
));
|
||||
fs::create_dir(&path).expect("create smoke test directory");
|
||||
Self(path)
|
||||
}
|
||||
|
||||
fn path(&self, name: &str) -> PathBuf {
|
||||
self.0.join(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestDir {
|
||||
fn drop(&mut self) {
|
||||
let _ = fs::remove_dir_all(&self.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn text(path: &Path) -> &str {
|
||||
path.to_str().expect("UTF-8 test path")
|
||||
}
|
||||
|
||||
fn run(args: &[&str]) -> Output {
|
||||
Command::new(env!("CARGO_BIN_EXE_live-grid-smoke"))
|
||||
.args(args)
|
||||
.env_remove("GRID_USER")
|
||||
.env_remove("GRID_PASSWORD")
|
||||
.env_remove("GRID_LOGIN_URL")
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.output()
|
||||
.expect("run live-grid-smoke")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn help_documents_credentials_side_effect_gates_and_unsupported_dangerous_actions() {
|
||||
let output = run(&["--help"]);
|
||||
assert!(output.status.success());
|
||||
let help = String::from_utf8(output.stdout).unwrap();
|
||||
for marker in [
|
||||
"GRID_USER",
|
||||
"workspace .env",
|
||||
"OpenSim/compatible-grid LLSD login endpoint",
|
||||
"--fake",
|
||||
"--audit-only",
|
||||
"--evidence",
|
||||
"--allow-live-login",
|
||||
"--allow-public-chat",
|
||||
"--allow-agent-movement",
|
||||
"--allow-reversible-inventory",
|
||||
"never spends L$",
|
||||
"permanently deletes",
|
||||
"targets another user",
|
||||
] {
|
||||
assert!(help.contains(marker), "help omitted {marker}:\n{help}");
|
||||
}
|
||||
assert_eq!(run(&[]).status.code(), Some(EXIT_USAGE));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fake_smoke_records_every_sanitized_stage_without_skips_or_credentials() {
|
||||
let directory = TestDir::new();
|
||||
let evidence = directory.path("evidence.jsonl");
|
||||
let output = run(&["--fake", "--evidence", text(&evidence)]);
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
let stderr = String::from_utf8(output.stderr).unwrap();
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"stdout:\n{stdout}\nstderr:\n{stderr}"
|
||||
);
|
||||
assert!(stderr.is_empty(), "{stderr}");
|
||||
let contents = fs::read_to_string(evidence).unwrap();
|
||||
assert!(!contents.contains("://"));
|
||||
assert!(!contents.to_ascii_lowercase().contains("password"));
|
||||
assert!(!contents.to_ascii_lowercase().contains("token"));
|
||||
let records: Vec<Value> = contents
|
||||
.lines()
|
||||
.map(|line| serde_json::from_str(line).unwrap())
|
||||
.collect();
|
||||
assert_eq!(records.len(), 9);
|
||||
let stages: Vec<_> = records
|
||||
.iter()
|
||||
.map(|record| record["stage"].as_str().unwrap())
|
||||
.collect();
|
||||
assert_eq!(
|
||||
stages,
|
||||
[
|
||||
"completeness",
|
||||
"login",
|
||||
"capabilities-simulator",
|
||||
"im-chat",
|
||||
"movement-teleport",
|
||||
"inventory-folder",
|
||||
"object-properties",
|
||||
"asset-texture",
|
||||
"logout",
|
||||
]
|
||||
);
|
||||
assert!(records.iter().all(|record| record["status"] == "ok"));
|
||||
assert!(records.iter().all(|record| record["schema"] == 1));
|
||||
assert!(
|
||||
records
|
||||
.iter()
|
||||
.all(|record| record["recorded_unix_seconds"].as_u64().is_some())
|
||||
);
|
||||
assert!(
|
||||
records
|
||||
.iter()
|
||||
.all(|record| record["program_version"].as_str().is_some())
|
||||
);
|
||||
assert!(
|
||||
records
|
||||
.iter()
|
||||
.all(|record| record["rust_commit"].as_str().is_some())
|
||||
);
|
||||
assert_eq!(records[0]["metrics"]["programs"], 9);
|
||||
assert_eq!(records[0]["metrics"]["pending_programs"], 0);
|
||||
assert_eq!(records[0]["metrics"]["pending_commands"], 0);
|
||||
assert_eq!(records[8]["metrics"]["active_tasks"], 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn audit_proves_all_programs_and_commands_without_credentials_or_evidence() {
|
||||
let output = run(&["--audit-only"]);
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
assert!(stdout.contains("programs=9"), "{stdout}");
|
||||
assert!(stdout.contains("pending-programs=0"), "{stdout}");
|
||||
assert!(stdout.contains("pending-commands=0"), "{stdout}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn live_mode_requires_every_literal_confirmation_before_loading_credentials() {
|
||||
let directory = TestDir::new();
|
||||
let evidence = directory.path("evidence.jsonl");
|
||||
let output = run(&[
|
||||
"--evidence",
|
||||
text(&evidence),
|
||||
"--allow-live-login",
|
||||
"--confirm-live-login",
|
||||
"LOGIN",
|
||||
]);
|
||||
assert_eq!(output.status.code(), Some(EXIT_USAGE));
|
||||
let stderr = String::from_utf8(output.stderr).unwrap();
|
||||
assert!(stderr.contains("--allow-public-chat"), "{stderr}");
|
||||
assert!(!evidence.exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn evidence_is_create_new_and_never_overwrites_a_previous_audit() {
|
||||
let directory = TestDir::new();
|
||||
let evidence = directory.path("evidence.jsonl");
|
||||
fs::write(&evidence, "preserve-me\n").unwrap();
|
||||
let output = run(&["--fake", "--evidence", text(&evidence)]);
|
||||
assert_eq!(output.status.code(), Some(EXIT_INPUT));
|
||||
assert_eq!(fs::read_to_string(evidence).unwrap(), "preserve-me\n");
|
||||
}
|
||||
@@ -54,7 +54,7 @@ fn run(args: &[&str]) -> Output {
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn full_offline_session_exercises_every_remaining_non_voice_command() {
|
||||
fn full_offline_session_exercises_every_remaining_service_and_voice_command() {
|
||||
let directory = TestDir::new();
|
||||
let script = directory.write(&format!(
|
||||
"!client\t{CLIENT}\tAlice\tBot\n\
|
||||
@@ -106,6 +106,8 @@ fn full_offline_session_exercises_every_remaining_non_voice_command() {
|
||||
invitegroup {AVATAR} {GROUP} {ROLE} --confirm\n\
|
||||
joingroup Builders Guild --confirm\n\
|
||||
leavegroup Builders Guild --confirm\n\
|
||||
voiceparcel\n\
|
||||
voiceaccount\n\
|
||||
quit\n"
|
||||
));
|
||||
let output = run(&[
|
||||
@@ -154,6 +156,8 @@ fn full_offline_session_exercises_every_remaining_non_voice_command() {
|
||||
"invited",
|
||||
"Joined the group Builders Guild",
|
||||
"has left the group Builders Guild",
|
||||
"Parcel voice info: region-name-present=true, parcel-local-id=42, channel=<redacted>",
|
||||
"Voice account provisioned: credentials=<redacted>",
|
||||
"CALL profile-clone",
|
||||
"CALL generic-message",
|
||||
"CALL animation-start",
|
||||
@@ -162,6 +166,8 @@ fn full_offline_session_exercises_every_remaining_non_voice_command() {
|
||||
"CALL group-invite",
|
||||
"CALL group-join",
|
||||
"CALL group-leave",
|
||||
"CALL voice-parcel-info",
|
||||
"CALL voice-provision-account credentials=<redacted>",
|
||||
] {
|
||||
assert!(stdout.contains(expected), "missing {expected:?}:\n{stdout}");
|
||||
}
|
||||
@@ -207,9 +213,6 @@ fn social_mutations_require_startup_and_per_command_confirmation() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_inventory_leaves_only_explicit_voice_adapters() {
|
||||
assert_eq!(
|
||||
libremetaverse_programs::test_client::pending_test_client_commands(),
|
||||
["ParcelVoiceInfo", "VoiceAcountCommand"]
|
||||
);
|
||||
fn command_inventory_has_no_pending_source_commands() {
|
||||
assert!(libremetaverse_programs::test_client::pending_test_client_commands().is_empty());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user