Integration tests for routing, filesystem, upgrades, rollback, cargo, and paths

This commit is contained in:
2026-10-15 11:24:07 +01:00
parent 7f76eab161
commit bfd9339d25
9 changed files with 1098 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
use std::collections::HashMap;
use std::io::{self, BufRead, Write};
use std::time::Duration;
use serde_json::{Value, json};
fn reply(out: &mut impl Write, message: &Value, body: Value) {
writeln!(
out,
"{}",
json!({"to": message["from"], "reply_to": message["id"], "body": body})
)
.unwrap();
out.flush().unwrap();
}
fn io_error(error: io::Error) -> Value {
json!({"type": "error", "error": error.to_string()})
}
fn main() {
let actor = std::env::var("SAUSAGE_ACTOR").unwrap_or_default();
let version = std::env::var("SAUSAGE_VERSION").unwrap_or_else(|_| "candidate".to_string());
if actor == "crashloop" && version == "2" {
std::fs::write("/data/state", "bad-v2").unwrap();
panic!("version 2 crashes on startup");
}
let mut open = HashMap::<String, std::fs::File>::new();
let stdin = io::stdin();
let mut out = io::stdout().lock();
for line in stdin.lock().lines() {
let Ok(line) = line else { break };
let Ok(message) = serde_json::from_str::<Value>(&line) else {
continue;
};
let body = &message["body"];
let path = body["path"].as_str().unwrap_or_default().to_string();
let text = body["text"].as_str().unwrap_or_default();
let response = match body["type"].as_str() {
Some("shutdown") => {
if actor == "queue" {
let _ = std::fs::write("/data/shutdown", "stopping");
std::thread::sleep(Duration::from_millis(800));
}
break;
}
Some("echo") => json!({"type": "echo", "value": body["value"]}),
Some("publish") => {
writeln!(
out,
"{}",
json!({"body": {"type": "published", "value": body["value"]}})
)
.unwrap();
out.flush().unwrap();
continue;
}
Some("version") => json!({"type": "version", "version": version}),
Some("ordered") => json!({"type": "ordered", "n": body["n"], "version": version}),
Some("write") => match std::fs::write(&path, text) {
Ok(()) => json!({"type": "ok"}),
Err(error) => io_error(error),
},
Some("open") => {
let result = (|| -> io::Result<()> {
let mut file = std::fs::File::create(&path)?;
file.write_all(text.as_bytes())?;
open.insert(path.clone(), file);
Ok(())
})();
match result {
Ok(()) => json!({"type": "opened"}),
Err(error) => io_error(error),
}
}
Some("append") => {
let result = (|| -> io::Result<()> {
let mut file = std::fs::OpenOptions::new().append(true).open(&path)?;
file.write_all(text.as_bytes())?;
open.insert(path.clone(), file);
Ok(())
})();
match result {
Ok(()) => json!({"type": "opened"}),
Err(error) => io_error(error),
}
}
Some("close") => json!({"type": "closed", "closed": open.remove(&path).is_some()}),
Some("read") => match std::fs::read_to_string(&path) {
Ok(text) => json!({"type": "content", "text": text}),
Err(error) => io_error(error),
},
Some("list") => match std::fs::read_dir(&path) {
Ok(entries) => {
let mut names: Vec<String> = entries
.filter_map(|entry| entry.ok())
.map(|entry| entry.file_name().to_string_lossy().to_string())
.collect();
names.sort();
json!({"type": "entries", "names": names})
}
Err(error) => io_error(error),
},
Some("crash") if version == "1" => panic!("asked to crash"),
Some("crash") => json!({"type": "replayed", "version": version}),
_ => json!({"type": "error", "error": "unknown message"}),
};
reply(&mut out, &message, response);
}
}